Skip to main content

Solver

Struct Solver 

Source
pub struct Solver { /* private fields */ }
Expand description

An exact expectimax solver over the full game.

The value table is keyed by State::index and filled by backward induction: a state’s expected further score is the mean over the turn’s roll/keep/roll/keep/roll/score lattice of the best reward plus the value of the resulting state. From the empty card this comes to an optimal solo expectation of 254.5877 points under the official forced-joker rules this crate implements. The widely cited 254.5896 (Verhoeff, 1999) uses a laxer convention — an extra Yahtzee may be scored in any open box, with joker values gated on the matching upper box being filled — which this engine reproduces exactly when the forcing in State::legal_categories is lifted.

Construction is cheap; values are computed on demand. Querying a mid-game state solves only the states reachable from it, so “what is best here” costs a fraction of a full solve. All queries break ties deterministically (first category in card order, then first keep in canonical order), so replays are stable.

Implementations§

Source§

impl Solver

Source

pub fn new() -> Self

Builds the dice tables; no states are solved yet.

Source

pub fn is_solved(&self, state: State) -> bool

Whether value for this state is already computed.

Source

pub fn solve_tier(&mut self, filled: u32)

Solves every state with exactly filled categories scored.

Tiers must be solved from 13 down to 0; solve does exactly that. Exposed so incremental front ends can report progress between tiers. With the parallel feature the tier is solved across threads, bit-identical to the serial build.

§Panics

Panics if filled > 13, or if the tiers above filled have not been solved yet — solving out of order would silently poison the table otherwise.

Source

pub fn solve(&mut self)

Solves the whole game, all reachable states, bottom-up.

Takes seconds in a release build (use the parallel feature to spread it across cores); afterwards every query is a table lookup plus one turn evaluation.

Source

pub fn value(&mut self, state: State) -> f64

The expected further score from the start of a turn in state, under optimal play.

Solves lazily: only states reachable from state are computed, so early queries from a part-filled card are much cheaper than a full solve.

Source

pub fn category_ev( &mut self, state: State, dice: Dice, category: Category, ) -> Option<f64>

The expected further score of scoring dice in category now and playing on optimally: the write, its bonuses, and everything after — points already on the card are not included.

Returns None if the category is illegal here; see State::legal_categories.

Source

pub fn keep_ev( &mut self, state: State, dice: Dice, keep: Keep, rolls_left: u8, ) -> Option<f64>

The expected further score of holding keep and rerolling the rest, with rolls_left rolls remaining (clamped to the turn’s two rerolls).

Returns None if the card is full, no rolls remain, or keep is not part of dice.

Source

pub fn best_category(&mut self, state: State, dice: Dice) -> Category

The best category for dice when the turn must end now.

§Panics

Panics if the card is already full.

Source

pub fn best_action( &mut self, state: State, dice: Dice, rolls_left: u8, ) -> TurnAction

The optimal action for dice with rolls_left rolls remaining (clamped to the turn’s two rerolls). Scoring wins ties against rerolling.

§Panics

Panics if the card is already full.

Trait Implementations§

Source§

impl Clone for Solver

Source§

fn clone(&self) -> Solver

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Solver

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Solver

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.