diff --git a/three-style-lib/src/commutator/finder.rs b/three-style-lib/src/commutator/finder.rs index e273a08..bdb8d6f 100644 --- a/three-style-lib/src/commutator/finder.rs +++ b/three-style-lib/src/commutator/finder.rs @@ -6,6 +6,8 @@ use crate::{ }; use std::{fmt, ops::Not}; +/// Tracks the state of a moving facelet position, +/// used for detecting interchange and insertions. #[derive(Debug, PartialEq, Clone)] struct Slot { initial_position: Facelet, @@ -108,7 +110,7 @@ impl CommutatorFinder { fn find_interchange(&mut self, params: SearchParams) { let threshold = match self.search_type { SearchType::Corner => 4, - SearchType::Edge => 2, + SearchType::Edge => 2, // possible four movers }; if self.max_depth - params.depth < threshold { @@ -134,14 +136,15 @@ impl CommutatorFinder { fn check_interchange(&self, params: &SearchParams, state: &FaceletCube) -> Option { for slot in ¶ms.slots { - let current = state[slot.current_position]; + let next_value = state[slot.current_position]; - if slot.value != current && params.inside_cycle(current) { - let source = params.get_remaining_slot(slot.value, current); + if slot.value != next_value && params.inside_cycle(next_value) { + let other = params.get_remaining_slot(slot.value, next_value); + let outside_interchage = state[other.current_position] == other.value; - if state[source.current_position] == source.value { + if outside_interchage { return Some(Insertion { - source, + source: other, target: slot.clone(), }); } diff --git a/three-style-lib/src/commutator/types.rs b/three-style-lib/src/commutator/types.rs index 512bd55..d7fd918 100644 --- a/three-style-lib/src/commutator/types.rs +++ b/three-style-lib/src/commutator/types.rs @@ -5,6 +5,7 @@ use crate::{ }; use std::fmt; +/// Represents a 3-cycle commutator. #[derive(Debug, Clone, PartialEq)] #[allow(clippy::len_without_is_empty)] pub struct Commutator { @@ -15,14 +16,17 @@ pub struct Commutator { } impl Commutator { + /// Returns `true` if the commutator has no setup moves. pub fn is_pure(&self) -> bool { self.setup.is_none() } + /// Returns the length og the commutator in its notation form. pub fn len(&self) -> usize { self.setup.as_ref().map_or(0, |s| s.len()) + self.insertion.len() + 1 } + /// Returns the non-reduced expanded algorithm. pub fn expand(&self) -> Alg { let interchange = Alg::new([self.interchange]); let (first, second) = if self.insertion_first { @@ -59,6 +63,7 @@ impl fmt::Display for Commutator { } } +/// Wrapper around 3-cycle targets (stickers). #[derive(Debug, Clone, Copy, PartialEq)] pub struct Cycle { targets: [T; 3], diff --git a/three-style-lib/src/facelet/moves.rs b/three-style-lib/src/facelet/moves.rs index 06b2090..0fa2478 100644 --- a/three-style-lib/src/facelet/moves.rs +++ b/three-style-lib/src/facelet/moves.rs @@ -2,6 +2,9 @@ use super::{types::DEFAULT_STATE, Facelet, FaceletCube, FaceletState}; use crate::moves::Move; use std::ops::Index; +/// State of the cube at the facelet level +/// in the "is carried to" representation. +/// It is used to know where facelets will land on #[derive(Debug, PartialEq, Clone)] pub struct FaceletPermutation(FaceletState); diff --git a/three-style-lib/src/facelet/state.rs b/three-style-lib/src/facelet/state.rs index 224c202..e40d6a5 100644 --- a/three-style-lib/src/facelet/state.rs +++ b/three-style-lib/src/facelet/state.rs @@ -14,6 +14,9 @@ use std::{ ops::{Index, IndexMut, Mul}, }; +/// State of the cube at the facelet level +/// in the "is replaced by" representation. +/// See https://kociemba.org/cube.htm for more details #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct FaceletCube(FaceletState); @@ -421,7 +424,7 @@ mod tests { let cube = FaceletCube::default().cycle(cycle).unwrap(); #[rustfmt::skip] - let expecte = FaceletCube([ + let expected = FaceletCube([ F::U0, F::U7, F::U2, F::U3, F::U4, F::U5, F::U6, F::F3, F::U8, F::R0, F::R1, F::R2, F::R3, F::R4, F::R5, F::R6, F::R7, F::R8, F::F0, F::L5, F::F2, F::U1, F::F4, F::F5, F::F6, F::F7, F::F8, @@ -430,7 +433,7 @@ mod tests { F::B0, F::F1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8, ]); - assert_eq!(expecte, cube); + assert_eq!(expected, cube); let cube = cube.cycle(cycle.inverse()).unwrap(); @@ -443,7 +446,7 @@ mod tests { let cube = FaceletCube::default().cycle(cycle).unwrap(); #[rustfmt::skip] - let expecte = FaceletCube([ + let expected = FaceletCube([ F::U0, F::U1, F::U2, F::U3, F::U4, F::U5, F::U8, F::U7, F::R6, F::D2, F::R1, F::R2, F::R3, F::R4, F::R5, F::U6, F::R7, F::R8, F::R0, F::F1, F::F8, F::F3, F::F4, F::F5, F::F6, F::F7, F::L2, @@ -452,7 +455,7 @@ mod tests { F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8, ]); - assert_eq!(expecte, cube); + assert_eq!(expected, cube); let cube = cube.cycle(cycle.inverse()).unwrap(); diff --git a/three-style-lib/src/facelet/types.rs b/three-style-lib/src/facelet/types.rs index a9c6b23..ddc9912 100644 --- a/three-style-lib/src/facelet/types.rs +++ b/three-style-lib/src/facelet/types.rs @@ -39,6 +39,7 @@ pub trait FaceletTarget { } } +/// Represents all the facelets of a cube in the`URFDLB` order. pub type FaceletState = [Facelet; 54]; #[rustfmt::skip] @@ -51,15 +52,9 @@ pub const DEFAULT_STATE: FaceletState = [ F::B0, F::B1, F::B2, F::B3, F::B4, F::B5, F::B6, F::B7, F::B8, ]; +#[rustfmt::skip] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Color { - U, - R, - F, - D, - L, - B, -} +pub enum Color { U, R, F, D, L, B } impl fmt::Display for Color { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/three-style-lib/src/moves/alg.rs b/three-style-lib/src/moves/alg.rs index 012c671..4e1712f 100644 --- a/three-style-lib/src/moves/alg.rs +++ b/three-style-lib/src/moves/alg.rs @@ -15,23 +15,30 @@ impl Alg { Self(moves.into_iter().collect()) } + /// Returns the number of moves of the algorithm. pub fn len(&self) -> usize { self.0.len() } + /// Returns `true` if the algorithm has no moves. pub fn is_empty(&self) -> bool { - self.len() == 0 + self.0.is_empty() } + /// Returns an iterator over moves. pub fn iter(&self) -> impl Iterator { self.0.iter() } + /// Reduces the algorithm by applying cancellations rules. + /// Example: `U2 D U' R M'` -> `U D r` pub fn reduce(self) -> Self { let mut moves = Vec::new(); let mut stack = Vec::new(); let mut group: BTreeMap = BTreeMap::new(); + // First pass, moves should constantly be re-evaluated + // as long as parallelism isn't broken for m in self.0 { let prev_value = group.remove(&m.kind); let has_prev_value = prev_value.is_some(); @@ -48,6 +55,7 @@ impl Alg { continue; } + // parallelism has been broken so commit the current moves while let Some((_, m)) = group.pop_first() { moves.push(m); } @@ -57,16 +65,17 @@ impl Alg { moves.extend(group.into_values()); + // Second pass, search wide moves generators and reductions + // by trying to reduce move pairs successively for m in moves { - if let Some(&last) = stack.last() { - if let Some(result) = last * m { - stack.pop(); - stack.push(result); - continue; - } - } + let result = stack.last().and_then(|&l| l * m); - stack.push(m); + if let Some(result) = result { + stack.pop(); + stack.push(result); + } else { + stack.push(m); + } } Alg::new(stack) diff --git a/three-style-lib/src/moves/core.rs b/three-style-lib/src/moves/core.rs index dae83a5..4a77755 100644 --- a/three-style-lib/src/moves/core.rs +++ b/three-style-lib/src/moves/core.rs @@ -226,7 +226,7 @@ impl Mul for Move { type Output = Option; fn mul(self, rhs: Move) -> Self::Output { - self.reduce(rhs).or(rhs.reduce(self)) + self.reduce(rhs).or(rhs.reduce(self)) // reduce is not commutative } } diff --git a/three-style-lib/src/sticker/mod.rs b/three-style-lib/src/sticker/mod.rs index f6d6726..cc128ce 100644 --- a/three-style-lib/src/sticker/mod.rs +++ b/three-style-lib/src/sticker/mod.rs @@ -7,6 +7,7 @@ use crate::{ use constants::{CORNER_FACELET_MAP, EDGE_FACELET_MAP}; use std::{fmt, str::FromStr}; +/// Layer based representation of corner stickers. #[rustfmt::skip] #[derive(Debug, PartialEq, Clone, Copy)] pub enum Corner { @@ -66,6 +67,7 @@ impl fmt::Display for Corner { } } +/// Layer based representation of edge stickers. #[rustfmt::skip] #[derive(Debug, PartialEq, Clone, Copy)] pub enum Edge {