:orphan: Conway's Game of Life (pygame version) ====================================== *Example script* (scamp_extensions): `examples/Composition & form/Algorithmic approaches/conway.py `__ *Download:* :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:* :doc:`Interactivity & visualization › Visualization `, :doc:`Composition & form › Algorithmic approaches ` .. raw:: html .. code-block:: python """ 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()