pub type PackedOrientationWithMod = u8;
const NUM_BYTE_VALUES: usize = 0x100;
const BOGUS_PACKED_VALUE: PackedOrientationWithMod = 0xFF;
const MAX_NUM_ORIENTATIONS: usize = 107;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct OrientationWithMod {
pub orientation: u8,
pub orientation_mod: u8,
}
impl OrientationWithMod {
pub fn new_using_default_orientation_mod(orientation: u8) -> Self {
Self {
orientation,
orientation_mod: 0,
}
}
}
const BOGUS_ORIENTATION_WITH_MOD: OrientationWithMod = OrientationWithMod {
orientation: 0xFE,
orientation_mod: 0xFD,
};
#[derive(Debug)]
pub struct OrientationPacker {
transformation_lookup: [[PackedOrientationWithMod; NUM_BYTE_VALUES]; MAX_NUM_ORIENTATIONS],
unpacking_table: [OrientationWithMod; NUM_BYTE_VALUES],
packing_table: [PackedOrientationWithMod; NUM_BYTE_VALUES],
}
impl OrientationPacker {
pub fn new(num_orientations: u8) -> Self {
let mut unpacking_table: [OrientationWithMod; NUM_BYTE_VALUES] =
[BOGUS_ORIENTATION_WITH_MOD; NUM_BYTE_VALUES];
let mut packing_table = [BOGUS_PACKED_VALUE; NUM_BYTE_VALUES];
let mut num_packed_values_sofar: u8 = 0;
#[allow(clippy::needless_range_loop)]
for orientation_mod in 0..=(u8::MAX) {
let factor = if orientation_mod == 0 {
num_orientations
} else {
orientation_mod
};
if num_orientations % factor != 0 {
continue;
}
packing_table[orientation_mod as usize] = num_packed_values_sofar; for orientation in 0..factor {
unpacking_table[num_packed_values_sofar as usize] = OrientationWithMod {
orientation,
orientation_mod,
};
num_packed_values_sofar += 1;
}
}
let mut transformation_lookup: [[u8; NUM_BYTE_VALUES]; MAX_NUM_ORIENTATIONS] =
[[BOGUS_PACKED_VALUE; NUM_BYTE_VALUES]; MAX_NUM_ORIENTATIONS];
#[allow(clippy::needless_range_loop)]
for orientation_delta in 0..num_orientations {
for packed_value in 0..num_packed_values_sofar {
let orientation_with_mod = &unpacking_table[packed_value as usize];
let new_orientation = (orientation_with_mod.orientation + orientation_delta)
% if orientation_with_mod.orientation_mod == 0 {
num_orientations
} else {
orientation_with_mod.orientation_mod
};
transformation_lookup[orientation_delta as usize][packed_value as usize] =
packing_table[orientation_with_mod.orientation_mod as usize] + new_orientation
}
}
Self {
transformation_lookup,
unpacking_table,
packing_table,
}
}
pub fn transform(
&self,
packed_value: PackedOrientationWithMod,
orientation_delta: u8,
) -> PackedOrientationWithMod {
self.transformation_lookup[orientation_delta as usize][packed_value as usize]
}
#[allow(dead_code)]
pub fn unpack(&self, packed_value: &PackedOrientationWithMod) -> &OrientationWithMod {
&self.unpacking_table[(*packed_value) as usize]
}
pub fn pack(&self, orientation_with_mod: &OrientationWithMod) -> PackedOrientationWithMod {
self.packing_table[orientation_with_mod.orientation_mod as usize]
+ orientation_with_mod.orientation
}
}
#[cfg(test)]
mod tests {
use crate::kpuzzle::{
KPattern, KPatternData, KPatternOrbitData, KPuzzle, KPuzzleDefinition, KPuzzleOrbitName,
};
#[test]
fn orientation_mod() {
let def: KPuzzleDefinition = serde_json::from_str(
r#"
{
"name": "custom",
"orbits": [{ "orbitName": "PIECES", "numPieces": 2, "numOrientations": 12 }],
"defaultPattern": {
"PIECES": {
"pieces": [0, 1],
"orientation": [0, 0],
"orientationMod": [3, 4]
}
},
"moves": {
"SWAP": { "PIECES": { "permutation": [1, 0], "orientationDelta": [0, 0] } },
"SPIN": { "PIECES": { "permutation": [0, 1], "orientationDelta": [2, 5] } }
},
"derivedMoves": null
}"#,
)
.unwrap();
let kpuzzle = KPuzzle::try_new(def).unwrap();
let spin = kpuzzle
.transformation_from_move(&"SPIN".try_into().unwrap())
.unwrap();
let swap = kpuzzle
.transformation_from_move(&"SWAP".try_into().unwrap())
.unwrap();
let pattern = kpuzzle.default_pattern();
let pattern = pattern.apply_transformation(&spin);
let pattern = pattern.apply_transformation(&swap);
let pattern = pattern.apply_transformation(&spin);
let expected = KPattern::try_from_data(
&kpuzzle,
&KPatternData::from([(
KPuzzleOrbitName("PIECES".to_owned()),
KPatternOrbitData {
pieces: vec![1, 0],
orientation: vec![3, 1],
orientation_mod: Some(vec![4, 3]),
},
)]),
)
.unwrap();
assert_eq!(pattern, expected);
println!("Custom puzzle test passes!\n--------");
}
}