Skip to content

Commit

Permalink
Switch from disallowed final quanta to canonical state post-moves.
Browse files Browse the repository at this point in the history
  • Loading branch information
lgarron committed Oct 9, 2024
1 parent 52299e9 commit 649fae8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 76 deletions.
6 changes: 3 additions & 3 deletions src/rs/_internal/canonical_fsm/search_generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ impl SearchGenerators {
let mut move_multiple = r#move.clone();
move_multiple.amount = canonicalize_center_amount(order, amount);
let info = MoveTransformationInfo {
r#move: move_multiple,
r#move: move_multiple.clone(),
// metric_turns: 1, // TODO
transformation: move_multiple_transformation.current().clone(),
inverse_transformation: move_multiple_transformation.current().invert(),
};
multiples.push(info.clone());
flat.push(info.clone());
by_move.insert(r#move.clone(), (move_class_index, info));
by_move.insert(move_multiple, (move_class_index, info));

amount += r#move.amount;
move_multiple_transformation.apply_transformation(&move_transformation);
Expand All @@ -160,7 +160,7 @@ impl SearchGenerators {
};
multiples.push(info.clone());
flat.push(info.clone());
by_move.insert(r#move.clone(), (move_class_index, info));
by_move.insert(r#move.invert(), (move_class_index, info));
}
}
}
Expand Down
127 changes: 65 additions & 62 deletions src/rs/_internal/search/idf_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::{
};

use cubing::{
alg::{Alg, AlgNode, Move, QuantumMove},
alg::{Alg, AlgNode, Move},
kpuzzle::{KPattern, KPuzzle},
};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -107,19 +107,7 @@ pub struct IndividualSearchOptions {
pub min_depth: Option<usize>, // inclusive
pub max_depth: Option<usize>, // exclusive
pub canonical_fsm_pre_moves: Option<Vec<Move>>,
pub disallowed_final_quanta: Option<Vec<QuantumMove>>, // TODO: Find a way to represent this using disallowed final FSM states?
}

fn is_move_disallowed(r#move: &Move, disallowed_quanta: &Option<Vec<QuantumMove>>) -> bool {
// TODO Use something like a `HashSet` to speed this up?
if let Some(disallowed_quanta) = disallowed_quanta {
for disallowed_quantum in disallowed_quanta {
if r#move.quantum.as_ref() == disallowed_quantum {
return true;
}
}
}
false
pub canonical_fsm_post_moves: Option<Vec<Move>>,
}

impl IndividualSearchOptions {
Expand Down Expand Up @@ -241,7 +229,14 @@ impl IDFSearch {
individual_search_data
.recursive_work_tracker
.start_depth(remaining_depth, Some("Starting search…"));
let initial_state = self.initial_state(&individual_search_data);
let initial_state = self
.apply_optional_fsm_moves(
CANONICAL_FSM_START_STATE,
&individual_search_data
.individual_search_options
.canonical_fsm_pre_moves,
)
.expect("TODO: invalid canonical FSM pre-moves.");
let recursion_result = self.recurse(
&mut individual_search_data,
&mut kpattern_stack,
Expand Down Expand Up @@ -272,17 +267,12 @@ impl IDFSearch {
.record_recursive_call();
let current_pattern = kpattern_stack.current_pattern();
if remaining_depth == 0 {
if let Some(previous_moves) = solution_moves.0 {
if is_move_disallowed(
previous_moves.latest_move,
&individual_search_data
.individual_search_options
.disallowed_final_quanta,
) {
return SearchRecursionResult::ContinueSearchingDefault();
}
}
return self.base_case(current_pattern, individual_search_data, solution_moves);
return self.base_case(
individual_search_data,
current_pattern,
current_state,
solution_moves,
);
}
let prune_table_depth = self.prune_table.lookup(current_pattern);
if prune_table_depth > remaining_depth + 1 {
Expand All @@ -294,15 +284,12 @@ impl IDFSearch {
for (move_class_index, move_transformation_multiples) in
self.api_data.search_generators.grouped.iter().enumerate()
{
let next_state = match self
let Some(next_state) = self
.api_data
.canonical_fsm
.next_state(current_state, MoveClassIndex(move_class_index))
{
Some(next_state) => next_state,
None => {
continue;
}
else {
continue;
};

for move_transformation_info in move_transformation_multiples {
Expand Down Expand Up @@ -333,12 +320,14 @@ impl IDFSearch {
SearchRecursionResult::ContinueSearchingDefault()
}

fn initial_state(&self, individual_search_data: &IndividualSearchData) -> CanonicalFSMState {
let mut current_state = CANONICAL_FSM_START_STATE;
if let Some(moves) = &individual_search_data
.individual_search_options
.canonical_fsm_pre_moves
{
// Returns `None` if the moves cannot be applied, else returns the result of applying the moves.
fn apply_optional_fsm_moves(
&self,
start_state: CanonicalFSMState,
moves: &Option<Vec<Move>>,
) -> Option<CanonicalFSMState> {
let mut current_state = start_state;
if let Some(moves) = moves {
for r#move in moves {
let move_class_index = self
.api_data
Expand All @@ -353,41 +342,55 @@ impl IDFSearch {
.next_state(current_state, move_class_index)
{
Some(next_state) => next_state,
None => {
panic!("TODO: invalid canonical FSM pre-moves.");
}
None => return None,
}
}
};
current_state
}
Some(current_state)
}

fn base_case(
&self,
current_pattern: &KPattern,
individual_search_data: &mut IndividualSearchData,
current_pattern: &KPattern,
current_state: CanonicalFSMState,
solution_moves: SolutionMoves,
) -> SearchRecursionResult {
if current_pattern == &self.api_data.target_pattern {
individual_search_data.num_solutions_sofar += 1;
let alg = Alg::from(solution_moves);
if current_pattern != &self.api_data.target_pattern {
return SearchRecursionResult::ContinueSearchingDefault();
}
if self
.apply_optional_fsm_moves(
current_state,
&individual_search_data
.individual_search_options
.canonical_fsm_post_moves,
)
.is_none()
{
self.api_data.search_logger.write_info(&format!(
"Rejecting potential solution for invalid end moves: {}",
Alg::from(solution_moves)
));
return SearchRecursionResult::ContinueSearchingDefault();
}

individual_search_data.num_solutions_sofar += 1;
let alg = Alg::from(solution_moves);
individual_search_data
.solution_sender
.send(Some(alg))
.expect("Internal error: could not send solution");
if individual_search_data.num_solutions_sofar
>= individual_search_data
.individual_search_options
.get_min_num_solutions()
{
individual_search_data
.solution_sender
.send(Some(alg))
.expect("Internal error: could not send solution");
if individual_search_data.num_solutions_sofar
>= individual_search_data
.individual_search_options
.get_min_num_solutions()
{
individual_search_data
.solution_sender
.send(None)
.expect("Internal error: could not send end of search");
SearchRecursionResult::DoneSearching()
} else {
SearchRecursionResult::ContinueSearchingDefault()
}
.send(None)
.expect("Internal error: could not send end of search");
SearchRecursionResult::DoneSearching()
} else {
SearchRecursionResult::ContinueSearchingDefault()
}
Expand Down
17 changes: 6 additions & 11 deletions src/rs/scramble/puzzles/cube3x3x3.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::Mutex;

use cubing::{
alg::{parse_move, Alg, AlgNode, Move, QuantumMove},
alg::{parse_move, Alg, AlgNode, Move},
kpuzzle::{KPattern, KPuzzle},
};
use lazy_static::lazy_static;
Expand Down Expand Up @@ -109,16 +109,12 @@ impl Scramble3x3x3TwoPhase {
pattern: &KPattern,
constraints: PrefixOrSuffixConstraints,
) -> Alg {
// TODO: once perf is good enough, use `F`` as "required first move" and `R'` as "required last move" in the search (overlapping with the affixes).
let (canonical_fsm_pre_moves, disallowed_final_quanta) = match constraints {
let (canonical_fsm_pre_moves, canonical_fsm_post_moves) = match constraints {
PrefixOrSuffixConstraints::None => (None, None),
PrefixOrSuffixConstraints::ForFMC => {
let canonical_fsm_pre_moves = Some(vec![
// We don't have to specify R' and U' because we know the FSM only depends on the final `F` move.
parse_move!("L"),
]);
let disallowed_final_quanta = Some(static_parsed_list::<QuantumMove>(&["R", "L"]));
(canonical_fsm_pre_moves, disallowed_final_quanta)
// For the pre-moves, we don't have to specify R' and U' because we know the FSM only depends on the final `F` move.
// For similar reasons, we only have to specify R' for the post-moves.
(Some(vec![parse_move!("F")]), Some(vec![parse_move!("R'")]))
}
};

Expand All @@ -130,7 +126,7 @@ impl Scramble3x3x3TwoPhase {
IndividualSearchOptions {
min_num_solutions: Some(1),
canonical_fsm_pre_moves,
disallowed_final_quanta: disallowed_final_quanta.clone(), // TODO: We currently need to pass this in case phase 2 return the empty alg. Can we handle this in another way?
canonical_fsm_post_moves, // TODO: We currently need to pass this in case phase 2 return the empty alg. Can we handle this in another way?
..Default::default()
},
)
Expand All @@ -146,7 +142,6 @@ impl Scramble3x3x3TwoPhase {
&phase2_search_pattern,
IndividualSearchOptions {
min_num_solutions: Some(1),
disallowed_final_quanta,
..Default::default()
},
)
Expand Down

0 comments on commit 649fae8

Please sign in to comment.