move game stuff into game
This commit is contained in:
parent
eddf9d81c0
commit
f1877a1e8c
280
src/game.rs
Normal file
280
src/game.rs
Normal file
@ -0,0 +1,280 @@
|
||||
use crate::actions::{Controls, ControlsHeld};
|
||||
use crate::board::Board;
|
||||
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
||||
|
||||
struct CurrentTetromino {
|
||||
tetromino: Tetromino,
|
||||
direction: Direction,
|
||||
x: i8,
|
||||
y: i8,
|
||||
}
|
||||
|
||||
impl CurrentTetromino {
|
||||
fn new(tetromino: Tetromino) -> Self {
|
||||
const PIECE_WIDTH: i8 = 2;
|
||||
Self {
|
||||
tetromino,
|
||||
direction: Direction::Up,
|
||||
x: (Board::WIDTH as i8 - PIECE_WIDTH) / 2,
|
||||
y: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Game {
|
||||
board: Board,
|
||||
next_tetrominos: [Tetromino; 3],
|
||||
current_tetromino: CurrentTetromino,
|
||||
held_tetromino: Option<Tetromino>,
|
||||
has_swapped_held: bool,
|
||||
score: Score,
|
||||
ticks: usize,
|
||||
}
|
||||
|
||||
struct Score {
|
||||
level: usize,
|
||||
points: usize,
|
||||
lines: usize,
|
||||
combo: usize,
|
||||
back_to_back: bool,
|
||||
}
|
||||
|
||||
impl Score {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
level: 0,
|
||||
points: 0,
|
||||
lines: 0,
|
||||
combo: 0,
|
||||
back_to_back: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn level_up(&mut self, lines_cleared: usize) {
|
||||
self.lines += lines_cleared;
|
||||
if self.lines > self.level * 5 {
|
||||
self.level += 1;
|
||||
self.lines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn point_multiplier_from_lines_cleared(lines_cleared: usize) -> f32 {
|
||||
match lines_cleared {
|
||||
0 => 0.0,
|
||||
1 => 100.0,
|
||||
2 => 300.0,
|
||||
3 => 500.0,
|
||||
4 => 800.0,
|
||||
_ => unreachable!("we cannot clear more than 4 lines"),
|
||||
}
|
||||
}
|
||||
|
||||
fn combos(&self, lines_cleared: usize) -> usize {
|
||||
if lines_cleared > 0 {
|
||||
self.combo * 50 * self.level
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn take_next_in_bag(&mut self, mut last: Tetromino) -> Tetromino {
|
||||
for value in self.next_tetrominos.iter_mut().rev() {
|
||||
std::mem::swap(value, &mut last)
|
||||
}
|
||||
last
|
||||
}
|
||||
|
||||
fn hard_drop(&mut self, controls: &ControlsHeld) {
|
||||
if !controls.just_pressed(self.ticks, &Controls::HardDrop) {
|
||||
return;
|
||||
}
|
||||
let start_y = self.current_tetromino.y;
|
||||
loop {
|
||||
self.current_tetromino.y += 1;
|
||||
if !self.board.colliding(&self.current_tetromino) {
|
||||
continue;
|
||||
}
|
||||
self.current_tetromino.y -= 1;
|
||||
self.score.points += (self.current_tetromino.y - start_y) as usize * 2;
|
||||
self.place_current_tetromino();
|
||||
self.check_line_clears();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn soft_drop(&mut self, controls: &ControlsHeld) {
|
||||
let mut delay = 32 - self.score.level * 2;
|
||||
if controls.contains_key(&Controls::SoftDrop) {
|
||||
delay /= 10;
|
||||
}
|
||||
|
||||
if self.ticks % delay != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.current_tetromino.y += 1;
|
||||
if self.board.colliding(&self.current_tetromino) {
|
||||
self.current_tetromino.y -= 1;
|
||||
self.place_current_tetromino();
|
||||
self.check_line_clears();
|
||||
} else if controls.contains_key(&Controls::SoftDrop) {
|
||||
self.score.points += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_horizontally(&mut self, controls: &ControlsHeld) {
|
||||
for key in [Controls::Left, Controls::Right] {
|
||||
let just_pressed = controls.just_pressed(self.ticks, &key);
|
||||
let long_press = controls.held_for(self.ticks, &key, |held_for| held_for > 15);
|
||||
if !just_pressed && !long_press {
|
||||
continue;
|
||||
}
|
||||
let offset = match key {
|
||||
Controls::Left => -1,
|
||||
Controls::Right => 1,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.current_tetromino.x += offset;
|
||||
if self.board.colliding(&self.current_tetromino) {
|
||||
self.current_tetromino.x -= offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_line_clears(&mut self) {
|
||||
let lines_cleared = self.board.lines_cleared();
|
||||
|
||||
self.score.level_up(lines_cleared);
|
||||
|
||||
let mut points =
|
||||
self.score.level as f32 * Score::point_multiplier_from_lines_cleared(lines_cleared);
|
||||
|
||||
if self.score.back_to_back && lines_cleared == 4 {
|
||||
points *= 1.5;
|
||||
}
|
||||
points += self.score.combos(lines_cleared) as f32;
|
||||
|
||||
self.score.points += points as usize;
|
||||
|
||||
if lines_cleared == 4 {
|
||||
self.score.back_to_back = true;
|
||||
} else if lines_cleared > 0 {
|
||||
self.score.back_to_back = false;
|
||||
}
|
||||
|
||||
if lines_cleared > 0 {
|
||||
self.score.combo += 1;
|
||||
// play_line_clears_sound();
|
||||
} else {
|
||||
self.score.combo = 0;
|
||||
// play_hard_drop_sound();
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&mut self, controls: &ControlsHeld) {
|
||||
// TODO: ensure game is running at 60 ticks per second? (`if !check_update_time(context, 60) { return; }`)
|
||||
self.hard_drop(controls);
|
||||
self.soft_drop(controls);
|
||||
self.move_horizontally(controls);
|
||||
|
||||
if controls.just_pressed(self.ticks, &Controls::Swap) {
|
||||
self.try_swap_tetromino();
|
||||
}
|
||||
|
||||
for (control, direction) in [
|
||||
(Controls::RotateCw, DirectionDiff::Cw),
|
||||
(Controls::RotateCcw, DirectionDiff::Ccw),
|
||||
] {
|
||||
if !controls.just_pressed(self.ticks, &control) {
|
||||
continue;
|
||||
}
|
||||
self.try_rotate(direction);
|
||||
}
|
||||
self.ticks += 1;
|
||||
}
|
||||
|
||||
fn try_rotate(&mut self, diff: DirectionDiff) -> bool {
|
||||
let rotated = self.current_tetromino.direction.rotate(&diff);
|
||||
let old_direction = std::mem::replace(&mut self.current_tetromino.direction, rotated);
|
||||
if !self.board.colliding(&self.current_tetromino) {
|
||||
return true;
|
||||
}
|
||||
let wall_kicks = self
|
||||
.current_tetromino
|
||||
.tetromino
|
||||
.wall_kicks(&old_direction, &diff);
|
||||
|
||||
for (x, y) in wall_kicks {
|
||||
self.current_tetromino.x += x;
|
||||
self.current_tetromino.y += y;
|
||||
if !(self.board.colliding(&self.current_tetromino)) {
|
||||
return true;
|
||||
}
|
||||
self.current_tetromino.x -= x;
|
||||
self.current_tetromino.y -= y;
|
||||
}
|
||||
|
||||
self.current_tetromino.direction = old_direction;
|
||||
false
|
||||
}
|
||||
|
||||
fn place_current_tetromino(&mut self) {
|
||||
let next = CurrentTetromino::new(self.take_next_in_bag(Tetromino::random()));
|
||||
let current = std::mem::replace(&mut self.current_tetromino, next);
|
||||
let pattern = current.tetromino.direction_pattern(¤t.direction);
|
||||
|
||||
for (y, row) in pattern.iter().enumerate() {
|
||||
for x in row
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, exists)| **exists)
|
||||
.map(|(x, _)| x)
|
||||
{
|
||||
let y = (current.y + y as i8) as usize;
|
||||
let x = (current.x + x as i8) as usize;
|
||||
self.board[y][x] = Some(current.tetromino.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.has_swapped_held = false;
|
||||
}
|
||||
|
||||
fn try_swap_tetromino(&mut self) {
|
||||
if self.has_swapped_held {
|
||||
return;
|
||||
}
|
||||
self.has_swapped_held = true;
|
||||
let held_or_first_in_bag_tetromino = self
|
||||
.held_tetromino
|
||||
.take()
|
||||
.unwrap_or_else(|| self.take_next_in_bag(Tetromino::random()));
|
||||
let current_tetromino = CurrentTetromino::new(held_or_first_in_bag_tetromino);
|
||||
let old_tetromino = std::mem::replace(&mut self.current_tetromino, current_tetromino);
|
||||
self.held_tetromino.replace(old_tetromino.tetromino);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Board, CurrentTetromino, Game, Score, Tetromino};
|
||||
|
||||
#[test]
|
||||
fn advance_bag() {
|
||||
let mut game = Game {
|
||||
board: Board::new(),
|
||||
score: Score::new(),
|
||||
next_tetrominos: [Tetromino::I, Tetromino::J, Tetromino::O],
|
||||
current_tetromino: CurrentTetromino::new(Tetromino::J),
|
||||
held_tetromino: None,
|
||||
has_swapped_held: false,
|
||||
ticks: 0,
|
||||
};
|
||||
assert_eq!(game.take_next_in_bag(Tetromino::S), Tetromino::I);
|
||||
assert_eq!(
|
||||
game.next_tetrominos,
|
||||
[Tetromino::J, Tetromino::O, Tetromino::S]
|
||||
);
|
||||
}
|
||||
}
|
288
src/main.rs
288
src/main.rs
@ -1,288 +1,24 @@
|
||||
use actions::{Controls, ControlsHeld};
|
||||
use board::Board;
|
||||
use tetromino::{Direction, DirectionDiff, Tetromino};
|
||||
use tetromino::Tetromino;
|
||||
|
||||
mod actions;
|
||||
mod board;
|
||||
mod game;
|
||||
mod tetromino;
|
||||
|
||||
struct Rgb(u8, u8, u8);
|
||||
|
||||
struct CurrentTetromino {
|
||||
tetromino: Tetromino,
|
||||
direction: Direction,
|
||||
x: i8,
|
||||
y: i8,
|
||||
}
|
||||
|
||||
impl CurrentTetromino {
|
||||
fn new(tetromino: Tetromino) -> Self {
|
||||
const PIECE_WIDTH: i8 = 2;
|
||||
Self {
|
||||
tetromino,
|
||||
direction: Direction::Up,
|
||||
x: (Board::WIDTH as i8 - PIECE_WIDTH) / 2,
|
||||
y: -1,
|
||||
impl Rgb {
|
||||
fn from_tetromino(tetromino: &Tetromino) -> Self {
|
||||
match tetromino {
|
||||
Tetromino::I => Self(0, 255, 255),
|
||||
Tetromino::J => Self(0, 0, 255),
|
||||
Tetromino::L => Self(255, 128, 0),
|
||||
Tetromino::O => Self(255, 255, 0),
|
||||
Tetromino::S => Self(0, 255, 0),
|
||||
Tetromino::T => Self(255, 0, 255),
|
||||
Tetromino::Z => Self(255, 0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Game {
|
||||
board: Board,
|
||||
next_tetrominos: [Tetromino; 3],
|
||||
current_tetromino: CurrentTetromino,
|
||||
held_tetromino: Option<Tetromino>,
|
||||
has_swapped_held: bool,
|
||||
score: Score,
|
||||
ticks: usize,
|
||||
}
|
||||
|
||||
struct Score {
|
||||
level: usize,
|
||||
points: usize,
|
||||
lines: usize,
|
||||
combo: usize,
|
||||
back_to_back: bool,
|
||||
}
|
||||
|
||||
impl Score {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
level: 0,
|
||||
points: 0,
|
||||
lines: 0,
|
||||
combo: 0,
|
||||
back_to_back: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn level_up(&mut self, lines_cleared: usize) {
|
||||
self.lines += lines_cleared;
|
||||
if self.lines > self.level * 5 {
|
||||
self.level += 1;
|
||||
self.lines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn point_multiplier_from_lines_cleared(lines_cleared: usize) -> f32 {
|
||||
match lines_cleared {
|
||||
0 => 0.0,
|
||||
1 => 100.0,
|
||||
2 => 300.0,
|
||||
3 => 500.0,
|
||||
4 => 800.0,
|
||||
_ => unreachable!("we cannot clear more than 4 lines"),
|
||||
}
|
||||
}
|
||||
|
||||
fn combos(&self, lines_cleared: usize) -> usize {
|
||||
if lines_cleared > 0 {
|
||||
self.combo * 50 * self.level
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Game {
|
||||
fn take_next_in_bag(&mut self, mut last: Tetromino) -> Tetromino {
|
||||
for value in self.next_tetrominos.iter_mut().rev() {
|
||||
std::mem::swap(value, &mut last)
|
||||
}
|
||||
last
|
||||
}
|
||||
|
||||
fn hard_drop(&mut self, controls: &ControlsHeld) {
|
||||
if !controls.just_pressed(self.ticks, &Controls::HardDrop) {
|
||||
return;
|
||||
}
|
||||
let start_y = self.current_tetromino.y;
|
||||
loop {
|
||||
self.current_tetromino.y += 1;
|
||||
if !self.board.colliding(&self.current_tetromino) {
|
||||
continue;
|
||||
}
|
||||
self.current_tetromino.y -= 1;
|
||||
self.score.points += (self.current_tetromino.y - start_y) as usize * 2;
|
||||
self.place_current_tetromino();
|
||||
self.check_line_clears();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn soft_drop(&mut self, controls: &ControlsHeld) {
|
||||
let mut delay = 32 - self.score.level * 2;
|
||||
if controls.contains_key(&Controls::SoftDrop) {
|
||||
delay /= 10;
|
||||
}
|
||||
|
||||
if self.ticks % delay != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.current_tetromino.y += 1;
|
||||
if self.board.colliding(&self.current_tetromino) {
|
||||
self.current_tetromino.y -= 1;
|
||||
self.place_current_tetromino();
|
||||
self.check_line_clears();
|
||||
} else if controls.contains_key(&Controls::SoftDrop) {
|
||||
self.score.points += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_horizontally(&mut self, controls: &ControlsHeld) {
|
||||
for key in [Controls::Left, Controls::Right] {
|
||||
let just_pressed = controls.just_pressed(self.ticks, &key);
|
||||
let long_press = controls.held_for(self.ticks, &key, |held_for| held_for > 15);
|
||||
if !just_pressed && !long_press {
|
||||
continue;
|
||||
}
|
||||
let offset = match key {
|
||||
Controls::Left => -1,
|
||||
Controls::Right => 1,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.current_tetromino.x += offset;
|
||||
if self.board.colliding(&self.current_tetromino) {
|
||||
self.current_tetromino.x -= offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_line_clears(&mut self) {
|
||||
let lines_cleared = self.board.lines_cleared();
|
||||
|
||||
self.score.level_up(lines_cleared);
|
||||
|
||||
let mut points =
|
||||
self.score.level as f32 * Score::point_multiplier_from_lines_cleared(lines_cleared);
|
||||
|
||||
if self.score.back_to_back && lines_cleared == 4 {
|
||||
points *= 1.5;
|
||||
}
|
||||
points += self.score.combos(lines_cleared) as f32;
|
||||
|
||||
self.score.points += points as usize;
|
||||
|
||||
if lines_cleared == 4 {
|
||||
self.score.back_to_back = true;
|
||||
} else if lines_cleared > 0 {
|
||||
self.score.back_to_back = false;
|
||||
}
|
||||
|
||||
if lines_cleared > 0 {
|
||||
self.score.combo += 1;
|
||||
// play_line_clears_sound();
|
||||
} else {
|
||||
self.score.combo = 0;
|
||||
// play_hard_drop_sound();
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&mut self, controls: &ControlsHeld) {
|
||||
// TODO: ensure game is running at 60 ticks per second? (`if !check_update_time(context, 60) { return; }`)
|
||||
self.hard_drop(controls);
|
||||
self.soft_drop(controls);
|
||||
self.move_horizontally(controls);
|
||||
|
||||
if controls.just_pressed(self.ticks, &Controls::Swap) {
|
||||
self.try_swap_tetromino();
|
||||
}
|
||||
|
||||
for (control, direction) in [
|
||||
(Controls::RotateCw, DirectionDiff::Cw),
|
||||
(Controls::RotateCcw, DirectionDiff::Ccw),
|
||||
] {
|
||||
if !controls.just_pressed(self.ticks, &control) {
|
||||
continue;
|
||||
}
|
||||
self.try_rotate(direction);
|
||||
}
|
||||
self.ticks += 1;
|
||||
}
|
||||
|
||||
fn try_rotate(&mut self, diff: DirectionDiff) -> bool {
|
||||
let rotated = self.current_tetromino.direction.rotate(&diff);
|
||||
let old_direction = std::mem::replace(&mut self.current_tetromino.direction, rotated);
|
||||
if !self.board.colliding(&self.current_tetromino) {
|
||||
return true;
|
||||
}
|
||||
let wall_kicks = self
|
||||
.current_tetromino
|
||||
.tetromino
|
||||
.wall_kicks(&old_direction, &diff);
|
||||
|
||||
for (x, y) in wall_kicks {
|
||||
self.current_tetromino.x += x;
|
||||
self.current_tetromino.y += y;
|
||||
if !(self.board.colliding(&self.current_tetromino)) {
|
||||
return true;
|
||||
}
|
||||
self.current_tetromino.x -= x;
|
||||
self.current_tetromino.y -= y;
|
||||
}
|
||||
|
||||
self.current_tetromino.direction = old_direction;
|
||||
false
|
||||
}
|
||||
|
||||
fn place_current_tetromino(&mut self) {
|
||||
let next = CurrentTetromino::new(self.take_next_in_bag(Tetromino::random()));
|
||||
let current = std::mem::replace(&mut self.current_tetromino, next);
|
||||
let pattern = current.tetromino.direction_pattern(¤t.direction);
|
||||
|
||||
for (y, row) in pattern.iter().enumerate() {
|
||||
for x in row
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, exists)| **exists)
|
||||
.map(|(x, _)| x)
|
||||
{
|
||||
let y = (current.y + y as i8) as usize;
|
||||
let x = (current.x + x as i8) as usize;
|
||||
self.board[y][x] = Some(current.tetromino.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.has_swapped_held = false;
|
||||
}
|
||||
|
||||
fn try_swap_tetromino(&mut self) {
|
||||
if self.has_swapped_held {
|
||||
return;
|
||||
}
|
||||
self.has_swapped_held = true;
|
||||
let held_or_first_in_bag_tetromino = self
|
||||
.held_tetromino
|
||||
.take()
|
||||
.unwrap_or_else(|| self.take_next_in_bag(Tetromino::random()));
|
||||
let current_tetromino = CurrentTetromino::new(held_or_first_in_bag_tetromino);
|
||||
let old_tetromino = std::mem::replace(&mut self.current_tetromino, current_tetromino);
|
||||
self.held_tetromino.replace(old_tetromino.tetromino);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{Board, CurrentTetromino, Game, Score, Tetromino};
|
||||
|
||||
#[test]
|
||||
fn advance_bag() {
|
||||
let mut game = Game {
|
||||
board: Board::new(),
|
||||
score: Score::new(),
|
||||
next_tetrominos: [Tetromino::I, Tetromino::J, Tetromino::O],
|
||||
current_tetromino: CurrentTetromino::new(Tetromino::J),
|
||||
held_tetromino: None,
|
||||
has_swapped_held: false,
|
||||
ticks: 0,
|
||||
};
|
||||
assert_eq!(game.take_next_in_bag(Tetromino::S), Tetromino::I);
|
||||
assert_eq!(
|
||||
game.next_tetrominos,
|
||||
[Tetromino::J, Tetromino::O, Tetromino::S]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
196
src/tetromino.rs
196
src/tetromino.rs
@ -1,5 +1,3 @@
|
||||
use crate::Rgb;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Tetromino {
|
||||
I,
|
||||
@ -53,65 +51,173 @@ impl Tetromino {
|
||||
}
|
||||
}
|
||||
|
||||
const fn color(&self) -> Rgb {
|
||||
match self {
|
||||
Self::I => Rgb(0, 255, 255),
|
||||
Self::J => Rgb(0, 0, 255),
|
||||
Self::L => Rgb(255, 128, 0),
|
||||
Self::O => Rgb(255, 255, 0),
|
||||
Self::S => Rgb(0, 255, 0),
|
||||
Self::T => Rgb(255, 0, 255),
|
||||
Self::Z => Rgb(255, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn direction_pattern(&self, direction: &Direction) -> [[bool; 4]; 4] {
|
||||
let dir = match self {
|
||||
Self::I => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0]],
|
||||
Direction::Left => [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '#', '#'],
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '#', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '#', '#'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::J => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [1, 0, 0, 0], [1, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 0], [0, 0, 1, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['#', '-', '-', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::L => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 0], [1, 0, 0, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
|
||||
},
|
||||
Self::O => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['#', '-', '-', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::S => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [0, 1, 1, 0], [1, 1, 0, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [0, 1, 1, 0], [1, 1, 0, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [1, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '-', '-', '-'],
|
||||
['#', '-', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::T => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [0, 1, 0, 0], [1, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0], [0, 1, 0, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '#', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::Z => match direction {
|
||||
Direction::Up => [[0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0]],
|
||||
Direction::Right => [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 1, 0], [0, 1, 0, 0]],
|
||||
Direction::Down => [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 1, 0]],
|
||||
Direction::Left => [[0, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0], [1, 0, 0, 0]],
|
||||
Direction::Up => [
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
Direction::Right => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '#', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
],
|
||||
Direction::Down => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
],
|
||||
Direction::Left => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '-', '-'],
|
||||
['#', '#', '-', '-'],
|
||||
['#', '-', '-', '-'],
|
||||
],
|
||||
},
|
||||
Self::O => [
|
||||
['-', '-', '-', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '#', '#', '-'],
|
||||
['-', '-', '-', '-'],
|
||||
],
|
||||
};
|
||||
|
||||
dir.map(|row| row.map(|v| v != 0))
|
||||
dir.map(|row| row.map(|v| v != '-'))
|
||||
}
|
||||
|
||||
pub const fn wall_kicks(&self, direction: &Direction, diff: &DirectionDiff) -> [(i8, i8); 5] {
|
||||
|
Loading…
Reference in New Issue
Block a user