Paste: game of life v3

Author: Daniel Gabriele
Mode: python
Date: Sun, 7 Jun 2009 01:58:14
Plain Text |
#!/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 {cell : neighbor vectors}
# 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)

def randgreen (lower=90, upper=250):
	cvals = range(lower, upper)
	g = random.choice(cvals)
	return pygame.Color(g-lower, g, 0)

def randred (lower=90, upper=250):
	cvals = range(lower, upper)
	r = random.choice(cvals)
	return pygame.Color(r, r-lower, 0)


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!
class Cell (object):
	
	
	def __init__ (self, **kwargs):
		
		# defaults
		self.size = (25, 25)
		self.alpha = 90	
		self.oncolor = randgreen()
		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 scale_alpha(self, inc):
		next_step = self.alpha + inc
		if next_step < 255 and next_step > 0:
			self.alpha = next_step

	
	def __repr__ (self):
		return "<cell>"


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!	
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, surface):
		newcolor = randgreen()
		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.oncolor = randred()
					cell.state = ON
				elif score == 3 and not cell.past:
					cell.state = ON
					cell.oncolor = newcolor
				else:
					cell.state = OFF
				
				# draw to screen
				surface.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]



#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~!	
class Automaton (object):
	
	
	def __init__ (self, grid_size, cell_size):
		self.grid = Grid(grid_size, cell_size)
		self.grid_size = grid_size
		self.cell_size = cell_size
		self.clock = pygame.time.Clock()
		self.running = False
		
		
	def seed (self):
		self.scrsize = [self.grid_size[i] * self.cell_size[i] for i in [0,1]] 
		self.screen = pygame.display.set_mode(self.scrsize)
		self.running = True
		
		while self.running:
			self.clock.tick(36)
			for e in pygame.event.get():
				if hasattr(e, 'pos') and e.type == pygame.MOUSEBUTTONDOWN:
					self._mouse_handler(e)
				elif hasattr(e, 'key') and e.type == pygame.KEYDOWN: 
					self._key_handler(e)
				
			self.grid.update(self.screen)		
			pygame.display.flip()
		
	def _mouse_handler (self, e):
		for cell in self.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:
					self.grid.cells[pos[0]][pos[1]].state = ON
	
	def _key_handler (self, e):
		if e.key == pygame.K_q or e.key == pygame.K_ESCAPE: 
			self.running = False
		elif e.key == pygame.K_UP:
			for cell in self.grid:
				cell.scale_alpha(10)
		elif e.key == pygame.K_DOWN:
			for cell in self.grid:
				cell.scale_alpha(-10)




# ///////////////////////!
if __name__ == '__main__':
	grid_size = (60, 60)  # grid and cell sizes must be squares
	cell_size = (8, 8)
	automaton = Automaton( grid_size, cell_size )
	automaton.seed()
	

New Annotation

Summary:
Author:
Mode:
Body: