Compare commits

...

3 Commits

Author SHA1 Message Date
Edward Shen 2aa1602dc1
bugged 2020-03-22 21:56:11 -04:00
Edward Shen 450900b167
implement holding 2020-03-22 18:21:48 -04:00
Edward Shen d719338097
fix lock timing 2020-03-22 17:33:38 -04:00
4 changed files with 86 additions and 31 deletions

View File

@ -25,6 +25,9 @@ pub struct Game {
next_lock_tick: u64, next_lock_tick: u64,
next_spawn_tick: u64, next_spawn_tick: u64,
is_game_over: bool, is_game_over: bool,
/// The last clear action performed, used for determining if a back-to-back
/// bonus is needed.
last_clear_action: ClearAction,
} }
impl fmt::Debug for Game { impl fmt::Debug for Game {
@ -47,6 +50,7 @@ impl Default for Game {
next_lock_tick: 0, next_lock_tick: 0,
next_spawn_tick: 0, next_spawn_tick: 0,
is_game_over: false, is_game_over: false,
last_clear_action: ClearAction::Single, // Doesn't matter what it's initialized to
} }
} }
} }
@ -60,7 +64,6 @@ impl Tickable for Game {
if self.is_game_over() { if self.is_game_over() {
return; return;
} }
self.tick += 1; self.tick += 1;
match self.tick { match self.tick {
t if t == self.next_spawn_tick => self.spawn_tetromino(), t if t == self.next_spawn_tick => self.spawn_tetromino(),
@ -79,13 +82,26 @@ impl Tickable for Game {
} }
} }
enum ClearAction {
Single,
Double,
Triple,
Tetris,
MiniTSpin,
TSpin,
TSpinSingle,
TSpinDouble,
TSpinTriple,
}
impl Game { impl Game {
pub fn is_game_over(&self) -> bool { pub fn is_game_over(&self) -> bool {
self.is_game_over || !self.playfield.is_active_piece_in_valid_position() self.is_game_over || !self.playfield.is_active_piece_in_valid_position()
} }
fn update_gravity_tick(&mut self) { fn update_gravity_tick(&mut self) {
self.next_gravity_tick = (-1 as i64) as u64; //self.tick + TICKS_PER_SECOND as u64; // self.next_gravity_tick = (-1 as i64) as u64;
self.next_gravity_tick = self.tick + TICKS_PER_SECOND as u64;
} }
fn update_lock_tick(&mut self) { fn update_lock_tick(&mut self) {
@ -94,14 +110,26 @@ impl Game {
fn spawn_tetromino(&mut self) { fn spawn_tetromino(&mut self) {
self.playfield.spawn_tetromino(); self.playfield.spawn_tetromino();
self.playfield.tick_gravity();
self.update_gravity_tick(); self.update_gravity_tick();
} }
/// Returns if some lines were cleared /// Returns if some lines were cleared
fn clear_lines(&mut self) -> bool { fn clear_lines(&mut self) -> usize {
// todo: award points based on how lines were cleared let rows = self
return false; .playfield
.active_piece
.map(|t| t.get_cur_occupied_spaces())
.map(|i| i.iter().map(|p| p.y).collect::<Vec<_>>())
.unwrap_or_default();
let mut rows_cleared = 0;
for row in rows {
if self.playfield.try_clear_row(row as usize).is_ok() {
rows_cleared += 1;
}
}
rows_cleared
} }
fn try_lock_tetromino(&mut self) -> bool { fn try_lock_tetromino(&mut self) -> bool {
@ -110,11 +138,14 @@ impl Game {
let positions = self.playfield.lock_active_piece(); let positions = self.playfield.lock_active_piece();
self.is_game_over = self.is_game_over =
self.is_game_over || positions.iter().all(|Position { x: _, y }| *y < 20); self.is_game_over || positions.iter().all(|Position { x: _, y }| *y < 20);
if self.clear_lines() {
if self.clear_lines() > 0 {
self.playfield.active_piece = None;
self.next_spawn_tick = self.tick + LINE_CLEAR_DELAY; self.next_spawn_tick = self.tick + LINE_CLEAR_DELAY;
} else { } else {
self.spawn_tetromino(); self.spawn_tetromino();
} }
true true
} else { } else {
false false
@ -141,11 +172,15 @@ pub trait Controllable {
impl Controllable for Game { impl Controllable for Game {
fn move_left(&mut self) { fn move_left(&mut self) {
self.playfield.move_offset(-1, 0); self.playfield.move_offset(-1, 0);
self.update_lock_tick(); if !self.playfield.can_active_piece_move_down() {
self.update_lock_tick();
}
} }
fn move_right(&mut self) { fn move_right(&mut self) {
self.playfield.move_offset(1, 0); self.playfield.move_offset(1, 0);
self.update_lock_tick(); if !self.playfield.can_active_piece_move_down() {
self.update_lock_tick();
}
} }
fn move_down(&mut self) { fn move_down(&mut self) {
if self.playfield.move_offset(0, 1) { if self.playfield.move_offset(0, 1) {
@ -187,23 +222,14 @@ impl Controllable for Game {
if !self.try_lock_tetromino() { if !self.try_lock_tetromino() {
println!("couldn't lock tetromino despite hard dropping!"); println!("couldn't lock tetromino despite hard dropping!");
} }
self.next_lock_tick = std::u64::MAX;
} }
fn hold(&mut self) { fn hold(&mut self) {
// if self.can_swap_hold { match self.playfield.try_swap_hold() {
// match self.hold_piece { Ok(_) => {}
// None => { Err(_) => (),
// self.hold_piece = Some(self.active_piece); }
// self.get_new_piece();
// }
// Some(piece) => {
// self.hold_piece = Some(self.active_piece);
// self.active_piece = piece;
// self.reset_position();
// }
// }
// self.can_swap_hold = false;
// }
} }
} }

View File

@ -86,5 +86,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
dbg!(game); dbg!(game);
Ok(()) Ok(())
} }

View File

@ -134,6 +134,7 @@ impl PlayField {
)); ));
self.next_pieces.push_back(self.bag.get_tetromino()); self.next_pieces.push_back(self.bag.get_tetromino());
self.can_swap_hold = true; self.can_swap_hold = true;
self.tick_gravity();
} }
pub fn tick_gravity(&mut self) { pub fn tick_gravity(&mut self) {
@ -170,19 +171,16 @@ impl PlayField {
self.field[*y as usize][*x as usize] = Some(active_color); self.field[*y as usize][*x as usize] = Some(active_color);
} }
self.active_piece = None;
new_pieces new_pieces
} }
None => panic!("Tried to lock active piece while active piece doesn't exist!"), None => vec![],
} }
} }
pub fn is_active_piece_in_valid_position(&self) -> bool { pub fn is_active_piece_in_valid_position(&self) -> bool {
match self.active_piece { match self.active_piece {
Some(active_piece) => { Some(active_piece) => self.can_piece_be_at_position(&active_piece),
self.can_piece_be_at_position(&active_piece) None => true,
},
None => panic!("Tried checking if active piece is in a valid position but active piece doesn't exist")
} }
} }
@ -196,6 +194,37 @@ impl PlayField {
&& self.field[*y as usize][*x as usize].is_none() && self.field[*y as usize][*x as usize].is_none()
}) })
} }
pub fn try_swap_hold(&mut self) -> Result<(), ()> {
if self.can_swap_hold {
match self.active_piece {
Some(piece) => {
match self.hold_piece {
Some(hold) => self.next_pieces.push_front(hold),
None => (),
}
self.hold_piece = Some(piece.piece_type);
self.spawn_tetromino();
self.can_swap_hold = false;
}
None => return Err(()),
}
}
Err(())
}
pub fn try_clear_row(&mut self, row: usize) -> Result<(), ()> {
if self.field[row].iter().all(|cell| cell.is_some()) {
self.field[row] = [None; PLAYFIELD_WIDTH];
for y in (1..=row).rev() {
self.field[y] = self.field[y - 1];
}
Ok(())
} else {
Err(())
}
}
} }
impl Renderable for PlayField { impl Renderable for PlayField {

View File

@ -45,7 +45,6 @@ impl SRS {
} }
let cur_offsets = offset_data.get(&test_tetromino.rotation_state).unwrap(); let cur_offsets = offset_data.get(&test_tetromino.rotation_state).unwrap();
let mut offsets = Vec::with_capacity(cur_offsets.len()); let mut offsets = Vec::with_capacity(cur_offsets.len());
for i in 0..cur_offsets.len() { for i in 0..cur_offsets.len() {