From e9112ec1ad67efddb470c454115e5f71acf524df Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Thu, 29 Aug 2024 15:28:33 +0200 Subject: [PATCH] GoL_fast: Add fast PropertyLayer implementation of Game of Life A special implementation of Conway's Game of Life, using only the PropertyLayer, without needing a Grid or even any Agents. --- examples/conways_game_of_life_fast/model.py | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 examples/conways_game_of_life_fast/model.py diff --git a/examples/conways_game_of_life_fast/model.py b/examples/conways_game_of_life_fast/model.py new file mode 100644 index 000000000..622e16e94 --- /dev/null +++ b/examples/conways_game_of_life_fast/model.py @@ -0,0 +1,35 @@ +import numpy as np +from mesa import Model +from mesa.space import PropertyLayer +from scipy.signal import convolve2d + +class GameOfLifeModel(Model): + def __init__(self, width, height, alive_fraction=0.2): + super().__init__() + # Initialize the property layer for cell states + self.cell_layer = PropertyLayer("cells", width, height, False, dtype=bool) + # Randomly set cells to alive + self.cell_layer.data = np.random.choice([True, False], size=(width, height), p=[alive_fraction, 1 - alive_fraction]) + + def step(self): + self._advance_time() + # Define a kernel for counting neighbors. The kernel has 1s around the center cell (which is 0). + # This setup allows us to count the live neighbors of each cell when we apply convolution. + kernel = np.array([[1, 1, 1], + [1, 0, 1], + [1, 1, 1]]) + + # Count neighbors using convolution. + # convolve2d applies the kernel to each cell of the grid, summing up the values of neighbors. + # boundary="wrap" ensures that the grid wraps around, simulating a toroidal surface. + neighbor_count = convolve2d(self.cell_layer.data, kernel, mode="same", boundary="wrap") + + # Apply Game of Life rules: + # 1. A live cell with 2 or 3 live neighbors survives, otherwise it dies. + # 2. A dead cell with exactly 3 live neighbors becomes alive. + # These rules are implemented using logical operations on the grid. + self.cell_layer.data = np.logical_or( + np.logical_and(self.cell_layer.data, np.logical_or(neighbor_count == 2, neighbor_count == 3)), + # Rule for live cells + np.logical_and(~self.cell_layer.data, neighbor_count == 3) # Rule for dead cells + )