#!/usr/bin/env python # life.py ''' Conway's Game of Life ''' import random import itertools import pygame # Set Up pygame.init() colors = pygame.color.THECOLORS ON, OFF = 1, 0 # The Plan: # Put the cells in a dict from {relative location : cell} # That way neighbors can be looked-up easily in a flat list. # # # Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~! def randcolor (lower=40, upper=200): cvals = range(lower, upper) r, g, b = [random.choice(cvals) for i in range(3)] return pygame.Color(r, g, b) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~! class Cell (object): def __init__ (self, **kwargs): # defaults self.size = (25, 25) self.alpha = 90 self.oncolor = colors['green'] self.offcolor = colors['black'] self.state = OFF # absolute topleft pixel position. self.position = None # unpack kwargs for k in kwargs: setattr(self, k, kwargs[k]) self._init_default_config() @property def width (self): return self.size[0] @property def height (self): return self.size[1] def _init_default_config (self): self.image = pygame.Surface(self.size) self.image.set_alpha(0) self.rect = self.image.get_rect() if self.state: self.image.fill(self.oncolor) else: self.image.fill(self.offcolor) if hasattr(self, 'position'): self.rect.topleft = self.position def update (self): if self.state: self.image.fill(self.oncolor) self.image.set_alpha(self.alpha) else: self.image.set_alpha(90) self.image.fill(self.offcolor) def __repr__ (self): return "" #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~! class Grid (object): def __init__ (self, gridsize, cellsize): self.cell_w, self.cell_h = cellsize # cell w, h self.w, self.h = gridsize # grid w, h self.n = self.w * self.h # n cells self.cells = [] # a 2D array # Initial Configuration self._do_init_config() def _do_init_config (self): absx = absy = 0 for i in xrange(self.h): row = [] for j in xrange(self.w): if i % random.randint(2,8) == 0: initstate = random.choice([ON,OFF]) else: initstate = OFF nhood = self._get_neighbors(i, j) cell = Cell( nhood = [n for n in nhood], size=(self.cell_w, self.cell_h), position=(absx, absy), state=initstate, past=initstate ) row.append(cell) absx += cell.width self.cells.append(row) absy += cell.height absx = 0 def update (self): newcolor = randcolor() for row in self.cells: for cell in row: cell.past = cell.state # stores previous state cell.update() # sets cell state color states = [1 for (i,j) in cell.nhood if self.cells[i][j].past] score = sum( states ) # set new cell state if score < 4 and score > 1 and cell.past: cell.state = ON elif score == 3 and not cell.past: cell.state = ON cell.oncolor = newcolor else: cell.state = OFF # draw to screen screen.blit(cell.image, cell.position) def _get_neighbors (self, x, y): for point in itertools.product(range(-1,2), range(-1,2)): _x, _y = point[0]+x, point[1]+y if (x, y) == (_x, _y): continue _x %= self.w _y %= self.h yield (_x, _y) def __getitem__ (self, i): i, j = divmod(i, self.w) return self.cells[i][j] # ///////////////////////! if __name__ == '__main__': grid = Grid((60, 60), (8, 8)) screen = pygame.display.set_mode((grid.w*grid.cell_w, grid.h*grid.cell_h)) clock = pygame.time.Clock() running = True while running: clock.tick(36) for e in pygame.event.get(): if hasattr(e, 'pos') and e.type == pygame.MOUSEBUTTONDOWN: for cell in grid: # OH MY GOD if e.pos[0] > cell.rect.left and \ e.pos[0] < cell.rect.right and \ e.pos[1] > cell.rect.top and \ e.pos[1] < cell.rect.bottom: for pos in cell.nhood: grid.cells[pos[0]][pos[1]].state = ON if not hasattr(e, 'key'): continue if e.key == pygame.K_q or e.key == pygame.K_ESCAPE: running = False elif e.key == pygame.K_UP: for cell in grid: if cell.alpha + 10 <= 255: cell.alpha += 10 elif e.key == pygame.K_DOWN: for cell in grid: if cell.alpha - 10 >= 20: cell.alpha -= 10 grid.update() pygame.display.flip()