render board
This commit is contained in:
parent
db57450452
commit
63a8c0bd78
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
pub enum Controls {
|
pub enum Action {
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
SoftDrop,
|
SoftDrop,
|
||||||
@ -11,17 +11,20 @@ pub enum Controls {
|
|||||||
RotateCcw,
|
RotateCcw,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ControlsHeld(HashMap<Controls, usize>);
|
pub struct ActionsHeld(HashMap<Action, usize>);
|
||||||
|
|
||||||
impl ControlsHeld {
|
impl ActionsHeld {
|
||||||
pub fn just_pressed(&self, ticks: usize, control: &Controls) -> bool {
|
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)
|
self.held_for(ticks, control, |held_for| held_for == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn held_for<F: Fn(usize) -> bool>(
|
pub fn held_for<F: Fn(usize) -> bool>(
|
||||||
&self,
|
&self,
|
||||||
ticks: usize,
|
ticks: usize,
|
||||||
control: &Controls,
|
control: &Action,
|
||||||
functor: F,
|
functor: F,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.get(control)
|
self.get(control)
|
||||||
@ -30,15 +33,15 @@ impl ControlsHeld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for ControlsHeld {
|
impl std::ops::Deref for ActionsHeld {
|
||||||
type Target = HashMap<Controls, usize>;
|
type Target = HashMap<Action, usize>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DerefMut for ControlsHeld {
|
impl std::ops::DerefMut for ActionsHeld {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
|
66
src/game.rs
66
src/game.rs
@ -1,4 +1,4 @@
|
|||||||
use crate::actions::{Controls, ControlsHeld};
|
use crate::actions::{Action, ActionsHeld};
|
||||||
use crate::board::Board;
|
use crate::board::Board;
|
||||||
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
||||||
|
|
||||||
@ -21,14 +21,14 @@ impl CurrentTetromino {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Game {
|
pub struct Game {
|
||||||
board: Board,
|
pub board: Board,
|
||||||
next_tetrominos: [Tetromino; 3],
|
pub next_tetrominos: [Tetromino; 3],
|
||||||
current_tetromino: CurrentTetromino,
|
pub current_tetromino: CurrentTetromino,
|
||||||
held_tetromino: Option<Tetromino>,
|
pub held_tetromino: Option<Tetromino>,
|
||||||
has_swapped_held: bool,
|
has_swapped_held: bool,
|
||||||
score: Score,
|
pub score: Score,
|
||||||
ticks: usize,
|
pub ticks: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Score {
|
struct Score {
|
||||||
@ -79,6 +79,17 @@ impl Score {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
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 {
|
fn take_next_in_bag(&mut self, mut last: Tetromino) -> Tetromino {
|
||||||
for value in self.next_tetrominos.iter_mut().rev() {
|
for value in self.next_tetrominos.iter_mut().rev() {
|
||||||
std::mem::swap(value, &mut last)
|
std::mem::swap(value, &mut last)
|
||||||
@ -86,8 +97,8 @@ impl Game {
|
|||||||
last
|
last
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hard_drop(&mut self, controls: &ControlsHeld) {
|
fn hard_drop(&mut self, actions: &ActionsHeld) {
|
||||||
if !controls.just_pressed(self.ticks, &Controls::HardDrop) {
|
if !actions.just_pressed(self.ticks, &Action::HardDrop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let start_y = self.current_tetromino.y;
|
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;
|
let mut delay = 32 - self.score.level * 2;
|
||||||
if controls.contains_key(&Controls::SoftDrop) {
|
if actions.contains_key(&Action::SoftDrop) {
|
||||||
delay /= 10;
|
delay /= 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,21 +130,21 @@ impl Game {
|
|||||||
self.current_tetromino.y -= 1;
|
self.current_tetromino.y -= 1;
|
||||||
self.place_current_tetromino();
|
self.place_current_tetromino();
|
||||||
self.check_line_clears();
|
self.check_line_clears();
|
||||||
} else if controls.contains_key(&Controls::SoftDrop) {
|
} else if actions.contains_key(&Action::SoftDrop) {
|
||||||
self.score.points += 1;
|
self.score.points += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_horizontally(&mut self, controls: &ControlsHeld) {
|
fn move_horizontally(&mut self, actions: &ActionsHeld) {
|
||||||
for key in [Controls::Left, Controls::Right] {
|
for key in [Action::Left, Action::Right] {
|
||||||
let just_pressed = controls.just_pressed(self.ticks, &key);
|
let just_pressed = actions.just_pressed(self.ticks, &key);
|
||||||
let long_press = controls.held_for(self.ticks, &key, |held_for| held_for > 15);
|
let long_press = actions.held_for(self.ticks, &key, |held_for| held_for > 15);
|
||||||
if !just_pressed && !long_press {
|
if !just_pressed && !long_press {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let offset = match key {
|
let offset = match key {
|
||||||
Controls::Left => -1,
|
Action::Left => -1,
|
||||||
Controls::Right => 1,
|
Action::Right => 1,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
self.current_tetromino.x += offset;
|
self.current_tetromino.x += offset;
|
||||||
@ -173,21 +184,20 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self, controls: &ControlsHeld) {
|
pub fn step(&mut self, actions: &ActionsHeld) {
|
||||||
// TODO: ensure game is running at 60 ticks per second? (`if !check_update_time(context, 60) { return; }`)
|
self.hard_drop(actions);
|
||||||
self.hard_drop(controls);
|
self.soft_drop(actions);
|
||||||
self.soft_drop(controls);
|
self.move_horizontally(actions);
|
||||||
self.move_horizontally(controls);
|
|
||||||
|
|
||||||
if controls.just_pressed(self.ticks, &Controls::Swap) {
|
if actions.just_pressed(self.ticks, &Action::Swap) {
|
||||||
self.try_swap_tetromino();
|
self.try_swap_tetromino();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (control, direction) in [
|
for (control, direction) in [
|
||||||
(Controls::RotateCw, DirectionDiff::Cw),
|
(Action::RotateCw, DirectionDiff::Cw),
|
||||||
(Controls::RotateCcw, DirectionDiff::Ccw),
|
(Action::RotateCcw, DirectionDiff::Ccw),
|
||||||
] {
|
] {
|
||||||
if !controls.just_pressed(self.ticks, &control) {
|
if !actions.just_pressed(self.ticks, &control) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self.try_rotate(direction);
|
self.try_rotate(direction);
|
||||||
|
@ -3,6 +3,7 @@ use tetromino::Tetromino;
|
|||||||
mod actions;
|
mod actions;
|
||||||
mod board;
|
mod board;
|
||||||
mod game;
|
mod game;
|
||||||
|
mod sdl_impl;
|
||||||
mod tetromino;
|
mod tetromino;
|
||||||
|
|
||||||
struct Rgb(u8, u8, u8);
|
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
146
src/sdl_impl.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user