diff --git a/card_game/src/lib.rs b/card_game/src/lib.rs index 172e987..24a5f85 100644 --- a/card_game/src/lib.rs +++ b/card_game/src/lib.rs @@ -312,6 +312,18 @@ impl Pile { } } +#[derive(Clone, Debug)] +pub enum SolveError { + MovesBudgetExceeded, + StatesBudgetExceeded, +} +impl std::fmt::Display for SolveError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} +impl std::error::Error for SolveError {} + #[derive(Clone, Debug)] pub enum SessionInstruction { Undo, @@ -338,12 +350,16 @@ impl SessionStats { pub struct SessionConfig { pub inner: C, pub undo_penalty: i32, + pub solve_moves_budget: u64, + pub solve_states_budget: u64, } impl SessionConfig { fn new_default(inner: C) -> Self { Self { inner, undo_penalty: -15, + solve_moves_budget: 100_000, + solve_states_budget: 100_000, } } } @@ -438,10 +454,18 @@ where pub fn is_win(&self) -> bool { self.state.is_win() } - pub fn is_winnable(&self) -> Option>> { + pub fn solve(&self) -> Result>>, SolveError> { let mut state_moves = std::collections::HashMap::new(); let mut state = self.clone(); + let mut moves = 0; while !state.is_win() { + moves += 1; + if self.config.solve_moves_budget < moves { + return Err(SolveError::MovesBudgetExceeded); + } + if self.config.solve_states_budget < state_moves.len() as u64 { + return Err(SolveError::StatesBudgetExceeded); + } // Continue existing iterator if it exists let it = state_moves .entry(state.state().state().clone()) @@ -460,12 +484,12 @@ where // No more moves. If we can't undo we're done if state.history().is_empty() { - return None; + return Ok(None); } else { state.undo(); } } - Some(state.state.history) + Ok(Some(state.state.history)) } } impl> Game for SessionState diff --git a/klondike-cli/src/test.rs b/klondike-cli/src/test.rs index 9e0229b..99d96de 100644 --- a/klondike-cli/src/test.rs +++ b/klondike-cli/src/test.rs @@ -3,8 +3,8 @@ use klondike::Klondike; #[test] fn test_is_winnable() { // is winnable - let is_winnable = Session::new_default(Klondike::with_seed(124)).is_winnable(); - if let Some(win_moves) = is_winnable { + let solution_result = Session::new_default(Klondike::with_seed(124)).solve(); + if let Ok(Some(win_moves)) = solution_result { // for (i, ins) in win_moves.into_iter().enumerate() { // println!("{i} = {:?}", ins.instruction()); // }