Improve AI difficulty scaling.

This commit is contained in:
Greyson Parrelli
2026-07-24 23:03:02 -04:00
parent 5a396e6efe
commit f3783b44bf
4 changed files with 188 additions and 1 deletions
+29
View File
@@ -149,6 +149,17 @@ func (b *Bot) pick(cands []candidate) candidate {
if len(cands) == 1 {
return cands[0]
}
// Below competent play, the bot sometimes ignores its evaluation entirely
// and plays a random legal move. Softmax temperature alone can't make a bot
// a genuine pushover: it still samples among sensibly-generated candidates
// and, on this game's shop/dice variance, that wins ~1 game in 5 even
// against expert play. Real, occasional mistakes — buying the wrong pet,
// passing with coins in hand, a scrambled battle order — are what let a
// competent human beat "easy" almost every time. The rate is zero at and
// above competent level, so medium and hard never blunder.
if p := blunderProb(b.level); p > 0 && rand.Float64() < p {
return cands[rand.IntN(len(cands))]
}
temp := 0.02 + 0.30*(1-b.level)
best := math.Inf(-1)
for _, c := range cands {
@@ -170,6 +181,24 @@ func (b *Bot) pick(cands []candidate) candidate {
return cands[len(cands)-1]
}
// competentLevel is the skill (on the [0,1] level scale) at which the bot is
// meant to be an even match for a competent human: at or above it the bot
// never throws a game on purpose. Difficulties are calibrated around it —
// easy well below, medium at it, hard above.
const competentLevel = 0.6
// blunderProb is the chance pick discards its evaluation and plays a uniformly
// random legal move. It is zero at competent level and above, and ramps up
// steeply below, so only the easy tier makes real mistakes. blunderMax is the
// rate at level 0 (the weakest possible bot).
func blunderProb(level float64) float64 {
const blunderMax = 0.75
if level >= competentLevel {
return 0
}
return blunderMax * (competentLevel - level) / competentLevel
}
// budget returns the rollout counts for this difficulty: how many opponent
// deck/order guesses to test against, and how many dice-randomized battle
// simulations to run per guess. Fewer samples means noisier estimates, which