diff --git a/klondike-cli/src/main.rs b/klondike-cli/src/main.rs index bbd1ab4..3721b75 100644 --- a/klondike-cli/src/main.rs +++ b/klondike-cli/src/main.rs @@ -212,51 +212,6 @@ fn find_valid_instruction( .then_some(instruction) } -fn get_good_move(state: &Klondike) -> Option { - fn useless_moves(instruction: &KlondikeInstruction) -> bool { - !matches!( - instruction, - // foundation -> foundation is a useless move - KlondikeInstruction::DstFoundation(DstFoundation { - src: KlondikePile::Foundation(_), - .. - }) - ) - } - fn instruction_priority(state: &Klondike, instruction: &KlondikeInstruction) -> usize { - // 1 Move into foundation - // 2 T->T Move to reveal new card (moving a non-king to reveal empty tableau also counts) - // 3 Move from stock - // 4 Rotate stock - // 5 T->T Move not revealing new card - // 6 Move from foundation - match instruction { - KlondikeInstruction::DstFoundation(_) => 1, - &KlondikeInstruction::DstTableau(dst_tableau) => match dst_tableau.src { - KlondikePileStack::Tableau(TableauStack { - tableau, - skip_cards: SkipCards::Skip0, - }) if !state.state().is_tableau_face_down_empty(tableau) - || state - .state() - .stack_bottom_card(dst_tableau.src) - .is_some_and(|card| card.rank() != Rank::King) => - { - 2 - } - KlondikePileStack::Stock => 3, - KlondikePileStack::Tableau(_) => 5, - KlondikePileStack::Foundation(_) => 6, - }, - KlondikeInstruction::RotateStock => 4, - } - } - state - .possible_instructions() - .filter(useless_moves) - .min_by_key(|ins| instruction_priority(state, ins)) -} - fn main() -> Result<(), std::io::Error> { use rand::RngExt; let mut rng = rand::rng(); @@ -297,7 +252,7 @@ fn main() -> Result<(), std::io::Error> { } } SessionInstruction::Auto => { - if let Some(instruction) = get_good_move(session.state()) { + if let Some(instruction) = session.state().get_auto_move() { session.process_instruction(instruction); } else { println!("No valid moves!"); diff --git a/klondike/src/lib.rs b/klondike/src/lib.rs index 1f483f8..c17d881 100644 --- a/klondike/src/lib.rs +++ b/klondike/src/lib.rs @@ -299,6 +299,16 @@ impl KlondikeInstruction { Self::RotateStock => return None, }) } + /// foundation -> foundation is a useless move + pub fn is_useless(&self) -> bool { + matches!( + self, + KlondikeInstruction::DstFoundation(DstFoundation { + src: KlondikePile::Foundation(_), + .. + }) + ) + } } const TABLEAUS: usize = 7; @@ -587,6 +597,48 @@ impl Klondike { && self.state.tableau6.face_down().is_empty() && self.state.tableau7.face_down().is_empty() } + fn instruction_priority(&self, instruction: &KlondikeInstruction) -> usize { + // 1 Move into foundation + // 2 T->T Move to reveal new card (moving a non-king to reveal empty tableau also counts) + // 3 Move from stock + // 4 Rotate stock + // 5 T->T Move not revealing new card + // 6 Move from foundation + match instruction { + KlondikeInstruction::DstFoundation(_) => 1, + &KlondikeInstruction::DstTableau(dst_tableau) => match dst_tableau.src { + KlondikePileStack::Tableau(TableauStack { + tableau, + skip_cards: SkipCards::Skip0, + }) if !self.state().is_tableau_face_down_empty(tableau) + || self + .state() + .stack_bottom_card(dst_tableau.src) + .is_some_and(|card| card.rank() != Rank::King) => + { + 2 + } + KlondikePileStack::Stock => 3, + KlondikePileStack::Tableau(_) => 5, + KlondikePileStack::Foundation(_) => 6, + }, + KlondikeInstruction::RotateStock => 4, + } + } + /// A list of possible moves sorted by a simple prioirty function + pub fn get_auto_move(&self) -> Option { + self.possible_instructions() + .filter(|ins| !ins.is_useless()) + .min_by_key(|ins| self.instruction_priority(ins)) + } + pub fn get_sorted_moves(&self) -> Vec { + let mut useful_moves: Vec<_> = self + .possible_instructions() + .filter(|ins| !ins.is_useless()) + .collect(); + useful_moves.sort_by_key(|ins| self.instruction_priority(ins)); + useful_moves + } } impl Game for Klondike {