use data::cell::{Cell, Status};
use rand;
use rand::Rng;
use rayon::prelude::*;
use std::mem;
pub const PAR_THRESHOLD_AREA: usize = 250000;
#[derive(Debug, PartialEq, Eq)]
pub struct GridIdx(pub usize);
#[derive(Debug)]
pub struct Grid {
cells: Vec<Cell>,
scratchpad_cells: Vec<Cell>,
max_i: usize,
max_j: usize,
area: usize,
neighbours: Vec<[GridIdx; 8]>,
}
#[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone)]
pub struct Coord {
pub i: usize,
pub j: usize,
}
impl Grid {
pub fn new(width: usize, height: usize) -> Grid {
let mut rng = rand::thread_rng();
let mut grid = Vec::with_capacity(height);
for _ in 0..height {
let mut row = Vec::with_capacity(width);
for _ in 0..width {
let status = if rng.gen() {
Status::Alive
} else {
Status::Dead
};
let cell = Cell(status);
row.push(cell);
}
grid.push(row);
}
let max_i = if height == 0 { 0 } else { height - 1 };
let max_j = if width == 0 { 0 } else { width - 1 };
let neighbours = neighbours(max_i, max_j, &grid);
let cells: Vec<Cell> = grid.into_iter().flat_map(|v| v).collect();
let scratchpad_cells = cells.clone();
let area = width * height;
Grid {
cells,
scratchpad_cells,
max_i,
max_j,
area,
neighbours,
}
}
pub fn get_idx(&self, &GridIdx(idx): &GridIdx) -> Option<&Cell> {
if idx < self.cells.len() {
Some(&self.cells[idx])
} else {
None
}
}
pub fn to_grid_idx(&self, &Coord { i, j }: &Coord) -> Option<GridIdx> {
if i <= self.max_i && j <= self.max_j {
Some(GridIdx(self.width() * i + j))
} else {
None
}
}
pub fn to_coord(&self, &GridIdx(idx): &GridIdx) -> Option<Coord> {
if idx < self.area() {
let width = self.width();
let i = idx / width;
let j = idx % width;
Some(Coord { i, j })
} else {
None
}
}
pub fn cells(&self) -> Vec<Vec<&Cell>> {
let mut rows = Vec::with_capacity(self.height());
let mut i = 0;
for _ in 0..self.height() {
let mut columns = Vec::with_capacity(self.width());
for _ in 0..self.width() {
columns.push(&self.cells[i]);
i += 1;
}
rows.push(columns);
}
rows
}
pub fn height(&self) -> usize {
self.max_i + 1
}
pub fn width(&self) -> usize {
self.max_j + 1
}
pub fn area(&self) -> usize {
self.area
}
pub fn advance(&mut self) -> () {
{
let neighbours = &self.neighbours;
let last_gen = &self.cells;
let area_requires_par = self.area() >= PAR_THRESHOLD_AREA;
let cells = &mut self.scratchpad_cells;
let cell_op = |(i, cell): (usize, &mut Cell)| {
let alives = neighbours[i]
.iter()
.fold(0,
|acc, &GridIdx(idx)| if last_gen[idx].0 == Status::Alive {
acc + 1
} else {
acc
});
let next_status = last_gen[i].next_status(alives);
cell.update(next_status);
};
if area_requires_par {
cells.par_iter_mut().enumerate().for_each(cell_op);
} else {
for (i, cell) in cells.iter_mut().enumerate() {
cell_op((i, cell))
}
}
}
mem::swap(&mut self.cells, &mut self.scratchpad_cells);
}
}
fn neighbours(max_i: usize, max_j: usize, cells: &[Vec<Cell>]) -> Vec<[GridIdx; 8]> {
let mut v = Vec::with_capacity((max_i + 1) * (max_j + 1));
for (i, row) in cells.iter().enumerate() {
for (j, _) in row.iter().enumerate() {
let coord = Coord { i, j };
v.push(neighbour_coords(max_i, max_j, &coord))
}
}
v
}
fn neighbour_coords(max_i: usize, max_j: usize, coord: &Coord) -> [GridIdx; 8] {
let width = max_j + 1;
let Coord { i, j } = *coord;
let to_grid_idx = |Coord { i, j }: Coord| GridIdx(width * i + j);
let i_up = match i {
0 => max_i,
_ => i - 1,
};
let i_down = match i {
_ if i == max_i => 0,
_ => i + 1,
};
let j_left = match j {
0 => max_j,
_ => j - 1,
};
let j_right = match j {
_ if j == max_j => 0,
_ => j + 1,
};
let north = Coord { i: i_up, j: j };
let north_east = Coord {
i: i_up,
j: j_right,
};
let east = Coord { i, j: j_right };
let south_east = Coord {
i: i_down,
j: j_right,
};
let south = Coord { i: i_down, j };
let south_west = Coord {
i: i_down,
j: j_left,
};
let west = Coord { i, j: j_left };
let north_west = Coord { i: i_up, j: j_left };
[to_grid_idx(north),
to_grid_idx(north_east),
to_grid_idx(east),
to_grid_idx(south_east),
to_grid_idx(south),
to_grid_idx(south_west),
to_grid_idx(west),
to_grid_idx(north_west)]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grid_new() {
let grid = Grid::new(10, 5);
assert_eq!(grid.cells().len(), 5);
assert_eq!(grid.cells()[0].len(), 10);
}
#[test]
fn test_neighbour_coords() {
let grid = Grid::new(3, 3);
let max_i = grid.max_i;
let max_j = grid.max_j;
let n0 = neighbour_coords(max_i, max_j, &Coord { i: 0, j: 0 });
assert_eq!(n0[0], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); assert_eq!(n0[1], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); assert_eq!(n0[2], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); assert_eq!(n0[3], grid.to_grid_idx(&Coord { i: 1, j: 1 }).unwrap()); assert_eq!(n0[4], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); assert_eq!(n0[5], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); assert_eq!(n0[6], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); assert_eq!(n0[7], grid.to_grid_idx(&Coord { i: 2, j: 2 }).unwrap()); let n1 = neighbour_coords(max_i, max_j, &Coord { i: 1, j: 1 });
assert_eq!(n1[0], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); assert_eq!(n1[1], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); assert_eq!(n1[2], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); assert_eq!(n1[3], grid.to_grid_idx(&Coord { i: 2, j: 2 }).unwrap()); assert_eq!(n1[4], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); assert_eq!(n1[5], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); assert_eq!(n1[6], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); assert_eq!(n1[7], grid.to_grid_idx(&Coord { i: 0, j: 0 }).unwrap()); let n2 = neighbour_coords(max_i, max_j, &Coord { i: 2, j: 2 });
assert_eq!(n2[0], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); assert_eq!(n2[1], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); assert_eq!(n2[2], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); assert_eq!(n2[3], grid.to_grid_idx(&Coord { i: 0, j: 0 }).unwrap()); assert_eq!(n2[4], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); assert_eq!(n2[5], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); assert_eq!(n2[6], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); assert_eq!(n2[7], grid.to_grid_idx(&Coord { i: 1, j: 1 }).unwrap()); }
#[test]
fn test_advance() {
let mut grid = Grid::new(50, 150);
print!("{:?}", grid);
for _ in 0..100 {
grid.advance();
}
}
#[test]
fn test_alive_count() {
let mut grid = Grid::new(3, 3);
let new_cells = vec![vec![Cell(Status::Alive),
Cell(Status::Alive),
Cell(Status::Alive)],
vec![Cell(Status::Alive), Cell(Status::Dead), Cell(Status::Alive)],
vec![Cell(Status::Alive),
Cell(Status::Alive),
Cell(Status::Alive)]]
.into_iter()
.flat_map(|v| v)
.collect();
grid.cells = new_cells;
assert_eq!(alive_count(&grid), 8)
}
#[test]
fn test_get_idx() {
let mut grid = Grid::new(3, 3);
let new_cells: Vec<Cell> =
vec![vec![Cell(Status::Alive),
Cell(Status::Alive),
Cell(Status::Alive)],
vec![Cell(Status::Alive), Cell(Status::Dead), Cell(Status::Alive)],
vec![Cell(Status::Alive),
Cell(Status::Alive),
Cell(Status::Alive)]]
.into_iter()
.flat_map(|v| v)
.collect();
grid.cells = new_cells;
for idx in 0..9 {
let cell = grid.get_idx(&GridIdx(idx)).unwrap();
if idx != 4 {
assert!(cell.alive())
} else {
assert!(!cell.alive())
}
}
}
#[test]
fn test_to_grid_idx() {
let grid = Grid::new(4, 3);
assert_eq!(grid.to_grid_idx(&Coord { i: 0, j: 0 }), Some(GridIdx(0)));
assert_eq!(grid.to_grid_idx(&Coord { i: 1, j: 2 }), Some(GridIdx(6)));
assert_eq!(grid.to_grid_idx(&Coord { i: 2, j: 3 }), Some(GridIdx(11)));
assert_eq!(grid.to_grid_idx(&Coord { i: 3, j: 3 }), None);
}
#[test]
fn test_to_coord() {
let grid = Grid::new(4, 3);
assert_eq!(grid.to_coord(&GridIdx(0)), Some(Coord { i: 0, j: 0 }));
assert_eq!(grid.to_coord(&GridIdx(6)), Some(Coord { i: 1, j: 2 }));
assert_eq!(grid.to_coord(&GridIdx(11)), Some(Coord { i: 2, j: 3 }));
assert_eq!(grid.to_coord(&GridIdx(12)), None);
}
fn alive_cells(grid: &Grid) -> Vec<Coord> {
let mut v = vec![];
for (i, row) in grid.cells().iter().enumerate() {
for (j, cell) in row.iter().enumerate() {
if cell.alive() {
let coord = Coord { i, j };
v.push(coord);
}
}
}
v
}
fn alive_count(grid: &Grid) -> usize {
alive_cells(grid).len()
}
}