diff --git a/klondike-bench/src/main.rs b/klondike-bench/src/main.rs index 611efa5..7f61c66 100644 --- a/klondike-bench/src/main.rs +++ b/klondike-bench/src/main.rs @@ -21,7 +21,9 @@ fn play_to_win(rng: &mut Rng) -> Option { return None; } - game.process_instruction(&mut stats, &CONFIG, instruction); + game.stage_instruction(&mut stats, &CONFIG, instruction) + .unwrap() + .commit(); } game.is_win().then_some(stats) } diff --git a/klondike-cli/src/main.rs b/klondike-cli/src/main.rs index 60c3740..faee0e5 100644 --- a/klondike-cli/src/main.rs +++ b/klondike-cli/src/main.rs @@ -210,7 +210,7 @@ fn find_valid_instruction( }); let instruction = KlondikeInstruction::DstTableau(DstTableau { tableau, src }); - if state.is_instruction_valid(config, instruction) { + if state.is_instruction_valid(config, &instruction) { return Some(instruction); } } @@ -228,7 +228,7 @@ fn find_valid_instruction( _ => return None, }; state - .is_instruction_valid(config, instruction) + .is_instruction_valid(config, &instruction) .then_some(instruction) } @@ -277,13 +277,16 @@ fn main() -> Result<(), std::io::Error> { .state() .get_auto_move(&session.config().inner) { - session.process_instruction(instruction); + session.stage_instruction(instruction).unwrap().commit(); } else { println!("No valid moves!"); } } SessionInstruction::Stock => { - session.process_instruction(KlondikeInstruction::RotateStock) + session + .stage_instruction(KlondikeInstruction::RotateStock) + .unwrap() + .commit(); } SessionInstruction::Klondike(naive_instruction) => { if let Some(instruction) = find_valid_instruction( @@ -291,7 +294,7 @@ fn main() -> Result<(), std::io::Error> { session.state().state(), naive_instruction, ) { - session.process_instruction(instruction); + session.stage_instruction(instruction).unwrap().commit(); } else { println!("Invalid move!"); } diff --git a/klondike/README.md b/klondike/README.md index 1cd8cd3..a86c762 100644 --- a/klondike/README.md +++ b/klondike/README.md @@ -16,7 +16,7 @@ let mut session = Session::new_default(game); // play game a bit while let Some(instruction) = session.state().state().get_auto_move(&config) { - session.process_instruction(instruction); + session.stage_instruction(instruction).unwrap().commit(); // quit after 200 moves or win if session.is_win() || 200 < session.stats().stats().moves() { diff --git a/klondike/src/lib.rs b/klondike/src/lib.rs index e8c2698..67dd402 100644 --- a/klondike/src/lib.rs +++ b/klondike/src/lib.rs @@ -1,6 +1,6 @@ pub type Rng = rand::rngs::StdRng; -use card_game::{Card, Game, Pile, Rank, Stack}; +use card_game::{Card, Game, InstructionConsumer, Pile, Rank, Stack, StagedInstruction}; #[cfg(test)] mod test; @@ -621,7 +621,7 @@ impl KlondikeState { pub fn is_instruction_valid( &self, config: &KlondikeConfig, - instruction: KlondikeInstruction, + instruction: &KlondikeInstruction, ) -> bool { match instruction { // Stock -> Stock draws a card or resets the stock @@ -830,54 +830,11 @@ impl Game for Klondike { let state = self.state.clone(); let config = config.clone(); KlondikeIter::new() - .filter(move |&instruction| state.is_instruction_valid(&config, instruction)) + .filter(move |instruction| state.is_instruction_valid(&config, instruction)) } - fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool { + fn is_instruction_valid(&self, config: &Self::Config, instruction: &Self::Instruction) -> bool { self.state.is_instruction_valid(config, instruction) } - fn process_instruction( - &mut self, - stats: &mut Self::Stats, - config: &Self::Config, - instruction: Self::Instruction, - ) { - stats.increment_moves(); - match instruction { - // Reset the stock if it's empty - KlondikeInstruction::RotateStock => { - if self.state.stock.face_down().is_empty() { - self.state.stock.flip_it_and_reverse_it(); - stats.increment_recycle_count(); - } else { - for _ in 0..config.draw_stock as usize { - self.state.stock.flip_up(); - } - } - } - // Move a card from anywhere to a foundation - KlondikeInstruction::DstFoundation(DstFoundation { src, foundation }) => { - stats.increment_move_to_foundation(); - let (card, did_flip_up) = self.state.take_top_card_flip_up(src); - if did_flip_up { - stats.increment_flip_up_bonus(); - } - self.state.extend_foundation(foundation, card); - } - // Move a stack of cards from anywhere to a tableau - KlondikeInstruction::DstTableau(DstTableau { src, tableau }) => { - match src { - KlondikePileStack::Stock => stats.increment_move_to_tableau(), - KlondikePileStack::Foundation(_) => stats.increment_move_from_foundation(), - KlondikePileStack::Tableau(_) => {} - } - let (cards, did_flip_up) = self.state.take_stack_flip_up(src); - if did_flip_up { - stats.increment_flip_up_bonus(); - } - self.state.extend_tableau(tableau, cards); - } - } - } fn is_win(&self) -> bool { // all foundations contain all ranks self.state.foundations.iter().all(|foundation| { @@ -889,6 +846,47 @@ impl Game for Klondike { }) } } +impl InstructionConsumer for Klondike { + fn process_instruction(valid_instruction: StagedInstruction<'_, Self>) { + let (game, stats, config, instruction) = valid_instruction.consume(); + stats.increment_moves(); + match instruction { + // Reset the stock if it's empty + KlondikeInstruction::RotateStock => { + if game.state.stock.face_down().is_empty() { + game.state.stock.flip_it_and_reverse_it(); + stats.increment_recycle_count(); + } else { + for _ in 0..config.draw_stock as usize { + game.state.stock.flip_up(); + } + } + } + // Move a card from anywhere to a foundation + KlondikeInstruction::DstFoundation(DstFoundation { src, foundation }) => { + stats.increment_move_to_foundation(); + let (card, did_flip_up) = game.state.take_top_card_flip_up(src); + if did_flip_up { + stats.increment_flip_up_bonus(); + } + game.state.extend_foundation(foundation, card); + } + // Move a stack of cards from anywhere to a tableau + KlondikeInstruction::DstTableau(DstTableau { src, tableau }) => { + match src { + KlondikePileStack::Stock => stats.increment_move_to_tableau(), + KlondikePileStack::Foundation(_) => stats.increment_move_from_foundation(), + KlondikePileStack::Tableau(_) => {} + } + let (cards, did_flip_up) = game.state.take_stack_flip_up(src); + if did_flip_up { + stats.increment_flip_up_bonus(); + } + game.state.extend_tableau(tableau, cards); + } + } + } +} #[cfg(feature = "serde")] impl serde::Serialize for KlondikeInstruction { diff --git a/klondike/src/test.rs b/klondike/src/test.rs index ed8168f..1ad8c87 100644 --- a/klondike/src/test.rs +++ b/klondike/src/test.rs @@ -23,7 +23,10 @@ fn test_json() { let solution_result = session.solve(); if let Ok(Some(solution)) = solution_result { for snapshot in solution.clean_solution() { - session.process_instruction(snapshot.instruction().clone()); + session + .stage_instruction(snapshot.instruction().clone()) + .unwrap() + .commit(); } } let serialized = serde_json::to_string(&session).unwrap(); @@ -42,7 +45,10 @@ fn test_rmp() { let solution_result = session.solve(); if let Ok(Some(solution)) = solution_result { for snapshot in solution.clean_solution() { - session.process_instruction(snapshot.instruction().clone()); + session + .stage_instruction(snapshot.instruction().clone()) + .unwrap() + .commit(); } } let serialized = rmp_serde::to_vec(&session).unwrap();