Implement SRS kicks code gen
This commit is contained in:
parent
6726326ec8
commit
4b9d10699f
|
@ -18,6 +18,7 @@ pub fn compile_ruleset(in_path: impl AsRef<Path>, out_path: impl AsRef<Path>) ->
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct RulesData {
|
struct RulesData {
|
||||||
shapes: BTreeMap<String, Vec<(i16, i16)>>,
|
shapes: BTreeMap<String, Vec<(i16, i16)>>,
|
||||||
|
kicks: BTreeMap<String, [Vec<(i16, i16)>; 4]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Output data (Rust code)
|
/// Output data (Rust code)
|
||||||
|
@ -27,6 +28,9 @@ struct RulesCode {
|
||||||
extents: Extents<Vec<i16>>,
|
extents: Extents<Vec<i16>>,
|
||||||
cells_bits: Vec<u16>,
|
cells_bits: Vec<u16>,
|
||||||
cells_indices: Vec<usize>,
|
cells_indices: Vec<usize>,
|
||||||
|
kicks_tests: Vec<(i16, i16)>,
|
||||||
|
kicks_counts: Vec<usize>,
|
||||||
|
kicks_indices: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Extents<E> {
|
struct Extents<E> {
|
||||||
|
@ -45,6 +49,7 @@ fn compile(rules: &RulesData) -> RulesCode {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let len_per_shape_rot = shape_indices.len() * 4;
|
let len_per_shape_rot = shape_indices.len() * 4;
|
||||||
|
let len_per_shape_kicks = shape_indices.len() * 4 * 4;
|
||||||
|
|
||||||
let mut extents = Extents {
|
let mut extents = Extents {
|
||||||
x0: vec![i16::MAX; len_per_shape_rot],
|
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_bits = vec![];
|
||||||
let mut cells_indices = vec![usize::MAX; len_per_shape_rot];
|
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() {
|
for (shape, &i0) in shape_indices.iter() {
|
||||||
let coords = rules.shapes.get(shape).unwrap();
|
let coords = rules.shapes.get(shape).unwrap();
|
||||||
for r in 0u8..4 {
|
for r in 0u8..4 {
|
||||||
|
@ -67,6 +84,16 @@ fn compile(rules: &RulesData) -> RulesCode {
|
||||||
extents.y1[i] = exts.y1;
|
extents.y1[i] = exts.y1;
|
||||||
cells_indices[i] = find_or_insert(&mut cells_bits, &data);
|
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 {
|
RulesCode {
|
||||||
|
@ -74,6 +101,9 @@ fn compile(rules: &RulesData) -> RulesCode {
|
||||||
extents,
|
extents,
|
||||||
cells_bits,
|
cells_bits,
|
||||||
cells_indices,
|
cells_indices,
|
||||||
|
kicks_tests,
|
||||||
|
kicks_counts,
|
||||||
|
kicks_indices,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +139,20 @@ fn rotate(x: i16, y: i16, r: u8) -> (i16, i16) {
|
||||||
xy
|
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
|
/// 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`.
|
/// 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 {
|
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();
|
let len = self.cells_indices.len();
|
||||||
writeln!(out, "pub const CELLS_INDEX: [usize; {len}] = {arr:?};")?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Implementation of SRS shapes and movement quirks.
|
//! Implementation of SRS shapes and movement quirks.
|
||||||
|
|
||||||
|
use crate::input::Kicks as KicksTrait;
|
||||||
use crate::piece::{Cells, Rot, Shape as ShapeTrait};
|
use crate::piece::{Cells, Rot, Shape as ShapeTrait};
|
||||||
|
|
||||||
mod code_gen {
|
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>;
|
pub type Piece = crate::piece::Piece<Shape>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::matrix::Mat;
|
||||||
use crate::piece::Loc;
|
use crate::piece::Loc;
|
||||||
|
use crate::{input, mat};
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::ops::Range;
|
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::T, &[(0, 1), (-1, 0), (0, 0), (1, 0)]);
|
||||||
test_cells_all_rotations(Shape::Z, &[(1, 0), (0, 0), (0, 1), (-1, 1)]);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue