Simulating Conway's Game of Life in 3D

Necessary Packages and Paths


    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib import animation
    from mpl_toolkits.mplot3d import Axes3D  # needed for 3D projection
    mpl.rcParams["animation.ffmpeg_path"] = "/usr/bin/ffmpeg"  # adjust path
    

Initial Setup


    NX, NY, NZ = 10, 10, 10   # grid size
    p_alive = 0.15            # initial alive probability

    # 3D grid: 0 = dead, 1 = alive
    grid = (np.random.rand(NZ, NY, NX) < p_alive).astype(np.uint8)
    

Simulation


    def count_neighbors(g):
    """
    Count alive neighbors for each cell in a 3D grid with periodic boundaries.
    g has shape (Z, Y, X).
    """
    nz, ny, nx = g.shape
    neighbors = np.zeros_like(g, dtype=np.uint8)

    # iterate over shifts in {-1, 0, 1} for each axis, skip (0,0,0)
    for dz in (-1, 0, 1):
        for dy in (-1, 0, 1):
            for dx in (-1, 0, 1):
                if dz == 0 and dy == 0 and dx == 0:
                    continue  # skip the cell itself
                neighbors += np.roll(
                    np.roll(
                        np.roll(g, dz, axis=0),
                        dy, axis=1
                    ),
                    dx, axis=2
                )

    return neighbors
    

Conditions


    def step(g):
        n = count_neighbors(g)
        birth = (g == 0) & (n == 6)
        survive = (g == 1) & ((n == 5) | (n == 6) | (n == 7))
        return (birth | survive).astype(np.uint8)
    

Visualization and Animation


    fig = plt.figure(figsize=(6, 6))
    ax = fig.add_subplot(111, projection="3d")
    ax.view_init(elev=30, azim=45)

    def setup_axes():
        ax.set_xlim(0, NX)
        ax.set_ylim(0, NY)
        ax.set_zlim(0, NZ)
        ax.set_xticks([])
        ax.set_yticks([])
        ax.set_zticks([])

    setup_axes()
    title = ax.set_title("Generation 0")

    # initial voxels
    def draw_voxels(g, generation):
        ax.clear()
        setup_axes()
        filled = g.astype(bool)  # True where alive
        # Draw 1x1x1 cubes at alive cells
        ax.voxels(filled, facecolors="#363636", edgecolors="k")
        return ax.set_title(f"Generation {generation}")

    def init():
        global grid, title
        title = draw_voxels(grid, 0)
        return (title,)

    def animate(frame):
        global grid, title
        grid = step(grid)
        title = draw_voxels(grid, frame)
        return (title,)

    n_frames = 300

    anim = animation.FuncAnimation(
        fig, animate, init_func=init,
        frames=n_frames, interval=50, blit=False
    )

    plt.show()
    

Saving Animation


    writer = animation.FFMpegWriter(fps=20)

    anim.save("game_of_life_3D.mp4", writer=writer)
    plt.close(fig)
    

Simulation Result