Skip to content

Commit

Permalink
update!: table handling & crates
Browse files Browse the repository at this point in the history
Breaking changes.
- Separated lib and bin crates
- Changed fs operations behaviour
- Table data is now bundeled in the binary
  • Loading branch information
luckasRanarison committed Nov 2, 2023
1 parent 7d77391 commit 566301d
Show file tree
Hide file tree
Showing 23 changed files with 227 additions and 290 deletions.
207 changes: 69 additions & 138 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 1 addition & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1 @@
[package]
name = "kewb"
version = "0.2.1"
edition = "2021"
description = "A crate for manipulating and solving a 3x3 rubik's cube with Kociemba two phase algorithm"
authors = [
"LIOKA Ranraison Fiderana <luckasranarison@gmail.com>",
"Miklos Vajna <vmiklos@vmiklos.hu>",
]
repository = "https://github.com/luckasRanarison/kewb"
keywords = ["rubiks-cube", "cube-solver", "kociemba", "two-phase"]
categories = ["algorithms"]
license = "MIT"
readme = "README.md"

[dependencies]
rand = "0.8.3"
bincode = { version = "2.0.0-rc", features = ["serde"] }
clap = { version = "4.2.5", features = ["derive"] }
spinners = "4.1.0"
workspace.members = ["kewb", "kewb-cli"]
23 changes: 23 additions & 0 deletions kewb-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "kewb-cli"
version = "0.1.0"
edition = "2021"
description = "CLI application for solving and scrambling the 3x3 Rubik's cube"
authors = [
"LIOKA Ranraison Fiderana <luckasranarison@gmail.com>",
"Miklos Vajna <vmiklos@vmiklos.hu>",
]
repository = "https://github.com/luckasRanarison/kewb"
keywords = ["rubiks-cube", "cube-solver", "kociemba", "two-phase"]
categories = ["command-line-utilities"]
license = "MIT"
readme = "../README.md"

[dependencies]
kewb = { path = "../kewb" }
clap = { version = "4.2.5", features = ["derive"] }
spinners = { version = "4.1.0" }
bincode = { version = "2.0.0-rc", features = ["serde"] }

[build-dependencies]
kewb = { path = "../kewb" }
Empty file added kewb-cli/bin/table.bin
Empty file.
6 changes: 6 additions & 0 deletions kewb-cli/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use kewb::{error::Error, fs::write_table};

fn main() -> Result<(), Error> {
write_table("bin/table.bin")?;
Ok(())
}
12 changes: 7 additions & 5 deletions src/main.rs → kewb-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use clap::{arg, command, Parser, Subcommand};
use kewb::{
error::Error,
fs::read_table,
fs::decode_table,
utils::{generate_random_state, scramble_from_string},
};
use kewb::{FaceCube, Move, Solver, State};
use spinners::Spinner;
use std::time::Instant;

const TABLE: &[u8] = include_bytes!("../bin/table.bin");

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(arg_required_else_help(true))]
Expand Down Expand Up @@ -49,8 +51,8 @@ enum Commands {
}

fn solve_state(state: State, max: u8, timeout: Option<f32>, details: bool) -> Result<(), Error> {
let (move_table, pruning_table) = read_table()?;
let mut solver = Solver::new(&move_table, &pruning_table, max, timeout);
let table = decode_table(TABLE)?;
let mut solver = Solver::new(&table, max, timeout);
let mut spinner = Spinner::new(spinners::Spinners::Dots, "Solving".to_owned());

let start = Instant::now();
Expand Down Expand Up @@ -108,11 +110,11 @@ fn solve_facelet(facelet: &str, max: u8, timeout: Option<f32>, details: bool) ->
fn scramble(number: usize) -> Result<(), Error> {
let mut spinner = Spinner::new(spinners::Spinners::Dots, "Generating scramble".to_owned());
let mut scrambles = Vec::new();
let (move_table, pruning_table) = read_table()?;
let table = decode_table(TABLE)?;
let start = Instant::now();

for _ in 0..number {
let mut solver = Solver::new(&move_table, &pruning_table, 25, None);
let mut solver = Solver::new(&table, 25, None);
let state = generate_random_state();
let scramble = solver.solve(state).unwrap().get_all_moves();
let scramble: Vec<Move> = scramble.iter().rev().map(|m| m.get_inverse()).collect();
Expand Down
18 changes: 18 additions & 0 deletions kewb/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "kewb"
version = "0.2.1"
edition = "2021"
description = "A crate for manipulating and solving a 3x3 Rubik's cube with Kociemba's two phase algorithm"
authors = [
"LIOKA Ranraison Fiderana <luckasranarison@gmail.com>",
"Miklos Vajna <vmiklos@vmiklos.hu>",
]
repository = "https://github.com/luckasRanarison/kewb"
keywords = ["rubiks-cube", "cube-solver", "kociemba", "two-phase"]
categories = ["algorithms"]
license = "MIT"
readme = "../README.md"

[dependencies]
rand = "0.8.3"
bincode = { version = "2.0.0-rc", features = ["serde"] }
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 10 additions & 2 deletions src/lib.rs → kewb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ pub(crate) mod cube;
pub(crate) mod two_phase;

pub use cube::{facelet::Color, facelet::FaceCube, moves::Move, state::State};
pub use two_phase::solver::{solve, Solution, Solver};
pub use two_phase::solver::{Solution, Solver};
pub use two_phase::utils::DataTable;

/// Table read and write operations.
/// Module containing table read and write operations.
pub mod fs {
pub use crate::two_phase::fs::*;
}
Expand All @@ -26,6 +27,13 @@ pub mod index {
pub use crate::cube::index::*;
}

/// Module containing 3x3 cube constants
pub mod constants {
pub use crate::two_phase::utils::{
CO_COUNT, CP_COUNT, EO_COUNT, E_COMBO_COUNT, E_EP_COUNT, UD_EP_COUNT,
};
}

/// Some utility functions.
pub mod utils {
pub use crate::cube::{moves::scramble_from_string, utils::generate_random_state};
Expand Down
43 changes: 43 additions & 0 deletions kewb/src/two_phase/fs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use super::utils::DataTable;
use crate::error::Error;
use bincode::{
config::{self, Configuration},
decode_from_slice, encode_to_vec,
error::DecodeError,
};
use std::{fs, path::Path};

const CONFIG: Configuration = config::standard();

pub fn write_table<P>(path: P) -> Result<(), Error>
where
P: AsRef<Path>,
{
let table = DataTable::default();
let encoded = encode_to_vec(table, CONFIG)?;

fs::write(path, encoded)?;

Ok(())
}

pub fn read_table<P>(path: P) -> Result<DataTable, Error>
where
P: AsRef<Path>,
{
let encoded = fs::read(path)?;
let table = decode_table(&encoded)?;

Ok(table)
}

pub fn decode_table(bytes: &[u8]) -> Result<DataTable, Error> {
let (decoded, written) = decode_from_slice(bytes, CONFIG)?;
let additional = bytes.len() - written;

if additional != 0 {
return Err(DecodeError::UnexpectedEnd { additional })?;
}

Ok(decoded)
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
53 changes: 16 additions & 37 deletions src/two_phase/solver.rs → kewb/src/two_phase/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@ use std::{
time::{Duration, Instant},
};

use crate::{
cube::{
index::*,
moves::{is_move_available, Move},
state::{State, SOLVED_STATE},
},
error::Error,
fs::read_table,
use crate::cube::{
index::*,
moves::{is_move_available, Move},
state::{State, SOLVED_STATE},
};

use super::moves::MoveTable;
use super::pruning::PruningTable;
use super::utils::{ALL_MOVES, PHASE2_MOVES};
use super::{moves::MoveTable, utils::DataTable};

trait Phase {
fn is_solved(&self) -> bool;
Expand Down Expand Up @@ -173,8 +169,7 @@ impl Solution {

/// Two phase solver struct for more control.
pub struct Solver<'a> {
move_table: &'a MoveTable,
pruning_table: &'a PruningTable,
data_table: &'a DataTable,
max_length: u8,
timeout: Option<Duration>,
initial_state: State,
Expand All @@ -184,17 +179,11 @@ pub struct Solver<'a> {
}

impl<'a> Solver<'a> {
pub fn new(
move_table: &'a MoveTable,
pruning_table: &'a PruningTable,
max_length: u8,
timeout: Option<f32>,
) -> Self {
pub fn new(data_table: &'a DataTable, max_length: u8, timeout: Option<f32>) -> Self {
let timeout = timeout.map(Duration::from_secs_f32);

Self {
move_table,
pruning_table,
data_table,
initial_state: SOLVED_STATE,
max_length,
timeout,
Expand Down Expand Up @@ -261,7 +250,7 @@ impl<'a> Solver<'a> {
return false;
}

if state.prune(self.pruning_table, depth) || depth == 0 {
if state.prune(&self.data_table.pruning_table, depth) || depth == 0 {
return false;
}

Expand All @@ -273,7 +262,7 @@ impl<'a> Solver<'a> {
}

self.solution_phase1.push(*m);
let new_state = state.next(self.move_table, i);
let new_state = state.next(&self.data_table.move_table, i);
let found = self.solve_phase1(new_state, depth - 1, time);

if found {
Expand Down Expand Up @@ -311,7 +300,7 @@ impl<'a> Solver<'a> {
return true;
}

if state.prune(self.pruning_table, depth) || depth == 0 {
if state.prune(&self.data_table.pruning_table, depth) || depth == 0 {
return false;
}

Expand All @@ -327,7 +316,7 @@ impl<'a> Solver<'a> {
}

self.solution_phase2.push(*m);
let new_state = state.next(self.move_table, i);
let new_state = state.next(&self.data_table.move_table, i);
let found = self.solve_phase2(new_state, depth - 1, time);

if found {
Expand All @@ -341,26 +330,14 @@ impl<'a> Solver<'a> {
}
}

/// Solves a state state using the two phase algorithm, shorthand for `solver_instance.solve()`.
pub fn solve(
state: State,
max_length: u8,
timeout: Option<f32>,
) -> Result<Option<Solution>, Error> {
let (move_table, pruning_table) = read_table()?;
let mut solver = Solver::new(&move_table, &pruning_table, max_length, timeout);

Ok(solver.solve(state))
}

#[cfg(test)]
mod test {
use crate::{
cube::{
moves::Move::*,
state::{State, SOLVED_STATE},
},
two_phase::solver::solve,
DataTable, Solver,
};

#[test]
Expand All @@ -369,7 +346,9 @@ mod test {
D3, R2, L3, U2, F, R, F3, D2, R2, F2, B2, U2, R2, F2, U, R2, U3, R2, D2,
];
let state = State::from(&scramble);
let solution = solve(state, 23, None).unwrap();
let table = DataTable::default();
let mut solver = Solver::new(&table, 23, None);
let solution = solver.solve(state);
let solved_state = state.apply_moves(&solution.unwrap().get_all_moves());

assert_eq!(solved_state, SOLVED_STATE);
Expand Down
34 changes: 34 additions & 0 deletions kewb/src/two_phase/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::{
cube::moves::Move::{self, *},
move_table::MoveTable,
pruning_table::PruningTable,
};
use bincode::{Decode, Encode};

/// Corner orientation count
pub const CO_COUNT: u16 = 2187;
/// Edge orientation count
pub const EO_COUNT: u16 = 2048;
/// E-slice edge combination count
pub const E_COMBO_COUNT: u16 = 495;

/// Corner premutation count
pub const CP_COUNT: u16 = 40320;
/// U-D layer edge premutation count
pub const UD_EP_COUNT: u16 = 40320;
/// E-slice edge permutation count
pub const E_EP_COUNT: u16 = 24;

pub const ALL_MOVES: [Move; 18] = [
U, U2, U3, D, D2, D3, R, R2, R3, L, L2, L3, F, F2, F2, B, B3, B2,
];
pub const PHASE2_MOVES: [Move; 10] = [U, U2, U3, D, D2, D3, R2, L2, F2, B2];

pub type Table<T> = Vec<Vec<T>>;

/// Contains the move and prunning table used by the two-phase algorithm
#[derive(Default, Encode, Decode)]
pub struct DataTable {
pub move_table: MoveTable,
pub pruning_table: PruningTable,
}
Loading

0 comments on commit 566301d

Please sign in to comment.