Conway’s Game of Life

This program is a simulation of the famous cellular automaton created by John Conway. The so-called ‘game’ is rather a simulated evolution of cells in a two-dimensional world. Each cell can either be dead (black) or alive (white) and always has eight neighbours surrounding them. In each cycle of the simulation their state will be changed based on two simple rules, which are listed below.
Despite the rule’s simplicity, the outcomes are often very interesting and complex.
For more details on the game of life, check this Wikipedia entry.

The rules

  • A live cell can only survive with exactly 2 or 3 neighbours, or else it dies.
  • A dead cell with exactly 3 neighbours comes to life.

Controls

  • Left click turns cell at current mouse position alive
  • Right click turns cell at current mouse position dead
  • ‘r’ turns every cell dead
  • ‘q’ cycles once
  • ‘w’ cycles continuously

Moving ships JBlog! Stars


# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech 12th of February, 2016

# importing needed modules
import pygame, sys, time, math, os, random

""" GAME """
# game class
class GAME():
	# initialize program
	def __init__(self):
		self.scale = 10
		self.width, self.height = 108, 72
		self.size = [self.width, self.height]
		self.surface = pygame.Surface(self.size)
		self.field = pygame.Surface(self.size)

		self.scaledsize = [self.width * self.scale, self.height * self.scale]
		self.screen = pygame.display.set_mode(self.scaledsize)

		self.ticks = 0
		self.running = True

		self.cells = []
		#self.template = ["#"]
		#self.template = ["  #", "# #", " ##"]
		#self.template = ["#  # ", "	#", "#   #", " ####"]
		self.template = ["	#	", "	#	", "	#	", "		 ", "### # ###", "		 ", "	#	", "	#	", "	#	"]

		# functions
		pygame.display.set_caption("Conway's Game of Life")

		# load from captured image
		#"""
		img = pygame.image.load(os.getcwd() + "/img.png")
		rct = img.get_rect()
		for x in range(0, rct.right):
			for y in range(0, rct.bottom):
				if img.get_at([x, y]) == (255, 255, 255, 255):
					self.cells.append([x, y])
		#"""
	
	# cycle one iteration
	def cycle(self):
		# calculate, which positions need to be checked
		todo = []
		for cell in self.cells:
			x, y = cell[0], cell[1]
			for _ in [[x-1, y-1], [x, y-1], [x+1, y-1], [x-1, y], [x, y], [x+1, y], [x-1, y+1], [x, y+1], [x+1, y+1]]:
				if _ not in todo:
					todo.append(_)

		# new cell list
		newcells = []

		# calculate previously determined positions
		for cell in todo:
			# calculate cell's neighbours
			neighbours = 0
			x, y = cell[0], cell[1]
			for _ in [[x-1, y-1], [x, y-1], [x+1, y-1], [x-1, y], [x+1, y], [x-1, y+1], [x, y+1], [x+1, y+1]]:
				if _ in self.cells:
					neighbours += 1
			
			# cell is alive
			if cell in self.cells:
				if neighbours == 2 or neighbours == 3:
					# add cell
					if cell[0] >= 0 and cell[1] >= 0 and cell[0] < self.width and cell[1] < self.height:
						newcells.append(cell)
			
			# cell is dead
			else:
				if neighbours == 3:
					# add cell
					if cell[0] >= 0 and cell[1] >= 0 and cell[0] < self.width and cell[1] < self.height:
						newcells.append(cell)

		# update self.cells
		self.cells = newcells

	# tick function
	def tick(self):
		# handle events
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				self.quit()
			
			if event.type == pygame.KEYDOWN:
				# screenshot
				if event.key == pygame.K_SPACE:
					self.screenshot()
				
				# cycle once
				if event.key == pygame.K_q:
					self.cycle()

				# clear all cells
				if event.key == pygame.K_r:
					self.cells = []

				# make a gif
				if event.key == pygame.K_h:
					self.gif(False)

		# cycle continuously
		if pygame.key.get_pressed()[pygame.K_w] and self.ticks % 5 == 0:
			self.cycle()

		# make a gif
		if pygame.key.get_pressed()[pygame.K_g] and self.ticks % 5 == 0:
			self.gif()

		# get mouse presses
		pressed = pygame.mouse.get_pressed()
		# get mouse position
		mouse = list(pygame.mouse.get_pos())
		mouse[0] /= self.scale
		mouse[1] /= self.scale

		# add cells
		if pressed[0]:
			for y in range(0, len(self.template)):
				for x in range(0, len(self.template[y])):
					if self.template[y][x] == "#":
						pos = [mouse[0] + x, mouse[1] + y]
						if pos not in self.cells:
							self.cells.append(pos)
		
		# remove cells
		elif pressed[2]:
			for y in range(0, len(self.template)):
				for x in range(0, len(self.template[y])):
					if self.template[y][x] == "#":
						pos = [mouse[0] + x, mouse[1] + y]
						if pos in self.cells:
							self.cells.remove(pos)

	def gif(self, cycle = True):
		self.render()
		self.screenshot("gif")
		if cycle:
			self.cycle()

	# render function
	def render(self):
		# fill
		self.field.fill([0, 0, 0])

		# render cells
		for cell in self.cells:
			self.field.set_at(cell, [255, 255, 255])

		# fill and blit
		self.surface.fill([0, 0, 0])
		self.surface.blit(self.field, [0, 0])

		# render mouse position
		mouse = list(pygame.mouse.get_pos())
		mouse[0] /= self.scale
		mouse[1] /= self.scale
		for y in range(0, len(self.template)):
			for x in range(0, len(self.template[y])):
				if self.template[y][x] == "#":
					pos = [mouse[0] + x, mouse[1] + y]
					self.surface.set_at(pos, [50, 50, 50])

		# blit, scale and flip
		self.screen.blit(pygame.transform.scale(self.surface, self.scaledsize), [0, 0])
		pygame.display.flip()
	
	# quits
	def quit(self):
		self.running = False

	# takes a screenshot
	def screenshot(self, directory = "out"):
		path = os.getcwd() + "/" + directory + "/"
		
		try:
			if not os.path.isdir(path):
				os.mkdir(path)
			
			if directory == "gif":
				n = str(len(os.listdir(path)))
				while len(n) < 5:
					n = "0" + n
				name = "img" + n + ".png"
			else:
				name = "img" + str(len(os.listdir(path))) + ".png"
			
			s = pygame.transform.scale(self.field, self.scaledsize)
			pygame.image.save(s, path + name)
		except:
			pass

	# run function (uses tick() and render())
	def run(self):
		ticksPerSecond = 60
		lastTime = time.time() * 1000000000
		nsPerTick =  1000000000.0 / float(ticksPerSecond)
	
		ticks = 0
		frames = 0
	
		lastTimer = time.time() * 1000
		delta = 0.0
		
		while self.running:
			now = time.time() * 1000000000
			delta += float(now - lastTime) / float(nsPerTick)
			lastTime = now
			shouldRender = False
					
			while delta >= 1:
				ticks += 1
				self.ticks += 1
				self.tick()
				delta -= 1
				shouldRender = True
			
			if shouldRender:
				frames += 1
				self.render()
			
			if time.time() * 1000 - lastTimer >= 1000:
				lastTimer += 1000
				
				# debug
				#print("Frames: " + str(frames) + ", ticks: " + str(ticks))
			
				frames = 0
				ticks = 0

# start game
G = GAME()
G.run()
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s