3 Commits

Author SHA1 Message Date
Quaternions 64e32b1010 better iter order 2026-05-19 11:43:23 -07:00
Quaternions 850bd0f8ee use i64 to avoid overflow 2026-05-19 11:43:13 -07:00
Quaternions bc2d1b126e clean history 2026-05-19 10:22:44 -07:00
2 changed files with 35 additions and 7 deletions
+28
View File
@@ -405,6 +405,7 @@ where
pub fn is_winnable(&self) -> Option<Vec<StateSnapshot<G>>> { pub fn is_winnable(&self) -> Option<Vec<StateSnapshot<G>>> {
let mut state_moves = std::collections::HashMap::new(); let mut state_moves = std::collections::HashMap::new();
let mut state = self.clone(); let mut state = self.clone();
let mut i: u64 = 0;
while !state.is_win() { while !state.is_win() {
// Continue existing iterator if it exists // Continue existing iterator if it exists
let it = state_moves let it = state_moves
@@ -424,6 +425,33 @@ where
state.undo(); state.undo();
} }
} }
// history includes cycles
let mut state_index: std::collections::HashMap<_, _> = state
.history()
.iter()
.enumerate()
.map(|(i, snapshot)| (snapshot.state().clone(), i))
.collect();
// find the longest range where the start and end are the same state
while let Some(longest_range) = state
.history()
.iter()
.enumerate()
.filter_map(|(index, snapshot)| {
let &last_index = state_index.get(snapshot.state())?;
let longness = last_index - index;
(longness != 0).then_some(index..last_index)
})
.max_by_key(|range| range.len())
{
state.state.history.drain(longest_range);
for (i, snapshot) in state.history().iter().enumerate() {
state_index.insert(snapshot.state().clone(), i);
}
}
Some(state.state.history) Some(state.state.history)
} }
} }
+7 -7
View File
@@ -629,18 +629,19 @@ impl Klondike {
KlondikeInstruction::RotateStock => 4, KlondikeInstruction::RotateStock => 4,
} }
} }
pub fn iter(&self) -> impl Iterator<Item = KlondikeInstruction> + use<> {
let state = self.state.clone();
KlondikeIter::new().filter(move |&instruction| state.is_instruction_valid(instruction))
}
/// A single move that usually makes progress towards a winning game /// A single move that usually makes progress towards a winning game
pub fn get_auto_move(&self) -> Option<KlondikeInstruction> { pub fn get_auto_move(&self) -> Option<KlondikeInstruction> {
self.possible_instructions() self.iter()
.filter(|ins| !ins.is_useless()) .filter(|ins| !ins.is_useless())
.min_by_key(|ins| self.instruction_priority(ins)) .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 /// A list of possible moves with useless moves filtered out and sorted by a simple priority function
pub fn get_sorted_moves(&self) -> Vec<KlondikeInstruction> { pub fn get_sorted_moves(&self) -> Vec<KlondikeInstruction> {
let mut useful_moves: Vec<_> = self let mut useful_moves: Vec<_> = self.iter().filter(|ins| !ins.is_useless()).collect();
.possible_instructions()
.filter(|ins| !ins.is_useless())
.collect();
useful_moves.sort_by_key(|ins| self.instruction_priority(ins)); useful_moves.sort_by_key(|ins| self.instruction_priority(ins));
useful_moves useful_moves
} }
@@ -651,8 +652,7 @@ impl Game for Klondike {
type Config = KlondikeConfig; type Config = KlondikeConfig;
type Instruction = KlondikeInstruction; type Instruction = KlondikeInstruction;
fn possible_instructions(&self) -> impl Iterator<Item = Self::Instruction> + use<> { fn possible_instructions(&self) -> impl Iterator<Item = Self::Instruction> + use<> {
let state = self.state.clone(); self.get_sorted_moves().into_iter()
KlondikeIter::new().filter(move |&instruction| state.is_instruction_valid(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(instruction) self.state.is_instruction_valid(instruction)