From 25d3c3aae4ec2a54a469b509678fa5d0130dbeaf Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 15 May 2026 08:01:06 -0700 Subject: [PATCH] klondike instruction validation --- src/card_game.rs | 20 +++++++++++++++++ src/klondike.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/card_game.rs b/src/card_game.rs index 284ce7f..e9c309f 100644 --- a/src/card_game.rs +++ b/src/card_game.rs @@ -27,7 +27,27 @@ impl Suit { self as u8 & 0b10 != 0 } } + +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct CardValue(u8); +impl CardValue { + pub fn checked_add(self, offset: u8) -> Option { + let new_value = self.0.checked_add(offset)?; + if 13 < new_value { + None + } else { + Some(CardValue(new_value)) + } + } + pub fn checked_sub(self, offset: u8) -> Option { + let new_value = self.0.checked_sub(offset)?; + if new_value < 1 { + None + } else { + Some(CardValue(new_value)) + } + } +} /// An identifier which specifies the deck id, suit, and card value. /// 2 bits for deck ID /// 2 bits for suit ID diff --git a/src/klondike.rs b/src/klondike.rs index 3a03a1b..d0e9eb1 100644 --- a/src/klondike.rs +++ b/src/klondike.rs @@ -86,7 +86,61 @@ impl Game for Klondike { vec![].into_iter() } fn validate_instruction(&self, instruction: Self::Instruction) -> bool { - todo!() + match instruction { + // Stock -> Stock draws a card or resets the stock + KlondikeInstruction { + src: KlondikePileId::Stock, + dst: KlondikePileId::Stock, + } => { + // cannot move stock when stock is empty + !self.pile(KlondikePileId::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::Foundation0 + | KlondikePileId::Foundation1 + | KlondikePileId::Foundation2 + | KlondikePileId::Foundation3 + ) => + { + // get the top cards + if let Some(src_card) = self.pile(src).face_up().last() + && let Some(dst_card) = self.pile(dst).face_up().last() + // suit matches? + && src_card.suit() == dst_card.suit() + // value is +1? + && dst_card.value().checked_add(1) == Some(src_card.value()) + { + true + } else { + false + } + } + // other = move to tableau + KlondikeInstruction { src, dst } => { + // get the top cards + if let Some(src_card) = self.pile(src).face_up().last() + && let Some(dst_card) = self.pile(dst).face_up().last() + // red-ness is opposite? + && src_card.is_red() != dst_card.is_red() + // value is -1? + && dst_card.value().checked_sub(1) == Some(src_card.value()) + { + true + } else { + false + } + } + } } fn process_instruction(&mut self, instruction: Self::Instruction) { let card = self.pile_mut(instruction.src).pop().unwrap();