Agents & AI

From a hand-tuned heuristic to a neural network that thinks before it plays — and above all the measurement that says which one wins, and by how much. Every number is a paired gap: at equal game index, the agents get the same dice on the same grid. Play against them →

The ranking

Head-to-head games, seats alternated, on seeds never used to tune or train the agents. Ties (2 to 5 % of games) are shared. deni-0.8 is the reference opponent: it is the best heuristic, and like the networks it looks at what a move leaves the opponent.

agentideavs v3-multivs deni-0.8time / movein the game
searchnetwork + simulating what follows each move84.0%*74.8%~0.5 snot yet
network + denialnetwork, minus what the dice left behind give the opponent73.4%55.4%a few msnot yet
networkvalue network, plays for the margin63.7%49.3%1.6 msnot yet
deni-0.8v3 heuristic + dice denial63.6%—1.2 msnot yet
v3-multiv3 heuristic tuned at a 4-player table—36.4%0.18 msyes
search
74.8%
network + denial
55.4%
network
49.3%
v3-multi
36.4%

wins against deni-0.8 head to head — 50% = even

* Overstated: the search simulates its opponent as v3-multi, so it knew exactly how that opponent would answer (see below).

The game currently plays v3-multi, weakened more or less depending on the difficulty level. The first three agents are measured but not wired in yet: the search takes half a second per move and will have to run off the main thread so the interface does not freeze.

The protocol mattered more than the algorithm

The agents were first tuned single-player: one sheet, no opponent, a 50-turn cap as an anti-loop safeguard. That framing ranks the agents upside down at the top.

single-player
hoarding agent38.88
the same, not allowed to pass~36
→
at a 4-player table
hoarding agent15.11
the same, not allowed to pass20.73

Single-player does not punish stalling, since the agent alone decides when the game ends. So it had learnt to hoard its 8 exclamation points — +8 points guaranteed at the count — and to pass 12 times a game. At a real table, the others finish the game for it.

Three things only exist in the multiplayer simulator: dice denial (the active player sets their pair aside, the passive players only get the remaining 4), an ending decided by someone else, and the first / others bonuses that reward speed. Everything below is measured there.

The neural network: five corrections

A value network: it scores the sheet obtained after each possible move, and plays the best-scored one. 735 sparse inputs (checked squares by colour, stars, the opponents' squares) and 61 computed inputs, written in TypeScript in one single place; Python only trains on those bytes, so the game and the training cannot drift apart. A test checks that PyTorch and TypeScript compute the same thing. It lost at first — each step fixes what the previous one measured.

  1. 1
    Predict the final score−7.67 points

    Trained on the outcome of simulated games, it predicted the score well and played badly: it had never seen two moves from the same position, and learnt the price of an exclamation point by comparing different games.

  2. 2
    Compare the moves of one position−2.42 points

    Each candidate is played out 8 times to the end with the same dice, by v3-multi, and the network learns the gaps between moves. Better, but still beaten — and the diagnosis below shows why.

  3. 3
    The network plays the rest itself+0.7 point

    Same simulations, but the network keeps its seat until the end: the data says what a move is worth to it. The first network ahead of v3-multi at a 4-player table — and yet 45% head to head.

  4. 4
    Play for the margin, not for your points45% → 58%

    The network has two outputs: its score, and its margin over the best opponent. Same weights, a different output played. Aiming for points makes a slow player who gets cut off; aiming for the margin makes it race: it ends 62% of games instead of 31%.

  5. 5
    Games of 2 to 4 players63.7%

    All the data came from 4-player tables, and the number of opponents is a network input: head to head, it was extrapolating. New data at 2, 3 and 4 players: it beats v3-multi at every table size (43.0% per seat at 3, 33.5% at 4).

same positions, same movesrest played bynetwork move − v3 move
the network's gamesv3+0.10 [−0.10, +0.30]
the network's gamesthe network−0.55 [−0.81, −0.30]

The diagnosis of step 2, and the most useful lesson of the project. Its moves were as good as v3's if v3 played the rest — exactly what its training data measured, since every simulation handed control back to v3. Played by itself, they were worth less. It was valuing positions for a player better than itself. Two move-by-move checks under v3 had come out null before this one showed it.

And the caveat of step 5: deni-0.8, a heuristic that also looks at the opponent, beats v3-multi almost as much (63.6%). Most of the gain came from the objective — playing for the margin — more than from the network itself.

Dice denial

As the active player, a choice has two effects: what it brings you, and what it takes from the others, who only get the 4 remaining dice. That requires enumerating moves per dice pair: the normal generator merges pairs that are equivalent for you — in more than 8 turns out of 10 — and throws away exactly the information that matters to the others.

heuristic, 4 playerswinspaired gap
weight 019.7%—
weight 0.623.8%+1.12 [+0.67, +1.56]
weight 0.826.9%+1.69 [+1.24, +2.13]
weight 1.129.6%+1.70 [+1.23, +2.16]
weight 1.627.1%+1.27 [+0.81, +1.73]
network, head to headvs v3-multipaired gap
weight 063.7%+3.24 [+2.78, +3.70]
weight 0.571.5%+5.53 [+5.08, +5.99]
weight 173.4%+6.00 [+5.53, +6.46]
weight 266.6%+4.30 [+3.82, +4.78]

The optimum is interior in both cases: denying too much hurts too. For the network, what the remaining dice give the opponent is valued by the same network from their seat, so in the same unit — and with 2 players, a weight of 1 is exactly the logic of the margin: what they gain, I lose. The gain shrinks with more players (46.1% per seat at 3 players, 34.5% at 4, against 43.0 and 33.5 without denial).

Useless once search is added: over the same 400 games, the gap is −0.98 point [−2.29, +0.33]. The search already simulates what the opponent does with the dice left; adding the denial price counts it twice.

Thinking before playing: search

The game allows up to 3 seconds per move; the network takes 1.6 ms. The search keeps the network's 4 best moves, then plays out each one 16 times, 4 turns ahead — the network in its seat, v3-multi for the opponent, the rest of the current turn included — and lets the network estimate the final margin of the position reached. The 4 moves see the same dice, otherwise chance would drown the difference.

opponenttablesearch winspaired gapgames
deni-0.82 players74.8%+7.18 [+6.11, +8.25]400
deni-0.83 players53.2% / seat+6.48 [+5.68, +7.27]402
deni-0.84 players, 2 of each40.8% / seat+5.73 [+5.09, +6.37]402
network + denial2 players61.8%+2.51 [+1.46, +3.56]400
v3-multi2 players84.0%+10.96 [+9.87, +12.05]400

The 84% is overstated. In its simulations, the search assumes the opponent plays like v3-multi — which was precisely the opponent of that duel: it knew exactly how it would answer. The other rows are against opponents it does not model, and those are the ones to remember.

At 3 and 4 players, parity would be 33% and 25% per seat: the search reaches 53% and 41%, so the network no longer extrapolates with table size.

Measured time: 0.55 s per move on average head to head, 1.0 s at worst; 0.7 s and 1.4 s at 4 players. A wider search (6 moves, 32 simulations, 6 turns) reached 4.9 s: too slow.

Method

  • Headless engine — the rules live in engine/, as plain data, with no Vue or Pinia. The interface, the agents and the training share the same implementation: no agent is tuned on rules other than the ones we play.
  • Paired comparisons — at equal game index, every agent gets the same seed, so the same dice on the same grid. The noise is shared and cancels out in the gaps, each reported with its 95% interval.
  • Reserved seeds — evaluation duels use seeds that neither tuning nor training ever saw, and results are confirmed on a second, disjoint block.
  • Data from the policy that plays — a move is scored by simulating the rest with the agent that will really play it; scoring for another player cost 2.4 points per game.
  • Always report the table size — the same network won at 4 players and lost head to head: the number of opponents is a network input.
  • Negative results kept — depth-2 expectimax brought nothing for 140× the cost, a 21-parameter heuristic did worse than a 6-parameter one, and denial adds nothing to search. These are measurements, not omissions.