diff --git a/Cargo.lock b/Cargo.lock index f0d3bb5..7f9256b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -43,9 +43,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] [[package]] name = "crossterm" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f67c7faacd4db07a939f55d66a983a5355358a1f17d32cc9a8d01d1266b9ce" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" dependencies = [ "bitflags", "crossterm_winapi", @@ -106,7 +106,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "gameoflife" -version = "1.1.2" +version = "1.2.0" dependencies = [ "crossterm", "ctrlc", @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "lock_api" @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -394,42 +394,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/Cargo.toml b/Cargo.toml index 54d8c95..cf830c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,19 @@ [package] name = "gameoflife" -version = "1.1.2" -edition = "2021" -authors = ["Maxwell Anderson "] -license = "MIT" -description = "Conway's Game of Life running in a terminal" +version = "1.2.0" +authors = ["Maxwell Anderson "] +description = "Conway's Game of Life for the terminal" repository = "https://github.com/Zaechus/gameoflife" +license = "MIT" +categories = ["games"] +edition = "2021" [dependencies] crossterm = "0.26" ctrlc = "3.2" rand = "0.8" -rayon = "1.6" +rayon = "1.7" + +[profile.release] +panic = "abort" +strip = true diff --git a/README.md b/README.md index 4276376..0f946c6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Current Crates.io Version](https://img.shields.io/crates/v/gameoflife)](https://crates.io/crates/gameoflife) -Conway's Game of Life running in a terminal +Conway's Game of Life for the terminal ### Usage ``` diff --git a/src/game.rs b/src/game.rs index ec1e423..b276609 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,38 +1,40 @@ -use std::{cell::RefCell, io::stdout, process, thread, time::Duration}; +use std::{io::stdout, process, thread, time::Duration}; use crossterm::{cursor, execute, terminal}; use crate::grid::Grid; -pub struct ConwaysGame { - grid: RefCell, -} +pub fn play(interval: u64) { + ctrlc::set_handler(move || { + execute!( + stdout(), + terminal::Clear(terminal::ClearType::All), + cursor::Show + ) + .unwrap(); + process::exit(0); + }) + .unwrap(); -impl ConwaysGame { - pub fn new(interval: u32) -> Self { - Self { - grid: RefCell::new(Grid::new(interval)), - } - } + execute!( + stdout(), + terminal::Clear(terminal::ClearType::All), + cursor::Hide + ) + .unwrap(); + + let mut grid = Grid::new(); + + loop { + grid.print().unwrap(); + + let handle = thread::spawn(move || { + thread::sleep(Duration::from_millis(interval)); + }); + + grid.change_cells(); + grid.update_cells(); - pub fn play(&self) { - ctrlc::set_handler(move || { - execute!( - stdout(), - terminal::Clear(terminal::ClearType::All), - cursor::Show - ) - .unwrap(); - process::exit(0); - }) - .expect("Error setting Ctrl-C handler"); - - loop { - self.grid.borrow_mut().print().unwrap(); - self.grid.borrow_mut().change_cells(); - self.grid.borrow_mut().update_cells(); - - thread::sleep(Duration::from_millis(self.grid.borrow().interval() as u64)); - } + handle.join().unwrap(); } } diff --git a/src/gamecell.rs b/src/gamecell.rs index 903b507..421b687 100644 --- a/src/gamecell.rs +++ b/src/gamecell.rs @@ -1,27 +1,27 @@ use std::sync::atomic::{AtomicBool, Ordering}; -pub struct GameCell { +pub(crate) struct GameCell { alive: bool, will_live: AtomicBool, } impl GameCell { - pub fn new(alive: bool) -> Self { + pub(crate) fn new(alive: bool) -> Self { Self { alive, will_live: AtomicBool::new(false), } } - pub fn is_alive(&self) -> bool { + pub(crate) fn is_alive(&self) -> bool { self.alive } - pub fn state(&self) -> u8 { + pub(crate) fn state(&self) -> u8 { self.alive as u8 } - pub fn symbol(&self) -> char { + pub(crate) fn symbol(&self) -> char { if self.alive { '█' } else { @@ -29,11 +29,11 @@ impl GameCell { } } - pub fn update(&mut self) { + pub(crate) fn update(&mut self) { self.alive = *self.will_live.get_mut() } - pub fn set_will_live(&self, b: bool) { + pub(crate) fn set_will_live(&self, b: bool) { self.will_live.store(b, Ordering::Relaxed) } } diff --git a/src/grid.rs b/src/grid.rs index 85c58e4..bff4ae6 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,7 +1,4 @@ -use std::{ - io::stdout, - sync::{Arc, Mutex}, -}; +use std::io::stdout; use crossterm::{cursor, execute, style, terminal}; @@ -9,23 +6,22 @@ use rayon::prelude::*; use crate::gamecell::GameCell; -pub struct Grid { +#[derive(Default)] +pub(crate) struct Grid { cells: Vec>, - buffer: Arc>>, - w: usize, - h: usize, - interval: u32, + width: usize, + height: usize, } impl Grid { - pub fn new(interval: u32) -> Self { - let (w, h): (usize, usize) = if let Ok((w, h)) = terminal::size() { - ((w - 1).into(), (h - 1).into()) + pub(crate) fn new() -> Self { + let (width, height): (usize, usize) = if let Ok((width, height)) = terminal::size() { + (width.into(), height.into()) } else { (80, 40) }; - let cells = vec![vec![0; w]; h] + let cells = vec![vec![0; width]; height] .par_iter() .map(|v| { v.par_iter() @@ -34,81 +30,64 @@ impl Grid { }) .collect(); - execute!( - stdout(), - terminal::Clear(terminal::ClearType::All), - cursor::Hide - ) - .unwrap(); Self { cells, - buffer: Arc::new(Mutex::new(vec![' '; (w + 1) * h])), - w, - h, - interval, + width, + height, } } - pub fn interval(&self) -> u32 { - self.interval - } - - pub fn print(&mut self) -> crossterm::Result<()> { - let mut stdout = stdout(); - - execute!(stdout, cursor::MoveTo(0, 0))?; - self.cells.par_iter().enumerate().for_each(|(y, row)| { - let offset = y * self.w + y; - row.par_iter().enumerate().for_each(|(x, cell)| { - self.buffer.lock().unwrap()[offset + x] = cell.symbol(); - }); - self.buffer.lock().unwrap()[y * self.w + self.w + y] = '\n'; - }); - - if let Ok(rendered) = self.buffer.lock() { - execute!(stdout, style::Print(rendered.iter().collect::()))?; - } + pub(crate) fn print(&mut self) -> crossterm::Result<()> { + execute!( + stdout(), + cursor::MoveTo(0, 0), + style::Print( + self.cells + .par_iter() + .flatten() + .collect::>() + .par_iter() + .map(|c| c.symbol()) + .collect::() + ) + )?; Ok(()) } - pub fn update_cells(&mut self) { + pub(crate) fn update_cells(&mut self) { self.cells .par_iter_mut() .for_each(|row| row.par_iter_mut().for_each(|cell| cell.update())); } - pub fn change_cells(&mut self) { + pub(crate) fn change_cells(&mut self) { self.cells.par_iter().enumerate().for_each(|(y, row)| { row.par_iter().enumerate().for_each(|(x, cell)| { - let (top, bottom) = if y as i32 - 1 < 0 { - (self.h - 1, y + 1) - } else if y + 1 >= self.h { + let (top, bottom) = if y == 0 { + (self.height - 1, y + 1) + } else if y == self.height - 1 { (y - 1, 0) } else { (y - 1, y + 1) }; - let (left, right) = if x as i32 - 1 < 0 { - (self.w - 1, x + 1) - } else if x + 1 >= self.w { + let (left, right) = if x == 0 { + (self.width - 1, x + 1) + } else if x == self.width - 1 { (x - 1, 0) } else { (x - 1, x + 1) }; - let adjacent_count: u8 = [ - self.cells[top][x].state(), - self.cells[bottom][x].state(), - self.cells[y][left].state(), - self.cells[y][right].state(), - self.cells[top][left].state(), - self.cells[top][right].state(), - self.cells[bottom][left].state(), - self.cells[bottom][right].state(), - ] - .iter() - .sum(); + let adjacent_count = self.cells[top][x].state() + + self.cells[bottom][x].state() + + self.cells[y][left].state() + + self.cells[y][right].state() + + self.cells[top][left].state() + + self.cells[top][right].state() + + self.cells[bottom][left].state() + + self.cells[bottom][right].state(); if cell.is_alive() && adjacent_count < 2 || cell.is_alive() && adjacent_count > 3 { cell.set_will_live(false); diff --git a/src/lib.rs b/src/lib.rs index bb98c18..bcc2d1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,4 @@ mod game; mod gamecell; mod grid; -pub use game::ConwaysGame; +pub use game::play; diff --git a/src/main.rs b/src/main.rs index 67bb7a5..3c77407 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ use std::{env, process}; -use gameoflife::ConwaysGame; - fn main() { - let speed: u32 = if let Some(arg) = env::args().nth(1) { + let interval = if let Some(arg) = env::args().nth(1) { if arg == "help" { println!("Usage: gameoflife [INTERVAL]"); process::exit(0); @@ -16,7 +14,5 @@ fn main() { 128 }; - let game = ConwaysGame::new(speed); - - game.play() + gameoflife::play(interval) }