Skip to content
Snippets Groups Projects
s4_automata.rs 3.05 KiB
Newer Older
Finn Bear's avatar
Finn Bear committed
use crate::component::grid::Grid;
Finn Bear's avatar
Finn Bear committed
use crate::governor::Governor;
Finn Bear's avatar
Finn Bear committed
use crate::Slide;
Finn Bear's avatar
Finn Bear committed
use eframe::egui::style::Margin;
use eframe::egui::{Color32, Frame, Ui};
Finn Bear's avatar
Finn Bear committed
use rand::Rng;
Finn Bear's avatar
Finn Bear committed

Finn Bear's avatar
Finn Bear committed
/// Conway's Game of Life, etc.
Finn Bear's avatar
Finn Bear committed
pub struct Automata {
Finn Bear's avatar
Finn Bear committed
    /// Persistent state of the grid.
Finn Bear's avatar
Finn Bear committed
    life: Grid,
Finn Bear's avatar
Finn Bear committed
    /// For knowing when to advance the simulation.
Finn Bear's avatar
Finn Bear committed
    governor: Governor,
}

Finn Bear's avatar
Finn Bear committed
impl Default for Automata {
Finn Bear's avatar
Finn Bear committed
    fn default() -> Self {
        let mut life = Grid::default();
Finn Bear's avatar
Finn Bear committed
        // Unique identifier for the grid.
Finn Bear's avatar
Finn Bear committed
        life.name = "life";
Finn Bear's avatar
Finn Bear committed

        // Fill grid with random cells.
Finn Bear's avatar
Finn Bear committed
        for y in 0..life.size_cells {
            for x in 0..life.size_cells {
                if rand::thread_rng().gen_bool(0.5) {
                    life.set_fill(x, y, Color32::BLACK);
                }
            }
        }

Finn Bear's avatar
Finn Bear committed
        // This would replace the grid with a single oscillator.
Finn Bear's avatar
Finn Bear committed
        /*
        life.clear_fill();
        life.set_fill(5, 5, Color32::BLACK);
        life.set_fill(6, 5, Color32::BLACK);
        life.set_fill(7, 5, Color32::BLACK);
         */

        Self {
            life,
            governor: Governor::default(),
        }
    }
Finn Bear's avatar
Finn Bear committed
}
Finn Bear's avatar
Finn Bear committed

Finn Bear's avatar
Finn Bear committed
impl Slide for Automata {
    fn show(&mut self, ui: &mut Ui) {
Finn Bear's avatar
Finn Bear committed
        Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
            ui.vertical_centered(|ui| {
                ui.heading("Cellular Automata");
            });
        });

Finn Bear's avatar
Finn Bear committed
        // Need to continuously animate the grid, or at least poll the governor.
        ui.ctx().request_repaint();
Finn Bear's avatar
Finn Bear committed

        if self.governor.ready(ui.ctx().input().time, 0.2) {
Finn Bear's avatar
Finn Bear committed
            self.life = conways_game_of_life(&self.life);
Finn Bear's avatar
Finn Bear committed
        }
        self.life.show(ui.ctx());
Finn Bear's avatar
Finn Bear committed
    }
}
Finn Bear's avatar
Finn Bear committed

Finn Bear's avatar
Finn Bear committed
/// Run one iteration of Conway's Game of Life on the grid, producing a new grid.
Finn Bear's avatar
Finn Bear committed
fn conways_game_of_life(grid: &Grid) -> Grid {
Finn Bear's avatar
Finn Bear committed
    let mut new = grid.clone();
    new.clear_fill();

    for cy in 0..grid.size_cells {
        for cx in 0..grid.size_cells {
            let mut neighbors = 0;
            for dy in -1..=1 {
                for dx in -1..=1 {
                    let (x, y) = match (cx.checked_add_signed(dx), cy.checked_add_signed(dy)) {
                        (Some(x), Some(y)) if x < grid.size_cells && y < grid.size_cells => (x, y),
                        _ => continue,
                    };
                    if x == cx && y == cy {
                        // Don't consider self.
                        continue;
                    }
                    if grid.fill(x, y).a() > 0 {
                        neighbors += 1;
                    }
                }
            }

            let mut alive = grid.fill(cx, cy).a() > 0;

            alive = match (alive, neighbors) {
                (false, 3) => true,
                (false, _) => false,
                (true, 2) | (true, 3) => true,
                (true, _) => false,
            };

            new.set_fill(
                cx,
                cy,
                if alive {
                    Color32::BLACK
                } else {
                    Color32::TRANSPARENT
                },
            );
        }
    }

    new
}