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.

179 lines
5.5 KiB

mod grid;
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, (isize, isize), (isize, isize));
impl<'a> std::fmt::Display for Disp<'a>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
// write!(f, "\x1b[2J\x1b[H")?;
for y in ((self.2).0 << TILEBITS .. (self.2).1 << TILEBITS).rev()
{
for x in (self.1).0 << TILEBITS .. (self.1).1 << TILEBITS
{
match self.0.get(x, y)
{
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, "▓")?; },
}
}
writeln!(f, "")?;
}
writeln!(f, "")
}
}
fn main()
{
for _ 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);
let (stability_steps, period) = find_first_oscillation(&mut grid, &[false, false, false, true, false, false, false, false, false], &[true, true, false, false, true, true, true, true, true]);
//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(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());
//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(25, 25, State::Alive());
//grid.set(25, 26, State::Alive());
//grid.set(26, 25, State::Alive());
//grid.set(26, 26, State::Alive());
// 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!("took {stability_steps} steps to converge to a period of {period}");
println!("cells off : {count_off}");
println!("cells on : {count_on}");
println!("ratio on : {}", (count_on as f64) / (count_off + count_on) as f64);
}
}
fn find_first_oscillation(g: &mut Grid, on: &[bool; 9], off: &[bool; 9]) -> (usize, usize) {
let mut tortoise = g.clone();
let mut hare = g.clone();
let mut i = 0;
tortoise.step(on, off, i % TILESIZE == 0);
hare.step(on, off, false);
hare.step(on, off, i % TILESIZE == 0);
while hare != tortoise {
tortoise.step(on, off, i % TILESIZE == 0);
hare.step(on, off, false);
hare.step(on, off, i % TILESIZE == 0);
i += 1;
}
let mut next_occurrence = tortoise.clone();
let mut period = 1;
next_occurrence.step(on, off, (i + period) % TILESIZE == 0);
while tortoise != next_occurrence {
next_occurrence.step(on, off, (i + period) % TILESIZE == 0);
period += 1;
}
let mut leading = g.clone();
for i in 0..period {
leading.step(on, off, i % TILESIZE == 0);
}
let mut first_index = 0;
while *g != leading {
g.step(on, off, first_index % TILESIZE == 0);
leading.step(on, off, first_index % TILESIZE == 0);
first_index += 1;
}
(first_index, period)
}