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_spawn_tick: u64,
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 {
@ -47,6 +50,7 @@ impl Default for Game {
next_lock_tick: 0,
next_spawn_tick: 0,
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() {
return;
}
self.tick += 1;
match self.tick {
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 {
pub fn is_game_over(&self) -> bool {
self.is_game_over || !self.playfield.is_active_piece_in_valid_position()
}
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) {
@ -94,14 +110,26 @@ impl Game {
fn spawn_tetromino(&mut self) {
self.playfield.spawn_tetromino();
self.playfield.tick_gravity();
self.update_gravity_tick();
}
/// Returns if some lines were cleared
fn clear_lines(&mut self) -> bool {
// todo: award points based on how lines were cleared
return false;
fn clear_lines(&mut self) -> usize {
let rows = self
.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 {
@ -110,11 +138,14 @@ impl Game {
let positions = self.playfield.lock_active_piece();
self.is_game_over =
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;
} else {
self.spawn_tetromino();
}
true
} else {
false
@ -141,11 +172,15 @@ pub trait Controllable {
impl Controllable for Game {
fn move_left(&mut self) {
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) {
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) {
if self.playfield.move_offset(0, 1) {
@ -187,23 +222,14 @@ impl Controllable for Game {
if !self.try_lock_tetromino() {
println!("couldn't lock tetromino despite hard dropping!");
}
self.next_lock_tick = std::u64::MAX;
}
fn hold(&mut self) {
// if self.can_swap_hold {
// match self.hold_piece {
// None => {
// 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;
// }
match self.playfield.try_swap_hold() {
Ok(_) => {}
Err(_) => (),
}
}
}

View File

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

View File

@ -134,6 +134,7 @@ impl PlayField {
));
self.next_pieces.push_back(self.bag.get_tetromino());
self.can_swap_hold = true;
self.tick_gravity();
}
pub fn tick_gravity(&mut self) {
@ -170,19 +171,16 @@ impl PlayField {
self.field[*y as usize][*x as usize] = Some(active_color);
}
self.active_piece = None;
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 {
match self.active_piece {
Some(active_piece) => {
self.can_piece_be_at_position(&active_piece)
},
None => panic!("Tried checking if active piece is in a valid position but active piece doesn't exist")
Some(active_piece) => self.can_piece_be_at_position(&active_piece),
None => true,
}
}
@ -196,6 +194,37 @@ impl PlayField {
&& 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 {

View File

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