conlife 0.1.1

Backend-only implementation of Game of Life with minimal dependencies
Documentation
#[derive(Debug, Clone)]
pub struct Cell {
    neighbour_indices: Vec<(usize, usize)>,
    pub alive: bool,
}

impl Cell {
    pub fn new() -> Self {
        Self {
            alive: false,
            neighbour_indices: vec![],
        }
    }
    pub fn neighbour_count(&self, cells: &Vec<Vec<Cell>>) -> u32 {
        let mut neighbour_count = 0;
        for &position in &self.neighbour_indices {
            neighbour_count += cells[position.0][position.1].alive as u32;
        }

        neighbour_count
    }
}

#[derive(Debug)]
pub struct Grid {
    pub width: u32,
    pub height: u32,
    pub cells: Vec<Vec<Cell>>,
}

pub enum HorizontalEdge {
    Left,
    Right,
    NoEdge,
}

pub enum VerticalEdge {
    Top,
    Bottom,
    NoEdge,
}

impl Grid {
    pub fn new(width: u32, height: u32) -> Self {
        let mut cells = vec![];
        for _ in 0..height {
            let mut row = vec![];
            for _ in 0..width {
                row.push(Cell::new())
            }
            cells.push(row);
        }
        let mut grid = Self {
            width,
            height,
            cells,
        };

        grid.compute_neighbour_indices();
        grid
    }

    pub fn advance(&mut self) {
        let old_cells = self.cells.clone();
        for row in self.cells.iter_mut() {
            for cell in row {
                match cell.neighbour_count(&old_cells) {
                    0..=1 => {
                        cell.alive = false;
                    }
                    3 => {
                        cell.alive = true;
                    }
                    4.. => {
                        cell.alive = false;
                    }
                    _ => {}
                };
            }
        }
    }

    pub fn compute_neighbour_indices(&mut self) {
        for (x, row) in self.cells.iter_mut().enumerate() {
            for (y, cell) in row.iter_mut().enumerate() {
                let mut x_indices = vec![x];
                let mut y_indices = vec![y];

                if x != 0 {
                    x_indices.push(x - 1);
                }

                if x != self.width as usize - 1 {
                    x_indices.push(x + 1);
                }

                if y != 0 {
                    y_indices.push(y - 1);
                }

                if y != self.height as usize - 1 {
                    y_indices.push(y + 1);
                }

                for neighbour_x in x_indices {
                    for &neighbour_y in &y_indices {
                        if neighbour_x != x || neighbour_y != y {
                            cell.neighbour_indices.push((neighbour_x, neighbour_y));
                        }
                    }
                }
            }
        }
    }

    pub fn load_object(&mut self, object: &Object, offset: (usize, usize)) {
        for (x, y) in &object.pixels {
            self.cells
                .get_mut(*y + offset.1)
                .unwrap()
                .get_mut(*x + offset.0)
                .unwrap()
                .alive = true;
        }
    }
}

pub struct Object {
    pub pixels: Vec<(usize, usize)>,
}

impl Object {
    pub fn from_file(filename: &str) -> Self {
        let file_contents = std::fs::read_to_string(filename).expect("Failed to read file");
        Self::from_string(file_contents)
    }

    fn from_string(buffer: String) -> Object {
        let mut pixels = vec![];
        let buffer = buffer.replace("(", "");
        let buffer = buffer.replace(")", "");
        let coords = buffer.split_whitespace();
        for coord in coords {
            let mut coord_vals = coord.split(",");
            let x: usize = coord_vals
                .next()
                .expect("Failed to get next value in comma split")
                .parse()
                .expect("Failed to parse coordinate to usize");
            let y: usize = coord_vals
                .next()
                .expect("Failed to get next value in comma split")
                .parse()
                .expect("Failed to parse coordinate to usize");
            pixels.push((x, y));
        }
        Self { pixels }
    }
}