A cellular automaton simulator that I wrote in a hurry (so it's pretty messy).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

509 lines
16 KiB

use std::collections::hash_map::HashMap;
use std::collections::HashSet;
use std::cell::{RefCell, Ref, RefMut};
use std::num::NonZeroUsize;
use rand::distributions::{Bernoulli, Distribution};
pub const TILEBITS: usize = 5;
pub const TILESIZE: usize = 1 << TILEBITS;
pub const TILEMASK: usize = TILESIZE - 1;
pub const TILEMASKI: isize = TILEMASK as isize;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Grid
{
tiles: HashMap<(isize, isize), RefCell<Tile>>,
wrapx: Option<NonZeroUsize>,
wrapy: Option<NonZeroUsize>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Tile
{
which: bool,
cells: [[Cell; TILESIZE]; TILESIZE],
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Cell
{
// Switch between which of the states we use.
first: State,
second: State,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum State
{
Dead(),
Alive(),
}
pub fn extend(extents: &mut ((isize, isize), (isize, isize)), coords: (isize, isize))
{
(extents.0).1 = isize::max((extents.0).1, coords.0 + 1);
(extents.0).0 = isize::min((extents.0).0, coords.0);
(extents.1).1 = isize::max((extents.1).1, coords.1 + 1);
(extents.1).0 = isize::min((extents.1).0, coords.1);
}
impl Grid
{
pub fn new_blank() -> Grid
{
Grid
{
tiles: HashMap::new(),
wrapx: None,
wrapy: None,
}
}
pub fn new_blank_wrapped(width: usize, height: usize) -> Grid
{
Grid
{
tiles: HashMap::new(),
wrapx: NonZeroUsize::new(width),
wrapy: NonZeroUsize::new(height),
}
}
pub fn new_random(width: NonZeroUsize, height: NonZeroUsize, prob: f64) -> Grid
{
let mut rng = rand::thread_rng();
let mut map = HashMap::new();
let d = Bernoulli::new(prob).unwrap();
for xtile in 0isize .. width.get() as isize
{
for ytile in 0isize .. height.get() as isize
{
let mut tile = Tile::new_blank(false);
for x in 0 .. TILESIZE
{
for y in 0 .. TILESIZE
{
tile.cells[x][y].set(if d.sample(&mut rng) { State::Alive() } else { State::Dead() }, false);
}
}
map.insert((xtile, ytile), RefCell::new(tile));
}
}
Grid
{
tiles: map,
wrapx: Some(width),
wrapy: Some(height),
}
}
pub fn per_tile<F: FnMut(&(isize, isize), Ref<Tile>)>(&self, mut f: F)
{
for t in self.tiles.iter()
{
f(t.0, t.1.borrow());
}
}
fn wrap_tile_coords(&self, x: isize, y: isize) -> (isize, isize)
{
(
match self.wrapx
{
None => x,
Some(wx) => x.rem_euclid(wx.get() as isize),
},
match self.wrapy
{
None => y,
Some(wy) => y.rem_euclid(wy.get() as isize),
}
)
}
fn to_tile_coords(&self, x: isize, y: isize) -> (isize, isize)
{
(
match self.wrapx
{
None => x >> TILEBITS,
Some(wx) => (x >> TILEBITS).rem_euclid(wx.get() as isize),
},
match self.wrapy
{
None => y >> TILEBITS,
Some(wy) => (y >> TILEBITS).rem_euclid(wy.get() as isize),
}
)
}
pub fn backed_at(&self, x: isize, y: isize) -> bool
{
let (xtile, ytile) = self.to_tile_coords(x, y);
match self.tiles.get(&(xtile, ytile))
{
None => false,
Some(_) => true,
}
}
pub fn get(&self, x: isize, y: isize) -> State
{
let (xtile, ytile) = self.to_tile_coords(x, y);
match self.tiles.get(&(xtile, ytile))
{
None => State::Dead(),
Some(tile) =>
{
let tile = tile.borrow();
*tile.cells[(x & (TILESIZE as isize - 1)) as usize][(y & (TILESIZE as isize - 1)) as usize].get(tile.which)
},
}
}
pub fn set(&mut self, x: isize, y: isize, state: State) { self.set_sel(x, y, state, false); }
fn set_sel(&mut self, x: isize, y: isize, state: State, other: bool)
{
let (xtile, ytile) = self.to_tile_coords(x, y);
let (xminor, yminor) = ((x & (TILESIZE - 1) as isize) as usize, (y & (TILESIZE - 1) as isize) as usize);
match self.tiles.get(&(xtile, ytile))
{
None => if let State::Alive() = state
{
let mut tile = Tile::new_blank(false);
let which = tile.which ^ other;
tile.cells[xminor][yminor].set(state, which);
self.tiles.insert((xtile, ytile), RefCell::new(tile));
},
Some(tile) =>
{
let mut tile = tile.borrow_mut();
let which = tile.which ^ other;
tile.cells[xminor][yminor].set(state, which);
},
}
}
fn set_noc(&self, x: isize, y: isize, state: State, other: bool)
{
let (xtile, ytile) = self.to_tile_coords(x, y);
let (xminor, yminor) = ((x & (TILESIZE - 1) as isize) as usize, (y & (TILESIZE - 1) as isize) as usize);
match self.tiles.get(&(xtile, ytile))
{
None => () ,
Some(tile) =>
{
let mut tile = tile.borrow_mut();
let which = tile.which ^ other;
tile.cells[xminor][yminor].set(state, which);
},
}
}
fn do_edge_case(on: &[bool; 9], off: &[bool; 9], tile: &mut RefMut<Tile>, adjacent: &Option<Ref<Tile>>, highidx: bool, horiz: bool, pretend: bool) -> bool
{
let mut rv = false;
let idx_c;
let idx_n;
let idx_f;
if horiz
{
if highidx
{
fn idx_c_f(cells: &mut[[Cell; TILESIZE]; TILESIZE], i: usize) -> &mut Cell
{ &mut cells[TILESIZE - 1][i] }
fn idx_n_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[TILESIZE - 2][i] }
fn idx_f_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[0][i] }
idx_c = idx_c_f as fn(&mut[[Cell; TILESIZE]; TILESIZE], usize) -> &mut Cell;
idx_n = idx_n_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
idx_f = idx_f_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
} else
{
fn idx_c_f(cells: &mut[[Cell; TILESIZE]; TILESIZE], i: usize) -> &mut Cell
{ &mut cells[0][i] }
fn idx_n_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[1][i] }
fn idx_f_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[TILESIZE - 1][i] }
idx_c = idx_c_f as fn(&mut[[Cell; TILESIZE]; TILESIZE], usize) -> &mut Cell;
idx_n = idx_n_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
idx_f = idx_f_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
}
} else
{
if highidx
{
fn idx_c_f(cells: &mut[[Cell; TILESIZE]; TILESIZE], i: usize) -> &mut Cell
{ &mut cells[i][TILESIZE - 1] }
fn idx_n_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[i][TILESIZE - 2] }
fn idx_f_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[i][0] }
idx_c = idx_c_f as fn(&mut[[Cell; TILESIZE]; TILESIZE], usize) -> &mut Cell;
idx_n = idx_n_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
idx_f = idx_f_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
} else
{
fn idx_c_f(cells: &mut[[Cell; TILESIZE]; TILESIZE], i: usize) -> &mut Cell
{ &mut cells[i][0] }
fn idx_n_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[i][1] }
fn idx_f_f(cells: &[[Cell; TILESIZE]; TILESIZE], i: usize) -> &Cell
{ &cells[i][TILESIZE - 1] }
idx_c = idx_c_f as fn(&mut[[Cell; TILESIZE]; TILESIZE], usize) -> &mut Cell;
idx_n = idx_n_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
idx_f = idx_f_f as fn(&[[Cell; TILESIZE]; TILESIZE], usize) -> &Cell;
}
}
let which = tile.which;
for i in 1 .. TILESIZE - 1
{
let neighbors = (i - 1 ..= i + 1)
.map(|i|
{
let mut c = 0;
if let State::Alive() = idx_n(&tile.cells, i).get(which)
{ c += 1; }
if let Some(lt) = adjacent
{
if let State::Alive() = idx_f(&lt.cells, i).get(lt.which)
{ c += 1; }
}
c
}).fold(0, |a, c| a + c) +
if let State::Alive() = idx_c(&mut tile.cells, i - 1).get(which) { 1 } else { 0 } +
if let State::Alive() = idx_c(&mut tile.cells, i + 1).get(which) { 1 } else { 0 };
if off[neighbors]
{ idx_c(&mut tile.cells, i).set_other(State::Dead(), which); }
else if on[neighbors]
{ if !pretend { idx_c(&mut tile.cells, i).set_other(State::Alive(), which); }; rv = true; }
else
{
let state = *idx_c(&mut tile.cells, i).get(which);
idx_c(&mut tile.cells, i).set_other(state, which);
}
}
rv
}
fn consider_cell(&self, coords: (isize, isize), on: &[bool; 9], off: &[bool; 9], pretend: bool) -> bool
{
let mut rv = false;
let neighbors = (coords.0 - 1 ..= coords.0 + 1)
.map(|i|
{
let mut c = 0;
if let State::Alive() = self.get(i, coords.1 + 1)
{ c += 1; }
if let State::Alive() = self.get(i, coords.1 - 1)
{ c += 1; }
c
}).fold(0, |a, c| a + c) +
if let State::Alive() = self.get(coords.0 - 1, coords.1) { 1 } else { 0 } +
if let State::Alive() = self.get(coords.0 + 1, coords.1) { 1 } else { 0 };
if off[neighbors]
{ self.set_noc(coords.0, coords.1, State::Dead(), true); }
else if on[neighbors]
{ if !pretend { self.set_noc(coords.0, coords.1, State::Alive(), true); }; rv = true; }
else
{
let state = self.get(coords.0, coords.1);
self.set_noc(coords.0, coords.1, state, true);
}
rv
}
pub fn step(&mut self, on: &[bool; 9], off: &[bool; 9], cullscan: bool) -> ((isize, isize), (isize, isize))
{
let mut candidates = HashSet::new();
for ent in self.tiles.iter()
{
{
let mut tile = ent.1.borrow_mut();
tile.do_step_internal(on, off);
let left = self.tiles.get(&self.wrap_tile_coords((ent.0).0 - 1, (ent.0).1)).map(|c| c.borrow());
let right = self.tiles.get(&self.wrap_tile_coords((ent.0).0 + 1, (ent.0).1)).map(|c| c.borrow());
let above = self.tiles.get(&self.wrap_tile_coords((ent.0).0, (ent.0).1 + 1)).map(|c| c.borrow());
let below = self.tiles.get(&self.wrap_tile_coords((ent.0).0, (ent.0).1 - 1)).map(|c| c.borrow());
if let None = left { candidates.insert((*ent.0, self.wrap_tile_coords((ent.0).0 - 1, (ent.0).1), true, true)); }
if let None = right { candidates.insert((*ent.0, self.wrap_tile_coords((ent.0).0 + 1, (ent.0).1), false, true)); }
if let None = above { candidates.insert((*ent.0, self.wrap_tile_coords((ent.0).0, (ent.0).1 + 1), false, false)); }
if let None = below { candidates.insert((*ent.0, self.wrap_tile_coords((ent.0).0, (ent.0).1 - 1), true, false)); }
Self::do_edge_case(on, off, &mut tile, &left, false, true, false);
Self::do_edge_case(on, off, &mut tile, &right, true, true, false);
Self::do_edge_case(on, off, &mut tile, &below, false, false, false);
Self::do_edge_case(on, off, &mut tile, &above, true, false, false);
}
let tilec = ((ent.0).0 << TILEBITS, (ent.0).1 << TILEBITS);
self.consider_cell((tilec.0, tilec.1), on, off, false);
self.consider_cell((tilec.0, tilec.1 | TILEMASKI), on, off, false);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1), on, off, false);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1 | TILEMASKI), on, off, false);
}
let mut blanktile = RefCell::new(Tile::new_blank(false));
for cand in candidates.drain()
{
match self.tiles.get(&cand.1)
{
Some(_) => (),
None =>
{
let adj = self.tiles.get(&cand.0).map(|c| c.borrow());
let tilec = ((cand.1).0 << TILEBITS, (cand.1).1 << TILEBITS);
let corners = match (cand.2, cand.3)
{
(false, false) => (tilec, (tilec.0 | TILEMASKI, tilec.1)),
(false, true) => (tilec, (tilec.0, tilec.1 | TILEMASKI)),
(true, false) => ((tilec.0, tilec.1 | TILEMASKI), (tilec.0 | TILEMASKI, tilec.1 | TILEMASKI)),
(true, true) => ((tilec.0 | TILEMASKI, tilec.1), (tilec.0 | TILEMASKI, tilec.1 | TILEMASKI)),
};
if Self::do_edge_case(on, off, &mut blanktile.borrow_mut(), &adj, cand.2, cand.3, true) ||
self.consider_cell(corners.0, on, off, true) ||
self.consider_cell(corners.1, on, off, true)
{
std::mem::drop(adj);
let left = self.tiles.get(&self.wrap_tile_coords((cand.1).0 - 1, (cand.1).1)).map(|c| c.borrow());
let right = self.tiles.get(&self.wrap_tile_coords((cand.1).0 + 1, (cand.1).1)).map(|c| c.borrow());
let above = self.tiles.get(&self.wrap_tile_coords((cand.1).0, (cand.1).1 + 1)).map(|c| c.borrow());
let below = self.tiles.get(&self.wrap_tile_coords((cand.1).0, (cand.1).1 - 1)).map(|c| c.borrow());
Self::do_edge_case(on, off, &mut blanktile.borrow_mut(), &left, false, true, false);
Self::do_edge_case(on, off, &mut blanktile.borrow_mut(), &right, true, true, false);
Self::do_edge_case(on, off, &mut blanktile.borrow_mut(), &below, false, false, false);
Self::do_edge_case(on, off, &mut blanktile.borrow_mut(), &above, true, false, false);
std::mem::drop((left, right, above, below));
let mut tmp_blanktile = RefCell::new(Tile::new_blank(false));
std::mem::swap(&mut tmp_blanktile, &mut blanktile);
self.tiles.insert(cand.1, tmp_blanktile);
self.consider_cell((tilec.0, tilec.1), on, off, false);
self.consider_cell((tilec.0, tilec.1 | TILEMASKI), on, off, false);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1), on, off, false);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1 | TILEMASKI), on, off, false);
}
}
}
}
let mut cull = vec![];
let mut extents = ((isize::max_value(), isize::min_value()), (isize::max_value(), isize::min_value()));
for ent in self.tiles.iter()
{
let mut rm = ent.1.borrow_mut();
rm.which ^= true;
if cullscan && rm.is_blank(rm.which)
{
cull.push(*ent.0);
} else
{
extend(&mut extents, *ent.0);
}
}
if cullscan
{
for t in cull.iter()
{
self.tiles.remove(t);
}
}
extents
}
}
impl Tile
{
fn new_blank(which: bool) -> Tile
{
Tile
{
which,
cells: [[Cell {first: State::Dead(), second: State::Dead()}; TILESIZE]; TILESIZE],
}
}
fn is_blank(&self, which: bool) -> bool
{
self.cells.iter().fold(true, |a, l| a && l.iter()
.fold(true, |a, c| if let State::Dead() = c.get(which) { a } else { false }))
}
fn do_step_internal(&mut self, on: &[bool; 9], off: &[bool; 9])
{
for x in 1 .. TILESIZE - 1
{
for y in 1 .. TILESIZE - 1
{
let neighbors = (y - 1 ..= y + 1).map(|i|
{
let mut c = 0;
if let State::Alive() = self.cells[x - 1][i].get(self.which)
{ c += 1; }
if let State::Alive() = self.cells[x + 1][i].get(self.which)
{ c += 1; }
c
}).fold(0, |a, c| a + c) +
if let State::Alive() = self.cells[x][y - 1].get(self.which) { 1 } else { 0 } +
if let State::Alive() = self.cells[x][y + 1].get(self.which) { 1 } else { 0 };
if off[neighbors]
{ self.cells[x][y].set_other(State::Dead(), self.which); }
else if on[neighbors]
{ self.cells[x][y].set_other(State::Alive(), self.which); }
else
{ self.cells[x][y].set_other(self.cells[x][y].get(self.which).clone(), self.which); }
}
}
}
pub fn get_at(&self, x: usize, y: usize) -> &State
{
self.cells[x][y].get(self.which)
}
}
impl Cell
{
fn get(&self, which: bool) -> &State
{
if which
{
&self.first
} else
{
&self.second
}
}
fn set(&mut self, new: State, which: bool)
{
if which
{
self.first = new;
} else
{
self.second = new;
}
}
fn set_other(&mut self, new: State, which: bool)
{
self.set(new, !which);
}
}