diff --git a/mino-code-gen/src/lib.rs b/mino-code-gen/src/lib.rs index acccdb2..8e9dae9 100644 --- a/mino-code-gen/src/lib.rs +++ b/mino-code-gen/src/lib.rs @@ -1,5 +1,5 @@ use anyhow::{Context as _, Result}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::path::Path; pub fn compile_ruleset(in_path: impl AsRef, out_path: impl AsRef) -> Result<()> { @@ -17,14 +17,13 @@ pub fn compile_ruleset(in_path: impl AsRef, out_path: impl AsRef) -> /// Input data (JSON file) #[derive(serde::Deserialize)] struct RulesData { - shapes: BTreeMap>, - kicks: BTreeMap; 4]>, + shapes: HashMap>, + kicks: HashMap; 4]>, spawn: (i16, i16), } /// Output data (Rust code) struct RulesCode { - // establish consistent mapping from shape name to integer shape_indices: HashMap, extents: Extents>, cells_bits: Vec, @@ -43,15 +42,24 @@ struct Extents { } fn compile(rules: &RulesData) -> RulesCode { - let shape_indices: HashMap = rules - .shapes - .keys() - .enumerate() - .map(|(i, sh)| (sh.clone(), i)) - .collect(); + let mut shape_names: Vec<&str> = rules.shapes.keys().map(String::as_str).collect(); + // ensure deterministic order + shape_names.sort(); - let len_per_shape_rot = shape_indices.len() * 4; - let len_per_shape_kicks = shape_indices.len() * 4 * 4; + // `kicks` is keyed by strings indicating multiple shape names, e.g. "LJSTZ" means + // that this entry applies to each of those types. split the shape names into entries + // per shape for easier lookup. + let mut shapes_kicks: HashMap = HashMap::new(); + for (shapes_string, kicks) in rules.kicks.iter() { + for ch in shapes_string.chars() { + let shape = std::iter::once(ch).collect::(); + let kicks = std::array::from_fn(|i| &kicks[i][..]); + shapes_kicks.insert(shape, kicks); + } + } + + let len_per_shape_rot = shape_names.len() * 4; + let len_per_shape_kicks = shape_names.len() * 16; let mut extents = Extents { x0: vec![i16::MAX; len_per_shape_rot], @@ -60,22 +68,21 @@ fn compile(rules: &RulesData) -> RulesCode { y1: vec![i16::MIN; len_per_shape_rot], }; + let shape_indices: HashMap = shape_names + .iter() + .enumerate() + .map(|(i, &name)| (name.to_owned(), i)) + .collect(); + let mut cells_bits = vec![]; let mut cells_indices = vec![usize::MAX; len_per_shape_rot]; - let mut shapes_kicks: HashMap; 4]> = HashMap::new(); - for (shapes_string, kicks) in rules.kicks.iter() { - for ch in shapes_string.chars() { - let shape = std::iter::once(ch).collect::(); - shapes_kicks.insert(shape, kicks); - } - } - let mut kicks_tests = vec![]; let mut kicks_counts = vec![0; len_per_shape_kicks]; let mut kicks_indices = vec![usize::MAX; len_per_shape_kicks]; - for (shape, &i0) in shape_indices.iter() { + for &shape in shape_names.iter() { + let i0 = shape_indices.get(shape).unwrap(); let coords = rules.shapes.get(shape).unwrap(); for r in 0u8..4 { let i = i0 * 4 + r as usize; @@ -87,7 +94,7 @@ fn compile(rules: &RulesData) -> RulesCode { cells_indices[i] = find_or_insert(&mut cells_bits, &data); } - let kicks = *shapes_kicks.get(shape).unwrap(); + let kicks = shapes_kicks.get(shape).unwrap(); for r0 in 0u8..4 { for r1 in 0u8..4 { let i = i0 * 16 + r0 as usize * 4 + r1 as usize; @@ -142,16 +149,14 @@ fn rotate(x: i16, y: i16, r: u8) -> (i16, i16) { xy } -fn process_kicks(kicks: &[Vec<(i16, i16)>; 4], r0: u8, r1: u8) -> Vec<(i16, i16)> { +fn process_kicks(kicks: &[&[(i16, i16)]], r0: u8, r1: u8) -> Vec<(i16, i16)> { if r0 == r1 { return vec![]; } - - let kicks0 = &kicks[r0 as usize]; - let kicks1 = &kicks[r1 as usize]; + let kicks0 = kicks[r0 as usize].iter(); + let kicks1 = kicks[r1 as usize].iter(); kicks0 - .iter() - .zip(kicks1.iter()) + .zip(kicks1) .map(|(&(x0, y0), &(x1, y1))| (x0 - x1, y0 - y1)) .collect() }