Repository navigation
Profiles
A Profile<G> is the learned strategy: a map from each information set
(namespaced by player) to a probability distribution over actions. It is the
output of training and implements the Policy trait, so it can act in a live
game.
let strategy = profile.policy(&game, player); // Vec<f64>, aligned to legal_actions order
let action = profile.act(&game, player); // deterministic argmax
let sampled = profile.act_stochastic(&game, player, &mut rng);
let pretty = profile.pretty(&game, player); // "Rock: 0.333, Paper: 0.333, ..."act and act_stochastic panic if game is not a decision node for player; only call them when player is to move. policy returns the uniform distribution for an information set never visited during training (a safe default that masks config drift). policy_strict returns a Result instead. Use it when a wrong (uniform) move is worse than a loud failure.
act and act_stochastic panic if game is not a decision node for
player (player_to_act() must be PlayerToAct::Player(player)). The crate's
guarantee is "never panics on valid input", so only call these at a node where
player is to move.
average_strategy(player, infoset_key) looks up the raw learned vector; the key
must be the bytes returned by Game::infoset_key. entropy_summary returns
(mean, min, max) Shannon entropy over all information sets, useful for
inspecting how mixed the strategy is.
strategy_map() exposes the whole table keyed by a player-namespaced key.
regret::namespace_key(player, infoset_key) builds that combined key (and
split_namespace reverses it), so strategy_map().get(&namespace_key(0, key))
is the raw vector for player 0 at key. namespace_key_into writes into a reused
buffer for hot loops.
Policy<G> is the small interface the trainer and the evaluation and blending
tools consume: given a game state and a player, return a distribution over the
legal actions, sample an action, or take the argmax. Profile implements it, so
a trained profile plugs into any of those consumers directly.
UniformPolicy is the trivial implementation: uniform over the legal actions at
every state. It is the baseline opponent for evaluation
and the default fallback that policy returns for an unseen information set.
The on-disk format is a compact, versioned, round-trippable binary layout
(managed in src/profile/format.rs; JSON dumper in src/profile/json.rs).
Strategies are stored by action index, so no serialization framework is forced
on the caller's action type.
- Versioned; legacy v3 checkpoints (no provenance, no CRC32) are loaded, migrated, and re-saved in the current v5 format.
- Format v5+ carries a
CRC32that detects corruption on load. - NaN / non-finite probabilities are rejected on load.
-
save/load/save_with_shape/load_with_shaperound-trip the strategy map, shape tag, and provenance.
profile.save("kuhn.cfr")?;
let loaded = Profile::<KuhnPoker>::load("kuhn.cfr")?;A Profile can embed Provenance (algorithm, regret variant, iterations, seed,
shape, f32_tables flag) recorded at training time, so a loaded artifact can be
validated against the game definition and a stale checkpoint (different
settings) detected.
Algorithm / RegretVariant are stored as stable string keys
("ExternalSampling", "DCFRPlus{alpha=1.5,gamma=4}"), and legacy v2.0.0
misnamed aliases are still parsed. Absence of provenance (Provenance::None)
means the checkpoint predates format v4.
Provenance is untrusted metadata written into the file; it can be forged. The CRC32 guards integrity, not authenticity. Use it to catch accidental config drift, not tampering.
set_shape("LiarsDice:2:3") embeds a runtime shape descriptor checked on load, so
a config-drifted game (LiarsDice::new(2,2) vs (2,3)) is rejected loudly
instead of silently producing a wrong strategy. None means unversioned / always
accepted.
A caller needing their own format can implement (de)serialization from the public
strategy_map() and ignore the bundled codec.