diff --git a/src/main.rs b/src/main.rs index df4b844..81192ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,40 +17,42 @@ const LOCK_RESET: u32 = 15; mod tetromino; fn main() { + let game_mode = args(); let mut game = Game::new(); - let mut speed_time = frame_time_for_level(0); let mut lock_timer = Instant::now(); let mut lock_count = 0; + let mut lock_override = false; + let total_time = Instant::now(); terminal::enable_raw_mode().unwrap(); print!("\x1b[?25l"); let mut current = Current::new(); - loop { + let expr = match game_mode { + GameMode::Marathon => true, + GameMode::Sprint => game.score.lines < 41, + GameMode::Ultra => total_time.elapsed() < Duration::from_secs(60 * 2), + }; + + while expr { let frame_time = frame_time_for_level(game.score.level()); let frame_dur = Instant::now(); - render(&game, ¤t); - while speed_time.checked_sub(frame_dur.elapsed()).is_some() { - if let Some(input) = input(&frame_dur, frame_time) { - let rot_index = handle_action(&input, &mut game, &mut speed_time, &frame_time); - render(&game, ¤t); - if input == Action::HardDrop { - lock_timer -= LOCK_DELAY; - break; - } else if input == Action::SonicDrop { - lock_timer = Instant::now(); - break; - } + let mut speed_time = frame_time; + render(&game, ¤t, &game_mode, &total_time.elapsed()); + while speed_time > frame_dur.elapsed() && !lock_override { + if let Some(input) = input(&frame_dur, speed_time) { + handle_action( + &input, + &mut game, + &mut speed_time, + &mut lock_timer, + &mut lock_override, + ); + render(&game, ¤t, &game_mode, &total_time.elapsed()); + if lock_count < LOCK_RESET { - lock_count += 1; lock_timer = Instant::now(); - } - if let Action::Rotate(r) = input { - if let Some(i) = rot_index { - game.last_rotation = Some((r, i)); - } - } else { - game.last_rotation = None; + lock_count += 1; } } } @@ -59,14 +61,11 @@ fn main() { true } else { game.last_rotation = None; + lock_timer = Instant::now(); false }; - if !should_lock { - lock_timer = Instant::now(); - } - - if should_lock && lock_timer.elapsed() > LOCK_DELAY { + if should_lock && (lock_timer.elapsed() > LOCK_DELAY || lock_override) { if game.tetromino.pos.1 == 17 { exit(); } @@ -89,10 +88,18 @@ fn main() { game.score.lines += current.lines; game.score.points += current.points; } - render(&game, ¤t); + render(&game, ¤t, &game_mode, &total_time.elapsed()); + lock_override = false; } } +#[derive(PartialEq)] +enum GameMode { + Marathon, + Sprint, + Ultra, +} + struct Game { score: Score, tetromino: Tetromino, @@ -404,13 +411,12 @@ fn handle_action( action: &Action, game: &mut Game, speed_time: &mut Duration, - frame_time: &Duration, -) -> Option { + lock_timer: &mut Instant, + lock_override: &mut bool, +) { let tetromino = &mut game.tetromino; let block_grid = &game.block_grid; - let mut rot_index = None; - match action { Action::MoveRight => { tetromino.move_tetromino((1, 0), block_grid); @@ -418,7 +424,11 @@ fn handle_action( Action::MoveLeft => { tetromino.move_tetromino((-1, 0), block_grid); } - Action::Rotate(r) => rot_index = tetromino.rotate(r, block_grid), + Action::Rotate(r) => { + if let Some(i) = tetromino.rotate(r, block_grid) { + game.last_rotation = Some((r.clone(), i)); + } + } Action::SoftDrop => { game.score.points += 1; if game.score.level() < 19 { @@ -429,14 +439,15 @@ fn handle_action( while tetromino.move_tetromino((0, -1), block_grid) { game.score.points += 2; } + *speed_time = Duration::from_secs(0); + *lock_override = true; + } + Action::SonicDrop => { + while tetromino.move_tetromino((0, -1), block_grid) {} + *lock_timer = Instant::now(); } - Action::SonicDrop => while tetromino.move_tetromino((0, -1), block_grid) {}, Action::Hold => game.hold(), } - if *action != Action::SoftDrop { - *speed_time = *frame_time; - } - rot_index } fn input(now: &Instant, frame_time: Duration) -> Option { @@ -444,19 +455,19 @@ fn input(now: &Instant, frame_time: Duration) -> Option { if event::poll(poll_duration).ok()? { if let Event::Key(k) = event::read().ok()? { if let KeyCode::Char(c) = k.code { - match c { - 'a' => return Some(Action::MoveLeft), - 'd' => return Some(Action::MoveRight), - 'l' => return Some(Action::Rotate(Rotation::Right)), - 'j' => return Some(Action::Rotate(Rotation::Left)), - 'k' => return Some(Action::Rotate(Rotation::Flip)), - 's' => return Some(Action::SoftDrop), - ' ' => return Some(Action::HardDrop), - 'w' => return Some(Action::SonicDrop), - 'c' => return Some(Action::Hold), + return match c { + 'a' => Some(Action::MoveLeft), + 'd' => Some(Action::MoveRight), + 'l' => Some(Action::Rotate(Rotation::Right)), + 'j' => Some(Action::Rotate(Rotation::Left)), + 'k' => Some(Action::Rotate(Rotation::Flip)), + 's' => Some(Action::SoftDrop), + ' ' => Some(Action::HardDrop), + 'w' => Some(Action::SonicDrop), + 'c' => Some(Action::Hold), 'q' => exit(), - _ => (), - } + _ => None, + }; } } } @@ -468,7 +479,7 @@ fn frame_time_for_level(level: u32) -> Duration { Duration::from_secs_f32((0.8 - ((level as f32 - 1.) * 0.007)).powf(level as f32 - 1.)) } -fn render(game: &Game, current: &Current) { +fn render(game: &Game, current: &Current, game_mode: &GameMode, total_time: &Duration) { let mut s = String::with_capacity(2 * 22 * 22); s.push_str("\x1b[48;2;128;128;128m \x1b[0m\n"); let next_pos = game.next_tetromino.get_blocks(&Rotation::Neutral); @@ -514,7 +525,9 @@ fn render(game: &Game, current: &Current) { if y == 18 { s.push_str(" \x1b[1mLINES "); s.push_str(&game.score.lines.to_string()); - s.push_str("\x1b[0m"); + if *game_mode == GameMode::Sprint { + s.push_str("/40"); + } } if y == 17 { s.push_str(" \x1b[1mSCORE "); @@ -522,9 +535,17 @@ fn render(game: &Game, current: &Current) { s.push_str("\x1b[0m"); } + if *game_mode == GameMode::Ultra && y == 16 { + s.push_str(" \x1b[1mTIME "); + s.push_str(&total_time.as_secs().to_string()); + s.push_str("/120"); + s.push_str("\x1b[0m"); + } + if y == 14 { s.push_str(" \x1b[1mNEXT\x1b[0m"); } + s.push_str("\x1b[0m"); if y < 15 && y > 9 { let y = y - 10; s.push_str(" "); @@ -598,6 +619,17 @@ fn exit() -> ! { std::process::exit(0); } +fn args() -> GameMode { + if let Some(arg) = std::env::args().nth(1) { + return match &arg[..] { + "s" | "sprint" => GameMode::Sprint, + "u" | "ultra" => GameMode::Ultra, + _ => GameMode::Marathon, + }; + } + GameMode::Marathon +} + #[cfg(test)] mod tests { use super::*; diff --git a/term_tetris b/term_tetris new file mode 100755 index 0000000..8b9d526 Binary files /dev/null and b/term_tetris differ