Skip to content

Commit

Permalink
Collapse moves between FMC scramble phases.
Browse files Browse the repository at this point in the history
  • Loading branch information
lgarron committed Jul 18, 2024
1 parent a42f38d commit e0493c4
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
87 changes: 87 additions & 0 deletions src/rs/scramble/collapse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use cubing::alg::{Alg, AlgNode, Move};

/// This is a minimal implementation of https://js.cubing.net/cubing/api/classes/alg.Alg.html#experimentalSimplify for collapsing moves between phases.
/// For face turns and face rotations of a cube, pass:
/// - `mod_n`: 4
/// - `mod_offset`: 1
pub fn collapse_adjacent_moves(alg: Alg, mod_n: i32, mod_offset: i32) -> Alg {
let mut nodes = Vec::<AlgNode>::new();

let mut maybe_pending_move: Option<Move> = None;
for node in alg.nodes {
maybe_pending_move = if let AlgNode::MoveNode(mut new_move) = node {
if let Some(pending_move) = maybe_pending_move {
if new_move.quantum.family == pending_move.quantum.family {
let new_amount =
(new_move.amount + pending_move.amount - mod_offset) % mod_n + mod_offset;
if new_amount == 0 {
if let Some(popped_node) = nodes.pop() {
if let AlgNode::MoveNode(popped_move) = popped_node {
Some(popped_move)
} else {
nodes.push(popped_node);
None
}
} else {
None
}
} else {
new_move.amount = new_amount;
Some(new_move)
}
} else {
nodes.push(pending_move.into());
Some(new_move)
}
} else {
Some(new_move)
}
} else {
if let Some(pending_move) = maybe_pending_move {
nodes.push(pending_move.into());
}
nodes.push(node);
None
}
}
if let Some(pending_move) = maybe_pending_move {
nodes.push(pending_move.into());
}
Alg { nodes }
}

#[test]
fn collapse_test() {
use cubing::alg::parse_alg;

assert_eq!(
collapse_adjacent_moves(
parse_alg!(
"R' U' F R2 D U B D U' L' B2 U' F F2 D' B2 D' L2 D' R2 B2 R2 F2 U' B2 D' R' U' F"
),
4,
-1
),
parse_alg!("R' U' F R2 D U B D U' L' B2 U' F' D' B2 D' L2 D' R2 B2 R2 F2 U' B2 D' R' U' F")
);

assert_eq!(
collapse_adjacent_moves(parse_alg!("R F F' R"), 4, -1),
parse_alg!("R2")
);

assert_eq!(
collapse_adjacent_moves(parse_alg!("R F F2 F R"), 4, -1),
parse_alg!("R2")
);

assert_eq!(
collapse_adjacent_moves(parse_alg!("R F F2 . F R"), 4, -1),
parse_alg!("R F' . F R")
);

assert_eq!(
collapse_adjacent_moves(parse_alg!("R F F2 R"), 5, -2),
parse_alg!("R F2' R")
);
}
1 change: 1 addition & 0 deletions src/rs/scramble/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod collapse;
mod puzzles;
mod randomize;
mod scramble_search;
Expand Down
6 changes: 5 additions & 1 deletion src/rs/scramble/puzzles/cube3x3x3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use lazy_static::lazy_static;
use crate::{
_internal::{IDFSearch, IndividualSearchOptions},
scramble::{
collapse::collapse_adjacent_moves,
randomize::{basic_parity, BasicParity},
scramble_search::{basic_idfs, idfs_with_target_pattern},
},
Expand Down Expand Up @@ -239,5 +240,8 @@ pub fn scramble_3x3x3_fmc() -> Alg {
nodes.push(r#move.into());
}

Alg { nodes }
// Note: `collapse_adjacent_moves(…)` is technically overkill, as it's only
// possible for a single move to overlap without completely cancelling.
// However, it's safer to use a common function for this instead of a one-off implementation.
collapse_adjacent_moves(Alg { nodes }, 4, -1)
}

0 comments on commit e0493c4

Please sign in to comment.