Browse Source

Added randomness and toroidal worlds

master
Thomas Johnson 2 years ago
parent
commit
dad8c64e2f
  1. 1
      Cargo.toml
  2. 224
      src/grid.rs
  3. 150
      src/main.rs

1
Cargo.toml

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.7"

224
src/grid.rs

@ -1,8 +1,11 @@
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;
@ -36,6 +39,14 @@ pub enum State
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
@ -48,22 +59,95 @@ impl Grid
}
}
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 % wx.get(),
Some(wx) => (x >> TILEBITS).rem_euclid(wx.get() as isize),
},
match self.wrapy
{
None => y >> TILEBITS,
Some(wy) => y >> TILEBITS % wy.get(),
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);
@ -80,7 +164,7 @@ impl Grid
pub fn set(&mut self, x: isize, y: isize, state: State) { self.set_sel(x, y, state, false); }
pub fn set_sel(&mut self, x: isize, y: isize, state: State, other: bool)
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);
@ -102,7 +186,7 @@ impl Grid
}
}
pub fn set_noc(&self, x: isize, y: isize, state: State, other: bool)
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);
@ -118,8 +202,9 @@ impl Grid
}
}
fn do_edge_case(on: &[bool; 9], off: &[bool; 9], tile: &mut RefMut<Tile>, adjacent: &Option<Ref<Tile>>, highidx: bool, horiz: bool)
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;
@ -195,17 +280,19 @@ impl Grid
if off[neighbors]
{ idx_c(&mut tile.cells, i).set_other(State::Dead(), which); }
else if on[neighbors]
{ idx_c(&mut tile.cells, i).set_other(State::Alive(), which); }
{ 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])
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|
{
@ -221,43 +308,120 @@ impl Grid
if off[neighbors]
{ self.set_noc(coords.0, coords.1, State::Dead(), true); }
else if on[neighbors]
{ self.set_noc(coords.0, coords.1, State::Alive(), true); }
{ 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])
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(&((ent.0).0 - 1, (ent.0).1)).map(|c| c.borrow());
let right = self.tiles.get(&((ent.0).0 + 1, (ent.0).1)).map(|c| c.borrow());
let above = self.tiles.get(&((ent.0).0, (ent.0).1 + 1)).map(|c| c.borrow());
let below = self.tiles.get(&((ent.0).0, (ent.0).1 - 1)).map(|c| c.borrow());
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);
Self::do_edge_case(on, off, &mut tile, &right, true, true);
Self::do_edge_case(on, off, &mut tile, &below, false, false);
Self::do_edge_case(on, off, &mut tile, &above, 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);
self.consider_cell((tilec.0, tilec.1 | TILEMASKI), on, off);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1), on, off);
self.consider_cell((tilec.0 | TILEMASKI, tilec.1 | TILEMASKI), on, off);
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()
{
ent.1.borrow_mut().which ^= true;
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
}
}
@ -272,6 +436,12 @@ impl Tile
}
}
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
@ -298,6 +468,11 @@ impl Tile
}
}
}
pub fn get_at(&self, x: usize, y: usize) -> &State
{
self.cells[x][y].get(self.which)
}
}
impl Cell
@ -313,11 +488,6 @@ impl Cell
}
}
fn get_other(&self, which: bool) -> &State
{
self.get(!which)
}
fn set(&mut self, new: State, which: bool)
{
if which

150
src/main.rs

@ -2,22 +2,26 @@
mod grid;
use crate::grid::{Grid, State};
use crate::grid::{Grid, State, TILESIZE, TILEMASKI, TILEBITS};
use std::thread::sleep;
use std::time::Duration;
use std::num::NonZeroUsize;
struct Disp<'a>(&'a Grid);
struct Disp<'a>(&'a Grid, (isize, isize), (isize, isize));
impl<'a> std::fmt::Display for Disp<'a>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
for y in (0..64).rev()
// write!(f, "\x1b[2J\x1b[H")?;
for y in ((self.2).0 << TILEBITS .. (self.2).1 << TILEBITS).rev()
{
for x in 0..64
for x in (self.1).0 << TILEBITS .. (self.1).1 << TILEBITS
{
match self.0.get(x, y)
{
State::Dead() => { write!(f, "░")?; },
State::Dead() if x & TILEMASKI == 0 || y & TILEMASKI == 0 => { write!(f, "▒")?; },
State::Dead() if self.0.backed_at(x, y) => { write!(f, "░")?; },
State::Dead() => { write!(f, " ")?; },
State::Alive() => { write!(f, "▓")?; },
}
}
@ -29,56 +33,106 @@ impl<'a> std::fmt::Display for Disp<'a>
fn main()
{
let mut grid = Grid::new_blank();
for _ii in 0 .. 1
{
//let mut grid = Grid::new_blank_wrapped(2, 2);
let mut grid = Grid::new_random(NonZeroUsize::new(2).unwrap(), NonZeroUsize::new(2).unwrap(), 0.5);
grid.set(50, 15, State::Alive());
grid.set(50, 50, State::Alive());
grid.set(15, 50, State::Alive());
//grid.set(15, 50, State::Alive());
//grid.set(50, 50, State::Alive());
//grid.set(50, 15, State::Alive());
// NE Glider
//grid.set(15, 10, State::Alive());
//grid.set(15, 9, State::Alive());
//grid.set(15, 8, State::Alive());
//grid.set(14, 10, State::Alive());
//grid.set(13, 9, State::Alive());
// NE Glider
//grid.set(15, 10, State::Alive());
//grid.set(15, 9, State::Alive());
//grid.set(15, 8, State::Alive());
//grid.set(14, 10, State::Alive());
//grid.set(13, 9, State::Alive());
// NE Glider
//grid.set(5, 15, State::Alive());
//grid.set(5, 14, State::Alive());
//grid.set(5, 13, State::Alive());
//grid.set(4, 15, State::Alive());
//grid.set(3, 14, State::Alive());
// NE Glider
//grid.set(5, 15, State::Alive());
//grid.set(5, 14, State::Alive());
//grid.set(5, 13, State::Alive());
//grid.set(4, 15, State::Alive());
//grid.set(3, 14, State::Alive());
// NE Glider
grid.set(15, 15, State::Alive());
grid.set(15, 14, State::Alive());
grid.set(15, 13, State::Alive());
grid.set(14, 15, State::Alive());
grid.set(13, 14, State::Alive());
// NE Glider
//grid.set(15, 15, State::Alive());
//grid.set(15, 14, State::Alive());
//grid.set(15, 13, State::Alive());
//grid.set(14, 15, State::Alive());
//grid.set(13, 14, State::Alive());
//grid.set(15, 17, State::Alive());
//grid.set(15, 16, State::Alive());
//grid.set(15, 15, State::Alive());
//grid.set(14, 16, State::Alive());
//grid.set(14, 15, State::Alive());
//grid.set(14, 14, State::Alive());
//grid.set(15, 17, State::Alive());
//grid.set(15, 16, State::Alive());
//grid.set(15, 15, State::Alive());
//grid.set(14, 16, State::Alive());
//grid.set(14, 15, State::Alive());
//grid.set(14, 14, State::Alive());
//grid.set(14, 6, State::Alive());
//grid.set(14, 7, State::Alive());
//grid.set(15, 6, State::Alive());
//grid.set(15, 7, State::Alive());
//grid.set(14, 6, State::Alive());
//grid.set(14, 7, State::Alive());
//grid.set(15, 6, State::Alive());
//grid.set(15, 7, State::Alive());
//grid.set(25, 25, State::Alive());
//grid.set(25, 26, State::Alive());
//grid.set(26, 25, State::Alive());
//grid.set(26, 26, State::Alive());
//grid.set(25, 25, State::Alive());
//grid.set(25, 26, State::Alive());
//grid.set(26, 25, State::Alive());
//grid.set(26, 26, State::Alive());
let mut disp = format!("{}", Disp(&grid));
for _steps in 0..200
{
print!("{}", disp);
grid.step(&[false, false, false, true, false, false, false, false, false], &[true, true, false, false, true, true, true, true, true]);
disp = format!("{}", Disp(&grid));
sleep(Duration::from_millis(10));
// Raster semicircle
//grid.set(0, 0, State::Alive());
//grid.set(1, 0, State::Alive());
//grid.set(2, 1, State::Alive());
//grid.set(3, 2, State::Alive());
//grid.set(3, 3, State::Alive());
//grid.set(3, 4, State::Alive());
//grid.set(2, 5, State::Alive());
//grid.set(1, 6, State::Alive());
//grid.set(0, 6, State::Alive());
// Line
//for i in 0..10
//{
// grid.set(i, 0, State::Alive());
//}
// Acorn
//grid.set(0, 0, State::Alive());
//grid.set(1, 0, State::Alive());
//grid.set(1, 2, State::Alive());
//grid.set(3, 1, State::Alive());
//grid.set(4, 0, State::Alive());
//grid.set(5, 0, State::Alive());
//grid.set(6, 0, State::Alive());
let mut disp = format!("{}", Disp(&grid, (0, 1), (0, 1)));
for steps in 0..3000
{
println!("{}", steps);
print!("{}", disp);
let extents = grid.step(&[false, false, false, true, false, false, false, false, false], &[true, true, false, false, true, true, true, true, true], steps % TILESIZE == 0);
disp = format!("{}", Disp(&grid, extents.0, extents.1));
sleep(Duration::from_millis(1000 / 60));
}
let mut count_off = 0;
let mut count_on = 0;
grid.per_tile(|_, t|
{
for x in 0 .. TILESIZE
{
for y in 0 .. TILESIZE
{
match t.get_at(x, y)
{
State::Dead() => { count_off += 1; },
State::Alive() => { count_on += 1; },
}
}
}
});
println!("cells off : {}", count_off);
println!("cells on : {}", count_on);
println!("ratio on : {}", (count_on as f64) / (count_off + count_on) as f64);
}
}
Loading…
Cancel
Save