Conway’s Game of Life (pygame version)
Example script (scamp_extensions): examples/Composition & form/Algorithmic approaches/conway.py
Download: conway.py
Requires the scamp_extensions package (pip install scamp_extensions).
Conway’s Game of Life sonified, with a pygame window used for visualization instead of matplotlib. Original by Raphael Radna; visualization ported to pygame.
Topics: Interactivity & visualization › Visualization, Composition & form › Algorithmic approaches
"""
SCAMP Example: Conway's Game of Life (pygame version)
Conway's Game of Life sonified, with a pygame window used for visualization
instead of matplotlib. Original by Raphael Radna; visualization ported to
pygame.
"""
import numpy
import pygame
from scamp import *
from scamp_extensions.pitch import Scale
import math
scale = Scale.melodic_minor(59)
WIDTH = 24
HEIGHT = 24
CELL_SIZE = 24 # pixels per cell in the pygame window
FPS = 20
ALIVE_COLOR = (255, 255, 255)
DEAD_COLOR = (0, 0, 0)
BG_COLOR = (0, 0, 0)
s = Session()
scamp1 = s.new_part("piano")
def bark_to_hz(bark): # Traunmüller formula
return 1960 / (26.81 / (bark + 0.53) - 1)
def ftom(hz, base=440):
return 12 * math.log(hz / base) / math.log(2) + 69
def init_grid(x, y):
return numpy.random.choice([0, 1], (x, y))
def wrap(val, lo, hi):
if val < lo:
val += hi
if val >= hi:
val -= hi
return val
def get_cell(a, x, y, dx, dy):
return a[wrap((x + dx), 0, WIDTH), wrap((y + dy), 0, HEIGHT)]
def sum_neighbors(a, x, y):
running_sum = 0
for i in range(3):
for j in range(3):
if i == 1 and j == 1:
continue
running_sum += get_cell(a, x, y, i - 1, j - 1)
return running_sum
def apply_rules(a, b, x, y):
state = a[x, y]
neighbor_count = sum_neighbors(a, x, y)
if state == 1 and (neighbor_count < 2 or neighbor_count > 3):
b[x, y] = 0
if state == 0 and neighbor_count == 3:
b[x, y] = 1
current_grid = init_grid(WIDTH, HEIGHT)
next_grid = numpy.array(current_grid)
note_grid = numpy.zeros((WIDTH, HEIGHT), dtype=object)
def grid_play(a, x, y):
pan = y / HEIGHT
pitch = ftom(bark_to_hz((x / WIDTH) * 20 + pan))
cell_state = a[x, y]
note_state = note_grid[x, y]
if cell_state == 1 and note_state == 0:
note_grid[x, y] = scamp1.start_note(scale.round(pitch), 0.125, "param_10:{}".format(pan))
if cell_state == 0 and note_state != 0:
note_grid[x, y].end()
note_grid[x, y] = 0
def update_grid():
global current_grid
global next_grid
for x in range(WIDTH):
for y in range(HEIGHT):
grid_play(current_grid, x, y)
apply_rules(current_grid, next_grid, x, y)
current_grid[:, :] = next_grid[:, :]
def randomize_grid():
global current_grid
global next_grid
# stop any currently sounding notes before scrambling the grid
for x in range(WIDTH):
for y in range(HEIGHT):
if note_grid[x, y] != 0:
note_grid[x, y].end()
note_grid[x, y] = 0
current_grid = init_grid(WIDTH, HEIGHT)
next_grid = numpy.array(current_grid)
def draw_grid(screen):
screen.fill(BG_COLOR)
for x in range(WIDTH):
for y in range(HEIGHT):
color = ALIVE_COLOR if current_grid[x, y] == 1 else DEAD_COLOR
rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pygame.draw.rect(screen, color, rect)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH * CELL_SIZE, HEIGHT * CELL_SIZE))
pygame.display.set_caption("Conway's Game of Life (SCAMP)")
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
randomize_grid()
update_grid()
draw_grid(screen)
pygame.display.flip()
clock.tick(FPS)
# Clean up any still-sounding notes on exit
for x in range(WIDTH):
for y in range(HEIGHT):
if note_grid[x, y] != 0:
note_grid[x, y].end()
pygame.quit()
if __name__ == "__main__":
main()