From 3506e0057f24a0b3c62d61703ecff1fecf887991 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Fri, 21 Jul 2023 17:46:03 +0200 Subject: [PATCH 1/3] cleanup --- src/region.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/region.rs b/src/region.rs index f20ab26..d793c9d 100644 --- a/src/region.rs +++ b/src/region.rs @@ -47,10 +47,13 @@ impl Region { } } - fn get_tile_relative(&self, x: usize, y: usize, relx: isize, rely: isize) -> Option<&Tile> { + fn get_tile_rel(&self, x: usize, y: usize, relx: isize, rely: isize) -> &Tile { + let (Some(x), Some(y)) = (x.checked_add_signed(relx), y.checked_add_signed(rely)) + else { return &Tile::EMPTY; }; self.tiles - .get(y.checked_add_signed(rely)?) - .and_then(|row| row.get(x.checked_add_signed(relx)?)) + .get(y) + .and_then(|row| row.get(x)) + .unwrap_or(&Tile::EMPTY) } pub fn step(&mut self) { @@ -59,14 +62,14 @@ impl Region { for y in 0..self.height() { let mut row = Vec::with_capacity(self.width()); for x in 0..self.width() { - let n = self.get_tile_relative(x, y, 0, -1).unwrap_or(&Tile::EMPTY); - let s = self.get_tile_relative(x, y, 0, 1).unwrap_or(&Tile::EMPTY); - let e = self.get_tile_relative(x, y, 1, 0).unwrap_or(&Tile::EMPTY); - let w = self.get_tile_relative(x, y, -1, 0).unwrap_or(&Tile::EMPTY); - let ne = self.get_tile_relative(x, y, 1, -1).unwrap_or(&Tile::EMPTY); - let nw = self.get_tile_relative(x, y, -1, -1).unwrap_or(&Tile::EMPTY); - let se = self.get_tile_relative(x, y, 1, 1).unwrap_or(&Tile::EMPTY); - let sw = self.get_tile_relative(x, y, -1, 1).unwrap_or(&Tile::EMPTY); + let n = self.get_tile_rel(x, y, 0, -1); + let s = self.get_tile_rel(x, y, 0, 1); + let e = self.get_tile_rel(x, y, 1, 0); + let w = self.get_tile_rel(x, y, -1, 0); + let ne = self.get_tile_rel(x, y, 1, -1); + let nw = self.get_tile_rel(x, y, -1, -1); + let se = self.get_tile_rel(x, y, 1, 1); + let sw = self.get_tile_rel(x, y, -1, 1); let edge = Edges::new(n, s, e, w, ne, nw, se, sw); row.push(edge); From 83ecee65096bdd3f97ec9b683c70b7f0b692c1cc Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Fri, 21 Jul 2023 18:09:17 +0200 Subject: [PATCH 2/3] add growing benchmark --- Cargo.toml | 2 +- benches/large_grid.rs | 17 ----------------- benches/main.rs | 33 +++++++++++++++++++++++++++++++++ src/region.rs | 4 ++++ src/tile.rs | 11 +++++++++++ 5 files changed, 49 insertions(+), 18 deletions(-) delete mode 100644 benches/large_grid.rs create mode 100644 benches/main.rs diff --git a/Cargo.toml b/Cargo.toml index f4ed9ac..b28847f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" rand = "0.8.5" [[bench]] -name = "large_grid" +name = "main" harness = false [dev-dependencies] diff --git a/benches/large_grid.rs b/benches/large_grid.rs deleted file mode 100644 index 60a771d..0000000 --- a/benches/large_grid.rs +++ /dev/null @@ -1,17 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use gol_bitwise::region::Region; - -fn large_grid(c: &mut Criterion) { - const SIZE: usize = 2048 / gol_bitwise::tile::WIDTH; // same number of cells regardless of tile size - let mut region = black_box(Region::new(SIZE, SIZE)); - region.randomise(); - - c.bench_function("large grid", |b| { - b.iter(|| { - region.step(); - }) - }); -} - -criterion_group!(benches, large_grid); -criterion_main!(benches); diff --git a/benches/main.rs b/benches/main.rs new file mode 100644 index 0000000..8ae3c42 --- /dev/null +++ b/benches/main.rs @@ -0,0 +1,33 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use gol_bitwise::{ + region::Region, + tile::{self, Tile}, +}; + +fn large_grid(c: &mut Criterion) { + const SIZE: usize = 2048 / tile::WIDTH; // same number of cells regardless of tile size + let mut region = black_box(Region::new(SIZE, SIZE)); + region.randomise(); + + c.bench_function("large grid", |b| { + b.iter(|| { + region.step(); + }) + }); +} + +fn growing(c: &mut Criterion) { + c.bench_function("growing", |b| { + b.iter(|| { + let mut region = black_box(Region::new(1, 1)); + region.set_tile(Tile::gliders(), 0, 0); + for _ in 0..1024 { + region.step(); + region.auto_grow(); + } + }) + }); +} + +criterion_group!(benches, large_grid, growing); +criterion_main!(benches); diff --git a/src/region.rs b/src/region.rs index d793c9d..619da53 100644 --- a/src/region.rs +++ b/src/region.rs @@ -19,6 +19,10 @@ impl Region { } } + pub fn set_tile(&mut self, tile: Tile, x: usize, y: usize) { + self.tiles[y][x] = tile; + } + pub fn height(&self) -> usize { self.tiles.len() } diff --git a/src/tile.rs b/src/tile.rs index ed6f5c6..3f5ad5b 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -27,6 +27,17 @@ impl Tile { Self::EMPTY } + pub fn gliders() -> Self { + let mut t = Self::EMPTY; + t.rows[1] = 0b1110000; + t.rows[2] = 0b1000000; + t.rows[3] = 0b0100000; + t.rows[WIDTH - 4] = 0b0100; + t.rows[WIDTH - 3] = 0b0010; + t.rows[WIDTH - 2] = 0b1110; + t + } + pub fn edge_east(&self) -> Row { let mut edge = 0; for n in 0..WIDTH { From b37fb0bfd9272484c5fa93e2826ee3433eeedda6 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Sat, 22 Jul 2023 17:04:10 +0200 Subject: [PATCH 3/3] rle parsing --- .gitignore | 1 + src/lib.rs | 1 + src/main.rs | 18 ++++++++++----- src/region.rs | 31 +++++++++++++++++++++++++- src/rle.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/tile.rs | 4 ++-- 6 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/rle.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..6631e2e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.rle diff --git a/src/lib.rs b/src/lib.rs index dae266f..106cec6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod region; +pub mod rle; pub mod tile; diff --git a/src/main.rs b/src/main.rs index 664dad8..a71f106 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,21 @@ -use gol_bitwise::region::Region; +use std::env; + +use gol_bitwise::{region::Region, rle}; fn main() { - let mut region = Region::new(1, 1); - region.randomise(); - print!("\x1B[2J"); // clear screen + let mut region = if let Some(file) = env::args().nth(1) { + Region::from_bools(rle::parse(&file).unwrap()) + } else { + let mut r = Region::new(1, 1); + r.randomise(); + r + }; + region.auto_grow(); loop { + print!("\x1B[2J"); // clear screen print!("\x1B[u"); // reset cursor - region.print_all(true); + region.print_all(false); region.step(); region.auto_grow(); { diff --git a/src/region.rs b/src/region.rs index 619da53..48f3d5e 100644 --- a/src/region.rs +++ b/src/region.rs @@ -1,4 +1,4 @@ -use crate::tile::{Edges, Tile, WIDTH}; +use crate::tile::{Edges, Row, Tile, WIDTH}; pub struct Region { /// rows of tiles @@ -11,6 +11,35 @@ impl Region { Self { tiles } } + pub fn from_bools(board: Vec>) -> Self { + let height = board.len() / WIDTH + 1; + let width = board[0].len() / WIDTH + 1; + let mut tiles = vec![vec![Tile::new(); width]; height]; + + for tile_y in 0..height { + for tile_x in 0..width { + let tile = &mut tiles[tile_y][tile_x]; + for y in 0..WIDTH { + let by = tile_y * WIDTH + y; + if by >= board.len() { + break; + } + let mut row = 0; + for x in 0..WIDTH { + let bx = tile_x * WIDTH + x; + if bx >= board[by].len() { + break; + } + row |= (board[by][bx] as Row) << (WIDTH - x - 1) as Row; + } + tile.rows[y] = row; + } + } + } + + Self { tiles } + } + pub fn randomise(&mut self) { for row in self.tiles.iter_mut() { for tile in row { diff --git a/src/rle.rs b/src/rle.rs new file mode 100644 index 0000000..0f079ef --- /dev/null +++ b/src/rle.rs @@ -0,0 +1,62 @@ +use std::fs; + +// one of the shittest RLE parsers in existence :) + +pub fn parse(filename: &str) -> Option>> { + let file = fs::read_to_string(filename).ok()?; + let mut meta = None; + let mut rle = String::new(); + for line in file.lines() { + if line.starts_with('#') { + continue; + } + if meta.is_none() { + meta = Some(line.to_owned()); + continue; + } + rle.push_str(line); + } + let meta = meta?; + + let properties = meta.split(", ").collect::>(); + let width: usize = properties.get(0)?.split('=').nth(1)?.trim().parse().ok()?; + let height: usize = properties.get(1)?.split('=').nth(1)?.trim().parse().ok()?; + + let mut board = vec![vec![false; width]; height]; + + let mut x = 0; + let mut y = 0; + let mut run_count = 0; + + for char in rle.chars() { + match char { + 'b' => { + run_count = run_count.max(1); + x += run_count; + run_count = 0; + } + 'o' => { + run_count = run_count.max(1); + for rx in x..(x + run_count) { + board[y][rx] = true; + } + x += run_count; + run_count = 0; + } + '$' => { + run_count = run_count.max(1); + y += run_count; + run_count = 0; + x = 0; + } + '0'..='9' => { + run_count *= 10; + run_count += ((char as u8) - b'0') as usize; + } + '!' => break, + '\n' => (), + other => println!("Unknown token {other} in RLE"), + } + } + Some(board) +} diff --git a/src/tile.rs b/src/tile.rs index 3f5ad5b..778029f 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,4 +1,4 @@ -type Row = u16; +pub type Row = u16; pub const WIDTH: usize = Row::BITS as usize; const LAST: usize = WIDTH - 1; @@ -15,7 +15,7 @@ pub struct Edges { se: bool, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Tile { pub rows: [Row; WIDTH], }