Mojo 🔥 Programming

Programming problems in mojo

View project on GitHub

Conway’s Game of Life is a simulation where grid cells evolve over time according to rules based on the number of neighboring live cells, creating complex patterns from simple beginnings.

import random


# Grid is a 2D structure holding cell states (0: dead, 1: alive)
# It supports string conversion, output writing, and cell access/update
@value
struct Grid(Stringable, Writable):
    var data: List[List[Int, True]]  # 2D grid of integers (1 = alive, 0 = dead)

    # Constructor to initialize the grid with given data
    fn __init__(out self, data: List[List[Int, True]]):
        self.data = data

    # Get the number of rows in the grid
    fn row_count(self) -> Int:
        if self.data:
            return len(self.data)
        else:
            return 0

    # Get the number of columns in the grid
    fn col_count(self) -> Int:
        if self.data[0]:
            return len(self.data[0])
        else:
            return 0

    # Convert the grid to a string for pretty-printing
    fn __str__(self) -> String:
        capacity = self.row_count() * self.col_count()
        if capacity == 0:
            return String()
        s = String(capacity=capacity)
        row_index = 0
        for row in self.data:
            for col in row[]:
                if col[] == 1:
                    s += "*"  # Alive cell represented by '*'
                else:
                    s += " "  # Dead cell is blank
            if row_index != self.row_count() - 1:
                s += "\n"  # Line break between rows
            row_index += 1
        return s

    # Allow writing the grid to any output writer
    fn write_to[W: Writer](self, mut writer: W) -> None:
        writer.write(self.__str__())

    # Access cell at (row, col)
    fn __getitem__(self, row: Int, col: Int) -> Int:
        return self.data[row][col]

    # Update cell at (row, col)
    fn __setitem__(mut self, row: Int, col: Int, value: Int) -> None:
        self.data[row][col] = value

    # Static method to create a random grid with specified size
    @staticmethod
    fn new(rows: Int, cols: Int) -> Self:
        random.seed()
        data = List[List[Int, True]](capacity=rows)
        for row in range(rows):
            record = List[Int, True](capacity=cols)
            for col in range(cols):
                # Initialize each cell randomly to 0 or 1
                record.append(Int(random.random_si64(0, 1)))
            data.append(record)
        return Self(data)

    # Perform one step of mutation (Game of Life rules)
    fn mutate(mut self):
        rows = self.row_count()
        cols = self.col_count()
        for row in range(rows):
            above = (row - 1) % rows
            below = (row + 1) % rows
            for col in range(cols):
                left = (col - 1) % cols
                right = (col + 1) % cols

                # Count live neighbors using 8-connected grid
                alive_neighbours = (
                    self[above, left]
                    + self[above, col]
                    + self[above, right]
                    + self[row, right]
                    + self[below, right]
                    + self[below, col]
                    + self[below, left]
                    + self[row, left]
                )

                # Apply Conway's Game of Life rules:
                # Rule 1 & 2: Any live cell with 2 or 3 live neighbors survives
                if self[row, col] == 1 and (
                    alive_neighbours == 2 or alive_neighbours == 3
                ):
                    continue  # Keep alive

                # Rule 3: All other live cells die
                else:
                    self[row, col] = 0

                # Rule 4: Any dead cell with exactly 3 live neighbors becomes alive
                if self[row, col] == 0 and alive_neighbours == 3:
                    self[row, col] = 1

fn run(owned grid: Grid) raises -> None:
    while True:
        print("Current mutation:\n\n")
        print(grid)
        print()
        print()
        if input("Enter 'q' to quit or press <Enter> to continue: ") == "q":
            break
        grid.mutate()


fn main() raises -> None:
    start = Grid.new(16, 16)
    run(start)

Source code

Pointer implementation of grid