tetris/src/srs.rs

159 lines
5.0 KiB
Rust

use crate::playfield::{Matrix, PlayField};
use crate::tetromino::{Position, RotationState, Tetromino, TetrominoType};
use std::collections::HashMap;
pub trait RotationSystem {
fn default() -> Self;
fn rotate_left(&self, playfield: &PlayField) -> Result<Position, ()>;
fn rotate_right(&self, playfield: &PlayField) -> Result<Position, ()>;
}
pub struct SRS {
jlstz_offset_data: HashMap<RotationState, Vec<Position>>,
i_offset_data: HashMap<RotationState, Vec<Position>>,
o_offset_data: HashMap<RotationState, Vec<Position>>,
}
enum RotationDirection {
Left,
Right,
}
impl SRS {
fn rotate_common(
&self,
playfield: &PlayField,
rotation: RotationDirection,
) -> Result<Position, ()> {
let active_piece = match playfield.active_piece {
Some(piece) => piece,
None => return Err(()),
};
let offset_data = match active_piece.piece_type {
TetrominoType::I => &self.i_offset_data,
TetrominoType::O => &self.o_offset_data,
_ => &self.jlstz_offset_data,
};
let prev_offsets = offset_data.get(&active_piece.rotation_state).unwrap();
let mut test_tetromino = active_piece.clone();
match rotation {
RotationDirection::Left => test_tetromino.rotate_left(),
RotationDirection::Right => test_tetromino.rotate_right(),
}
let cur_offsets = offset_data.get(&test_tetromino.rotation_state).unwrap();
let mut offsets = Vec::with_capacity(cur_offsets.len());
for i in 0..cur_offsets.len() {
offsets.push(prev_offsets[i] - cur_offsets[i]);
}
for offset in offsets {
let x = offset.x;
let y = offset.y;
let test_position = active_piece.position.offset(x, y);
test_tetromino.position = test_position;
if playfield.can_piece_be_at_position(&test_tetromino) {
return Ok(offset);
}
}
Err(())
}
}
impl RotationSystem for SRS {
fn default() -> Self {
let mut jlstz_offset_data = HashMap::with_capacity(4);
let mut i_offset_data = HashMap::with_capacity(4);
let mut o_offset_data = HashMap::with_capacity(4);
jlstz_offset_data.insert(RotationState::O, vec![Position { x: 0, y: 0 }].repeat(5));
jlstz_offset_data.insert(
RotationState::R,
vec![
Position { x: 0, y: 0 },
Position { x: 1, y: 0 },
Position { x: 1, y: 1 },
Position { x: 0, y: -2 },
Position { x: 1, y: -2 },
],
);
jlstz_offset_data.insert(RotationState::U, vec![Position { x: 0, y: 0 }].repeat(5));
jlstz_offset_data.insert(
RotationState::L,
vec![
Position { x: 0, y: 0 },
Position { x: -1, y: 0 },
Position { x: -1, y: 1 },
Position { x: 0, y: -2 },
Position { x: -1, y: -2 },
],
);
i_offset_data.insert(
RotationState::O,
vec![
Position { x: 0, y: 0 },
Position { x: -1, y: 0 },
Position { x: 2, y: 0 },
Position { x: -1, y: 0 },
Position { x: 2, y: 0 },
],
);
i_offset_data.insert(
RotationState::R,
vec![
Position { x: -1, y: 0 },
Position { x: 0, y: 0 },
Position { x: 0, y: 0 },
Position { x: 0, y: -1 },
Position { x: 0, y: 2 },
],
);
i_offset_data.insert(
RotationState::U,
vec![
Position { x: -1, y: -1 },
Position { x: 1, y: -1 },
Position { x: -2, y: -1 },
Position { x: 1, y: 0 },
Position { x: -2, y: 0 },
],
);
i_offset_data.insert(
RotationState::L,
vec![
Position { x: 0, y: -1 },
Position { x: 0, y: -1 },
Position { x: 0, y: -1 },
Position { x: 0, y: 1 },
Position { x: 0, y: -2 },
],
);
o_offset_data.insert(RotationState::O, vec![Position { x: 0, y: 0 }]);
o_offset_data.insert(RotationState::R, vec![Position { x: 0, y: 1 }]);
o_offset_data.insert(RotationState::U, vec![Position { x: -1, y: 1 }]);
o_offset_data.insert(RotationState::L, vec![Position { x: -1, y: 0 }]);
SRS {
jlstz_offset_data,
i_offset_data,
o_offset_data,
}
}
fn rotate_left(&self, playfield: &PlayField) -> Result<Position, ()> {
self.rotate_common(playfield, RotationDirection::Left)
}
fn rotate_right(&self, playfield: &PlayField) -> Result<Position, ()> {
self.rotate_common(playfield, RotationDirection::Right)
}
}