Yahtzee Engine
Complete Yahtzee rules and bots: a multiplayer state machine with the official forced-joker scoring, a fast heuristic player, and an exact expectimax solver that knows the best move — and its expected value — in every position.
The design triangle:
State: the compact rules authority. Which categories are legal, what they pay, joker forcing, both bonuses — one implementation shared by the scorecard, the game, and the solver, so they can never disagree.Game: the table-level state machine. Deterministic dice injection is the primitive (replays and front ends stay trivial); illegal moves are rejected without changing anything. AStrategydecides from aView, and drivers run whole turns and games.Solver: exact expected values by backward induction over every scorecard state. Under the official rules the empty card is worth 254.5877 points; the widely cited 254.5896 (Verhoeff, 1999) uses a laxer joker convention that the same engine reproduces when the forcing is lifted.
Bots
HeuristicBot: deterministic rules of thumb, microsecond decisions, no tables. Averages 216 points over 10 000 seeded games.OptimalBot: provably perfect solo play backed by the solver. Solves lazily by default;OptimalBot::presolved()computes the whole table up front (seconds in a release build, spread across cores with theparallelfeature).
Quick start
The rules need no features — inject dice, hold, and score:
use ;
let mut game = new;
game.roll?;
game.act?;
game.roll?;
game.act?;
game.roll?;
let delta = game.act?;
assert_eq!;
# Ok::
Neither does the solver — ask it what any position is worth:
use ;
let mut solver = new;
// With only Chance open, optimal play is worth exactly 70/3 points.
let state = new;
assert!;
With the (default) rand feature, roll for real and pit bots against
each other:
#
#
#
#
Writing your own bot is implementing Strategy's two decisions
against a View; the driver handles all bookkeeping.
Feature flags
rand(default): dice-rolling helpers (Game::roll_with,play_turn,play_game) and theplay/arenaexamples. The rules and the solver are deterministic; disable it for a dependency-light build.parallel: solves scorecard tiers across the CPU cores via rayon. The table is bit-identical to the serial build, it just arrives faster. Off by default.
Examples
solve: build the full table and print the optimal expected score —cargo run --release --example solve --features parallelplay: play against the heuristic bot in the terminal —cargo run --release --example playarena: bot strength measurements —cargo run --release --example arena -- 10000 --optimal
Play in the browser
The web/ crate wraps the engine in WebAssembly with the
solver as a live advisor: https://jdh8.github.io/yahtzee-engine/.