refactor patterns

This commit is contained in:
Theis Pieter Hollebeek 2025-03-03 19:57:32 +01:00
parent 099317ffc9
commit 33748e4956
5 changed files with 98 additions and 65 deletions

View File

@ -37,42 +37,35 @@ impl Board {
y, y,
}: &CurrentTetromino, }: &CurrentTetromino,
) -> i8 { ) -> i8 {
let pattern = tetromino.direction_pattern(direction); let pattern = tetromino.pattern(direction);
let mut y = *y; let mut y = *y;
loop { loop {
if self.pattern_and_position_colliding(pattern, *x, y) { if self.pattern_and_position_colliding(&pattern, *x, y) {
break y - 1; break y - 1;
} }
y += 1; y += 1;
} }
} }
fn pattern_and_position_colliding(&self, pattern: [[bool; 4]; 4], x: i8, y: i8) -> bool { fn pattern_and_position_colliding(&self, pattern: &Vec<(usize, usize)>, x: i8, y: i8) -> bool {
for (y_offset, row) in pattern.iter().enumerate() { for (x_offset, y_offset) in pattern {
for x_offset in row let x = *x_offset as i8 + x;
.iter() let y = *y_offset as i8 + y;
.enumerate()
.filter(|(_, exists)| **exists)
.map(|(x, _)| x)
{
let x = x_offset as i8 + x;
let y = y_offset as i8 + y;
if y < 0 { if y < 0 {
continue; continue;
} }
if y >= Board::HEIGHT as i8 { if y >= Board::HEIGHT as i8 {
return true; return true;
} }
if x < 0 || x >= Board::WIDTH as i8 { if x < 0 || x >= Board::WIDTH as i8 {
return true; return true;
} }
if self.0[y as usize][x as usize].is_some() { if self.0[y as usize][x as usize].is_some() {
return true; return true;
}
} }
} }
@ -88,8 +81,7 @@ impl Board {
y, y,
}: &CurrentTetromino, }: &CurrentTetromino,
) -> bool { ) -> bool {
let pattern = tetromino.direction_pattern(direction); self.pattern_and_position_colliding(&tetromino.pattern(direction), *x, *y)
self.pattern_and_position_colliding(pattern, *x, *y)
} }
pub fn lines_cleared(&mut self) -> usize { pub fn lines_cleared(&mut self) -> usize {

View File

@ -250,27 +250,20 @@ impl Game {
fn place_current_tetromino(&mut self) { fn place_current_tetromino(&mut self) {
let next = CurrentTetromino::new(self.take_next_in_bag(Tetromino::random())); let next = CurrentTetromino::new(self.take_next_in_bag(Tetromino::random()));
let current = std::mem::replace(&mut self.current_tetromino, next); let current = std::mem::replace(&mut self.current_tetromino, next);
let pattern = current.tetromino.direction_pattern(&current.direction); let pattern = current.tetromino.pattern(&current.direction);
if current.y <= 0 { if current.y <= 0 {
self.game_over = true; self.game_over = true;
} }
for (y, row) in pattern.iter().enumerate() { for (x, y) in pattern {
for x in row let y = current.y + y as i8;
.iter() if y < 0 {
.enumerate() continue;
.filter(|(_, exists)| **exists)
.map(|(x, _)| x)
{
let y = current.y + y as i8;
if y < 0 {
continue;
}
let y = y as usize;
let x = (current.x + x as i8) as usize;
self.board[y][x] = Some(current.tetromino.clone());
} }
let y = y as usize;
let x = (current.x + x as i8) as usize;
self.board[y][x] = Some(current.tetromino.clone());
} }
self.has_swapped_held = false; self.has_swapped_held = false;

View File

@ -194,6 +194,7 @@ pub fn start_game() -> Result<(), String> {
} }
ctx.draw_board(&game.board, &game.current_tetromino)?; ctx.draw_board(&game.board, &game.current_tetromino)?;
ctx.draw_bag(&game.held_tetromino, &game.next_tetrominos)?;
if paused { if paused {
ctx.draw_important_text( ctx.draw_important_text(

View File

@ -1,4 +1,8 @@
use crate::{board::Board, game::CurrentTetromino, tetromino::Tetromino}; use crate::{
board::Board,
game::CurrentTetromino,
tetromino::{Direction, Tetromino},
};
pub trait UiCtx<Err> { pub trait UiCtx<Err> {
fn window_size(&self) -> Result<(i32, i32), Err>; fn window_size(&self) -> Result<(i32, i32), Err>;
@ -34,25 +38,18 @@ pub trait GameUiCtx<Err>: UiCtx<Err> {
x: i8, x: i8,
y: i8, y: i8,
color: Rgb, color: Rgb,
pattern: [[bool; 4]; 4], pattern: &Vec<(usize, usize)>,
filled: bool, filled: bool,
) -> Result<(), Err> { ) -> Result<(), Err> {
for (y_offset, row) in pattern.iter().enumerate() { for (x_offset, y_offset) in pattern {
for x_offset in row let x = *x_offset as i8 + x;
.iter() let y = *y_offset as i8 + y;
.enumerate()
.filter(|(_, exists)| **exists)
.map(|(x, _)| x)
{
let x = x_offset as i8 + x;
let y = y_offset as i8 + y;
if y < 0 { if y < 0 {
continue; continue;
}
self.draw_board_tile(x as i32, y as i32, &color, filled)?
} }
self.draw_board_tile(x as i32, y as i32, &color, filled)?
} }
Ok(()) Ok(())
} }
@ -63,13 +60,37 @@ pub trait GameUiCtx<Err>: UiCtx<Err> {
let x = center(tile_size * Board::WIDTH as i32, win_width) + x * tile_size; let x = center(tile_size * Board::WIDTH as i32, win_width) + x * tile_size;
let y = center(tile_size * Board::HEIGHT as i32, win_height) + y * tile_size; let y = center(tile_size * Board::HEIGHT as i32, win_height) + y * tile_size;
if filled { if filled {
self.fill_rect(x, y, 24, 24, color)?; self.fill_rect(x, y, tile_size, tile_size, color)?;
} else { } else {
self.outline_rect(x, y, 24, 24, color)?; self.outline_rect(x, y, tile_size, tile_size, color)?;
} }
Ok(()) Ok(())
} }
fn draw_bag(&mut self, held: &Option<Tetromino>, next_up: &[Tetromino; 3]) -> Result<(), Err> {
let (win_width, win_height) = self.window_size()?;
let x = center(24 * Board::WIDTH as i32, win_width);
let y = center(24 * Board::HEIGHT as i32, win_height);
let size = 24 * 5;
let x = x - size - 16;
self.fill_rect(x, y, size, size, &Rgb(0, 0, 0))?;
self.outline_rect(x - 1, y - 1, size + 2, size + 2, &Rgb(255, 255, 255))?;
if let Some(tetromino) = held {
let color = Rgb::from_tetromino(&tetromino);
let pattern = tetromino.pattern(&Direction::Up);
for (x_offset, y_offset) in pattern {
let x = x + (x_offset * 24) as i32;
let y = y + (y_offset * 24) as i32;
self.fill_rect(x, y, 24, 24, &color)?;
}
}
Ok(())
}
fn draw_board(&mut self, board: &Board, current: &CurrentTetromino) -> Result<(), Err> { fn draw_board(&mut self, board: &Board, current: &CurrentTetromino) -> Result<(), Err> {
let (win_width, win_height) = self.window_size()?; let (win_width, win_height) = self.window_size()?;
self.outline_rect( self.outline_rect(
@ -90,13 +111,13 @@ pub trait GameUiCtx<Err>: UiCtx<Err> {
} }
} }
let pattern = current.tetromino.direction_pattern(&current.direction); let pattern = current.tetromino.pattern(&current.direction);
self.draw_tetromino_from_parts( self.draw_tetromino_from_parts(
current.x, current.x,
board.lowest_y(&current), board.lowest_y(&current),
Rgb(255, 255, 255), Rgb(255, 255, 255),
pattern, &pattern,
false, false,
)?; )?;
@ -104,7 +125,7 @@ pub trait GameUiCtx<Err>: UiCtx<Err> {
current.x, current.x,
current.y, current.y,
Rgb::from_tetromino(&current.tetromino), Rgb::from_tetromino(&current.tetromino),
pattern, &pattern,
true, true,
)?; )?;
@ -124,9 +145,23 @@ pub trait GameUiCtx<Err>: UiCtx<Err> {
let x = center(width, win_width); let x = center(width, win_width);
let y = center(height, win_height); let y = center(height, win_height);
self.outline_rect(x - 9, y - 9, width + 18, height + 18, &Rgb(255, 255, 255))?; let padding = 8;
self.fill_rect(x - 8, y - 8, width + 16, height + 16, &Rgb(16, 16, 16))?; self.outline_rect(
x - padding - 1,
y - padding - 1,
width + padding * 2 + 2,
height + padding * 2 + 2,
&Rgb(255, 255, 255),
)?;
self.fill_rect(
x - padding,
y - padding,
width + padding * 2,
height + padding * 2,
&Rgb(16, 16, 16),
)?;
self.fill_text(font, text, x, y, width, height)?; self.fill_text(font, text, x, y, width, height)?;
Ok(()) Ok(())

View File

@ -51,7 +51,19 @@ impl Tetromino {
} }
} }
pub fn direction_pattern(&self, direction: &Direction) -> [[bool; 4]; 4] { pub fn pattern(&self, direction: &Direction) -> Vec<(usize, usize)> {
self.raw_pattern(direction)
.into_iter()
.enumerate()
.flat_map(|(y, row)| {
row.into_iter()
.enumerate()
.filter_map(move |(x, available)| if available { Some((x, y)) } else { None })
})
.collect()
}
fn raw_pattern(&self, direction: &Direction) -> [[bool; 4]; 4] {
let dir = match self { let dir = match self {
Self::I => match direction { Self::I => match direction {
Direction::Up => [ Direction::Up => [