Skip to main content

gin_rummy_engine/
strategy.rs

1//! The [`Strategy`] trait: a decision procedure for one seat
2
3use crate::{DrawAction, Layoff, TurnAction, UpcardAction, View};
4
5/// A decision procedure for one seat of gin rummy
6///
7/// Every method receives a [`View`] restricted to the information the seat
8/// may legally see.  Methods take `&mut self` so strategies can keep state —
9/// an internal random number generator, an opponent model — and the trait is
10/// object-safe, so the driver works with `&mut dyn Strategy`.
11///
12/// A strategy never applies its decisions itself; the [`Table`] driver
13/// validates and applies them, rejecting illegal choices as
14/// [`EngineError::IllegalAction`].
15///
16/// [`Table`]: crate::Table
17/// [`EngineError::IllegalAction`]: crate::EngineError::IllegalAction
18pub trait Strategy {
19    /// Take or pass the initial upcard
20    fn offer_upcard(&mut self, view: &View<'_>) -> UpcardAction;
21
22    /// Draw from the stock or the discard pile
23    ///
24    /// Not consulted on the forced stock draw after both players pass the
25    /// upcard — the driver draws from the stock directly, so implementations
26    /// may treat [`DrawAction::TakeDiscard`] as always available.
27    fn choose_draw(&mut self, view: &View<'_>) -> DrawAction;
28
29    /// Discard, knock, or declare big gin
30    ///
31    /// The drawn card is already in [`View::hand`], which holds 11 cards;
32    /// [`View::taken_discard`] is the card that may not be shed this turn.
33    fn play_turn(&mut self, view: &View<'_>) -> TurnAction;
34
35    /// Lay one card off onto the knocker's spread, or `None` to finish
36    ///
37    /// Called repeatedly with a refreshed view — the spread grows with every
38    /// accepted layoff — until the strategy returns `None`.
39    fn choose_layoff(&mut self, view: &View<'_>) -> Option<Layoff>;
40
41    /// A display name for tournament output
42    fn name(&self) -> &str {
43        "unnamed"
44    }
45}