From 82b5020da476bfb096e8566e172cf8935fcd9568 Mon Sep 17 00:00:00 2001 From: Rhys Lloyd Date: Fri, 15 May 2026 09:10:52 -0700 Subject: [PATCH] implement is_winnable --- src/card_game.rs | 25 ++++++++++++++++++++----- src/klondike.rs | 6 +++--- src/test.rs | 4 ++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/card_game.rs b/src/card_game.rs index 23f4d66..ad165fb 100644 --- a/src/card_game.rs +++ b/src/card_game.rs @@ -97,7 +97,7 @@ impl Card { } } -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Stack(Vec); impl Stack { pub fn new() -> Self { @@ -131,7 +131,7 @@ impl std::ops::DerefMut for Stack { } } -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Pile { face_down: Stack, face_up: Stack, @@ -175,14 +175,15 @@ impl Pile { } } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Session { seed: G, state: G, history: Vec, } -impl Session +impl Session where - G::Instruction: Clone, + G::Instruction: Clone + Eq + core::hash::Hash, { pub fn new(state: G) -> Self { Self { @@ -195,7 +196,21 @@ where &self.history } pub fn is_winnable(&self) -> Option> { - None + let mut observed_states = std::collections::HashSet::new(); + let mut state = self.clone(); + 'outer: while !state.is_win() { + observed_states.insert(state.clone()); + for instruction in state.possible_instructions() { + let mut next_state = state.clone(); + next_state.process_instruction(instruction); + if !observed_states.contains(&next_state) { + state = next_state; + continue 'outer; + } + } + return None; + } + Some(state.history) } pub fn undo(&mut self) { // replay the entire history of the game except one move diff --git a/src/klondike.rs b/src/klondike.rs index 4b4720e..b5ada92 100644 --- a/src/klondike.rs +++ b/src/klondike.rs @@ -1,7 +1,7 @@ use crate::Rng; use crate::card_game::{CardValue, Game, Pile, Stack}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct KlondikeConfig {} impl Default for KlondikeConfig { fn default() -> Self { @@ -67,7 +67,7 @@ impl KlondikeInstruction { } } -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] struct KlondikeState { piles: [Pile; 13], } @@ -163,7 +163,7 @@ impl Iterator for KlondikeIter { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Klondike { config: KlondikeConfig, state: KlondikeState, diff --git a/src/test.rs b/src/test.rs index 14aee24..bedeae0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -8,7 +8,8 @@ fn test_klondike() { let mut session = Session::new(game); // is winnable - let is_winnable = session.is_winnable().is_some(); + let is_winnable = session.is_winnable(); + println!("is_winnable = {is_winnable:?}"); // play game while let Some(instruction) = session.possible_instructions().next() { @@ -23,6 +24,5 @@ fn test_klondike() { println!("move {i} = {instruction:?}"); } - println!("is_winnable = {is_winnable}"); println!("is_win = {is_win}"); }