use cubing::kpuzzle::{KPattern, OrientationWithMod};
use rand::{seq::SliceRandom, Rng};
use crate::scramble::parity::{set_parity, BasicParity};
pub(crate) enum OrbitPermutationConstraint {
EvenParity,
OddParity,
IdentityPermutation,
}
pub(crate) enum OrbitOrientationConstraint {
IgnoreAllOrientations,
SumToZero,
EvenOddHackSumToZero(Vec<u8>),
}
pub(crate) enum ConstraintForPiece0 {
KeepSolved,
IgnoredOrientation,
}
#[derive(Default)]
pub(crate) struct OrbitRandomizationConstraints {
pub(crate) permutation: Option<OrbitPermutationConstraint>,
pub(crate) orientation: Option<OrbitOrientationConstraint>,
pub(crate) piece_0: Option<ConstraintForPiece0>,
pub(crate) subset: Option<Vec<u8>>,
}
pub(crate) fn randomize_orbit<R: Rng>(
pattern: &mut KPattern,
orbit_idx: usize,
orbit_name: &str,
constraints: OrbitRandomizationConstraints,
mut rng: R,
) -> Vec<u8> {
let orbit_info = &pattern.kpuzzle().clone().data.ordered_orbit_info[orbit_idx];
assert_eq!(orbit_info.name.0, orbit_name);
let piece_order_original = constraints
.subset
.unwrap_or_else(|| (0..orbit_info.num_pieces).collect());
let mut piece_order_shuffled = piece_order_original.clone();
let first_randomized_piece = piece_order_shuffled[0];
let first_shuffled_piece_order_index =
if matches!(constraints.piece_0, Some(ConstraintForPiece0::KeepSolved)) {
1
} else {
0
};
let shuffling_slice = piece_order_shuffled
.split_at_mut_checked(first_shuffled_piece_order_index)
.unwrap()
.1;
match constraints.permutation {
None => {
shuffling_slice.shuffle(&mut rng);
}
Some(OrbitPermutationConstraint::EvenParity) => {
shuffling_slice.shuffle(&mut rng);
set_parity(shuffling_slice, BasicParity::Even);
}
Some(OrbitPermutationConstraint::OddParity) => {
shuffling_slice.shuffle(&mut rng);
set_parity(shuffling_slice, BasicParity::Odd);
}
Some(OrbitPermutationConstraint::IdentityPermutation) => {}
}
let mut total_orientation = 0;
for (shuffled_i, p) in piece_order_shuffled.iter().enumerate() {
let original_i = piece_order_original[shuffled_i];
pattern.set_piece(orbit_info, original_i, *p);
let is_last_shuffled_piece = shuffled_i == piece_order_original.len() - 1;
let orientation_with_mod = match (
&constraints.orientation,
&constraints.piece_0,
is_last_shuffled_piece,
*p == first_randomized_piece,
) {
(Some(OrbitOrientationConstraint::IgnoreAllOrientations), _, _, _) => {
OrientationWithMod {
orientation: 0,
orientation_mod: 1,
}
}
(Some(OrbitOrientationConstraint::SumToZero), _, true, _) => OrientationWithMod {
orientation: subtract_u8_mod(0, total_orientation, orbit_info.num_orientations),
orientation_mod: 0,
},
(_, Some(ConstraintForPiece0::KeepSolved), _, true) => OrientationWithMod {
orientation: 0,
orientation_mod: 0,
},
(_, Some(ConstraintForPiece0::IgnoredOrientation), _, true) => OrientationWithMod {
orientation: 0,
orientation_mod: 1,
},
(
Some(OrbitOrientationConstraint::EvenOddHackSumToZero(even_set)),
_,
is_last_shuffled_piece,
_,
) => {
let random_orientation = if is_last_shuffled_piece {
subtract_u8_mod(0, total_orientation, orbit_info.num_orientations)
} else {
let even_orientation_offset = if even_set.binary_search(&original_i).is_ok()
== even_set.binary_search(p).is_ok()
{
0
} else {
1
};
rng.random_range(
0..(orbit_info.num_orientations + 1 - even_orientation_offset) / 2,
) * 2
+ even_orientation_offset
};
total_orientation = add_u8_mod(
total_orientation,
random_orientation,
orbit_info.num_orientations,
);
OrientationWithMod {
orientation: random_orientation,
orientation_mod: 0,
}
}
(_, _, _, _) => {
let random_orientation = rng.random_range(0..orbit_info.num_orientations);
total_orientation = add_u8_mod(
total_orientation,
random_orientation,
orbit_info.num_orientations,
);
OrientationWithMod {
orientation: random_orientation,
orientation_mod: 0,
}
}
};
pattern.set_orientation_with_mod(orbit_info, original_i, &orientation_with_mod);
}
piece_order_shuffled
}
fn add_u8_mod(v1: u8, v2: u8, modulus: u8) -> u8 {
((v1 as u32) + (v2 as u32)).rem_euclid(modulus as u32) as u8
}
fn subtract_u8_mod(v1: u8, v2: u8, modulus: u8) -> u8 {
((v1 as i32) - (v2 as i32)).rem_euclid(modulus as i32) as u8
}