diff --git a/card_game/src/lib.rs b/card_game/src/lib.rs index 20d92eb..8c09b7a 100644 --- a/card_game/src/lib.rs +++ b/card_game/src/lib.rs @@ -10,7 +10,10 @@ pub trait Game { type Stats; type Config; type Instruction; - fn possible_instructions(&self) -> impl Iterator + use; + fn possible_instructions( + &self, + config: &Self::Config, + ) -> impl Iterator + use; fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool; fn process_instruction( &mut self, @@ -377,7 +380,7 @@ where .process_instruction(&mut self.stats, &self.config, SessionInstruction::Undo) } pub fn possible_instructions(&self) -> impl Iterator + use { - self.state.state.possible_instructions() + self.state.state.possible_instructions(&self.config) } pub fn process_instruction(&mut self, instruction: G::Instruction) { self.state.process_instruction( @@ -399,9 +402,12 @@ where type Stats = SessionStats; type Config = G::Config; type Instruction = SessionInstruction; - fn possible_instructions(&self) -> impl Iterator + use { + fn possible_instructions( + &self, + config: &Self::Config, + ) -> impl Iterator + use { self.state - .possible_instructions() + .possible_instructions(config) .map(SessionInstruction::InnerInstruction) } fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool { diff --git a/klondike-bench/src/main.rs b/klondike-bench/src/main.rs index a63d6d9..632e541 100644 --- a/klondike-bench/src/main.rs +++ b/klondike-bench/src/main.rs @@ -9,9 +9,10 @@ fn play_to_win(rng: &mut Rng) -> Option { let mut stats = KlondikeStats::new(); const CONFIG: KlondikeConfig = KlondikeConfig { draw_stock: klondike::DrawStockConfig::DrawOne, + move_from_foundation: klondike::MoveFromFoundationConfig::Allowed, }; // play game a bit - while let Some(instruction) = game.get_auto_move() + while let Some(instruction) = game.get_auto_move(&CONFIG) && !game.is_win() { // quit before 250 moves diff --git a/klondike-cli/src/main.rs b/klondike-cli/src/main.rs index 9ca81d2..d30725d 100644 --- a/klondike-cli/src/main.rs +++ b/klondike-cli/src/main.rs @@ -274,7 +274,7 @@ fn main() -> Result<(), std::io::Error> { } } SessionInstruction::Auto => { - if let Some(instruction) = session.state().get_auto_move() { + if let Some(instruction) = session.state().get_auto_move(session.config()) { session.process_instruction(instruction); } else { println!("No valid moves!"); diff --git a/klondike/README.md b/klondike/README.md index d6d425f..c063ac0 100644 --- a/klondike/README.md +++ b/klondike/README.md @@ -7,14 +7,15 @@ Klondike ```rust use card_game::Session; -use klondike::Klondike; +use klondike::{Klondike, KlondikeConfig}; // create game session let game = Klondike::with_seed(123); +let config = KlondikeConfig::default(); let mut session = Session::new_default(game); // play game a bit -while let Some(instruction) = session.state().get_auto_move() { +while let Some(instruction) = session.state().get_auto_move(&config) { session.process_instruction(instruction); // quit after 200 moves or win diff --git a/klondike/src/lib.rs b/klondike/src/lib.rs index f396e45..5df4589 100644 --- a/klondike/src/lib.rs +++ b/klondike/src/lib.rs @@ -14,9 +14,17 @@ pub enum DrawStockConfig { DrawThree = 3, } +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub enum MoveFromFoundationConfig { + #[default] + Allowed, + Disallowed, +} + #[derive(Clone, Debug, Default)] pub struct KlondikeConfig { pub draw_stock: DrawStockConfig, + pub move_from_foundation: MoveFromFoundationConfig, } #[derive(Clone, Debug, Default)] @@ -459,7 +467,11 @@ impl KlondikeState { Tableau::Tableau7 => self.tableau7.extend(cards), } } - pub fn is_instruction_valid(&self, instruction: KlondikeInstruction) -> bool { + pub fn is_instruction_valid( + &self, + config: &KlondikeConfig, + instruction: KlondikeInstruction, + ) -> bool { match instruction { // Stock -> Stock draws a card or resets the stock KlondikeInstruction::RotateStock => { @@ -488,6 +500,11 @@ impl KlondikeState { } // other = move to tableau KlondikeInstruction::DstTableau(dst_tableau) => { + if config.move_from_foundation == MoveFromFoundationConfig::Disallowed + && let KlondikePileStack::Foundation(_) = dst_tableau.src + { + return false; + } // get the cards if let Some(src_card) = self.stack_bottom_card(dst_tableau.src) { match self.top_card(dst_tableau.tableau) { @@ -620,15 +637,15 @@ impl Klondike { } } /// A single move that usually makes progress towards a winning game - pub fn get_auto_move(&self) -> Option { - self.possible_instructions() + pub fn get_auto_move(&self, config: &KlondikeConfig) -> Option { + self.possible_instructions(config) .filter(|ins| !ins.is_useless()) .min_by_key(|ins| self.instruction_priority(ins)) } /// A list of possible moves with useless moves filtered out and sorted by a simple priority function - pub fn get_sorted_moves(&self) -> Vec { + pub fn get_sorted_moves(&self, config: &KlondikeConfig) -> Vec { let mut useful_moves: Vec<_> = self - .possible_instructions() + .possible_instructions(config) .filter(|ins| !ins.is_useless()) .collect(); useful_moves.sort_by_key(|ins| self.instruction_priority(ins)); @@ -640,12 +657,17 @@ impl Game for Klondike { type Stats = KlondikeStats; type Config = KlondikeConfig; type Instruction = KlondikeInstruction; - fn possible_instructions(&self) -> impl Iterator + use<> { + fn possible_instructions( + &self, + config: &Self::Config, + ) -> impl Iterator + use<> { let state = self.state.clone(); - KlondikeIter::new().filter(move |&instruction| state.is_instruction_valid(instruction)) + let config = config.clone(); + KlondikeIter::new() + .filter(move |&instruction| state.is_instruction_valid(&config, instruction)) } - fn is_instruction_valid(&self, _config: &Self::Config, instruction: Self::Instruction) -> bool { - self.state.is_instruction_valid(instruction) + fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool { + self.state.is_instruction_valid(config, instruction) } fn process_instruction( &mut self,