From a8051b221bd469d616efe9790f35652884823c76 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Sat, 16 May 2026 19:09:11 +0000 Subject: [PATCH] Refactor KlondikeInstruction (#4) Fixes #2 Reviewed-on: https://git.aleshym.co/Quaternions/card_game/pulls/4 Co-authored-by: Rhys Lloyd Co-committed-by: Rhys Lloyd --- src/klondike.rs | 598 +++++++++++++++++++++++------------------------- src/main.rs | 163 +++++-------- 2 files changed, 341 insertions(+), 420 deletions(-) diff --git a/src/klondike.rs b/src/klondike.rs index 3763c84..4c4e807 100644 --- a/src/klondike.rs +++ b/src/klondike.rs @@ -9,9 +9,8 @@ impl Default for KlondikeConfig { } } -#[repr(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum KlondikePileId { +pub enum Tableau { Tableau1, Tableau2, Tableau3, @@ -19,15 +18,11 @@ pub enum KlondikePileId { Tableau5, Tableau6, Tableau7, - Foundation1, - Foundation2, - Foundation3, - Foundation4, - Stock, } -impl KlondikePileId { +impl Tableau { + const ITER_BEGIN: Self = Self::Tableau1; const fn next(self) -> Option { - use KlondikePileId::*; + use Tableau::*; Some(match self { Tableau1 => Tableau2, Tableau2 => Tableau3, @@ -35,229 +30,228 @@ impl KlondikePileId { Tableau4 => Tableau5, Tableau5 => Tableau6, Tableau6 => Tableau7, - Tableau7 => Foundation1, - Foundation1 => Foundation2, - Foundation2 => Foundation3, - Foundation3 => Foundation4, - Foundation4 => Stock, - Stock => return None, + Tableau7 => return None, }) } } -/// high four bits for stack depth, low four bits for Pile Id #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct InstructionSrc(u8); -impl InstructionSrc { - const STOCK: Self = InstructionSrc::new(KlondikePileStack::Stock); - pub const fn new(src: KlondikePileStack) -> Self { - match src { - KlondikePileStack::Tableau1(skip_cards) => { - Self(KlondikePileId::Tableau1 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau2(skip_cards) => { - Self(KlondikePileId::Tableau2 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau3(skip_cards) => { - Self(KlondikePileId::Tableau3 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau4(skip_cards) => { - Self(KlondikePileId::Tableau4 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau5(skip_cards) => { - Self(KlondikePileId::Tableau5 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau6(skip_cards) => { - Self(KlondikePileId::Tableau6 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Tableau7(skip_cards) => { - Self(KlondikePileId::Tableau7 as u8 + ((skip_cards as u8) << 4)) - } - KlondikePileStack::Foundation1 => Self(KlondikePileId::Foundation1 as u8), - KlondikePileStack::Foundation2 => Self(KlondikePileId::Foundation2 as u8), - KlondikePileStack::Foundation3 => Self(KlondikePileId::Foundation3 as u8), - KlondikePileStack::Foundation4 => Self(KlondikePileId::Foundation4 as u8), - KlondikePileStack::Stock => Self(KlondikePileId::Stock as u8), - } - } - const fn into_spec(self) -> KlondikePileStack { - // SAFETY: there is no way to construct an invalid InstructionSrc - let pile = unsafe { core::mem::transmute(self.0 & 0b1111) }; - match pile { - KlondikePileId::Tableau1 => { - KlondikePileStack::Tableau1(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau2 => { - KlondikePileStack::Tableau2(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau3 => { - KlondikePileStack::Tableau3(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau4 => { - KlondikePileStack::Tableau4(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau5 => { - KlondikePileStack::Tableau5(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau6 => { - KlondikePileStack::Tableau6(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Tableau7 => { - KlondikePileStack::Tableau7(unsafe { core::mem::transmute(self.0 >> 4) }) - } - KlondikePileId::Foundation1 => KlondikePileStack::Foundation1, - KlondikePileId::Foundation2 => KlondikePileStack::Foundation2, - KlondikePileId::Foundation3 => KlondikePileStack::Foundation3, - KlondikePileId::Foundation4 => KlondikePileStack::Foundation4, - KlondikePileId::Stock => KlondikePileStack::Stock, - } - } +pub enum Foundation { + Foundation1, + Foundation2, + Foundation3, + Foundation4, +} +impl Foundation { + const ITER_BEGIN: Self = Self::Foundation1; const fn next(self) -> Option { - match self.into_spec().next() { - Some(s) => Some(Self::new(s)), - None => None, - } + use Foundation::*; + Some(match self { + Foundation1 => Foundation2, + Foundation2 => Foundation3, + Foundation3 => Foundation4, + Foundation4 => return None, + }) } } -impl From for KlondikePileId { - fn from(value: InstructionSrc) -> Self { - match value.into_spec() { - KlondikePileStack::Tableau1(_) => KlondikePileId::Tableau1, - KlondikePileStack::Tableau2(_) => KlondikePileId::Tableau2, - KlondikePileStack::Tableau3(_) => KlondikePileId::Tableau3, - KlondikePileStack::Tableau4(_) => KlondikePileId::Tableau4, - KlondikePileStack::Tableau5(_) => KlondikePileId::Tableau5, - KlondikePileStack::Tableau6(_) => KlondikePileId::Tableau6, - KlondikePileStack::Tableau7(_) => KlondikePileId::Tableau7, - KlondikePileStack::Foundation1 => KlondikePileId::Foundation1, - KlondikePileStack::Foundation2 => KlondikePileId::Foundation2, - KlondikePileStack::Foundation3 => KlondikePileId::Foundation3, - KlondikePileStack::Foundation4 => KlondikePileId::Foundation4, - KlondikePileStack::Stock => KlondikePileId::Stock, - } + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum KlondikePile { + Tableau(Tableau), + Stock, + Foundation(Foundation), +} +impl KlondikePile { + const ITER_BEGIN: Self = Self::Tableau(Tableau::ITER_BEGIN); + const fn next(self) -> Option { + Some(match self { + Self::Tableau(tableau_stack) => match tableau_stack.next() { + Some(tableau_stack) => Self::Tableau(tableau_stack), + None => Self::Stock, + }, + Self::Stock => Self::Foundation(Foundation::ITER_BEGIN), + Self::Foundation(foundation) => match foundation.next() { + Some(foundation) => Self::Foundation(foundation), + None => return None, + }, + }) + } +} +impl From for KlondikePile { + fn from(value: Tableau) -> Self { + KlondikePile::Tableau(value) + } +} +impl From for KlondikePile { + fn from(value: Foundation) -> Self { + KlondikePile::Foundation(value) } } #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SkipCards { - Zero, - One, - Two, - Three, - Four, - Five, - Six, - Seven, - Eight, - Nine, - Ten, - Eleven, - Twelve, + Skip0, + Skip1, + Skip2, + Skip3, + Skip4, + Skip5, + Skip6, + Skip7, + Skip8, + Skip9, + Skip10, + Skip11, + Skip12, } impl SkipCards { + const ITER_BEGIN: Self = Self::Skip0; const fn next(self) -> Option { use SkipCards::*; Some(match self { - Zero => One, - One => Two, - Two => Three, - Three => Four, - Four => Five, - Five => Six, - Six => Seven, - Seven => Eight, - Eight => Nine, - Nine => Ten, - Ten => Eleven, - Eleven => Twelve, - Twelve => return None, - }) - } -} -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum KlondikePileStack { - Tableau1(SkipCards), - Tableau2(SkipCards), - Tableau3(SkipCards), - Tableau4(SkipCards), - Tableau5(SkipCards), - Tableau6(SkipCards), - Tableau7(SkipCards), - Foundation1, - Foundation2, - Foundation3, - Foundation4, - Stock, -} -impl KlondikePileStack { - const fn next(self) -> Option { - use KlondikePileStack::*; - Some(match self { - Tableau1(skip) => match skip.next() { - Some(next) => Tableau1(next), - None => Tableau2(SkipCards::Zero), - }, - Tableau2(skip) => match skip.next() { - Some(next) => Tableau2(next), - None => Tableau3(SkipCards::Zero), - }, - Tableau3(skip) => match skip.next() { - Some(next) => Tableau3(next), - None => Tableau4(SkipCards::Zero), - }, - Tableau4(skip) => match skip.next() { - Some(next) => Tableau4(next), - None => Tableau5(SkipCards::Zero), - }, - Tableau5(skip) => match skip.next() { - Some(next) => Tableau5(next), - None => Tableau6(SkipCards::Zero), - }, - Tableau6(skip) => match skip.next() { - Some(next) => Tableau6(next), - None => Tableau7(SkipCards::Zero), - }, - Tableau7(skip) => match skip.next() { - Some(next) => Tableau7(next), - None => Foundation1, - }, - Foundation1 => Foundation2, - Foundation2 => Foundation3, - Foundation3 => Foundation4, - Foundation4 => Stock, - Stock => return None, + Skip0 => Skip1, + Skip1 => Skip2, + Skip2 => Skip3, + Skip3 => Skip4, + Skip4 => Skip5, + Skip5 => Skip6, + Skip6 => Skip7, + Skip7 => Skip8, + Skip8 => Skip9, + Skip9 => Skip10, + Skip10 => Skip11, + Skip11 => Skip12, + Skip12 => return None, }) } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct KlondikeInstruction { - pub src: InstructionSrc, - pub dst: KlondikePileId, +pub struct TableauStack { + pub tableau: Tableau, + pub skip_cards: SkipCards, } -impl KlondikeInstruction { - pub const fn stock() -> Self { - Self { - src: InstructionSrc::STOCK, - dst: KlondikePileId::Stock, - } - } + +impl TableauStack { + const ITER_BEGIN: Self = Self { + tableau: Tableau::ITER_BEGIN, + skip_cards: SkipCards::ITER_BEGIN, + }; const 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() { + let TableauStack { + tableau, + skip_cards, + } = self; + if let Some(skip_cards) = skip_cards.next() { return Some(Self { - src: next_src, - dst: KlondikePileId::Tableau1, + tableau, + skip_cards, + }); + } + if let Some(tableau) = tableau.next() { + let skip_cards = SkipCards::Skip0; + return Some(Self { + tableau, + skip_cards, }); } None } } +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum KlondikePileStack { + Tableau(TableauStack), + Stock, + Foundation(Foundation), +} +impl KlondikePileStack { + const ITER_BEGIN: Self = Self::Tableau(TableauStack::ITER_BEGIN); + const fn next(self) -> Option { + Some(match self { + Self::Tableau(tableau_stack) => match tableau_stack.next() { + Some(tableau_stack) => Self::Tableau(tableau_stack), + None => Self::Stock, + }, + Self::Stock => Self::Foundation(Foundation::ITER_BEGIN), + Self::Foundation(foundation) => match foundation.next() { + Some(foundation) => Self::Foundation(foundation), + None => return None, + }, + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct DstFoundation { + pub src: KlondikePile, + pub foundation: Foundation, +} +impl DstFoundation { + const ITER_BEGIN: Self = Self { + src: KlondikePile::ITER_BEGIN, + foundation: Foundation::ITER_BEGIN, + }; + const fn next(self) -> Option { + let DstFoundation { src, foundation } = self; + if let Some(src) = src.next() { + return Some(Self { src, foundation }); + } + if let Some(foundation) = foundation.next() { + let src = KlondikePile::ITER_BEGIN; + return Some(Self { src, foundation }); + } + None + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct DstTableau { + pub src: KlondikePileStack, + pub tableau: Tableau, +} +impl DstTableau { + const ITER_BEGIN: Self = Self { + src: KlondikePileStack::ITER_BEGIN, + tableau: Tableau::ITER_BEGIN, + }; + const fn next(self) -> Option { + let DstTableau { src, tableau } = self; + if let Some(src) = src.next() { + return Some(Self { src, tableau }); + } + if let Some(tableau) = tableau.next() { + let src = KlondikePileStack::ITER_BEGIN; + return Some(Self { src, tableau }); + } + None + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum KlondikeInstruction { + DstFoundation(DstFoundation), + DstTableau(DstTableau), + RotateStock, +} +impl KlondikeInstruction { + const ITER_BEGIN: Self = Self::DstFoundation(DstFoundation::ITER_BEGIN); + const fn next(self) -> Option { + Some(match self { + Self::DstFoundation(dst_foundation) => match dst_foundation.next() { + Some(dst_foundation) => Self::DstFoundation(dst_foundation), + None => Self::DstTableau(DstTableau::ITER_BEGIN), + }, + Self::DstTableau(tableau) => match tableau.next() { + Some(tableau) => Self::DstTableau(tableau), + None => Self::RotateStock, + }, + Self::RotateStock => return None, + }) + } +} + const TABLEAUS: usize = 7; const fn sum(n: usize) -> usize { n * (n + 1) / 2 @@ -313,128 +307,106 @@ impl KlondikeState { pub const fn tableau7(&self) -> &Pile<6, 13> { &self.tableau7 } - fn src_card(&self, src: InstructionSrc) -> Option<&Card> { - match src.into_spec() { - KlondikePileStack::Tableau1(skip_cards) => { - self.tableau1.face_up().get(skip_cards as usize) + fn card(&self, src: KlondikePileStack) -> Option<&Card> { + match src { + KlondikePileStack::Tableau(TableauStack { + tableau, + skip_cards, + }) => match tableau { + Tableau::Tableau1 => self.tableau1.face_up().get(skip_cards as usize), + Tableau::Tableau2 => self.tableau2.face_up().get(skip_cards as usize), + Tableau::Tableau3 => self.tableau3.face_up().get(skip_cards as usize), + Tableau::Tableau4 => self.tableau4.face_up().get(skip_cards as usize), + Tableau::Tableau5 => self.tableau5.face_up().get(skip_cards as usize), + Tableau::Tableau6 => self.tableau6.face_up().get(skip_cards as usize), + Tableau::Tableau7 => self.tableau7.face_up().get(skip_cards as usize), + }, + KlondikePileStack::Foundation(foundation) => { + self.foundations[foundation as usize].last() } - KlondikePileStack::Tableau2(skip_cards) => { - self.tableau2.face_up().get(skip_cards as usize) - } - KlondikePileStack::Tableau3(skip_cards) => { - self.tableau3.face_up().get(skip_cards as usize) - } - KlondikePileStack::Tableau4(skip_cards) => { - self.tableau4.face_up().get(skip_cards as usize) - } - KlondikePileStack::Tableau5(skip_cards) => { - self.tableau5.face_up().get(skip_cards as usize) - } - KlondikePileStack::Tableau6(skip_cards) => { - self.tableau6.face_up().get(skip_cards as usize) - } - KlondikePileStack::Tableau7(skip_cards) => { - self.tableau7.face_up().get(skip_cards as usize) - } - KlondikePileStack::Foundation1 => self.foundations[1 - 1].last(), - KlondikePileStack::Foundation2 => self.foundations[2 - 1].last(), - KlondikePileStack::Foundation3 => self.foundations[3 - 1].last(), - KlondikePileStack::Foundation4 => self.foundations[4 - 1].last(), KlondikePileStack::Stock => self.stock.face_up().last(), } } - fn take_src_cards(&mut self, src: InstructionSrc) -> Stack<13> { - match src.into_spec() { - KlondikePileStack::Tableau1(skip_cards) => { - self.tableau1.take_range_flip_up(skip_cards as usize..) + fn top_card(&self, src: KlondikePile) -> Option<&Card> { + match src { + KlondikePile::Tableau(tableau) => match tableau { + Tableau::Tableau1 => self.tableau1.face_up().last(), + Tableau::Tableau2 => self.tableau2.face_up().last(), + Tableau::Tableau3 => self.tableau3.face_up().last(), + Tableau::Tableau4 => self.tableau4.face_up().last(), + Tableau::Tableau5 => self.tableau5.face_up().last(), + Tableau::Tableau6 => self.tableau6.face_up().last(), + Tableau::Tableau7 => self.tableau7.face_up().last(), + }, + KlondikePile::Foundation(foundation) => self.foundations[foundation as usize].last(), + KlondikePile::Stock => self.stock.face_up().last(), + } + } + fn take_cards(&mut self, src: KlondikePileStack) -> Stack<13> { + match src { + KlondikePileStack::Tableau(TableauStack { + tableau, + skip_cards, + }) => match tableau { + Tableau::Tableau1 => self.tableau1.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau2 => self.tableau2.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau3 => self.tableau3.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau4 => self.tableau4.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau5 => self.tableau5.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau6 => self.tableau6.take_range_flip_up(skip_cards as usize..), + Tableau::Tableau7 => self.tableau7.take_range_flip_up(skip_cards as usize..), + }, + KlondikePileStack::Foundation(foundation) => { + Stack::from_iter(self.foundations[foundation as usize].pop()) } - KlondikePileStack::Tableau2(skip_cards) => { - self.tableau2.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Tableau3(skip_cards) => { - self.tableau3.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Tableau4(skip_cards) => { - self.tableau4.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Tableau5(skip_cards) => { - self.tableau5.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Tableau6(skip_cards) => { - self.tableau6.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Tableau7(skip_cards) => { - self.tableau7.take_range_flip_up(skip_cards as usize..) - } - KlondikePileStack::Foundation1 => Stack::from_iter(self.foundations[1 - 1].pop()), - KlondikePileStack::Foundation2 => Stack::from_iter(self.foundations[2 - 1].pop()), - KlondikePileStack::Foundation3 => Stack::from_iter(self.foundations[3 - 1].pop()), - KlondikePileStack::Foundation4 => Stack::from_iter(self.foundations[4 - 1].pop()), KlondikePileStack::Stock => Stack::from_iter(self.stock.pop()), } } - fn dst_card(&self, dst: KlondikePileId) -> Option<&Card> { - match dst { - KlondikePileId::Tableau1 => self.tableau1.face_up().last(), - KlondikePileId::Tableau2 => self.tableau2.face_up().last(), - KlondikePileId::Tableau3 => self.tableau3.face_up().last(), - KlondikePileId::Tableau4 => self.tableau4.face_up().last(), - KlondikePileId::Tableau5 => self.tableau5.face_up().last(), - KlondikePileId::Tableau6 => self.tableau6.face_up().last(), - KlondikePileId::Tableau7 => self.tableau7.face_up().last(), - KlondikePileId::Foundation1 => self.foundations[1 - 1].last(), - KlondikePileId::Foundation2 => self.foundations[2 - 1].last(), - KlondikePileId::Foundation3 => self.foundations[3 - 1].last(), - KlondikePileId::Foundation4 => self.foundations[4 - 1].last(), - KlondikePileId::Stock => None, + fn take_top_card(&mut self, src: KlondikePile) -> Option { + match src { + KlondikePile::Tableau(tableau) => match tableau { + Tableau::Tableau1 => self.tableau1.pop(), + Tableau::Tableau2 => self.tableau2.pop(), + Tableau::Tableau3 => self.tableau3.pop(), + Tableau::Tableau4 => self.tableau4.pop(), + Tableau::Tableau5 => self.tableau5.pop(), + Tableau::Tableau6 => self.tableau6.pop(), + Tableau::Tableau7 => self.tableau7.pop(), + }, + KlondikePile::Foundation(foundation) => self.foundations[foundation as usize].pop(), + KlondikePile::Stock => self.stock.pop(), } } - fn extend_dst_pile(&mut self, dst: KlondikePileId, cards: Stack<13>) { + fn extend>(&mut self, dst: KlondikePile, cards: I) { match dst { - KlondikePileId::Tableau1 => self.tableau1.extend(cards), - KlondikePileId::Tableau2 => self.tableau2.extend(cards), - KlondikePileId::Tableau3 => self.tableau3.extend(cards), - KlondikePileId::Tableau4 => self.tableau4.extend(cards), - KlondikePileId::Tableau5 => self.tableau5.extend(cards), - KlondikePileId::Tableau6 => self.tableau6.extend(cards), - KlondikePileId::Tableau7 => self.tableau7.extend(cards), - KlondikePileId::Foundation1 => self.foundations[1 - 1].extend(cards), - KlondikePileId::Foundation2 => self.foundations[2 - 1].extend(cards), - KlondikePileId::Foundation3 => self.foundations[3 - 1].extend(cards), - KlondikePileId::Foundation4 => self.foundations[4 - 1].extend(cards), - KlondikePileId::Stock => (), + KlondikePile::Tableau(tableau) => match tableau { + Tableau::Tableau1 => self.tableau1.extend(cards), + Tableau::Tableau2 => self.tableau2.extend(cards), + Tableau::Tableau3 => self.tableau3.extend(cards), + Tableau::Tableau4 => self.tableau4.extend(cards), + Tableau::Tableau5 => self.tableau5.extend(cards), + Tableau::Tableau6 => self.tableau6.extend(cards), + Tableau::Tableau7 => self.tableau7.extend(cards), + }, + KlondikePile::Foundation(foundation) => { + self.foundations[foundation as usize].extend(cards) + } + KlondikePile::Stock => self.stock.extend(cards), } } fn is_instruction_valid(&self, instruction: KlondikeInstruction) -> bool { match instruction { // Stock -> Stock draws a card or resets the stock - KlondikeInstruction { - src: InstructionSrc::STOCK, - dst: KlondikePileId::Stock, - } => { + KlondikeInstruction::RotateStock => { // cannot move stock when stock is empty !self.stock.is_empty() } - // cannot move cards to stock - KlondikeInstruction { - src: _, - dst: KlondikePileId::Stock, - } => false, - // moving to foundation has special rules - KlondikeInstruction { src, dst } - if matches!( - dst, - KlondikePileId::Foundation1 - | KlondikePileId::Foundation2 - | KlondikePileId::Foundation3 - | KlondikePileId::Foundation4 - ) => - { + KlondikeInstruction::DstFoundation(dst_foundation) => { // get the top cards - if let Some(src_card) = self.src_card(src) { - match self.dst_card(dst) { + if let Some(src_card) = self.top_card(dst_foundation.src) { + match self.top_card(dst_foundation.foundation.into()) { // destination card exists Some(dst_card) => { // suit matches? @@ -450,10 +422,10 @@ impl KlondikeState { } } // other = move to tableau - KlondikeInstruction { src, dst } => { - // get the top cards - if let Some(src_card) = self.src_card(src) { - match self.dst_card(dst) { + KlondikeInstruction::DstTableau(dst_tableau) => { + // get the cards + if let Some(src_card) = self.card(dst_tableau.src) { + match self.top_card(dst_tableau.tableau.into()) { // destination card exists Some(dst_card) => { // red-ness is opposite? @@ -478,10 +450,7 @@ pub struct KlondikeIter { impl KlondikeIter { const fn new() -> Self { Self { - instruction: Some(KlondikeInstruction { - src: InstructionSrc::new(KlondikePileStack::Tableau1(SkipCards::Zero)), - dst: KlondikePileId::Tableau2, - }), + instruction: Some(KlondikeInstruction::ITER_BEGIN), } } } @@ -558,19 +527,20 @@ impl Game for Klondike { fn process_instruction(&mut self, instruction: Self::Instruction) { match instruction { // Reset the stock if it's empty - KlondikeInstruction { - src: InstructionSrc::STOCK, - dst: KlondikePileId::Stock, - } => { + KlondikeInstruction::RotateStock => { if self.state.stock.face_down().is_empty() { self.state.stock.flip_it_and_reverse_it(); } else { self.state.stock.flip_up(); } } - KlondikeInstruction { src, dst } => { - let cards = self.state.take_src_cards(src); - self.state.extend_dst_pile(dst, cards); + KlondikeInstruction::DstFoundation(DstFoundation { src, foundation }) => { + let cards = self.state.take_top_card(src); + self.state.extend(foundation.into(), cards); + } + KlondikeInstruction::DstTableau(DstTableau { src, tableau }) => { + let cards = self.state.take_cards(src); + self.state.extend(tableau.into(), cards); } } } diff --git a/src/main.rs b/src/main.rs index c64e5e9..3d304ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,8 @@ pub type Rng = rand::rngs::ThreadRng; use card_game::{Card, Game, Pile, Session, Suit}; use klondike::{ - InstructionSrc, Klondike, KlondikeInstruction, KlondikePileId, KlondikePileStack, - KlondikeState, SkipCards, + DstFoundation, DstTableau, Foundation, Klondike, KlondikeInstruction, KlondikePile, + KlondikePileStack, SkipCards, Tableau, TableauStack, }; use std::fmt::Display; @@ -91,8 +91,8 @@ impl Display for Klondike { struct Invalid; struct Parsed(T); struct NaiveInstruction { - src: KlondikePileId, - dst: KlondikePileId, + src: KlondikePile, + dst: KlondikePile, } impl core::str::FromStr for NaiveInstruction { type Err = Invalid; @@ -102,22 +102,22 @@ impl core::str::FromStr for NaiveInstruction { Ok(NaiveInstruction { src, dst }) } } -impl core::str::FromStr for Parsed { +impl core::str::FromStr for Parsed { type Err = Invalid; fn from_str(s: &str) -> Result { Ok(Parsed(match s { - "st" => KlondikePileId::Stock, - "t1" => KlondikePileId::Tableau1, - "t2" => KlondikePileId::Tableau2, - "t3" => KlondikePileId::Tableau3, - "t4" => KlondikePileId::Tableau4, - "t5" => KlondikePileId::Tableau5, - "t6" => KlondikePileId::Tableau6, - "t7" => KlondikePileId::Tableau7, - "f1" => KlondikePileId::Foundation1, - "f2" => KlondikePileId::Foundation2, - "f3" => KlondikePileId::Foundation3, - "f4" => KlondikePileId::Foundation4, + "st" => KlondikePile::Stock, + "t1" => KlondikePile::Tableau(Tableau::Tableau1), + "t2" => KlondikePile::Tableau(Tableau::Tableau2), + "t3" => KlondikePile::Tableau(Tableau::Tableau3), + "t4" => KlondikePile::Tableau(Tableau::Tableau4), + "t5" => KlondikePile::Tableau(Tableau::Tableau5), + "t6" => KlondikePile::Tableau(Tableau::Tableau6), + "t7" => KlondikePile::Tableau(Tableau::Tableau7), + "f1" => KlondikePile::Foundation(Foundation::Foundation1), + "f2" => KlondikePile::Foundation(Foundation::Foundation2), + "f3" => KlondikePile::Foundation(Foundation::Foundation3), + "f4" => KlondikePile::Foundation(Foundation::Foundation4), _ => return Err(Invalid), })) } @@ -152,99 +152,48 @@ fn find_valid_instruction( naive_instruction: NaiveInstruction, ) -> Option { const SKIP_LIST: [SkipCards; 13] = [ - SkipCards::Zero, - SkipCards::One, - SkipCards::Two, - SkipCards::Three, - SkipCards::Four, - SkipCards::Five, - SkipCards::Six, - SkipCards::Seven, - SkipCards::Eight, - SkipCards::Nine, - SkipCards::Ten, - SkipCards::Eleven, - SkipCards::Twelve, + SkipCards::Skip0, + SkipCards::Skip1, + SkipCards::Skip2, + SkipCards::Skip3, + SkipCards::Skip4, + SkipCards::Skip5, + SkipCards::Skip6, + SkipCards::Skip7, + SkipCards::Skip8, + SkipCards::Skip9, + SkipCards::Skip10, + SkipCards::Skip11, + SkipCards::Skip12, ]; - let dst = naive_instruction.dst; - let src = match naive_instruction.src { - KlondikePileId::Tableau1 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau1(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); + let instruction = match (naive_instruction.dst, naive_instruction.src) { + (KlondikePile::Tableau(tableau), src) => { + let src = match src { + KlondikePile::Tableau(src_tableau) => { + for skip_cards in SKIP_LIST { + let src = KlondikePileStack::Tableau(TableauStack { + tableau: src_tableau, + skip_cards, + }); + let instruction = + KlondikeInstruction::DstTableau(DstTableau { tableau, src }); + if state.is_instruction_valid(instruction) { + return Some(instruction); + } + } + return None; } - } - return None; + KlondikePile::Stock => KlondikePileStack::Stock, + KlondikePile::Foundation(foundation) => KlondikePileStack::Foundation(foundation), + }; + KlondikeInstruction::DstTableau(DstTableau { tableau, src }) } - KlondikePileId::Tableau2 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau2(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; + (KlondikePile::Stock, KlondikePile::Stock) => KlondikeInstruction::RotateStock, + (KlondikePile::Foundation(foundation), src) => { + KlondikeInstruction::DstFoundation(DstFoundation { foundation, src }) } - KlondikePileId::Tableau3 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau3(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; - } - KlondikePileId::Tableau4 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau4(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; - } - KlondikePileId::Tableau5 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau5(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; - } - KlondikePileId::Tableau6 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau6(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; - } - KlondikePileId::Tableau7 => { - for skip in SKIP_LIST { - let src = InstructionSrc::new(KlondikePileStack::Tableau7(skip)); - let instruction = KlondikeInstruction { src, dst }; - if state.is_instruction_valid(instruction) { - return Some(instruction); - } - } - return None; - } - KlondikePileId::Foundation1 => InstructionSrc::new(KlondikePileStack::Foundation1), - KlondikePileId::Foundation2 => InstructionSrc::new(KlondikePileStack::Foundation2), - KlondikePileId::Foundation3 => InstructionSrc::new(KlondikePileStack::Foundation3), - KlondikePileId::Foundation4 => InstructionSrc::new(KlondikePileStack::Foundation4), - KlondikePileId::Stock => InstructionSrc::new(KlondikePileStack::Stock), + _ => return None, }; - let instruction = KlondikeInstruction { src, dst }; state .is_instruction_valid(instruction) .then_some(instruction) @@ -281,7 +230,9 @@ fn main() -> Result<(), std::io::Error> { println!("No valid moves!"); } } - SessionInstruction::Stock => session.process_instruction(KlondikeInstruction::stock()), + SessionInstruction::Stock => { + session.process_instruction(KlondikeInstruction::RotateStock) + } SessionInstruction::Klondike(naive_instruction) => { if let Some(instruction) = find_valid_instruction(session.state(), naive_instruction)