pub fn sample_chance<G: Game>(
game: &G,
state: &G::State,
rng: &mut Prng,
) -> Option<G::Action>Expand description
Samples one outcome from game.chance_outcomes(state), weighted by
probability, using rng. Returns None if there are no outcomes.
rng is the chance sampler’s generator, owned by the driver or search
loop, deliberately separate from any per-state generator a game uses for
implicit rolls inside apply. Given the same generator position and the
same outcome list, the draw is reproducible, which is what makes replay and
undo/redo of a dealt card land on the same result.
The returned action is one of the outcomes; the caller applies it with
PlayerId::CHANCE to commit it to state. Use it
in a driver or rollout loop whenever the chance pseudo-player is active.
§Example
use turnbase::{ActivePlayers, Game, PlayerId, Prng, sample_chance};
// One chance node that reveals a card 0, 1, or 2 (uniform by default).
struct Reveal;
impl Game for Reveal {
type State = ();
type Action = u8;
type View = ();
fn new_initial_state(&self, _seed: u64) {}
fn num_players(&self) -> usize { 0 }
fn active_players(&self, _s: &()) -> ActivePlayers { ActivePlayers::one(PlayerId::CHANCE) }
fn legal_actions(&self, _s: &(), p: PlayerId) -> Vec<u8> {
if p.is_chance() { vec![0, 1, 2] } else { vec![] }
}
fn apply(&self, _s: &mut (), _p: PlayerId, _a: u8) {}
fn is_terminal(&self, _s: &()) -> bool { false }
fn reward(&self, _s: &(), _p: PlayerId) -> f64 { 0.0 }
fn view(&self, _s: &(), _v: Option<PlayerId>) {}
}
let mut sampler = Prng::new(42);
let mut state = ();
if Reveal.active_players(&state).contains(PlayerId::CHANCE) {
let card = sample_chance(&Reveal, &state, &mut sampler).unwrap();
Reveal.apply(&mut state, PlayerId::CHANCE, card); // commit the draw
assert!(card <= 2);
}