implement regions of multiple tiles

This commit is contained in:
Crispy 2023-07-20 18:25:15 +02:00
parent 47ff50be5f
commit d0bed390a5
3 changed files with 213 additions and 206 deletions

View file

@ -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{:#<w$}", "", w = WIDTH * 2 + 1);
print_tiles(&tile, &tile2);
// print_tile(&tile3);
println!("{:-<w$}", "", w = WIDTH * 2 + 1);
print_tiles(&tile3, &tile4);
// print_tile(&tile2);
// print_tile(&tile4);
step(&mut tile, &edges);
step(&mut tile2, &edges2);
step(&mut tile3, &edges3);
step(&mut tile4, &edges4);
println!("---");
region.print_all();
region.step();
{
let mut a = String::new();
std::io::stdin().read_line(&mut a).unwrap();
}
}
}
// fn print_tile(tile: &Tile) {
// for y in 0..(WIDTH / 2) {
// let top = tile[y * 2];
// let bot = tile[y * 2 + 1];
// let mut row = String::with_capacity(WIDTH);
// for bit in (0..WIDTH).rev() {
// let states = ((top >> 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}");
}
}

77
src/region.rs Normal file
View file

@ -0,0 +1,77 @@
use crate::tile::{Edges, Tile, WIDTH};
pub struct Region {
/// rows of tiles
tiles: Vec<Vec<Tile>>,
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
}
}
}

View file

@ -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,
}
}