render board

This commit is contained in:
Theis Pieter Hollebeek 2025-03-03 13:07:46 +01:00
parent db57450452
commit 63a8c0bd78
4 changed files with 199 additions and 37 deletions

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
#[derive(Hash, PartialEq, Eq)]
pub enum Controls {
pub enum Action {
Left,
Right,
SoftDrop,
@ -11,17 +11,20 @@ pub enum Controls {
RotateCcw,
}
pub struct ControlsHeld(HashMap<Controls, usize>);
pub struct ActionsHeld(HashMap<Action, usize>);
impl ControlsHeld {
pub fn just_pressed(&self, ticks: usize, control: &Controls) -> bool {
impl ActionsHeld {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn just_pressed(&self, ticks: usize, control: &Action) -> bool {
self.held_for(ticks, control, |held_for| held_for == 0)
}
pub fn held_for<F: Fn(usize) -> bool>(
&self,
ticks: usize,
control: &Controls,
control: &Action,
functor: F,
) -> bool {
self.get(control)
@ -30,15 +33,15 @@ impl ControlsHeld {
}
}
impl std::ops::Deref for ControlsHeld {
type Target = HashMap<Controls, usize>;
impl std::ops::Deref for ActionsHeld {
type Target = HashMap<Action, usize>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for ControlsHeld {
impl std::ops::DerefMut for ActionsHeld {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}

View File

@ -1,4 +1,4 @@
use crate::actions::{Controls, ControlsHeld};
use crate::actions::{Action, ActionsHeld};
use crate::board::Board;
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
@ -21,14 +21,14 @@ impl CurrentTetromino {
}
}
struct Game {
board: Board,
next_tetrominos: [Tetromino; 3],
current_tetromino: CurrentTetromino,
held_tetromino: Option<Tetromino>,
pub struct Game {
pub board: Board,
pub next_tetrominos: [Tetromino; 3],
pub current_tetromino: CurrentTetromino,
pub held_tetromino: Option<Tetromino>,
has_swapped_held: bool,
score: Score,
ticks: usize,
pub score: Score,
pub ticks: usize,
}
struct Score {
@ -79,6 +79,17 @@ impl Score {
}
impl Game {
pub fn new() -> Self {
Self {
board: Board::new(),
next_tetrominos: std::array::from_fn(|_| Tetromino::random()),
current_tetromino: CurrentTetromino::new(Tetromino::random()),
held_tetromino: None,
has_swapped_held: false,
score: Score::new(),
ticks: 0,
}
}
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)
@ -86,8 +97,8 @@ impl Game {
last
}
fn hard_drop(&mut self, controls: &ControlsHeld) {
if !controls.just_pressed(self.ticks, &Controls::HardDrop) {
fn hard_drop(&mut self, actions: &ActionsHeld) {
if !actions.just_pressed(self.ticks, &Action::HardDrop) {
return;
}
let start_y = self.current_tetromino.y;
@ -104,9 +115,9 @@ impl Game {
}
}
fn soft_drop(&mut self, controls: &ControlsHeld) {
fn soft_drop(&mut self, actions: &ActionsHeld) {
let mut delay = 32 - self.score.level * 2;
if controls.contains_key(&Controls::SoftDrop) {
if actions.contains_key(&Action::SoftDrop) {
delay /= 10;
}
@ -119,21 +130,21 @@ impl Game {
self.current_tetromino.y -= 1;
self.place_current_tetromino();
self.check_line_clears();
} else if controls.contains_key(&Controls::SoftDrop) {
} else if actions.contains_key(&Action::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);
fn move_horizontally(&mut self, actions: &ActionsHeld) {
for key in [Action::Left, Action::Right] {
let just_pressed = actions.just_pressed(self.ticks, &key);
let long_press = actions.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,
Action::Left => -1,
Action::Right => 1,
_ => unreachable!(),
};
self.current_tetromino.x += offset;
@ -173,21 +184,20 @@ impl Game {
}
}
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);
pub fn step(&mut self, actions: &ActionsHeld) {
self.hard_drop(actions);
self.soft_drop(actions);
self.move_horizontally(actions);
if controls.just_pressed(self.ticks, &Controls::Swap) {
if actions.just_pressed(self.ticks, &Action::Swap) {
self.try_swap_tetromino();
}
for (control, direction) in [
(Controls::RotateCw, DirectionDiff::Cw),
(Controls::RotateCcw, DirectionDiff::Ccw),
(Action::RotateCw, DirectionDiff::Cw),
(Action::RotateCcw, DirectionDiff::Ccw),
] {
if !controls.just_pressed(self.ticks, &control) {
if !actions.just_pressed(self.ticks, &control) {
continue;
}
self.try_rotate(direction);

View File

@ -3,6 +3,7 @@ use tetromino::Tetromino;
mod actions;
mod board;
mod game;
mod sdl_impl;
mod tetromino;
struct Rgb(u8, u8, u8);
@ -21,4 +22,6 @@ impl Rgb {
}
}
fn main() {}
fn main() {
sdl_impl::start_game();
}

146
src/sdl_impl.rs Normal file
View File

@ -0,0 +1,146 @@
use crate::actions::{Action, ActionsHeld};
use crate::board::Board;
use crate::game::Game;
use crate::Rgb;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::WindowCanvas;
use std::time::Duration;
fn draw_board(canvas: &mut WindowCanvas, width: usize, height: usize, color: Rgb) {
canvas.set_draw_color(Color::RGB(color.0, color.1, color.2));
canvas
.fill_rect(Rect::new(
(800 - 24 * Board::WIDTH as i32) / 2 + width as i32 * 24,
(600 - 24 * Board::HEIGHT as i32) / 2 + height as i32 * 24,
24,
24,
))
.unwrap();
}
pub fn start_game() {
let mut game = Game::new();
let mut actions = ActionsHeld::new();
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("reimtris2", 800, 600)
.position_centered()
.build()
.unwrap();
let mut canvas = window.into_canvas().build().unwrap();
let mut event_pump = sdl_context.event_pump().unwrap();
'running: loop {
canvas.set_draw_color(Color::RGB(16, 16, 16));
canvas.clear();
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'running,
Event::KeyDown {
keycode: Some(keycode),
..
} => {
let keycode = match keycode {
Keycode::Left | Keycode::A => Action::Left,
Keycode::Right | Keycode::D => Action::Right,
Keycode::Down | Keycode::S => Action::SoftDrop,
Keycode::Space => Action::HardDrop,
Keycode::Z => Action::RotateCcw,
Keycode::X => Action::RotateCw,
Keycode::C => Action::Swap,
_ => continue,
};
actions.insert(keycode, game.ticks);
}
Event::KeyUp {
keycode: Some(keycode),
..
} => {
let keycode = match keycode {
Keycode::Left | Keycode::A => Action::Left,
Keycode::Right | Keycode::D => Action::Right,
Keycode::Down | Keycode::S => Action::SoftDrop,
Keycode::Space => Action::HardDrop,
Keycode::Z => Action::RotateCcw,
Keycode::X => Action::RotateCw,
Keycode::C => Action::Swap,
_ => continue,
};
actions.remove(&keycode);
}
_ => {}
}
}
canvas.set_draw_color(Color::WHITE);
canvas
.draw_rect(Rect::new(
(800 - 24 * Board::WIDTH as i32) / 2 - 1,
(600 - 24 * Board::HEIGHT as i32) / 2 - 1,
24 * Board::WIDTH as u32 + 2,
24 * Board::HEIGHT as u32 + 2,
))
.unwrap();
for (y, row) in game.board.iter().enumerate() {
for (x, piece) in row.iter().enumerate() {
let color = match piece {
Some(t) => Rgb::from_tetromino(t),
None => Rgb(0, 0, 0),
};
draw_board(&mut canvas, x, y, color)
}
}
let pattern = game
.current_tetromino
.tetromino
.direction_pattern(&game.current_tetromino.direction);
for (y, row) in pattern.iter().enumerate() {
for x in row
.iter()
.enumerate()
.filter(|(_, exists)| **exists)
.map(|(x, _)| x)
{
let x = x as i8 + game.current_tetromino.x;
let y = y as i8 + game.current_tetromino.y;
if y < 0 {
continue;
}
if y >= Board::HEIGHT as i8 {
continue;
}
if x < 0 || x >= Board::WIDTH as i8 {
continue;
}
draw_board(
&mut canvas,
x as usize,
y as usize,
Rgb::from_tetromino(&game.current_tetromino.tetromino),
)
}
}
game.step(&actions);
canvas.present();
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
}