diff --git a/src/main.rs b/src/main.rs index ad0d71d..136381e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,125 +1,17 @@ +mod region; mod tile; -use tile::*; +use region::Region; fn main() { - let mut tile = EMPTY_TILE; - let mut tile2 = EMPTY_TILE; - let mut tile3 = EMPTY_TILE; - let mut tile4 = EMPTY_TILE; - // tile[WIDTH - 8] = (0b_1111_1111_1100_0000 as Row).reverse_bits(); - tile[WIDTH - 8] = 0b_00100000; - tile[WIDTH - 7] = 0b_00010000; - tile[WIDTH - 6] = 0b_01110000; + let mut region = Region::new(); - tile[WIDTH - 3] = 0b_0010; - tile[WIDTH - 2] = 0b_0001; - tile[WIDTH - 1] = 0b_0111; loop { - let edges = Edges::full( - &EMPTY_TILE, - &tile3, - &tile2, - &EMPTY_TILE, - &EMPTY_TILE, - &EMPTY_TILE, - &tile4, - &EMPTY_TILE, - ); - let edges2 = Edges::full( - &EMPTY_TILE, - &tile4, - &EMPTY_TILE, - &tile, - &EMPTY_TILE, - &EMPTY_TILE, - &EMPTY_TILE, - &tile3, - ); - let edges3 = Edges::full( - &tile, - &EMPTY_TILE, - &tile4, - &EMPTY_TILE, - &tile2, - &EMPTY_TILE, - &EMPTY_TILE, - &EMPTY_TILE, - ); - let edges4 = Edges::full( - &tile2, - &EMPTY_TILE, - &EMPTY_TILE, - &tile3, - &EMPTY_TILE, - &tile, - &EMPTY_TILE, - &EMPTY_TILE, - ); - println!(); - println!("\n{:#> bit) & 1, (bot >> bit) & 1); -// row.push(match states { -// (0, 0) => ' ', -// (1, 0) => '▀', -// (0, 1) => '▄', -// (1, 1) => '█', -// _ => unreachable!(), -// }); -// } -// println!("{row}"); -// } -// } - -fn blocks(top: Row, bottom: Row, bit: usize) -> char { - let states = ((top >> bit) & 1, (bottom >> bit) & 1); - match states { - (0, 0) => ' ', - (1, 0) => '▀', - (0, 1) => '▄', - (1, 1) => '█', - _ => unreachable!(), - } -} - -fn print_tiles(left: &TileArr, right: &TileArr) { - for y in 0..(WIDTH / 2) { - let a = left[y * 2]; - let b = left[y * 2 + 1]; - let mut row = String::with_capacity(WIDTH * 2 + 1); - for bit in (0..WIDTH).rev() { - row.push(blocks(a, b, bit)); - } - row.push('|'); - let a = right[y * 2]; - let b = right[y * 2 + 1]; - for bit in (0..WIDTH).rev() { - row.push(blocks(a, b, bit)); - } - println!("{row}"); - } -} diff --git a/src/region.rs b/src/region.rs new file mode 100644 index 0000000..1a2c23f --- /dev/null +++ b/src/region.rs @@ -0,0 +1,77 @@ +use crate::tile::{Edges, Tile, WIDTH}; + +pub struct Region { + /// rows of tiles + tiles: Vec>, + size: (usize, usize), + offset: (isize, isize), + auto_expand: bool, +} + +impl Region { + pub fn new() -> Self { + let tiles = vec![vec![Tile::glider(); 2]; 2]; + Self { + tiles, + size: (2, 2), + offset: (-1, -1), + auto_expand: false, + } + } + + pub fn print_all(&self) { + for y in 0..self.size.1 { + for ch_row in 0..(WIDTH / 2) { + for x in 0..self.size.1 { + self.tiles[y][x].print_row(ch_row); + // print!("|"); + } + println!() + } + // println!("------"); + } + } + + pub fn set_cell(&mut self, x: isize, y: isize, state: bool) { + // + } + + fn get_tile_relative(&self, x: usize, y: usize, relx: isize, rely: isize) -> Option<&Tile> { + self.tiles + .get(y.checked_add_signed(rely)?) + .and_then(|row| row.get(x.checked_add_signed(relx)?)) + } + + pub fn step(&mut self) { + // store edges + let mut edges = Vec::with_capacity(self.size.1); + for y in 0..self.size.1 { + let mut row = Vec::with_capacity(self.size.0); + for x in 0..self.size.0 { + 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 edge = Edges::full(n, s, e, w, ne, nw, se, sw); + // dbg!(&edge); + row.push(edge); + } + edges.push(row); + } + // dbg!(&edges); + for y in 0..self.size.1 { + for x in 0..self.size.0 { + // dbg!(x, y, &edges[y][x]); + self.tiles[y][x].step(&edges[y][x]); + } + } + if self.auto_expand { + // todo + } + } +} diff --git a/src/tile.rs b/src/tile.rs index 1232ff5..0e82bce 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,10 +1,9 @@ -pub type Row = u32; -pub type TileArr = [Row; WIDTH]; -pub const EMPTY_TILE: TileArr = [0; WIDTH]; +pub type Row = u16; pub const WIDTH: usize = Row::BITS as usize; const LAST: usize = WIDTH - 1; +#[derive(Debug)] pub struct Edges { n: Row, s: Row, @@ -16,116 +15,155 @@ pub struct Edges { se: bool, } -pub fn step(tile: &mut TileArr, edges: &Edges) { - fn step_row(state: &mut Row, a0: Row, a1: Row, b0: Row, b1: Row, c0: Row, c1: Row) { - // simulates addition of [WIDTH] groups of 3 2-bit numbers using bitwise operations +#[derive(Clone)] +pub struct Tile { + pub rows: [Row; WIDTH], +} - // partial sum (first and second number/row) - let t0 = a0 ^ b0; - let t1 = (a0 & b0) ^ (a1 ^ b1); - let t2 = (a0 & b0 & (a1 ^ b1)) | (a1 & b1); +impl Tile { + pub const EMPTY: Tile = Tile { rows: [0; WIDTH] }; - // total neighbor count (incl. center cell) - let n0 = t0 ^ c0; - let n1 = (t0 & c0) ^ (t1 ^ c1); - let n2 = t2 ^ ((t0 & c0 & (t1 ^ c1)) | (t1 & c1)); - - // count == 3 || (old_state && count == 4) - *state = (!n2 & n1 & n0) | (*state & n2 & !(n0 | n1)); + pub fn new() -> Self { + Self { rows: [0; WIDTH] } } - let mut partial_sums_1 = EMPTY_TILE; - let mut partial_sums_2 = EMPTY_TILE; - - for (y, row) in tile.iter().enumerate() { - let left = (row >> 1) | edges.west_bit(y); - let right = (row << 1) | edges.east_bit(y); - partial_sums_1[y] = row ^ left ^ right; - partial_sums_2[y] = (left & right) | ((left ^ right) & row); + pub fn is_empty(&self) -> bool { + self.rows.iter().fold(0, |a, r| a | r) == 0 } - for y in 1..LAST { + pub fn glider() -> Self { + let mut tile = Self::new(); + + tile.rows[WIDTH - 8] = 0b_00100000; + tile.rows[WIDTH - 7] = 0b_00010000; + tile.rows[WIDTH - 6] = 0b_01110000; + + tile.rows[WIDTH - 3] = 0b_0010; + tile.rows[WIDTH - 2] = 0b_0001; + tile.rows[WIDTH - 1] = 0b_0111; + tile + } + + pub fn print_row(&self, ch_row: usize) { + let mut row = String::with_capacity(WIDTH * 2 + 1); + let top = self.rows[ch_row * 2]; + let bottom = self.rows[ch_row * 2 + 1]; + for bit in (0..WIDTH).rev() { + let states = ((top >> bit) & 1, (bottom >> bit) & 1); + let ch = match states { + (0, 0) => ' ', + (1, 0) => '▀', + (0, 1) => '▄', + (1, 1) => '█', + _ => unreachable!(), + }; + row.push(ch); + } + print!("{row}"); + } + + pub fn step(&mut self, edges: &Edges) { + fn step_row(state: &mut Row, a0: Row, a1: Row, b0: Row, b1: Row, c0: Row, c1: Row) { + // simulates addition of [WIDTH] groups of 3 2-bit numbers using bitwise operations + + // partial sum (first and second number/row) + let t0 = a0 ^ b0; + let t1 = (a0 & b0) ^ (a1 ^ b1); + let t2 = (a0 & b0 & (a1 ^ b1)) | (a1 & b1); + + // total neighbor count (incl. center cell) + let n0 = t0 ^ c0; + let n1 = (t0 & c0) ^ (t1 ^ c1); + let n2 = t2 ^ ((t0 & c0 & (t1 ^ c1)) | (t1 & c1)); + + // count == 3 || (old_state && count == 4) + *state = (!n2 & n1 & n0) | (*state & n2 & !(n0 | n1)); + } + + let mut partial_sums_1 = [0; WIDTH]; + let mut partial_sums_2 = [0; WIDTH]; + + let tile = &mut self.rows; + + for (y, row) in tile.iter().enumerate() { + let left = (row >> 1) | edges.west_bit(y); + let right = (row << 1) | edges.east_bit(y); + partial_sums_1[y] = row ^ left ^ right; + partial_sums_2[y] = (left & right) | ((left ^ right) & row); + } + + for y in 1..LAST { + step_row( + &mut tile[y], + partial_sums_1[y - 1], + partial_sums_2[y - 1], + partial_sums_1[y], + partial_sums_2[y], + partial_sums_1[y + 1], + partial_sums_2[y + 1], + ); + } + + // top and bottom cases + let (partial_north_1, partial_north_2) = { + let row = edges.n; + let left = (row >> 1) | edges.nw_bit(); + let right = (row << 1) | edges.ne_bit(); + (row ^ left ^ right, (left & right) | ((left ^ right) & row)) + }; step_row( - &mut tile[y], - partial_sums_1[y - 1], - partial_sums_2[y - 1], - partial_sums_1[y], - partial_sums_2[y], - partial_sums_1[y + 1], - partial_sums_2[y + 1], + &mut tile[0], + partial_north_1, + partial_north_2, + partial_sums_1[0], + partial_sums_2[0], + partial_sums_1[1], + partial_sums_2[1], + ); + let (partial_south_1, partial_south_2) = { + let row = edges.s; + let left = (row >> 1) | edges.sw_bit(); + let right = (row << 1) | edges.se_bit(); + (row ^ left ^ right, (left & right) | ((left ^ right) & row)) + }; + step_row( + &mut tile[LAST], + partial_sums_1[WIDTH - 2], + partial_sums_2[WIDTH - 2], + partial_sums_1[LAST], + partial_sums_2[LAST], + partial_south_1, + partial_south_2, ); } - - // top and bottom cases - let (partial_north_1, partial_north_2) = { - let row = edges.n; - let left = (row >> 1) | edges.nw_bit(); - let right = (row << 1) | edges.ne_bit(); - (row ^ left ^ right, (left & right) | ((left ^ right) & row)) - }; - step_row( - &mut tile[0], - partial_north_1, - partial_north_2, - partial_sums_1[0], - partial_sums_2[0], - partial_sums_1[1], - partial_sums_2[1], - ); - let (partial_south_1, partial_south_2) = { - let row = edges.s; - let left = (row >> 1) | edges.sw_bit(); - let right = (row << 1) | edges.se_bit(); - (row ^ left ^ right, (left & right) | ((left ^ right) & row)) - }; - step_row( - &mut tile[LAST], - partial_sums_1[WIDTH - 2], - partial_sums_2[WIDTH - 2], - partial_sums_1[LAST], - partial_sums_2[LAST], - partial_south_1, - partial_south_2, - ); } impl Edges { - // pub const EMPTY: Self = Edges { - // n: 0, - // s: 0, - // e: 0, - // w: 0, - // ne: false, - // nw: false, - // se: false, - // sw: false, - // }; - pub fn full( - n: &TileArr, - s: &TileArr, - e: &TileArr, - w: &TileArr, - ne: &TileArr, - nw: &TileArr, - se: &TileArr, - sw: &TileArr, + n: &Tile, + s: &Tile, + e: &Tile, + w: &Tile, + ne: &Tile, + nw: &Tile, + se: &Tile, + sw: &Tile, ) -> Self { let mut east = 0; let mut west = 0; for n in 0..WIDTH { - east |= (e[n] >> LAST) << n; - west |= (w[n] & 1) << n; + east |= (e.rows[n] >> LAST) << n; + west |= (w.rows[n] & 1) << n; } Self { - n: n[LAST], - s: s[0], + n: n.rows[LAST], + s: s.rows[0], e: east, w: west, - nw: (nw[LAST] & 1) != 0, - ne: (ne[LAST] >> LAST) != 0, - sw: (sw[0] & 1) != 0, - se: (se[0] >> LAST) != 0, + nw: (nw.rows[LAST] & 1) != 0, + ne: (ne.rows[LAST] >> LAST) != 0, + sw: (sw.rows[0] & 1) != 0, + se: (se.rows[0] >> LAST) != 0, } }