Skip to content

Commit

Permalink
fix: column landing timer; added terminal drop guard
Browse files Browse the repository at this point in the history
  • Loading branch information
Rendez committed Mar 3, 2023
1 parent 1bff133 commit 6bc0cea
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 50 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
.vscode
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "rust_columns"
version = "0.1.0"
version = "0.1.1"
authors = [ "Luis Merino <mail@luismerino.name> "]
edition = "2021"
description = "Open source terminal arcade game with audio - based off of the classic Sega 'Columns'"
homepage = ""
repository = ""
homepage = "https://github.com/Rendez/rust_columns"
repository = "https://github.com/Rendez/rust_columns/tree/main/rust_columns"
readme = "README.md"
keywords = [ "terminal", "game" ]
categories = [ "games", "command-line-utilities" ]
Expand All @@ -14,3 +14,8 @@ license = "MIT OR Apache-2.0"
[dependencies]
crossterm = "0.26.0"
rand = "0.8.5"

[profile.release]
strip = true # Automatically strip symbols from the binary.
lto = true
panic = "abort"
53 changes: 31 additions & 22 deletions src/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,31 @@ impl Column {
}
}

pub fn landing(&mut self, heap: &mut Heap) -> Option<Vec<Vec2>> {
// Reached the bottom of the pit or there is a upcoming hit with an existing block
// in both cases we copy the blocks into our matrix of blocks
pub fn detect_landing(&mut self, heap: &mut Heap, delta: Duration) -> Option<Vec<Vec2>> {
if self.detect_hit_downwards(heap) {
self.dropping = false;
// transfer shaft block to heap of blocks
let mut origins = Vec::new();
for (i, block) in self.shaft.into_iter().rev().enumerate() {
if i > self.y {
// y points to the base block, if any above it are out of the matrix, stop transfer to teh heap.
break;
// reached the bottom of the pit or there is a upcoming hit with an existing block
let mut move_timer_copy = self.move_timer;
move_timer_copy.update(delta);
// we will be ready when the timer finishes, to give the player
// the chance to cycle the column before we have fully landed
if move_timer_copy.ready {
// now that we have landed, we copy the blocks into our matrix of blocks
self.dropping = false;
// transfer shaft block to heap of blocks
let mut origins = Vec::new();
for (i, block) in self.shaft.into_iter().rev().enumerate() {
if i > self.y {
// y points to the base block, if any above it are out of the matrix, stop transfer to teh heap.
break;
}
let origin = Vec2::xy(self.x, self.y - i);
heap[origin.x][origin.y] = block;
origins.push(origin);
}
let origin = Vec2::xy(self.x, self.y - i);
heap[origin.x][origin.y] = block;
origins.push(origin);
return Some(origins);
}
Some(origins)
} else {
None
}
None
}

pub fn update(&mut self, heap: &Heap, delta: Duration) -> bool {
Expand Down Expand Up @@ -158,6 +163,8 @@ mod test {
Vec2, NUM_ROWS, STARTING_X, STARTING_Y,
};

const DELTA: Duration = Duration::from_millis(Column::MOVE_MILLIS);

#[test]
fn test_new() {
let col = Column::new();
Expand Down Expand Up @@ -205,21 +212,23 @@ mod test {
let heap = Pit::new_heap(None);
let mut col = Column::new();

assert!(!col.update(&heap, Duration::from_millis(Column::MOVE_MILLIS - 1)));
assert!(col.update(&heap, Duration::from_millis(1)));
col.update(&heap, Duration::from_millis(Column::MOVE_MILLIS - 1));
assert_eq!(col.y, 0);
col.update(&heap, Duration::from_millis(1));
assert_eq!(col.y, 1);
}

#[test]
fn test_landing_on_heap() {
let mut heap: Heap = Pit::new_heap(None);
let mut col = Column::new();

assert_eq!(col.landing(&mut heap), None);
assert_eq!(col.detect_landing(&mut heap, DELTA), None);

heap[STARTING_X][STARTING_Y + 1] = Block::new(Some(BlockKind::Blue));

assert_eq!(
col.landing(&mut heap),
col.detect_landing(&mut heap, DELTA),
Some(vec![Vec2::xy(STARTING_X, STARTING_Y)])
);
}
Expand All @@ -229,14 +238,14 @@ mod test {
let mut heap: Heap = Pit::new_heap(None);
let mut col = Column::new();

assert_eq!(col.landing(&mut heap), None);
assert_eq!(col.detect_landing(&mut heap, DELTA), None);

for _ in 1..NUM_ROWS {
col.move_down(&heap);
}

assert_eq!(
col.landing(&mut heap),
col.detect_landing(&mut heap, DELTA),
Some(vec![
Vec2::xy(STARTING_X, NUM_ROWS - 1),
Vec2::xy(STARTING_X, NUM_ROWS - 2),
Expand Down
32 changes: 22 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,31 @@ use std::{
time::{Duration, Instant},
};

fn main() -> Result<()> {
assert_screen_size().expect("Failed when asserting the screen size requirements");
struct TerminalGuard;

// Terminal
let mut stdout = io::stdout();
impl TerminalGuard {
fn create() -> TerminalGuard {
let mut stdout = io::stdout();
enable_raw_mode().unwrap();
stdout.execute(EnterAlternateScreen).unwrap();
stdout.execute(Hide).unwrap();
TerminalGuard
}
}

enable_raw_mode()?;
stdout.execute(EnterAlternateScreen)?.execute(Hide)?;
impl Drop for TerminalGuard {
fn drop(&mut self) {
let mut stdout = io::stdout();
stdout.execute(LeaveAlternateScreen).unwrap();
stdout.execute(Show).unwrap();
disable_raw_mode().unwrap();
}
}

fn main() -> Result<()> {
assert_screen_size().expect("Failed when asserting the screen size requirements");
// Drop guard for terminal setup and cleanup
let mut _t = TerminalGuard::create();
// Render loop in a separate thread
let (render_tx, render_rx) = mpsc::channel::<Frame>();
thread::spawn(move || -> Result<()> {
Expand Down Expand Up @@ -96,9 +112,5 @@ fn main() -> Result<()> {
thread::sleep(fps_duration.saturating_sub(instant.elapsed()));
}

// Cleanup
stdout.execute(Show)?.execute(LeaveAlternateScreen)?;
disable_raw_mode()?;

Ok(())
}
20 changes: 6 additions & 14 deletions src/pit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,10 @@ impl Pit {

match &self.state.stage {
Stable => {
self.state.move_timer.update(delta);

if self.state.move_timer.ready {
if let Some(origins) = column.landing(&mut self.heap) {
self.active_origins = origins;
self.state.stage = Matching;
} else {
self.state.move_timer.reset();
}
if let Some(origins) = column.detect_landing(&mut self.heap, delta) {
self.active_origins = origins;
self.state.stage = Matching;
self.state.move_timer.finish();
}
}
Matching => {
Expand Down Expand Up @@ -400,8 +395,6 @@ mod test {
mod test_stage_transition {
use super::*;

const DELTA: Duration = Duration::from_millis(0);

#[test]
fn test_update_stable_stage() {
let mut pit = Pit::new();
Expand All @@ -417,8 +410,7 @@ mod test {
col.move_down(&pit.heap);
}

pit.update(&mut col, DELTA);
pit.update(&mut col, DELTA);
pit.update(&mut col, Duration::from_millis(0));

assert!(pit.stable());
}
Expand All @@ -437,7 +429,7 @@ mod test {
for _ in 1..NUM_ROWS {
col.move_down(&pit.heap);
}
pit.update(&mut col, DELTA);
pit.update(&mut col, Duration::from_millis(Column::MOVE_MILLIS));

assert!(!pit.stable());
}
Expand Down
2 changes: 2 additions & 0 deletions src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub fn init(stdout: &mut Stdout, frame: &Frame) -> crossterm::Result<()> {
}
}

stdout.flush()?;

Ok(())
}

Expand Down
6 changes: 5 additions & 1 deletion src/timer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::time::Duration;

#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct Timer {
pub ready: bool,
millis: u64,
Expand All @@ -21,6 +21,10 @@ impl Timer {
self.ready = self.duration.is_zero();
}

pub fn finish(&mut self) {
self.duration = Duration::from_millis(0);
}

pub fn reset(&mut self) {
*self = Timer::from_millis(self.millis);
}
Expand Down

0 comments on commit 6bc0cea

Please sign in to comment.