diff --git a/README.md b/README.md index bdfe7ef..9998af2 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,12 @@ See https://docs.rs/kewb/latest/kewb/ for an exhaustive list of APIs provided by The solver needs some precomputed data which is represented by the `DataTable` struct. However, generating it takes some amount of time so it's recommended to write it on the disk or bundle it with the executable. You can use the `write_table()` function or the `table` command from `kewb-cli` to generate the table. ```rust -use kewb::{error::Error, utils::scramble_from_string, DataTable, FaceCube, Solver, CubieCube}; - +use kewb::{ + error::Error, + generators::generate_state_cross_solved, + scramble::{scramble_from_state, scramble_from_str}, + CubieCube, DataTable, FaceCube, Solver, +}; fn main() -> Result<(), Error> { // Method 1: Bundling the table in the executable @@ -31,12 +35,14 @@ fn main() -> Result<(), Error> { let table = DataTable::default(); let mut solver = Solver::new(&table, 23, None); - let scramble = scramble_from_string("R U R' U'").unwrap(); // vec![R, U, R3, U3] + let scramble = scramble_from_str("R U R' U'")?; // vec![R, U, R3, U3] let state = CubieCube::from(&scramble); let solution = solver.solve(state).unwrap(); println!("{}", solution); + solver.clear(); + let faces = "DRBLUURLDRBLRRBFLFFUBFFDRUDURRBDFBBULDUDLUDLBUFFDBFLRL"; let face_cube = FaceCube::try_from(faces)?; let state = CubieCube::try_from(&face_cube)?; @@ -44,6 +50,13 @@ fn main() -> Result<(), Error> { println!("{}", solution); + solver.clear(); + + let cross_solved = generate_state_cross_solved(); + let scramble = scramble_from_state(edges_solved, &mut solver)?; + + println!("{:?}", scramble); + Ok(()) } ``` @@ -58,9 +71,10 @@ kewb-cli help kewb-cli solve --scramble "R U R' U'" --max 22 --timeout 1 --details kewb-cli solve -s "R U R' U'" -m 22 -t 1 -d kewb-cli solve --facelet DRBLUURLDRBLRRBFLFFUBFFDRUDURRBDFBBULDUDLUDLBUFFDBFLRL -# default values: number = 1, preview = false +# default values: state = random, number = 1, preview = false kewb-cli scramble -p kewb-cli scramble -n 5 +kewb-cli scramble f2l-solved # generates the table used by the solver kewb-cli table ./path_to_file ``` diff --git a/kewb-cli/src/main.rs b/kewb-cli/src/main.rs index 555fabf..56f7556 100644 --- a/kewb-cli/src/main.rs +++ b/kewb-cli/src/main.rs @@ -1,4 +1,4 @@ -use clap::{arg, command, Parser, Subcommand}; +use clap::{arg, command, Parser, Subcommand, ValueEnum}; use crossterm::{ cursor::{MoveLeft, MoveRight, MoveUp}, execute, @@ -7,10 +7,11 @@ use crossterm::{ use kewb::{ error::Error, fs::{decode_table, write_table}, - utils::{generate_random_state, scramble_from_string}, + generators::*, + scramble::{scramble_from_state, scramble_from_str}, Color, }; -use kewb::{CubieCube, FaceCube, Move, Solver}; +use kewb::{CubieCube, FaceCube, Solver}; use spinners::Spinner; use std::{ io::{self, stdout}, @@ -54,6 +55,9 @@ enum Commands { #[command(about = "generates scramble")] Scramble { + #[arg(default_value = "random")] + state: State, + #[arg(short, long, default_value_t = 1)] number: usize, @@ -65,6 +69,17 @@ enum Commands { Table { path: String }, } +#[derive(ValueEnum, Clone)] +enum State { + Random, + CrossSolved, + F2LSolved, + OllSolved, + OllCrossSolved, + EdgesSolved, + CornersSolved, +} + fn solve( scramble: &Option, facelet: &Option, @@ -124,12 +139,10 @@ fn solve_scramble( timeout: Option, details: bool, ) -> Result<(), Error> { - if let Some(scramble) = scramble_from_string(scramble) { - let state = CubieCube::from(&scramble); - Ok(solve_state(state, max, timeout, details)?) - } else { - Err(Error::InvalidScramble) - } + let scramble = scramble_from_str(scramble)?; + let state = CubieCube::from(&scramble); + + solve_state(state, max, timeout, details) } fn solve_facelet(facelet: &str, max: u8, timeout: Option, details: bool) -> Result<(), Error> { @@ -196,21 +209,29 @@ fn print_facelet(facelet: &FaceCube) -> Result<(), io::Error> { Ok(()) } -fn scramble(number: usize, preview: bool) -> Result<(), Error> { +fn scramble(state: &State, number: usize, preview: bool) -> Result<(), Error> { + let table = decode_table(TABLE)?; + let start = Instant::now(); let mut spinner = Spinner::new(spinners::Spinners::Dots, "Generating scramble".to_owned()); + let mut solver = Solver::new(&table, 25, None); let mut scrambles = Vec::new(); let mut states = Vec::new(); - let table = decode_table(TABLE)?; - let start = Instant::now(); for _ in 0..number { - 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 = scramble.iter().rev().map(|m| m.get_inverse()).collect(); + let state = match state { + State::Random => generate_random_state(), + State::CrossSolved => generate_state_cross_solved(), + State::F2LSolved => generate_state_f2l_solved(), + State::OllSolved => generate_state_oll_solved(), + State::OllCrossSolved => generate_state_oll_cross_solved(), + State::EdgesSolved => generate_state_edges_solved(), + State::CornersSolved => generate_state_corners_solved(), + }; + let scramble = scramble_from_state(state, &mut solver)?; states.push(state); scrambles.push(scramble); + solver.clear(); } let end = Instant::now(); @@ -267,7 +288,11 @@ fn main() { timeout, details, }) => solve(scramble, facelet, *max, *timeout, *details), - Some(Commands::Scramble { number, preview }) => scramble(*number, *preview), + Some(Commands::Scramble { + state, + number, + preview, + }) => scramble(state, *number, *preview), Some(Commands::Table { path }) => table(path), _ => Ok(()), }; diff --git a/kewb/src/cube/generators.rs b/kewb/src/cube/generators.rs new file mode 100644 index 0000000..1918625 --- /dev/null +++ b/kewb/src/cube/generators.rs @@ -0,0 +1,143 @@ +use crate::constants::{CO_COUNT, CP_COUNT, EO_COUNT, EP_COUNT}; + +use super::{cubie::CubieCube, index::*}; +use rand::{rngs::ThreadRng, seq::SliceRandom, thread_rng, Rng}; + +/// Randomly swaps corner or edges to fix parity. +fn fix_parity(state: &mut CubieCube, rng: &mut ThreadRng, corners: Vec, edges: Vec) { + if rng.gen_bool(0.5) { + swap_edges(state, rng, edges) + } else { + swap_corners(state, rng, corners) + } +} + +fn swap_edges(state: &mut CubieCube, rng: &mut ThreadRng, edges: Vec) { + let pos: Vec<&usize> = edges.choose_multiple(rng, 2).collect(); + let a = *pos[0]; + let b = *pos[1]; + state.ep.swap(a, b) +} + +fn swap_corners(state: &mut CubieCube, rng: &mut ThreadRng, corners: Vec) { + let pos: Vec<&usize> = corners.choose_multiple(rng, 2).collect(); + let a = *pos[0]; + let b = *pos[1]; + state.cp.swap(a, b) +} + +/// Generates a random state with corners solved. +pub fn generate_state_corners_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + ep: index_to_ep(rng.gen_range(0..EP_COUNT)), + eo: index_to_eo(rng.gen_range(0..EO_COUNT)), + ..Default::default() + }; + + if !state.is_solvable() { + swap_edges(&mut state, &mut rng, (0..12).collect()); + } + + state +} + +/// Generates a random state with edges solved. +pub fn generate_state_edges_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp(rng.gen_range(0..CP_COUNT)), + co: index_to_co(rng.gen_range(0..CO_COUNT)), + ..Default::default() + }; + + if !state.is_solvable() { + swap_corners(&mut state, &mut rng, (0..8).collect()); + } + + state +} + +/// Generates a random state with oriented solved last layer cross. +pub fn generate_state_oll_cross_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp_f2l(rng.gen_range(0..4)), + co: index_to_co_f2l(rng.gen_range(0..27)), + ep: index_to_ep_f2l(rng.gen_range(0..24)), + ..Default::default() + }; + + if !state.is_solvable() { + fix_parity(&mut state, &mut rng, (0..4).collect(), (4..8).collect()) + } + + state +} + +/// Generates a random state with oriented last layer corners and edges. +pub fn generate_state_oll_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp_f2l(rng.gen_range(0..4)), + ep: index_to_ep_f2l(rng.gen_range(0..24)), + ..Default::default() + }; + + if !state.is_solvable() { + fix_parity(&mut state, &mut rng, (0..4).collect(), (4..8).collect()) + } + + state +} + +/// Generates a random state with solved First two layer. +pub fn generate_state_f2l_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp_f2l(rng.gen_range(0..4)), + co: index_to_co_f2l(rng.gen_range(0..27)), + ep: index_to_ep_f2l(rng.gen_range(0..24)), + eo: index_to_eo_f2l(rng.gen_range(0..8)), + }; + + if !state.is_solvable() { + fix_parity(&mut state, &mut rng, (0..4).collect(), (4..8).collect()) + } + + state +} + +/// Generates a random state with solved cross. +pub fn generate_state_cross_solved() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp(rng.gen_range(0..CP_COUNT)), + co: index_to_co(rng.gen_range(0..CO_COUNT)), + ep: index_to_ep_cross(rng.gen_range(0..40320)), + eo: index_to_eo_cross(rng.gen_range(0..128)), + }; + + if !state.is_solvable() { + fix_parity(&mut state, &mut rng, (0..8).collect(), (0..8).collect()) + } + + state +} + +/// Generates a random state on the cubie level. +pub fn generate_random_state() -> CubieCube { + let mut rng = thread_rng(); + let mut state = CubieCube { + cp: index_to_cp(rng.gen_range(0..CP_COUNT)), + co: index_to_co(rng.gen_range(0..CO_COUNT)), + ep: index_to_ep(rng.gen_range(0..EP_COUNT)), + eo: index_to_eo(rng.gen_range(0..EO_COUNT)), + }; + + if !state.is_solvable() { + fix_parity(&mut state, &mut rng, (0..8).collect(), (0..12).collect()) + } + + state +} diff --git a/kewb/src/cube/index.rs b/kewb/src/cube/index.rs index fb4c1c4..80ed4b8 100644 --- a/kewb/src/cube/index.rs +++ b/kewb/src/cube/index.rs @@ -1,5 +1,55 @@ use super::cubie::{Corner, Edge}; +fn fill_orientation_slice(slice: &mut [u8], cases: u8, index: u16) { + let len = slice.len(); + let mut index = index; + let mut orientation_sum = 0; + + for i in (0..len - 1).rev() { + slice[i] = (index % cases as u16) as u8; + index /= cases as u16; + orientation_sum += slice[i]; + } + + slice[len - 1] = (cases - orientation_sum % cases) % cases; +} + +fn fill_perm_slice(slice: &mut [u8], index: usize) { + let len = slice.len(); + let mut index = index; + let mut perm = vec![0; len]; + + for i in (0..(len - 1)).rev() { + perm[i] = (index % (len - i)) as u8; + index /= len - i; + for j in (i + 1)..len { + if perm[j] >= perm[i] { + perm[j] += 1; + } + } + } + + for i in 0..len { + slice[i] += perm[i]; + } +} + +pub fn slice_to_index(cp: &[u8]) -> usize { + let len = cp.len(); + let mut index = 0; + + for i in 0..len { + index *= len - i; + for j in i + 1..len { + if cp[i] > cp[j] { + index += 1; + } + } + } + + index +} + pub fn co_to_index(corner: &[u8; 8]) -> u16 { let mut index = 0; @@ -10,16 +60,9 @@ pub fn co_to_index(corner: &[u8; 8]) -> u16 { index } -pub fn index_to_co(mut index: u16) -> [u8; 8] { +pub fn index_to_co(index: u16) -> [u8; 8] { let mut co = [0; 8]; - let mut co_sum = 0; - - for i in (0..7).rev() { - co[i] = (index % 3) as u8; - index /= 3; - co_sum += co[i]; - } - co[7] = (3 - co_sum % 3) % 3; + fill_orientation_slice(&mut co, 3, index); co } @@ -34,16 +77,9 @@ pub fn eo_to_index(edge: &[u8; 12]) -> u16 { index } -pub fn index_to_eo(mut index: u16) -> [u8; 12] { +pub fn index_to_eo(index: u16) -> [u8; 12] { let mut eo = [0; 12]; - let mut eo_sum = 0; - - for i in (0..11).rev() { - eo[i] = (index % 2) as u8; - index /= 2; - eo_sum += eo[i]; - } - eo[11] = (2 - eo_sum % 2) % 2; + fill_orientation_slice(&mut eo, 2, index); eo } @@ -54,6 +90,7 @@ fn calculate_combo(n: u8, k: u8) -> u16 { } let mut result: u16 = 1; + for i in 0..k as u16 { result *= n as u16 - i; result /= i + 1; @@ -92,133 +129,95 @@ pub fn index_to_e_combo(mut index: u16) -> [Edge; 12] { } pub fn cp_to_index(cp: &[Corner; 8]) -> u16 { - let mut index = 0; - - for i in 0..8 { - index *= 8 - i as u16; - for j in i + 1..8 { - if cp[i] > cp[j] { - index += 1; - } - } - } - - index + let slice = cp.map(|c| c as u8); + slice_to_index(&slice) as u16 } -pub fn index_to_cp(mut index: u16) -> [Corner; 8] { +pub fn index_to_cp(index: u16) -> [Corner; 8] { let mut cp: [u8; 8] = [0; 8]; - for i in (0..7).rev() { - cp[i] = (index % (8 - i as u16)) as u8; - index /= 8 - i as u16; - for j in (i + 1)..8 { - if cp[j] >= cp[i] { - cp[j] += 1; - } - } - } - + fill_perm_slice(&mut cp, index as usize); cp.map(|value| Corner::try_from(value).unwrap()) } pub fn ep_to_index(ep: &[Edge; 12]) -> u32 { - let mut index = 0; + let slice = ep.map(|e| e as u8); + slice_to_index(&slice) as u32 +} - for i in 0..12 { - index *= 12 - i as u32; - for j in i + 1..12 { - if ep[i] > ep[j] { - index += 1; - } - } - } +pub fn index_to_ep(index: u32) -> [Edge; 12] { + let mut ep: [u8; 12] = [0; 12]; - index + fill_perm_slice(&mut ep, index as usize); + ep.map(|value| Edge::try_from(value).unwrap()) } -pub fn index_to_ep(mut index: u32) -> [Edge; 12] { - let mut ep = [0; 12]; +pub fn ud_ep_to_index(ep: &[Edge; 12]) -> u16 { + let slice = ep[4..12].iter().map(|&e| e as u8).collect::>(); + slice_to_index(&slice) as u16 +} - for i in (0..11).rev() { - ep[i] = (index % (12 - i as u32)) as u8; - index /= 12 - i as u32; - for j in (i + 1)..12 { - if ep[j] >= ep[i] { - ep[j] += 1; - } - } - } +pub fn index_to_ud_ep(index: u16) -> [Edge; 12] { + let mut ep = [4; 12]; // fake ep + fill_perm_slice(&mut ep[4..12], index as usize); ep.map(|value| Edge::try_from(value).unwrap()) } -pub fn ud_ep_to_index(ep: &[Edge; 12]) -> u16 { - let mut index = 0; - let slice = &ep[4..12]; +pub fn e_ep_to_index(ep: &[Edge; 12]) -> u16 { + let slice = ep[..4].iter().map(|&e| e as u8).collect::>(); + slice_to_index(&slice) as u16 +} - for i in 0..8 { - index *= 8 - i as u16; - for j in i + 1..8 { - if slice[i] > slice[j] { - index += 1; - } - } - } +pub fn index_to_e_ep(index: u16) -> [Edge; 12] { + let mut ep = [0; 12]; // fake ep - index + fill_perm_slice(&mut ep[..4], index as usize); + ep.map(|value| Edge::try_from(value).unwrap()) } -pub fn index_to_ud_ep(mut index: u16) -> [Edge; 12] { - let mut ep = [0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4]; // fake ep - let slice = &mut ep[4..12]; - - for i in (0..7).rev() { - slice[i] = (index % (8 - i as u16) + 4) as u8; - index /= 8 - i as u16; - for j in (i + 1)..8 { - if slice[j] >= slice[i] { - slice[j] += 1; - } - } - } +pub fn index_to_ep_cross(index: u16) -> [Edge; 12] { + let mut ep = [0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10, 11]; // fake ep + fill_perm_slice(&mut ep[..8], index as usize); ep.map(|value| Edge::try_from(value).unwrap()) } -pub fn e_ep_to_index(ep: &[Edge; 12]) -> u16 { - let mut index = 0; - let slice = &ep[0..4]; +pub fn index_to_eo_cross(index: u16) -> [u8; 12] { + let mut eo = [0; 12]; + fill_orientation_slice(&mut eo[..8], 2, index); - for i in 0..4 { - index *= 4 - i as u16; - for j in i + 1..4 { - if slice[i] > slice[j] { - index += 1; - } - } - } + eo +} - index +pub fn index_to_cp_f2l(index: u16) -> [Corner; 8] { + let mut cp: [u8; 8] = [0, 0, 0, 0, 4, 5, 6, 7]; + + fill_perm_slice(&mut cp[..4], index as usize); + cp.map(|value| Corner::try_from(value).unwrap()) } -pub fn index_to_e_ep(mut index: u16) -> [Edge; 12] { - let mut ep = [0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11]; - let slice = &mut ep[0..4]; +pub fn index_to_co_f2l(index: u16) -> [u8; 8] { + let mut co: [u8; 8] = [0; 8]; + fill_orientation_slice(&mut co[..4], 3, index); - for i in (0..3).rev() { - slice[i] = (index % (4 - i as u16)) as u8; - index /= 4 - i as u16; - for j in (i + 1)..4 { - if slice[j] >= slice[i] { - slice[j] += 1; - } - } - } + co +} + +pub fn index_to_ep_f2l(index: u16) -> [Edge; 12] { + let mut ep = [0, 1, 2, 3, 4, 4, 4, 4, 8, 9, 10, 11]; // fake ep + fill_perm_slice(&mut ep[4..8], index as usize); ep.map(|value| Edge::try_from(value).unwrap()) } +pub fn index_to_eo_f2l(index: u16) -> [u8; 12] { + let mut eo = [0; 12]; + fill_orientation_slice(&mut eo[4..8], 2, index); + + eo +} + #[cfg(test)] mod test { use super::*; @@ -269,21 +268,25 @@ mod test { #[test] fn test_ud_ep() { assert_eq!(ud_ep_to_index(&SOLVED_CUBIE_CUBE.ep), 0); - assert_eq!(index_to_ud_ep(0), SOLVED_CUBIE_CUBE.ep); + let ud_ep = index_to_ud_ep(0); + assert_eq!(&ud_ep[4..12], &SOLVED_CUBIE_CUBE.ep[4..12]); let edges = [BL, BR, FR, FL, DL, DB, DR, DF, UL, UF, UR, UB]; assert_eq!(ud_ep_to_index(&edges), 40319); - assert_eq!(index_to_ud_ep(40319), edges); + let ud_ep = index_to_ud_ep(40319); + assert_eq!(&ud_ep[4..12], &edges[4..12]); } #[test] fn test_e_ep() { assert_eq!(e_ep_to_index(&SOLVED_CUBIE_CUBE.ep), 0); - assert_eq!(index_to_e_ep(0), SOLVED_CUBIE_CUBE.ep); + let e_ep = index_to_e_ep(0); + assert_eq!(&e_ep[..4], &SOLVED_CUBIE_CUBE.ep[..4]); let edges = [FL, FR, BR, BL, UB, UR, UF, UL, DF, DR, DB, DL]; assert_eq!(e_ep_to_index(&edges), 23); - assert_eq!(index_to_e_ep(23), edges); + let e_ep = index_to_e_ep(23); + assert_eq!(&e_ep[..4], &edges[..4]); } #[test] diff --git a/kewb/src/cube/mod.rs b/kewb/src/cube/mod.rs index 5d51e0a..9feda5d 100644 --- a/kewb/src/cube/mod.rs +++ b/kewb/src/cube/mod.rs @@ -1,6 +1,7 @@ pub mod constants; pub mod cubie; pub mod facelet; +pub mod generators; pub mod index; pub mod moves; -pub mod utils; +pub mod scramble; diff --git a/kewb/src/cube/moves.rs b/kewb/src/cube/moves.rs index 9b90a8b..619bbea 100644 --- a/kewb/src/cube/moves.rs +++ b/kewb/src/cube/moves.rs @@ -77,38 +77,6 @@ pub fn is_move_available(prev: Move, current: Move) -> bool { current != prev && !current.is_inverse(prev) && !current.is_same_layer(prev) } -pub fn scramble_from_string(string: &str) -> Option> { - let mut scramble = vec![]; - - for word in string.split_whitespace() { - let m = match word.trim() { - "R" => R, - "R'" => R3, - "R2" => R2, - "L" => L, - "L'" => L3, - "L2" => L2, - "U" => U, - "U'" => U3, - "U2" => U2, - "D" => D, - "D'" => D3, - "D2" => D2, - "F" => F, - "F'" => F3, - "F2" => F2, - "B" => B, - "B'" => B3, - "B2" => B2, - _ => return None, - }; - - scramble.push(m); - } - - Some(scramble) -} - pub const U_MOVE: CubieCube = CubieCube { cp: [UFL, UBL, UBR, UFR, DFL, DFR, DBR, DBL], co: [0, 0, 0, 0, 0, 0, 0, 0], diff --git a/kewb/src/cube/scramble.rs b/kewb/src/cube/scramble.rs new file mode 100644 index 0000000..f1fc48d --- /dev/null +++ b/kewb/src/cube/scramble.rs @@ -0,0 +1,48 @@ +use crate::{cube::moves::Move::*, error::Error, CubieCube, Move, Solver}; + +pub fn scramble_from_str(string: &str) -> Result, Error> { + let mut scramble = vec![]; + + for word in string.split_whitespace() { + let m = match word.trim() { + "R" => R, + "R'" => R3, + "R2" => R2, + "L" => L, + "L'" => L3, + "L2" => L2, + "U" => U, + "U'" => U3, + "U2" => U2, + "D" => D, + "D'" => D3, + "D2" => D2, + "F" => F, + "F'" => F3, + "F2" => F2, + "B" => B, + "B'" => B3, + "B2" => B2, + _ => return Err(Error::InvalidScramble), + }; + + scramble.push(m); + } + + Ok(scramble) +} + +pub fn scramble_from_state(state: CubieCube, solver: &mut Solver) -> Result, Error> { + let solution = solver.solve(state); + + if let Some(solution) = solution { + Ok(solution + .get_all_moves() + .iter() + .rev() + .map(|m| m.get_inverse()) + .collect()) + } else { + Err(Error::InvalidCubieValue) + } +} diff --git a/kewb/src/cube/utils.rs b/kewb/src/cube/utils.rs deleted file mode 100644 index 1c0b844..0000000 --- a/kewb/src/cube/utils.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::constants::{CO_COUNT, CP_COUNT, EO_COUNT, EP_COUNT}; - -use super::{ - cubie::CubieCube, - index::{index_to_co, index_to_cp, index_to_eo, index_to_ep}, -}; -use rand::{thread_rng, Rng}; - -/// Generates a random state on the cubie level. -pub fn generate_random_state() -> CubieCube { - let mut rng = thread_rng(); - let mut state = CubieCube { - cp: index_to_cp(rng.gen_range(0..CP_COUNT)), - co: index_to_co(rng.gen_range(0..CO_COUNT)), - ep: index_to_ep(rng.gen_range(0..EP_COUNT)), - eo: index_to_eo(rng.gen_range(0..EO_COUNT)), - }; - - let c_perm = state.count_corner_perm(); - let e_perm = state.count_edge_perm(); - let is_even = |a| a % 2 == 0; - - if !is_even(c_perm) && is_even(e_perm) { - state.cp.swap(0, 1); - } else if !is_even(e_perm) && is_even(c_perm) { - state.ep.swap(0, 1); - } - - state -} diff --git a/kewb/src/lib.rs b/kewb/src/lib.rs index 04ecd11..ef09cfa 100644 --- a/kewb/src/lib.rs +++ b/kewb/src/lib.rs @@ -32,9 +32,14 @@ pub mod index { pub use crate::cube::index::*; } -/// Some utility functions. -pub mod utils { - pub use crate::cube::{moves::scramble_from_string, utils::generate_random_state}; +/// Module containing functions for generating states on the cubie level. +pub mod generators { + pub use crate::cube::generators::*; +} + +/// Module containing functions for scrambling the cube. +pub mod scramble { + pub use crate::cube::scramble::*; } pub mod error;