From 2e1bab8c52d53ff70ad2686fe23a24bf0ec1c8d8 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 15 May 2026 08:27:02 -0700 Subject: [PATCH] possible_instructions --- src/card_game.rs | 4 +- src/klondike.rs | 181 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 57 deletions(-) diff --git a/src/card_game.rs b/src/card_game.rs index 249144f..70b2a15 100644 --- a/src/card_game.rs +++ b/src/card_game.rs @@ -86,7 +86,7 @@ impl Card { } } -#[derive(Hash)] +#[derive(Clone, Debug, Hash)] pub struct Stack(Vec); impl Stack { pub fn new() -> Self { @@ -120,7 +120,7 @@ impl std::ops::DerefMut for Stack { } } -#[derive(Hash)] +#[derive(Clone, Debug, Hash)] pub struct Pile { face_down: Stack, face_up: Stack, diff --git a/src/klondike.rs b/src/klondike.rs index b46e9ee..6d98328 100644 --- a/src/klondike.rs +++ b/src/klondike.rs @@ -7,12 +7,9 @@ impl Default for KlondikeConfig { KlondikeConfig {} } } -#[derive(Hash)] -struct KlondikeState { - piles: [Pile; 13], -} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum KlondikePileId { + Stock, Tableau0, Tableau1, Tableau2, @@ -21,71 +18,64 @@ pub enum KlondikePileId { Tableau5, Tableau6, Tableau7, - Stock, Foundation0, Foundation1, Foundation2, Foundation3, } +impl KlondikePileId { + fn next(self) -> Option { + use KlondikePileId::*; + Some(match self { + Stock => Tableau0, + Tableau0 => Tableau1, + Tableau1 => Tableau2, + Tableau2 => Tableau3, + Tableau3 => Tableau4, + Tableau4 => Tableau5, + Tableau5 => Tableau6, + Tableau6 => Tableau7, + Tableau7 => Foundation0, + Foundation0 => Foundation1, + Foundation1 => Foundation2, + Foundation2 => Foundation3, + Foundation3 => return None, + }) + } +} #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct KlondikeInstruction { pub src: KlondikePileId, pub dst: KlondikePileId, } -pub struct Klondike { - config: KlondikeConfig, - state: KlondikeState, -} -impl Klondike { - pub fn new(mut seed: Rng, config: KlondikeConfig) -> Self { - // shuffle a new deck - let mut deck = Stack::full_deck(0); - use rand::seq::SliceRandom; - deck.shuffle(&mut seed); - - // generate tableaus - let [t0, t1, t2, t3, t4, t5, t6, t7] = core::array::from_fn(|i| { - let stack = deck.split_off(i).into(); - let mut pile = Pile::new_face_down(stack); - pile.push(deck.pop().unwrap()); - pile - }); - - // stock is remaining cards - let stock = Pile::new_face_down(deck); - - let state = KlondikeState { - piles: [ - t0, - t1, - t2, - t3, - t4, - t5, - t6, - t7, - stock, - Pile::new(), - Pile::new(), - Pile::new(), - Pile::new(), - ], - }; - Self { config, state } +impl KlondikeInstruction { + fn next(self) -> Option { + let KlondikeInstruction { src, dst } = self; + if let Some(next_dst) = dst.next() { + return Some(Self { src, dst: next_dst }); + } + if let Some(next_src) = src.next() { + return Some(Self { + src: next_src, + dst: KlondikePileId::Stock, + }); + } + None } - pub fn pile(&self, index: KlondikePileId) -> &Pile { - &self.state.piles[index as usize] +} + +#[derive(Clone, Hash)] +struct KlondikeState { + piles: [Pile; 13], +} +impl KlondikeState { + fn pile(&self, index: KlondikePileId) -> &Pile { + &self.piles[index as usize] } fn pile_mut(&mut self, index: KlondikePileId) -> &mut Pile { - &mut self.state.piles[index as usize] + &mut self.piles[index as usize] } -} -impl Game for Klondike { - type Instruction = KlondikeInstruction; - fn possible_instructions(&self) -> impl Iterator + use<> { - vec![].into_iter() - } - fn validate_instruction(&self, instruction: Self::Instruction) -> bool { + fn validate_instruction(&self, instruction: KlondikeInstruction) -> bool { match instruction { // Stock -> Stock draws a card or resets the stock KlondikeInstruction { @@ -142,6 +132,87 @@ impl Game for Klondike { } } } +} + +pub struct Klondike { + config: KlondikeConfig, + state: KlondikeState, +} +impl Klondike { + pub fn new(mut seed: Rng, config: KlondikeConfig) -> Self { + // shuffle a new deck + let mut deck = Stack::full_deck(0); + use rand::seq::SliceRandom; + deck.shuffle(&mut seed); + + // generate tableaus + let [t0, t1, t2, t3, t4, t5, t6, t7] = core::array::from_fn(|i| { + let stack = deck.split_off(i).into(); + let mut pile = Pile::new_face_down(stack); + pile.push(deck.pop().unwrap()); + pile + }); + + // stock is remaining cards + let stock = Pile::new_face_down(deck); + + let state = KlondikeState { + piles: [ + t0, + t1, + t2, + t3, + t4, + t5, + t6, + t7, + stock, + Pile::new(), + Pile::new(), + Pile::new(), + Pile::new(), + ], + }; + Self { config, state } + } + #[inline] + pub fn pile(&self, index: KlondikePileId) -> &Pile { + self.state.pile(index) + } + #[inline] + fn pile_mut(&mut self, index: KlondikePileId) -> &mut Pile { + self.state.pile_mut(index) + } +} +pub struct KlondikeIter { + instruction: Option, +} +impl KlondikeIter { + fn new() -> Self { + Self { + instruction: Some(KlondikeInstruction { + src: KlondikePileId::Stock, + dst: KlondikePileId::Stock, + }), + } + } +} +impl Iterator for KlondikeIter { + type Item = KlondikeInstruction; + fn next(&mut self) -> Option { + self.instruction = self.instruction?.next(); + self.instruction + } +} +impl Game for Klondike { + type Instruction = KlondikeInstruction; + fn possible_instructions(&self) -> impl Iterator + use<> { + let state = self.state.clone(); + KlondikeIter::new().filter(move |&instruction| state.validate_instruction(instruction)) + } + fn validate_instruction(&self, instruction: Self::Instruction) -> bool { + self.state.validate_instruction(instruction) + } fn process_instruction(&mut self, instruction: Self::Instruction) { let card = self.pile_mut(instruction.src).pop().unwrap(); self.pile_mut(instruction.dst).push(card);