216 lines
6.1 KiB
Python
216 lines
6.1 KiB
Python
import math
|
|
import time
|
|
from random import random
|
|
|
|
import machine
|
|
|
|
import badger2040
|
|
|
|
# Overclock the RP2040 to run the sim faster
|
|
badger2040.system_speed(badger2040.SYSTEM_TURBO)
|
|
|
|
# ------------------------------
|
|
# Program setup
|
|
# ------------------------------
|
|
|
|
# Global constants
|
|
CELL_SIZE = 6 # Size of cell in pixels
|
|
INITIAL_DENSITY = 0.3 # Density of cells at start
|
|
|
|
# Create a new Badger and set it to update TURBO
|
|
screen = badger2040.Badger2040()
|
|
screen.led(128)
|
|
screen.update_speed(badger2040.UPDATE_TURBO)
|
|
|
|
restart = False # should sim be restarted
|
|
|
|
# ------------------------------
|
|
# Button functions
|
|
# ------------------------------
|
|
|
|
|
|
# Button handling function
|
|
def button(pin):
|
|
global restart
|
|
# if 'a' button is pressed, restart the sim
|
|
if pin == button_a:
|
|
restart = True
|
|
|
|
|
|
# Set up button
|
|
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
|
|
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button)
|
|
|
|
# ------------------------------
|
|
# Screen functions
|
|
# ------------------------------
|
|
|
|
|
|
# Remove everything from the screen
|
|
def init_screen():
|
|
screen.update_speed(badger2040.UPDATE_NORMAL)
|
|
screen.pen(15)
|
|
screen.clear()
|
|
screen.update()
|
|
screen.update_speed(badger2040.UPDATE_TURBO)
|
|
|
|
|
|
# ------------------------------
|
|
# Classes
|
|
# ------------------------------
|
|
|
|
# Define a 'cell'
|
|
class Cell:
|
|
def __init__(self):
|
|
self._alive = False
|
|
|
|
def make_alive(self):
|
|
self._alive = True
|
|
|
|
def make_dead(self):
|
|
self._alive = False
|
|
|
|
def is_alive(self):
|
|
return self._alive
|
|
|
|
|
|
# Define the whole board
|
|
class Board:
|
|
def __init__(self):
|
|
self._rows = math.floor(badger2040.WIDTH / CELL_SIZE)
|
|
self._columns = math.floor(badger2040.HEIGHT / CELL_SIZE)
|
|
self._grid = [[Cell() for _ in range(self._columns)] for _ in range(self._rows)]
|
|
|
|
self._initialise_board()
|
|
|
|
# Draw the board to the screen
|
|
def draw_board(self):
|
|
row_idx = 0
|
|
column_idx = 0
|
|
|
|
for row in self._grid:
|
|
column_idx = 0
|
|
for cell in row:
|
|
if cell.is_alive():
|
|
screen.pen(0)
|
|
else:
|
|
screen.pen(15)
|
|
screen.rectangle(
|
|
row_idx * CELL_SIZE, column_idx * CELL_SIZE, CELL_SIZE, CELL_SIZE
|
|
)
|
|
column_idx += 1
|
|
row_idx += 1
|
|
|
|
screen.update()
|
|
|
|
# Generate the first iteration of the board
|
|
def _initialise_board(self):
|
|
for row in self._grid:
|
|
for cell in row:
|
|
if random() <= INITIAL_DENSITY:
|
|
cell.make_alive()
|
|
|
|
# Get the neighbour cells for a given cell
|
|
def get_neighbours(self, current_row, current_column):
|
|
# Cells either side of current cell
|
|
neighbour_min = -1
|
|
neighbour_max = 2
|
|
neighbours = []
|
|
|
|
for row in range(neighbour_min, neighbour_max):
|
|
for column in range(neighbour_min, neighbour_max):
|
|
neighbour_row = current_row + row
|
|
neighbour_column = current_column + column
|
|
# Don't count the current cell
|
|
if not (
|
|
neighbour_row == current_row and neighbour_column == current_column
|
|
):
|
|
# It's a toroidal world so go all the way round if necessary
|
|
if (neighbour_row) < 0:
|
|
neighbour_row = self._rows - 1
|
|
elif (neighbour_row) >= self._rows:
|
|
neighbour_row = 0
|
|
|
|
if (neighbour_column) < 0:
|
|
neighbour_column = self._columns - 1
|
|
elif (neighbour_column) >= self._columns:
|
|
neighbour_column = 0
|
|
|
|
neighbours.append(self._grid[neighbour_row][neighbour_column])
|
|
return neighbours
|
|
|
|
# Calculate the next generation
|
|
def create_next_generation(self):
|
|
to_alive = []
|
|
to_dead = []
|
|
changed = False
|
|
|
|
for row in range(len(self._grid)):
|
|
for column in range(len(self._grid[row])):
|
|
# Get all the neighours that are alive
|
|
alive_neighbours = []
|
|
for neighbour_cell in self.get_neighbours(row, column):
|
|
if neighbour_cell.is_alive():
|
|
alive_neighbours.append(neighbour_cell)
|
|
|
|
current_cell = self._grid[row][column]
|
|
# Apply the Conway GoL rules (B3/S23)
|
|
if current_cell.is_alive():
|
|
if len(alive_neighbours) < 2 or len(alive_neighbours) > 3:
|
|
to_dead.append(current_cell)
|
|
if len(alive_neighbours) == 3 or len(alive_neighbours) == 2:
|
|
to_alive.append(current_cell)
|
|
else:
|
|
if len(alive_neighbours) == 3:
|
|
to_alive.append(current_cell)
|
|
|
|
for cell in to_alive:
|
|
if not cell.is_alive():
|
|
# The board has changed since the previous generation
|
|
changed = True
|
|
cell.make_alive()
|
|
|
|
for cell in to_dead:
|
|
if cell.is_alive():
|
|
# The board has changed since the previous generation
|
|
changed = True
|
|
cell.make_dead()
|
|
|
|
return changed
|
|
|
|
|
|
# ------------------------------
|
|
# Main program loop
|
|
# ------------------------------
|
|
|
|
|
|
def main():
|
|
global restart
|
|
|
|
init_screen()
|
|
board = Board()
|
|
board.draw_board()
|
|
time.sleep(0.5)
|
|
|
|
while True:
|
|
# The 'a' button has been pressed so restart sim
|
|
if restart:
|
|
init_screen()
|
|
restart = False
|
|
board = Board()
|
|
board.draw_board()
|
|
time.sleep(0.5)
|
|
# The board didn't update since the previous generation
|
|
if not board.create_next_generation():
|
|
screen.update_speed(badger2040.UPDATE_NORMAL)
|
|
board.draw_board()
|
|
screen.update_speed(badger2040.UPDATE_TURBO)
|
|
time.sleep(5)
|
|
restart = True
|
|
# Draw the next generation
|
|
else:
|
|
board.draw_board()
|
|
|
|
|
|
main()
|