From 33748e4956541287d3155cfb1317ad844a54f7e7 Mon Sep 17 00:00:00 2001 From: Theis Pieter Hollebeek Date: Mon, 3 Mar 2025 19:57:32 +0100 Subject: [PATCH] refactor patterns --- src/board.rs | 44 +++++++++++--------------- src/game.rs | 23 +++++--------- src/gui/sdl.rs | 1 + src/gui/ui.rs | 81 ++++++++++++++++++++++++++++++++++-------------- src/tetromino.rs | 14 ++++++++- 5 files changed, 98 insertions(+), 65 deletions(-) diff --git a/src/board.rs b/src/board.rs index 48de877..6ff2989 100644 --- a/src/board.rs +++ b/src/board.rs @@ -37,42 +37,35 @@ impl Board { y, }: &CurrentTetromino, ) -> i8 { - let pattern = tetromino.direction_pattern(direction); + let pattern = tetromino.pattern(direction); let mut y = *y; loop { - if self.pattern_and_position_colliding(pattern, *x, y) { + if self.pattern_and_position_colliding(&pattern, *x, y) { break y - 1; } y += 1; } } - fn pattern_and_position_colliding(&self, pattern: [[bool; 4]; 4], x: i8, y: i8) -> bool { - for (y_offset, row) in pattern.iter().enumerate() { - for x_offset in row - .iter() - .enumerate() - .filter(|(_, exists)| **exists) - .map(|(x, _)| x) - { - let x = x_offset as i8 + x; - let y = y_offset as i8 + y; + fn pattern_and_position_colliding(&self, pattern: &Vec<(usize, usize)>, x: i8, y: i8) -> bool { + for (x_offset, y_offset) in pattern { + let x = *x_offset as i8 + x; + let y = *y_offset as i8 + y; - if y < 0 { - continue; - } + if y < 0 { + continue; + } - if y >= Board::HEIGHT as i8 { - return true; - } + if y >= Board::HEIGHT as i8 { + return true; + } - if x < 0 || x >= Board::WIDTH as i8 { - return true; - } + if x < 0 || x >= Board::WIDTH as i8 { + return true; + } - if self.0[y as usize][x as usize].is_some() { - return true; - } + if self.0[y as usize][x as usize].is_some() { + return true; } } @@ -88,8 +81,7 @@ impl Board { y, }: &CurrentTetromino, ) -> bool { - let pattern = tetromino.direction_pattern(direction); - self.pattern_and_position_colliding(pattern, *x, *y) + self.pattern_and_position_colliding(&tetromino.pattern(direction), *x, *y) } pub fn lines_cleared(&mut self) -> usize { diff --git a/src/game.rs b/src/game.rs index b571688..fea0404 100644 --- a/src/game.rs +++ b/src/game.rs @@ -250,27 +250,20 @@ impl Game { 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); + let pattern = current.tetromino.pattern(¤t.direction); if current.y <= 0 { self.game_over = true; } - 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; - 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()); + for (x, y) in pattern { + 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()); } self.has_swapped_held = false; diff --git a/src/gui/sdl.rs b/src/gui/sdl.rs index bca1b50..39337d2 100644 --- a/src/gui/sdl.rs +++ b/src/gui/sdl.rs @@ -194,6 +194,7 @@ pub fn start_game() -> Result<(), String> { } ctx.draw_board(&game.board, &game.current_tetromino)?; + ctx.draw_bag(&game.held_tetromino, &game.next_tetrominos)?; if paused { ctx.draw_important_text( diff --git a/src/gui/ui.rs b/src/gui/ui.rs index 64a3f92..647e364 100644 --- a/src/gui/ui.rs +++ b/src/gui/ui.rs @@ -1,4 +1,8 @@ -use crate::{board::Board, game::CurrentTetromino, tetromino::Tetromino}; +use crate::{ + board::Board, + game::CurrentTetromino, + tetromino::{Direction, Tetromino}, +}; pub trait UiCtx { fn window_size(&self) -> Result<(i32, i32), Err>; @@ -34,25 +38,18 @@ pub trait GameUiCtx: UiCtx { x: i8, y: i8, color: Rgb, - pattern: [[bool; 4]; 4], + pattern: &Vec<(usize, usize)>, filled: bool, ) -> Result<(), Err> { - for (y_offset, row) in pattern.iter().enumerate() { - for x_offset in row - .iter() - .enumerate() - .filter(|(_, exists)| **exists) - .map(|(x, _)| x) - { - let x = x_offset as i8 + x; - let y = y_offset as i8 + y; + for (x_offset, y_offset) in pattern { + let x = *x_offset as i8 + x; + let y = *y_offset as i8 + y; - if y < 0 { - continue; - } - - self.draw_board_tile(x as i32, y as i32, &color, filled)? + if y < 0 { + continue; } + + self.draw_board_tile(x as i32, y as i32, &color, filled)? } Ok(()) } @@ -63,13 +60,37 @@ pub trait GameUiCtx: UiCtx { 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; if filled { - self.fill_rect(x, y, 24, 24, color)?; + self.fill_rect(x, y, tile_size, tile_size, color)?; } else { - self.outline_rect(x, y, 24, 24, color)?; + self.outline_rect(x, y, tile_size, tile_size, color)?; } Ok(()) } + fn draw_bag(&mut self, held: &Option, 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> { let (win_width, win_height) = self.window_size()?; self.outline_rect( @@ -90,13 +111,13 @@ pub trait GameUiCtx: UiCtx { } } - let pattern = current.tetromino.direction_pattern(¤t.direction); + let pattern = current.tetromino.pattern(¤t.direction); self.draw_tetromino_from_parts( current.x, board.lowest_y(¤t), Rgb(255, 255, 255), - pattern, + &pattern, false, )?; @@ -104,7 +125,7 @@ pub trait GameUiCtx: UiCtx { current.x, current.y, Rgb::from_tetromino(¤t.tetromino), - pattern, + &pattern, true, )?; @@ -124,9 +145,23 @@ pub trait GameUiCtx: UiCtx { let x = center(width, win_width); 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)?; Ok(()) diff --git a/src/tetromino.rs b/src/tetromino.rs index 546cac9..4097b87 100644 --- a/src/tetromino.rs +++ b/src/tetromino.rs @@ -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 { Self::I => match direction { Direction::Up => [