Implement SRS kicks code gen

This commit is contained in:
tali 2022-12-14 12:57:26 -05:00
parent 6726326ec8
commit 4b9d10699f
2 changed files with 138 additions and 0 deletions

View File

@ -18,6 +18,7 @@ pub fn compile_ruleset(in_path: impl AsRef<Path>, out_path: impl AsRef<Path>) ->
#[derive(serde::Deserialize)]
struct RulesData {
shapes: BTreeMap<String, Vec<(i16, i16)>>,
kicks: BTreeMap<String, [Vec<(i16, i16)>; 4]>,
}
/// Output data (Rust code)
@ -27,6 +28,9 @@ struct RulesCode {
extents: Extents<Vec<i16>>,
cells_bits: Vec<u16>,
cells_indices: Vec<usize>,
kicks_tests: Vec<(i16, i16)>,
kicks_counts: Vec<usize>,
kicks_indices: Vec<usize>,
}
struct Extents<E> {
@ -45,6 +49,7 @@ fn compile(rules: &RulesData) -> RulesCode {
.collect();
let len_per_shape_rot = shape_indices.len() * 4;
let len_per_shape_kicks = shape_indices.len() * 4 * 4;
let mut extents = Extents {
x0: vec![i16::MAX; len_per_shape_rot],
@ -56,6 +61,18 @@ fn compile(rules: &RulesData) -> RulesCode {
let mut cells_bits = vec![];
let mut cells_indices = vec![usize::MAX; len_per_shape_rot];
let mut shapes_kicks: HashMap<String, &[Vec<(i16, i16)>; 4]> = HashMap::new();
for (shapes_string, kicks) in rules.kicks.iter() {
for ch in shapes_string.chars() {
let shape = std::iter::once(ch).collect::<String>();
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() {
let coords = rules.shapes.get(shape).unwrap();
for r in 0u8..4 {
@ -67,6 +84,16 @@ fn compile(rules: &RulesData) -> RulesCode {
extents.y1[i] = exts.y1;
cells_indices[i] = find_or_insert(&mut cells_bits, &data);
}
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;
let tests = process_kicks(kicks, r0, r1);
kicks_counts[i] = tests.len();
kicks_indices[i] = find_or_insert(&mut kicks_tests, &tests);
}
}
}
RulesCode {
@ -74,6 +101,9 @@ fn compile(rules: &RulesData) -> RulesCode {
extents,
cells_bits,
cells_indices,
kicks_tests,
kicks_counts,
kicks_indices,
}
}
@ -109,6 +139,20 @@ 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)> {
if r0 == r1 {
return vec![];
}
let kicks0 = &kicks[r0 as usize];
let kicks1 = &kicks[r1 as usize];
kicks0
.iter()
.zip(kicks1.iter())
.map(|(&(x0, y0), &(x1, y1))| (x0 - x1, y0 - y1))
.collect()
}
/// Finds the first occurence of `needle` in `haystack`, or appends a suffix of it (up to
/// the entire length), returning the index within `haystack` that now contains `needle`.
fn find_or_insert<T: Eq + Copy>(haystack: &mut Vec<T>, needle: &[T]) -> usize {
@ -150,6 +194,18 @@ impl RulesCode {
let len = self.cells_indices.len();
writeln!(out, "pub const CELLS_INDEX: [usize; {len}] = {arr:?};")?;
let arr = &self.kicks_tests;
let len = self.kicks_tests.len();
writeln!(out, "pub const KICKS_TESTS: [(i16, i16); {len}] = {arr:?};")?;
let arr = &self.kicks_counts;
let len = self.kicks_counts.len();
writeln!(out, "pub const KICKS_COUNT: [usize; {len}] = {arr:?};")?;
let arr = &self.kicks_indices;
let len = self.kicks_indices.len();
writeln!(out, "pub const KICKS_INDEX: [usize; {len}] = {arr:?};")?;
Ok(())
}
}

View File

@ -1,5 +1,6 @@
//! Implementation of SRS shapes and movement quirks.
use crate::input::Kicks as KicksTrait;
use crate::piece::{Cells, Rot, Shape as ShapeTrait};
mod code_gen {
@ -29,12 +30,23 @@ impl ShapeTrait<'static> for Shape {
}
}
impl KicksTrait<'static> for Shape {
fn kicks(&self, r0: Rot, r1: Rot) -> &'static [(i16, i16)] {
let i = (*self as usize) * 16 + (r0 as usize) * 4 + r1 as usize;
let idx = code_gen::KICKS_INDEX[i];
let len = code_gen::KICKS_COUNT[i];
&code_gen::KICKS_TESTS[idx..idx + len]
}
}
pub type Piece = crate::piece::Piece<Shape>;
#[cfg(test)]
mod test {
use super::*;
use crate::matrix::Mat;
use crate::piece::Loc;
use crate::{input, mat};
use alloc::vec::Vec;
use core::ops::Range;
@ -90,4 +102,74 @@ mod test {
test_cells_all_rotations(Shape::T, &[(0, 1), (-1, 0), (0, 0), (1, 0)]);
test_cells_all_rotations(Shape::Z, &[(1, 0), (0, 0), (0, 1), (-1, 1)]);
}
#[test]
fn test_srs_o_kicks_in_place() {
use input::Spin::*;
let mut piece = Piece {
shape: Shape::O,
loc: Loc {
x: 6,
y: 6,
r: Rot::N,
},
};
assert_eq!(input::rotate(&mut piece, Mat::EMPTY, Cw), Some(0));
assert_eq!(piece.loc.x, 6);
assert_eq!(piece.loc.y, 7);
assert_eq!(input::rotate(&mut piece, Mat::EMPTY, Cw), Some(0));
assert_eq!(piece.loc.x, 7);
assert_eq!(piece.loc.y, 7);
assert_eq!(input::rotate(&mut piece, Mat::EMPTY, Cw), Some(0));
assert_eq!(piece.loc.x, 7);
assert_eq!(piece.loc.y, 6);
assert_eq!(input::rotate(&mut piece, Mat::EMPTY, Cw), Some(0));
assert_eq!(piece.loc.x, 6);
assert_eq!(piece.loc.y, 6);
}
#[test]
fn test_srs_t_spin_triple() {
const SETUP: &Mat = mat! {
"......xx..";
".......x..";
"xxxxxx.xxx";
"xxxxx..xxx";
"xxxxxx.xxx";
};
let mut piece = Piece {
shape: Shape::T,
loc: Loc {
x: 5,
y: 3,
r: Rot::N,
},
};
assert_eq!(input::rotate(&mut piece, SETUP, input::Spin::Ccw), Some(4));
assert_eq!(piece.loc.x, 6);
assert_eq!(piece.loc.y, 1);
assert_eq!(piece.loc.r, Rot::W);
}
#[test]
fn test_srs_z_spin_triple() {
const SETUP: &Mat = mat! {
"....x.....";
"xxxxxx.xxx";
"xxxxx..xxx";
"xxxxx.xxxx";
};
let mut piece = Piece {
shape: Shape::Z,
loc: Loc {
x: 5,
y: 3,
r: Rot::N,
},
};
assert_eq!(input::rotate(&mut piece, SETUP, input::Spin::Cw), Some(3));
assert_eq!(piece.loc.x, 5);
assert_eq!(piece.loc.y, 1);
assert_eq!(piece.loc.r, Rot::E);
}
}