From 91c9f61fe1b04faee11a2613c1bf90b986ce867c Mon Sep 17 00:00:00 2001 From: Huck Boles Date: Sun, 28 May 2023 16:58:44 -0500 Subject: [PATCH] refactored: made maps into a trait and genericized masks --- src/cells.rs | 87 +++++++++++++++++++----------- src/cli.rs | 121 ------------------------------------------ src/graphics.rs | 75 ++++++++++++++++++++++++++ src/graphics/map.rs | 55 +++++++++++++++++++ src/graphics/point.rs | 62 ++++++++++++++++++++++ src/lib.rs | 11 +++- src/main.rs | 11 ++-- 7 files changed, 263 insertions(+), 159 deletions(-) delete mode 100644 src/cli.rs create mode 100644 src/graphics.rs create mode 100644 src/graphics/map.rs create mode 100644 src/graphics/point.rs diff --git a/src/cells.rs b/src/cells.rs index ca92d48..c0fceb8 100644 --- a/src/cells.rs +++ b/src/cells.rs @@ -1,6 +1,7 @@ +use crate::{Map, Mask, Point}; use ndarray::Array2; use rand::{thread_rng, Rng}; -use std::{cell::Cell, fmt::Display, ops::Deref}; +use std::{cell::Cell, ops::Deref}; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum State { @@ -8,24 +9,47 @@ pub enum State { Alive, } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Map { - pub map: Array2>, - pub rows: usize, - pub cols: usize, +#[derive(Clone, Debug)] +pub struct World { + pub map: Mask>, } -impl Map { +impl World { pub fn new(x: usize, y: usize) -> Self { - let map = Array2::from_elem((y, x), Cell::new(State::Dead)); - Self { - map, - cols: x, - rows: y, + let map = Mask::new(x, y, Cell::from(State::Dead)); + Self { map } + } + + pub fn randomize(&mut self, val: f64) { + let mut rng = thread_rng(); + for cell in self.map.iter() { + if rng.gen::() > val { + cell.set(State::Alive) + } else { + cell.set(State::Dead) + } + } + } + + fn wrap_walls(&mut self) { + for (bottom, top) in self.row(self.y_size() - 1).iter().zip(self.row(0).iter()) { + bottom.set(State::Dead); + top.set(State::Dead); + } + + for (right, left) in self + .column(self.x_size() - 1) + .iter() + .zip(self.column(0).iter()) + { + right.set(State::Dead); + left.set(State::Dead); } } +} - pub fn update(&mut self) { +impl Map> for World { + fn update(&mut self) { let mut previous = self.clone(); previous.wrap_walls(); for (next, prev) in self @@ -52,31 +76,32 @@ impl Map { } } - pub fn randomize(&mut self, val: f64) { - let mut rng = thread_rng(); - for cell in self.map.iter() { - if rng.gen::() > val { - cell.set(State::Alive) - } else { - cell.set(State::Dead) - } - } + fn x_size(&self) -> usize { + self.ncols() } - fn wrap_walls(&mut self) { - for (bottom, top) in self.row(self.rows - 1).iter().zip(self.row(0).iter()) { - bottom.set(State::Dead); - top.set(State::Dead); - } + fn y_size(&self) -> usize { + self.nrows() + } - for (right, left) in self.column(self.cols - 1).iter().zip(self.column(0).iter()) { - right.set(State::Dead); - left.set(State::Dead); + fn graphics(&self) -> (char, char) { + ('●', '◌') + } + + fn try_point(&self, point: Point) -> bool { + if let Some(cell) = self.get((point.y, point.x)) { + if cell.get() == State::Alive { + return true; + } } + false + } + fn get_point(&self, point: Point) -> Option> { + self.get((point.y, point.x)).cloned() } } -impl Deref for Map { +impl Deref for World { type Target = Array2>; fn deref(&self) -> &Self::Target { &self.map diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 0ef0a8f..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{Map, State}; -use crossterm::{ - cursor, - event::{self, read, Event}, - execute, queue, - style::{self, Stylize}, - terminal, Result, -}; -use ndarray::Array2; -use std::{ - io::{stdout, Write}, - ops::Deref, - thread, - time::Duration, -}; - -pub type Origin = (usize, usize); - -#[derive(Clone, Debug)] -pub struct Mask { - pub cols: usize, - pub rows: usize, - pub mask: Array2>, -} - -impl Mask { - pub fn new(x: usize, y: usize) -> Self { - let mask = Array2::from_elem((y, x), None); - Self { - cols: x, - rows: y, - mask, - } - } -} - -impl Deref for Mask { - type Target = Array2>; - fn deref(&self) -> &Self::Target { - &self.mask - } -} - -pub fn edit_mask(mask: &Mask, origin: Origin) -> Result<()> { - todo!() -} - -pub fn draw_mask(mask: &Mask, origin: Origin) -> Result<()> { - execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; - for x in 1..(mask.cols - 1) { - for y in 1..(mask.rows - 1) { - let x_off: u16 = (x + origin.0).try_into().unwrap(); - let y_off: u16 = (y + origin.1).try_into().unwrap(); - if mask.get((y, x)).unwrap().is_some() { - queue!( - stdout(), - cursor::Hide, - cursor::MoveTo(x_off, y_off), - style::PrintStyledContent("■".green()) - )?; - } else { - queue!( - stdout(), - cursor::Hide, - cursor::MoveTo(x_off, y_off), - style::PrintStyledContent("□".grey()) - )?; - } - } - } - stdout().flush() -} - -pub fn draw_frame(map: &Map, origin: Origin) -> Result<()> { - for x in 1..(map.cols - 1) { - for y in 1..(map.rows - 1) { - let x_off: u16 = (x + origin.0).try_into().unwrap(); - let y_off: u16 = (y + origin.1).try_into().unwrap(); - if map.get((y, x)).unwrap().get() == State::Alive { - queue!( - stdout(), - cursor::Hide, - cursor::MoveTo(x_off, y_off), - style::PrintStyledContent("●".green()) - )?; - } else { - queue!( - stdout(), - cursor::Hide, - cursor::MoveTo(x_off, y_off), - style::PrintStyledContent("◌".grey()) - )?; - } - } - } - stdout().flush() -} - -pub fn run_map(map: &mut Map, origin: Origin, time: Duration) -> Result<()> { - loop { - map.update(); - - execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; - - draw_frame(&map, origin)?; - - thread::sleep(time); - } -} - -pub fn loop_map(map: &mut Map, origin: (usize, usize), time: Duration, steps: usize) -> Result<()> { - loop { - let mut tmp = map.clone(); - for _ in 0..steps { - execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; - draw_frame(&tmp, origin)?; - tmp.update(); - thread::sleep(time); - } - } -} diff --git a/src/graphics.rs b/src/graphics.rs new file mode 100644 index 0000000..44fc0dd --- /dev/null +++ b/src/graphics.rs @@ -0,0 +1,75 @@ +mod map; +mod point; + +pub use map::*; +pub use point::*; + +use crossterm::{ + cursor, execute, queue, + style::{self, Stylize}, + terminal, Result, +}; +use std::{ + io::{stdout, Write}, + thread, + time::Duration, +}; + +pub struct Cursor {} + +pub fn draw_frame(map: &mut impl Map, offset: Point) -> Result<()> { + let (on, off) = map.graphics(); + for x in 1..(map.x_size() - 1) { + for y in 1..(map.y_size() - 1) { + let point = Point::new(x, y); + let offset = point + offset; + let x_off: u16 = offset.x.try_into().unwrap(); + let y_off: u16 = offset.y.try_into().unwrap(); + if map.try_point(point) { + queue!( + stdout(), + cursor::Hide, + cursor::MoveTo(x_off, y_off), + style::PrintStyledContent(on.green()) + )?; + } else { + queue!( + stdout(), + cursor::Hide, + cursor::MoveTo(x_off, y_off), + style::PrintStyledContent(off.grey()) + )?; + } + } + } + stdout().flush() +} + +pub fn run_map(map: &mut impl Map, offset: Point, time: Duration) -> Result<()> { + loop { + map.update(); + + execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; + + draw_frame(map, offset)?; + + thread::sleep(time); + } +} + +pub fn loop_map( + map: &mut (impl Map + Clone), + offset: Point, + time: Duration, + steps: usize, +) -> Result<()> { + loop { + let mut tmp = map.clone(); + for _ in 0..steps { + execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; + draw_frame(&mut tmp, offset)?; + tmp.update(); + thread::sleep(time); + } + } +} diff --git a/src/graphics/map.rs b/src/graphics/map.rs new file mode 100644 index 0000000..a003edc --- /dev/null +++ b/src/graphics/map.rs @@ -0,0 +1,55 @@ +use crate::Point; +use ndarray::Array2; +use std::ops::Deref; + +pub trait Map { + fn try_point(&self, point: Point) -> bool; + fn get_point(&self, point: Point) -> Option; + fn x_size(&self) -> usize; + fn y_size(&self) -> usize; + fn graphics(&self) -> (char, char); + fn update(&mut self); +} + +#[derive(Debug, Clone)] +pub struct Mask { + pub mask: Array2, +} + +impl Mask { + pub fn new(x: usize, y: usize, default: T) -> Self { + let mask = Array2::from_elem((y, x), default); + Self { mask } + } +} + +impl Deref for Mask { + type Target = Array2; + fn deref(&self) -> &Self::Target { + &self.mask + } +} + +impl Map for Mask { + fn x_size(&self) -> usize { + self.ncols() + } + + fn y_size(&self) -> usize { + self.nrows() + } + + fn try_point(&self, point: Point) -> bool { + self.get((point.y, point.x)).is_some() + } + + fn get_point(&self, point: Point) -> Option { + self.get((point.y, point.x)).cloned() + } + + fn graphics(&self) -> (char, char) { + ('■', '□') + } + + fn update(&mut self) {} +} diff --git a/src/graphics/point.rs b/src/graphics/point.rs new file mode 100644 index 0000000..2585d35 --- /dev/null +++ b/src/graphics/point.rs @@ -0,0 +1,62 @@ +use crossterm::style::{Attribute, Color}; +use std::ops::{Add, Div, Mul, Sub}; + +pub struct Pixel { + pub location: Point, + pub value: char, + pub fg: Color, + pub bg: Color, + pub style: Attribute, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Point { + pub x: usize, + pub y: usize, +} + +impl Point { + pub fn new(x: usize, y: usize) -> Self { + Self { x, y } + } +} + +impl Add for Point { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} + +impl Sub for Point { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} + +impl Mul for Point { + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } +} + +impl Div for Point { + type Output = Self; + fn div(self, rhs: Self) -> Self::Output { + Self { + x: self.x / rhs.x, + y: self.y / rhs.y, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6f19c4e..f5e7525 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,14 @@ mod cells; -mod cli; +mod graphics; mod music; pub use cells::*; -pub use cli::*; +pub use graphics::*; pub use music::*; + +use std::time::Duration; + +pub fn bpm_to_ms(bpm: usize) -> Duration { + let ms = 60000 / bpm; + Duration::from_millis(ms.try_into().unwrap()) +} diff --git a/src/main.rs b/src/main.rs index c5a7442..fe995fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,13 @@ use cellseq::*; use eyre::Result; fn main() -> Result<()> { - let mut mask: Mask = Mask::new(48, 24); - let mut map = Map::new(48, 24); + let mut mask: Mask = Mask::new(48, 24, false); + let mut map = World::new(48, 24); map.randomize(0.75); - // loop_map(&mut map, (10, 5), std::time::Duration::from_millis(250), 32)?; - // draw_mask::(&mask, (10, 5))?; - edit_mask(&mask, (10, 5))?; + let time = bpm_to_ms(120); + + loop_map(&mut map, Point::new(10, 5), time, 32)?; + // edit_mask(&mask, (10, 5))?; Ok(()) } -- 2.44.2