Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
07f7399
Phase one coordinates
b-paul Jul 17, 2025
29436f4
Use old coordinates for the phase 1 cube
b-paul Jul 18, 2025
3f7eed2
Phase 2 coordinates
b-paul Jul 19, 2025
3c0dd38
Create move tables
b-paul Jul 21, 2025
189d496
Test move tables
b-paul Jul 21, 2025
2394a70
Attempt to improve dev mode perf, but there's only so much that's pos…
b-paul Jul 21, 2025
d28e080
consistent naming
b-paul Jul 24, 2025
e1b54b1
Add option for submoves to generate successor cube states more effici…
b-paul Jul 24, 2025
b0ec925
Use boxed array to get contiguous memory over moves
b-paul Jul 25, 2025
d88e4c4
Apply and invert operations on the cubiecube group
b-paul Jul 25, 2025
a527147
DR Symmetries of a cubiecube
b-paul Jul 26, 2025
42b76e0
DrSymmetry multiplication table
b-paul Jul 26, 2025
99fd60e
sym and raw coordinate conversion tables
b-paul Jul 26, 2025
fee2942
Move symmetries into a trait
b-paul Jul 28, 2025
f0ab1de
Half symmetry type
b-paul Jul 29, 2025
3ff0de5
Make orientation sym coords and find bug!
b-paul Jul 29, 2025
3def010
Correct symmetries to allow being outside of the cube group
b-paul Aug 1, 2025
c792818
Incorrect sym move table implementation (tests fail)
b-paul Aug 3, 2025
d7c1c5b
Check raw sym tables generate correct representatives
b-paul Nov 2, 2025
c3ee97e
Is multiple of
b-paul Nov 2, 2025
d1a4bfc
Change sym coord api to make more sense
b-paul Nov 2, 2025
387a82a
Test half symmetry correctness
b-paul Nov 2, 2025
82f22b0
Test raw_to_sym is a right inverse
b-paul Nov 27, 2025
8a8d10e
Make a move conjugate table (fixes the sym move table)
b-paul Nov 27, 2025
e758148
Document symmetry move table stuff
b-paul Dec 1, 2025
eeeb2ae
Realise we need conjugate tables
b-paul Dec 4, 2025
fb5d820
Conjugate tables
b-paul Dec 15, 2025
43a2fd1
Conj table tests (need e slice fromcoord)
b-paul Dec 15, 2025
f9ae93b
E slice from coord
b-paul Dec 15, 2025
7528e71
dr e slice from coord
b-paul Dec 15, 2025
9fd860c
Correctly take inverse conjugates in conjugate tables
b-paul Dec 17, 2025
a88e6e0
Incomplete pruning table generation
b-paul Dec 17, 2025
82e38ef
Admissable pruning table tests
b-paul Dec 17, 2025
ec23734
Improve prune table gen speed
b-paul Dec 17, 2025
b2abdac
prune table docs
b-paul Dec 18, 2025
bdf8fdb
Support dr permutation coordinates (it all works!)
b-paul Dec 19, 2025
1959325
Make itertools a dev dependency
b-paul Dec 22, 2025
67453cd
it solves cubes????
b-paul Dec 25, 2025
12fd0da
Abstract phases to remove duplication
b-paul Dec 25, 2025
0e9f68f
Search result enum
b-paul Dec 25, 2025
26bd888
Fix warnings
b-paul Dec 25, 2025
d9b7e53
Eliminate redundant moves
b-paul Dec 25, 2025
170973f
Solved fn
b-paul Dec 25, 2025
5da3eae
Comments
b-paul Dec 26, 2025
203df62
fmt
b-paul Dec 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[package]
name = "cube-lib"
version = "0.1.0"
edition = "2021"
edition = "2024"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
thiserror = "1.0.50"

[dev-dependencies]
itertools = "0.14.0"
proptest = "1.7.0"
proptest-derive = "0.6.0"
36 changes: 36 additions & 0 deletions src/coord.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! We give a general description of a coordinate, which is a type used to encode coset information
//! of a puzzle.

/// A coordinate type, encoding cosets of the puzzle P.
pub trait Coordinate<P>: Copy + Default + Eq {
// TODO this API assumes that coord conversion doesn't require any additional data, perhaps
// this should be changed
/// Obtain the coordinate that corresponds to the given puzzle.
fn from_puzzle(puzzle: &P) -> Self;

// TODO should this be kept
/// Determine whether the given coordinate represents a solved state
fn solved(self) -> bool {
self.repr() == 0
}

/// The number of possible coordinate states.
fn count() -> usize;

/// A representation of this coordinate as a usize, for use in table lookups.
fn repr(self) -> usize;

// TODO this might not be ideal it's not very type safe idk
/// Convert the representation of a coordinate to the coordinate itself. We assume 0
/// corresponds to the solved state.
fn from_repr(n: usize) -> Self;
}

/// Gives the ability to set a coordinate onto a puzzle.
pub trait FromCoordinate<C>: Sized
where
C: Coordinate<Self>,
{
/// Modify the puzzle so that its coordinate for `C` is `coord`.
fn set_coord(&mut self, coord: C);
}
217 changes: 204 additions & 13 deletions src/cube333/coordcube.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,182 @@
use super::{Corner, CornerTwist, CubieCube, Edge, EdgeFlip};
use crate::coord::{Coordinate, FromCoordinate};

#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
struct COCoord(u16);
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
struct CPCoord(u16);
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
struct EOCoord(u16);
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
struct EPCoord(u32);
/// A coordinate representation of the corner orientation of a cube with respect to the U/F faces.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct COCoord(u16);

/// A coordinate representation of the corner permutation of a cube with respect to the U/F faces.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct CPCoord(u16);

/// A coordinate representation of the edge orientation of a cube with respect to the U/F faces.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct EOCoord(u16);

/// A coordinate representation of the edge permutation of a cube with respect to the U/F faces.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)]
pub struct EPCoord(u32);

// TODO AHHHH
// this is copied from the two phase solver!!! so messy!!! AHHHH
fn set_p_coord<const COUNT: usize, const LOWER: usize, const UPPER: usize, P: Copy>(
mut c: usize,
perm: &mut [P; COUNT],
mut bag: Vec<P>,
) {
let mut n = [0; COUNT];
for (i, n) in n.iter_mut().enumerate() {
*n = c % (i + 1);
c /= i + 1;
}

for i in (LOWER..=UPPER).rev() {
let index = n[i - LOWER];
perm[i] = bag[index];
bag.remove(index);
}
}

impl Coordinate<CubieCube> for COCoord {
fn from_puzzle(puzzle: &CubieCube) -> Self {
COCoord(to_o_coord::<8, 3>(&puzzle.co.map(|n| n.into())))
}

fn count() -> usize {
// 3^7
2187
}

fn repr(self) -> usize {
self.0 as usize
}

fn from_repr(n: usize) -> Self {
COCoord(n as u16)
}
}

impl FromCoordinate<COCoord> for CubieCube {
fn set_coord(&mut self, coord: COCoord) {
let mut first = CornerTwist::Oriented;
let mut n = coord.0;

for i in (1..8).rev() {
self.co[i] = match n % 3 {
0 => CornerTwist::Oriented,
1 => {
first = first.anticlockwise();
CornerTwist::Clockwise
}
2 => {
first = first.clockwise();
CornerTwist::AntiClockwise
}
_ => unreachable!(),
};
n /= 3;
}

self.co[0] = first;
}
}

impl Coordinate<CubieCube> for CPCoord {
fn from_puzzle(puzzle: &CubieCube) -> Self {
CPCoord(to_p_coord::<8>(&puzzle.cp.map(|n| n.into())) as u16)
}

fn count() -> usize {
40320
}

fn repr(self) -> usize {
self.0 as usize
}

fn from_repr(n: usize) -> Self {
CPCoord(n as u16)
}
}

impl FromCoordinate<CPCoord> for CubieCube {
fn set_coord(&mut self, coord: CPCoord) {
use crate::cube333::corner::Corner as C;
#[rustfmt::skip]
let bag = vec![C::DBR, C::DBL, C::DFL, C::DFR, C::UBR, C::UBL, C::UFL, C::UFR];

set_p_coord::<8, 0, 7, C>(coord.repr(), &mut self.cp, bag);
}
}

impl Coordinate<CubieCube> for EOCoord {
fn from_puzzle(puzzle: &CubieCube) -> Self {
EOCoord(to_o_coord::<12, 2>(&puzzle.eo.map(|n| n.into())))
}

fn count() -> usize {
// 2^11
2048
}

fn repr(self) -> usize {
self.0 as usize
}

fn from_repr(n: usize) -> Self {
EOCoord(n as u16)
}
}

impl FromCoordinate<EOCoord> for CubieCube {
fn set_coord(&mut self, coord: EOCoord) {
let mut first = EdgeFlip::Oriented;
let mut n = coord.0;

for i in (1..12).rev() {
self.eo[i] = match n % 2 {
0 => EdgeFlip::Oriented,
1 => {
first = first.flip();
EdgeFlip::Flipped
}
_ => unreachable!(),
};
n /= 2;
}

self.eo[0] = first;
}
}

impl Coordinate<CubieCube> for EPCoord {
fn from_puzzle(puzzle: &CubieCube) -> Self {
EPCoord(to_p_coord::<12>(&puzzle.ep.map(|n| n.into())))
}

fn count() -> usize {
// a lot
479001600
}

fn repr(self) -> usize {
self.0 as usize
}

fn from_repr(n: usize) -> Self {
EPCoord(n as u32)
}
}

impl FromCoordinate<EPCoord> for CubieCube {
fn set_coord(&mut self, coord: EPCoord) {
use crate::cube333::edge::Edge as E;
#[rustfmt::skip]
let bag = vec![E::BR, E::BL, E::FL, E::FR, E::DR, E::DB, E::DL, E::DF, E::UR, E::UB, E::UL, E::UF];

set_p_coord::<12, 0, 11, E>(coord.repr(), &mut self.ep, bag);
}
}

/// Implementation of a coord cube, representing pieces using coordinates, which are values which
/// are isomorphic to arrays represented in a cubie cube.
Expand Down Expand Up @@ -146,21 +315,22 @@ impl CubieCube {
});
}

let co = COCoord(to_o_coord::<8, 3>(&self.co.map(|n| n.into())));
let cp = CPCoord(to_p_coord::<8>(&self.cp.map(|n| n.into())) as u16);
let eo = EOCoord(to_o_coord::<12, 2>(&self.eo.map(|n| n.into())));
let ep = EPCoord(to_p_coord::<12>(&self.ep.map(|n| n.into())));
let co = COCoord::from_puzzle(self);
let cp = CPCoord::from_puzzle(self);
let eo = EOCoord::from_puzzle(self);
let ep = EPCoord::from_puzzle(self);

Ok(CoordCube { co, cp, eo, ep })
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::cube333::{
Corner, CornerTwist, CubieCube, Edge, EdgeFlip, StickerCube,
coordcube::{CoordCube, CubieToCoordError},
moves::{Move333, Move333Type},
Corner, CornerTwist, CubieCube, Edge, EdgeFlip, StickerCube,
};
use crate::mv;

Expand Down Expand Up @@ -246,4 +416,25 @@ mod tests {
swap.cp[3] = Corner::UFR;
assert!(swap.to_coord().is_ok());
}

use proptest::prelude::*;

proptest! {
// TODO this test is not good, it should take a *random state* as input to test that the
// last piece is set correctly, but I haven't been bothered to make that yet...

#[test]
fn convert_invertible_co(c in (0..2187u16).prop_map(COCoord)) {
let mut cube = CubieCube::SOLVED;
cube.set_coord(c);
assert_eq!(c, COCoord::from_puzzle(&cube));
}

#[test]
fn convert_invertible_eo(c in (0..2048u16).prop_map(EOCoord)) {
let mut cube = CubieCube::SOLVED;
cube.set_coord(c);
assert_eq!(c, EOCoord::from_puzzle(&cube));
}
}
}
50 changes: 24 additions & 26 deletions src/cube333/corner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ use crate::error::TryFromIntToEnumError;
/// An enum for every corner piece location.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
#[repr(u8)]
pub enum Corner {
UFR,
UFL,
UBL,
UBR,
DFR,
DFL,
DBL,
DBR,
UFR = 0,
UFL = 1,
UBL = 2,
UBR = 3,
DFR = 4,
DFL = 5,
DBL = 6,
DBR = 7,
}

use Corner as C;
Expand Down Expand Up @@ -47,16 +48,7 @@ impl Corner {

impl From<Corner> for u8 {
fn from(value: Corner) -> Self {
match value {
C::UFR => 0,
C::UFL => 1,
C::UBL => 2,
C::UBR => 3,
C::DFR => 4,
C::DFL => 5,
C::DBL => 6,
C::DBR => 7,
}
value as u8
}
}

Expand All @@ -81,21 +73,18 @@ impl TryFrom<u8> for Corner {
/// An enum for every corner twist case.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
#[repr(u8)]
pub enum CornerTwist {
Oriented,
Clockwise,
AntiClockwise,
Oriented = 0,
Clockwise = 1,
AntiClockwise = 2,
}

use CornerTwist as CT;

impl From<CornerTwist> for u8 {
fn from(value: CornerTwist) -> Self {
match value {
CornerTwist::Oriented => 0,
CornerTwist::Clockwise => 1,
CornerTwist::AntiClockwise => 2,
}
value as u8
}
}

Expand Down Expand Up @@ -139,6 +128,15 @@ impl CornerTwist {
CT::AntiClockwise => self.anticlockwise(),
}
}

/// Calculate the inverse twist of a given twist.
pub fn inverse(self) -> CornerTwist {
match self {
CT::Oriented => CT::Oriented,
CT::Clockwise => CT::AntiClockwise,
CT::AntiClockwise => CT::Clockwise,
}
}
}

/// An enum to represent every corner sticker position.
Expand Down
Loading