# It is not intended for manual editing.
version = 3
+[[package]]
+name = "ab_glyph"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
+[[package]]
+name = "ahash"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "atomic_refcell"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31"
+
[[package]]
name = "autocfg"
version = "1.1.0"
name = "cellseq"
version = "0.1.0"
dependencies = [
- "crossterm",
+ "egui",
"eyre",
- "ndarray",
"rand",
"tokio",
]
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
-name = "crossterm"
-version = "0.26.1"
+name = "ecolor"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
+checksum = "2e479a7fa3f23d4e794f8b2f8b3568dd4e47886ad1b12c9c095e141cb591eb63"
+
+[[package]]
+name = "egui"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3aef8ec3ae1b772f340170c65bf27d5b8c28f543a0116c844d2ac08d01123e7"
dependencies = [
- "bitflags",
- "crossterm_winapi",
- "libc",
- "mio",
- "parking_lot",
- "signal-hook",
- "signal-hook-mio",
- "winapi",
+ "ahash",
+ "epaint",
+ "nohash-hasher",
]
[[package]]
-name = "crossterm_winapi"
-version = "0.9.0"
+name = "emath"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
+checksum = "3857d743a6e0741cdd60b622a74c7a36ea75f5f8f11b793b41d905d2c9721a4b"
+
+[[package]]
+name = "epaint"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09333964d4d57f40a85338ba3ca5ed4716070ab184dcfed966b35491c5c64f3b"
dependencies = [
- "winapi",
+ "ab_glyph",
+ "ahash",
+ "atomic_refcell",
+ "ecolor",
+ "emath",
+ "nohash-hasher",
+ "parking_lot",
]
[[package]]
"scopeguard",
]
-[[package]]
-name = "log"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
-
-[[package]]
-name = "matrixmultiply"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
-dependencies = [
- "autocfg",
- "rawpointer",
-]
-
[[package]]
name = "mio"
version = "0.8.8"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
- "log",
"wasi",
"windows-sys",
]
[[package]]
-name = "ndarray"
-version = "0.15.6"
+name = "nohash-hasher"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
-dependencies = [
- "matrixmultiply",
- "num-complex",
- "num-integer",
- "num-traits",
- "rawpointer",
-]
-
-[[package]]
-name = "num-complex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
-dependencies = [
- "autocfg",
-]
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "num_cpus"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+[[package]]
+name = "owned_ttf_parser"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4"
+dependencies = [
+ "ttf-parser",
+]
+
[[package]]
name = "parking_lot"
version = "0.12.1"
"getrandom",
]
-[[package]]
-name = "rawpointer"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
-
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-[[package]]
-name = "signal-hook"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
-dependencies = [
- "libc",
- "signal-hook-registry",
-]
-
-[[package]]
-name = "signal-hook-mio"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
-dependencies = [
- "libc",
- "mio",
- "signal-hook",
-]
-
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
"syn",
]
+[[package]]
+name = "ttf-parser"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746"
+
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.1.0"
edition = "2021"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
-ndarray = "0.15"
rand = "0.8"
-crossterm = "0.26"
eyre = "0.6"
tokio = { version = "1", features = ["full"] }
+egui = "0.22"
+++ /dev/null
-use std::{io::stdout, time::Duration};
-
-use super::*;
-
-use crossterm::{
- cursor::Show,
- event::{poll, read, Event, KeyCode},
- execute,
- terminal::{disable_raw_mode, Clear, ClearType::All},
-};
-use tokio::sync::mpsc::{self, Receiver, Sender};
-
-#[derive(Debug, Clone, Copy)]
-pub enum Action {
- Exit,
- Help,
- MoveCursor(Direction),
- SwapSide,
- Remove,
- Select,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum Direction {
- Up,
- Down,
- Left,
- Right,
-}
-
-pub async fn run_keys(snd: Sender<Action>) -> Result<()> {
- loop {
- if poll(Duration::from_millis(100)).unwrap() {
- match read().unwrap() {
- Event::Resize(_x, _y) => todo!(),
- Event::Key(k) => match k.code {
- KeyCode::Backspace => todo!(),
- KeyCode::Enter => todo!(),
- KeyCode::Tab => snd.send(Action::SwapSide).await?,
- KeyCode::Esc => snd.send(Action::Exit).await?,
- KeyCode::Up => snd.send(Action::MoveCursor(Direction::Up)).await?,
- KeyCode::Down => snd.send(Action::MoveCursor(Direction::Down)).await?,
- KeyCode::Left => snd.send(Action::MoveCursor(Direction::Left)).await?,
- KeyCode::Right => snd.send(Action::MoveCursor(Direction::Right)).await?,
- KeyCode::Char(c) => match c {
- ' ' => snd.send(Action::Select).await?,
- 'q' => snd.send(Action::Exit).await?,
- '?' => snd.send(Action::Help).await?,
- 'h' => snd.send(Action::MoveCursor(Direction::Left)).await?,
- 'j' => snd.send(Action::MoveCursor(Direction::Down)).await?,
- 'k' => snd.send(Action::MoveCursor(Direction::Up)).await?,
- 'l' => snd.send(Action::MoveCursor(Direction::Right)).await?,
- _ => continue,
- },
- _ => continue,
- },
- _ => continue,
- }
- }
- }
-}
-
-pub async fn run_action(rcv: &mut Receiver<Action>) -> Result<()> {
- while let Some(action) = rcv.recv().await {
- match action {
- Action::Exit => exit(),
- Action::Help => todo!(),
- Action::MoveCursor(_) => todo!(),
- Action::SwapSide => todo!(),
- Action::Remove => todo!(),
- Action::Select => todo!(),
- }
- }
-
- Ok(())
-}
-
-fn exit() {
- disable_raw_mode().unwrap();
- execute!(stdout(), Clear(All), Show).unwrap();
-
- std::process::exit(0);
-}
+++ /dev/null
-use crossterm::{
- cursor::MoveTo,
- queue,
- style::{
- Attribute::{Bold, Reset},
- Color::{Black, Green, Grey},
- ContentStyle, Print, SetStyle,
- },
-};
-use ndarray::Array2;
-use rand::{thread_rng, Rng};
-use std::{
- cell::Cell,
- io::{stdout, Write},
- ops::Deref,
-};
-use tokio::sync::watch::Receiver;
-
-use super::*;
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum State {
- Dead,
- Alive,
-}
-
-#[derive(Debug, Clone)]
-pub struct World {
- pub map: Array2<Cell<State>>,
- pub area: Area,
-}
-
-impl World {
- pub fn new(area: Area) -> Self {
- let width = area.width().try_into().unwrap_or(0);
- let height = area.height().try_into().unwrap_or(0);
-
- let map = Array2::from_elem((height + 1, width + 1), Cell::new(State::Dead));
- Self { map, area }
- }
-
- pub fn randomize(&mut self, val: f64) {
- let mut rng = thread_rng();
- for cell in self.map.iter() {
- if rng.gen::<f64>() > val {
- cell.set(State::Alive);
- } else {
- cell.set(State::Dead);
- }
- }
- }
-
- fn wrap_walls(&mut self) {
- for (hidden, shown) in self
- .column(0)
- .iter()
- .zip(self.column((self.area.width() - 2).try_into().unwrap_or(0)))
- {
- hidden.set(shown.get());
- }
-
- for (hidden, shown) in self
- .column((self.area.width() - 1).try_into().unwrap_or(0))
- .iter()
- .zip(self.column(1))
- {
- hidden.set(shown.get());
- }
-
- for (hidden, shown) in self
- .row(0)
- .iter()
- .zip(self.row((self.area.height() - 2).try_into().unwrap_or(0)))
- {
- hidden.set(shown.get());
- }
-
- for (hidden, shown) in self
- .row((self.area.height() - 1).try_into().unwrap_or(0))
- .iter()
- .zip(self.row(1))
- {
- hidden.set(shown.get());
- }
- }
-
- pub fn update(&mut self) {
- let mut previous = self.clone();
- previous.wrap_walls();
- for (next, prev) in self
- .windows((3, 3))
- .into_iter()
- .zip(previous.windows((3, 3)))
- {
- let count = prev.iter().filter(|x| x.get() == State::Alive).count();
-
- let cell = next.get((1, 1)).unwrap();
-
- match cell.get() {
- State::Dead => {
- if count == 3 {
- cell.set(State::Alive);
- }
- }
- State::Alive => {
- if !(3..=4).contains(&count) {
- cell.set(State::Dead);
- }
- }
- }
- }
- }
-
- pub fn draw_point(&self, cell: Point) -> Result<()> {
- let mut style = ContentStyle {
- foreground_color: None,
- background_color: Some(Black),
- underline_color: None,
- attributes: Reset.into(),
- };
- let (col, row) = cell.into();
- let char = if let Some(cell) = self.get((row, col)) {
- if cell.get() == State::Alive {
- style.foreground_color = Some(Green);
- style.attributes = Bold.into();
- '●'
- } else {
- style.background_color = Some(Grey);
- '◌'
- }
- } else {
- ' '
- };
-
- queue!(
- stdout(),
- MoveTo(cell.x + self.area.origin.x, cell.y + self.area.origin.y),
- SetStyle(style),
- Print(char)
- )?;
- Ok(())
- }
-}
-
-impl Deref for World {
- type Target = Array2<Cell<State>>;
- fn deref(&self) -> &Self::Target {
- &self.map
- }
-}
-
-pub async fn run_world(clock: &mut Receiver<usize>, world: &mut World) -> Result<()> {
- loop {
- world.update();
-
- for x in 1..(world.area.width() - 1).try_into()? {
- for y in 1..(world.area.height()).try_into()? {
- world.draw_point(Point::new(x, y))?;
- }
- }
-
- clock.changed().await?;
-
- stdout().flush()?;
- }
-}
+++ /dev/null
-use super::*;
-
-use crossterm::{
- cursor::MoveTo,
- queue,
- style::{
- Attribute::Bold,
- Color::{Black, White},
- ContentStyle, Print, SetStyle,
- },
-};
-use std::{
- io::{stdout, Write},
- ops::{Add, Sub},
-};
-
-#[derive(Clone, Debug, Copy, PartialEq, Eq)]
-pub struct Point {
- pub x: u16,
- pub y: u16,
-}
-
-impl Point {
- pub fn new(x: u16, y: u16) -> 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: if rhs.x < self.x { self.x - rhs.x } else { 0 },
- y: if rhs.y < self.y { self.y - rhs.y } else { 0 },
- }
- }
-}
-
-impl From<(usize, usize)> for Point {
- fn from(value: (usize, usize)) -> Self {
- Self {
- x: value.0.try_into().unwrap_or(0),
- y: value.1.try_into().unwrap_or(0),
- }
- }
-}
-
-impl Into<(usize, usize)> for Point {
- fn into(self) -> (usize, usize) {
- (
- self.x.try_into().unwrap_or(0),
- self.y.try_into().unwrap_or(0),
- )
- }
-}
-
-#[derive(Clone, Debug, Copy, PartialEq, Eq)]
-pub struct Area {
- pub origin: Point,
- pub max: Point,
-}
-
-impl Area {
- pub fn new(origin: Point, max: Point) -> Self {
- Self { origin, max }
- }
-
- pub fn contains(&self, point: Point) -> bool {
- point.x >= self.origin.x
- && point.y >= self.origin.y
- && point.x <= self.max.x
- && point.y <= self.max.y
- }
-
- pub fn height(&self) -> u16 {
- self.max.y - self.origin.y
- }
-
- pub fn width(&self) -> u16 {
- self.max.x - self.origin.x
- }
-
- pub fn draw_outline(&self) -> Result<()> {
- let style = ContentStyle {
- foreground_color: Some(White),
- background_color: Some(Black),
- underline_color: None,
- attributes: Bold.into(),
- };
-
- for x in 0..self.width() {
- queue!(
- stdout(),
- MoveTo(x + self.origin.x, self.origin.y),
- SetStyle(style),
- Print('━'),
- MoveTo(x + self.origin.x, self.max.y),
- SetStyle(style),
- Print('━')
- )?;
- }
-
- for y in 0..self.height() {
- queue!(
- stdout(),
- MoveTo(self.origin.x, y + self.origin.y),
- SetStyle(style),
- Print('┃'),
- MoveTo(self.max.x, y + self.origin.y),
- SetStyle(style),
- Print('┃')
- )?;
- }
-
- queue!(
- stdout(),
- MoveTo(self.origin.x, self.origin.y),
- SetStyle(style),
- Print('┏'),
- MoveTo(self.origin.x, self.max.y),
- SetStyle(style),
- Print('┗'),
- MoveTo(self.max.x, self.origin.y),
- SetStyle(style),
- Print('┓'),
- MoveTo(self.max.x, self.max.y),
- SetStyle(style),
- Print('┛'),
- )?;
-
- stdout().flush()?;
- Ok(())
- }
-}
-
-impl From<(Point, Point)> for Area {
- fn from(value: (Point, Point)) -> Self {
- Self {
- origin: value.0,
- max: value.1,
- }
- }
-}
-
-impl From<(u16, u16, u16, u16)> for Area {
- fn from(value: (u16, u16, u16, u16)) -> Self {
- Self {
- origin: Point::new(value.0, value.1),
- max: Point::new(value.2, value.3),
- }
- }
-}
-
-impl Into<(Point, Point)> for Area {
- fn into(self) -> (Point, Point) {
- (self.origin, self.max)
- }
-}
-
-impl Into<(u16, u16, u16, u16)> for Area {
- fn into(self) -> (u16, u16, u16, u16) {
- (self.origin.x, self.origin.y, self.max.x, self.max.y)
- }
-}
-mod action;
-mod cells;
-mod graphics;
-mod timing;
-
-pub use action::*;
-pub use cells::*;
-pub use graphics::*;
-pub use timing::*;
-
-#[cfg(test)]
-mod tests;
-
-use eyre::Result;
-use crossterm::{
- cursor::{Hide, Show},
- execute,
- terminal::{enable_raw_mode, Clear, ClearType::All},
-};
-use eyre::Result;
-use std::io::stdout;
-use tokio::{
- sync::{mpsc, watch},
- try_join,
-};
-
-use cellseq::*;
-
-#[tokio::main]
-async fn main() -> Result<()> {
- enable_raw_mode()?;
- execute!(stdout(), Clear(All), Hide)?;
- let (clock_snd, clock_rcv) = watch::channel(0);
- let (action_snd, mut action_rcv) = mpsc::channel::<Action>(16);
-
- let mut metro = Metronome::new(clock_snd, 120);
- let clock = run_clock(&mut metro);
-
- let cell_area = Area::new(Point::from((1, 1)), Point::from((60, 25)));
- cell_area.draw_outline()?;
- let mut map = World::new(cell_area);
- map.randomize(0.5);
- let mut world_clock = clock_rcv.clone();
- let world = run_world(&mut world_clock, &mut map);
-
- let input = run_keys(action_snd);
- let action = run_action(&mut action_rcv);
-
- if let Err(e) = try_join!(clock, world, input, action) {
- execute!(stdout(), Clear(All), Show)?;
- eprintln!("{e}");
- return Err(e);
- } else {
- Ok(())
- }
-}
+++ /dev/null
-use super::*;
-
-#[test]
-fn area_tests() -> Result<()> {
- let origin = Point::new(5, 5);
- let max = Point::new(35, 25);
- let area = Area::new(origin, max);
-
- assert_eq!(area.height(), 20);
- assert_eq!(area.width(), 30);
-
- assert!(area.contains(Point::new(20, 20)));
- assert!(area.contains(Point::new(5, 10)));
- assert!(area.contains(Point::new(24, 25)));
- assert!(area.contains(Point::new(35, 25)));
- assert!(area.contains(Point::new(5, 5)));
-
- assert!(!area.contains(Point::new(0, 10)));
- assert!(!area.contains(Point::new(15, 0)));
- assert!(!area.contains(Point::new(55, 24)));
- assert!(!area.contains(Point::new(15, 60)));
-
- assert_eq!(area, Area::from((Point::new(5, 5), Point::new(35, 25))));
- assert_eq!(area, Area::from((5, 5, 35, 25)));
- assert_ne!(area, Area::from((5, 55, 35, 25)));
- Ok(())
-}
-
-#[test]
-fn point_tests() -> Result<()> {
- let p1 = Point::new(10, 10);
- let p2 = Point::new(15, 30);
-
- assert_eq!(p2 - p1, Point::new(5, 20));
- assert_eq!(p1 - p2, Point::new(0, 0));
-
- assert_eq!(Point::new(5, 5), Point::from((5, 5)));
- Ok(())
-}
+++ /dev/null
-use super::*;
-
-use std::time::Duration;
-use tokio::{
- sync::watch::Sender,
- time::{interval, Interval},
-};
-
-pub struct Metronome {
- pub interval: Interval,
- pub tick_len: Duration,
- pub bpm: usize,
- pub clock: Sender<usize>,
- pub tick: usize,
-}
-
-impl Metronome {
- pub fn new(clock: Sender<usize>, bpm: usize) -> Self {
- let tick_len = Duration::from_millis((60000 / bpm).try_into().unwrap_or(0));
- let interval = interval(tick_len);
- Self {
- interval,
- tick_len,
- bpm,
- clock,
- tick: 0,
- }
- }
-}
-
-pub async fn run_clock(metro: &mut Metronome) -> Result<()> {
- loop {
- metro.interval.tick().await;
- metro.clock.send(metro.tick)?;
- metro.tick += 1;
- }
-}