2 Commits

Author SHA1 Message Date
Quaternions 90d46902ea move auto moves into klondike 2026-05-18 13:08:15 -07:00
Quaternions fd6b2a23ea add is_win_trivial 2026-05-18 13:00:15 -07:00
2 changed files with 65 additions and 46 deletions
+1 -46
View File
@@ -212,51 +212,6 @@ fn find_valid_instruction(
.then_some(instruction)
}
fn get_good_move(state: &Klondike) -> Option<KlondikeInstruction> {
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!");
+64
View File
@@ -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;
@@ -575,6 +585,60 @@ impl Klondike {
pub const fn state(&self) -> &KlondikeState {
&self.state
}
/// Check if the game should be auto-completed
pub fn is_win_trivial(&self) -> bool {
// all face down cards empty means win
self.state.stock.face_down().is_empty()
&& self.state.tableau1.face_down().is_empty()
&& self.state.tableau2.face_down().is_empty()
&& self.state.tableau3.face_down().is_empty()
&& self.state.tableau4.face_down().is_empty()
&& self.state.tableau5.face_down().is_empty()
&& 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<KlondikeInstruction> {
self.possible_instructions()
.filter(|ins| !ins.is_useless())
.min_by_key(|ins| self.instruction_priority(ins))
}
pub fn get_sorted_moves(&self) -> Vec<KlondikeInstruction> {
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 {