Compare commits

...

3 commits

Author SHA1 Message Date
b37fb0bfd9 rle parsing 2023-07-22 17:04:10 +02:00
83ecee6509 add growing benchmark 2023-07-21 18:09:17 +02:00
3506e0057f cleanup 2023-07-21 17:46:03 +02:00
9 changed files with 172 additions and 37 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target /target
*.rle

View file

@ -9,7 +9,7 @@ edition = "2021"
rand = "0.8.5" rand = "0.8.5"
[[bench]] [[bench]]
name = "large_grid" name = "main"
harness = false harness = false
[dev-dependencies] [dev-dependencies]

View file

@ -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);

33
benches/main.rs Normal file
View file

@ -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);

View file

@ -1,2 +1,3 @@
pub mod region; pub mod region;
pub mod rle;
pub mod tile; pub mod tile;

View file

@ -1,13 +1,21 @@
use gol_bitwise::region::Region; use std::env;
use gol_bitwise::{region::Region, rle};
fn main() { fn main() {
let mut region = Region::new(1, 1); let mut region = if let Some(file) = env::args().nth(1) {
region.randomise(); Region::from_bools(rle::parse(&file).unwrap())
print!("\x1B[2J"); // clear screen } else {
let mut r = Region::new(1, 1);
r.randomise();
r
};
region.auto_grow();
loop { loop {
print!("\x1B[2J"); // clear screen
print!("\x1B[u"); // reset cursor print!("\x1B[u"); // reset cursor
region.print_all(true); region.print_all(false);
region.step(); region.step();
region.auto_grow(); region.auto_grow();
{ {

View file

@ -1,4 +1,4 @@
use crate::tile::{Edges, Tile, WIDTH}; use crate::tile::{Edges, Row, Tile, WIDTH};
pub struct Region { pub struct Region {
/// rows of tiles /// rows of tiles
@ -11,6 +11,35 @@ impl Region {
Self { tiles } Self { tiles }
} }
pub fn from_bools(board: Vec<Vec<bool>>) -> 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) { pub fn randomise(&mut self) {
for row in self.tiles.iter_mut() { for row in self.tiles.iter_mut() {
for tile in row { for tile in row {
@ -19,6 +48,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 { pub fn height(&self) -> usize {
self.tiles.len() self.tiles.len()
} }
@ -47,10 +80,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 self.tiles
.get(y.checked_add_signed(rely)?) .get(y)
.and_then(|row| row.get(x.checked_add_signed(relx)?)) .and_then(|row| row.get(x))
.unwrap_or(&Tile::EMPTY)
} }
pub fn step(&mut self) { pub fn step(&mut self) {
@ -59,14 +95,14 @@ impl Region {
for y in 0..self.height() { for y in 0..self.height() {
let mut row = Vec::with_capacity(self.width()); let mut row = Vec::with_capacity(self.width());
for x in 0..self.width() { for x in 0..self.width() {
let n = self.get_tile_relative(x, y, 0, -1).unwrap_or(&Tile::EMPTY); let n = self.get_tile_rel(x, y, 0, -1);
let s = self.get_tile_relative(x, y, 0, 1).unwrap_or(&Tile::EMPTY); let s = self.get_tile_rel(x, y, 0, 1);
let e = self.get_tile_relative(x, y, 1, 0).unwrap_or(&Tile::EMPTY); let e = self.get_tile_rel(x, y, 1, 0);
let w = self.get_tile_relative(x, y, -1, 0).unwrap_or(&Tile::EMPTY); let w = self.get_tile_rel(x, y, -1, 0);
let ne = self.get_tile_relative(x, y, 1, -1).unwrap_or(&Tile::EMPTY); let ne = self.get_tile_rel(x, y, 1, -1);
let nw = self.get_tile_relative(x, y, -1, -1).unwrap_or(&Tile::EMPTY); let nw = self.get_tile_rel(x, y, -1, -1);
let se = self.get_tile_relative(x, y, 1, 1).unwrap_or(&Tile::EMPTY); let se = self.get_tile_rel(x, y, 1, 1);
let sw = self.get_tile_relative(x, y, -1, 1).unwrap_or(&Tile::EMPTY); let sw = self.get_tile_rel(x, y, -1, 1);
let edge = Edges::new(n, s, e, w, ne, nw, se, sw); let edge = Edges::new(n, s, e, w, ne, nw, se, sw);
row.push(edge); row.push(edge);

62
src/rle.rs Normal file
View file

@ -0,0 +1,62 @@
use std::fs;
// one of the shittest RLE parsers in existence :)
pub fn parse(filename: &str) -> Option<Vec<Vec<bool>>> {
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::<Vec<_>>();
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)
}

View file

@ -1,4 +1,4 @@
type Row = u16; pub type Row = u16;
pub const WIDTH: usize = Row::BITS as usize; pub const WIDTH: usize = Row::BITS as usize;
const LAST: usize = WIDTH - 1; const LAST: usize = WIDTH - 1;
@ -15,7 +15,7 @@ pub struct Edges {
se: bool, se: bool,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Tile { pub struct Tile {
pub rows: [Row; WIDTH], pub rows: [Row; WIDTH],
} }
@ -27,6 +27,17 @@ impl Tile {
Self::EMPTY 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 { pub fn edge_east(&self) -> Row {
let mut edge = 0; let mut edge = 0;
for n in 0..WIDTH { for n in 0..WIDTH {