Object-oriented Game of Life in Python

Object-oriented Game of Life in Python

Intro

I have been planning to build a superior GUI alternative to NetLogo for a long time. When I finally started working on a project, I decided to test the basic implementations in Python. Sure enough, the first app was Conway’s Game of Life - a great special case of cellular automata.

Can you imagine my surprise that I haven’t found an object-oriented implementation of the game in Python — all codes were either written in Java or in imperative Python.

So, I decided to build the model. I used two classes — Person, which corresponded to each cell, and Game, which controlled the system dynamics. A great article by Giorgio Sironi also adviced to create a third class Generation, but I didn’t find it necessary.

Model

So, first, I imported three libraries that I used:

from math import ceil, floor, sqrt
import random
from matplotlib import pyplot as plt

Then I defined a Person class with (x,y)-coordinates and empty vector of alive-dead statuses for every time period. Every Person object contains neighbours method which returns adjacent cells and kill-resurrect methods to change the life status:

class Person:
	people = []

	def __init__(self,x,y,alive):
		self.x = x
		self.y = y
		self.alive = alive
		Person.people.append(self)
		return
	
	def kill(self,t):
		self.alive[t] = False
		return
	
	def resurrect(self,t):
		self.alive[t] = True
		return

	def neighbours(self):
		a = []
		people = Person.people
		a += [z for z in people if z.x == self.x and z.y == self.y+1]
		a += [z for z in people if z.x == self.x and z.y == self.y-1]
		a += [z for z in people if z.x == self.x-1 and z.y == self.y]
		a += [z for z in people if z.x == self.x-1 and z.y == self.y+1]
		a += [z for z in people if z.x == self.x-1 and z.y == self.y-1]
		a += [z for z in people if z.x == self.x+1 and z.y == self.y-1]
		a += [z for z in people if z.x == self.x+1 and z.y == self.y+1]
		a += [z for z in people if z.x == self.x+1 and z.y == self.y]
		return a

	def alive_neighbours(self,t):
		a = [z for z in self.neighbours() if z.alive[t]]
		return a

Then I defined a Game class with setup method which randomly draws initial stage, and stage method which executes system dynamics:

class Game:
	def __init__(self, n, m, t):
		self.n = n
		self.m = m
		self.t = t
		return
	
	def setup(self):
		for i in range(self.n):
			for j in range(self.m):
				a = [False]*self.t
				a[0] = random.choice([True, False])
				Person(i,j,a)
		return

	def stage(self, t):
		for person in Person.people:
			if person.alive[t-1]:
				if len(person.alive_neighbours(t-1))<2:
					person.kill(t)
				if len(person.alive_neighbours(t-1)) in [2,3]:
					person.resurrect(t)
				if len(person.alive_neighbours(t-1))>3:
					person.kill(t)
			else:
				if len(person.alive_neighbours(t-1))==3:
					person.resurrect(t)
		return

	def play(self):
		self.setup()
		for i in range(1,self.t):
			self.stage(i)
		return

	def results(self):
		people = Person.people
		a = [[z.x, z.y, z.alive] for z in people]
		print(a)
		return a

So, this was it. Now, you just have to input number of rows and columns (x,y) and number of time periods t and play the game. The resulting plots will be exported to your working directory:

# game:
a = Game(x,y,t)
a.play()
a.results()
# plot:
for j in range(t):
	b = []
	for i in range(x):
		b.append([z[2][j] for z in a.results() if z[0]==i])
	plt.spy(b)
	plt.savefig(f"time{j}.png")

For presentation purposes I animated the resulting graphs:

Game of life dynamics
"Game of life" dynamics

Conclusion

Here we go, I have built and shared the very basic object-oriented implementation of the very basic game in the most popular language, and somehow I ended up being the first one to do it. So, I will just leave it here as a starting point for future learners.

Be sure to check my other posts in ravshansk.com/articles and to follow me on Twitter @ravshansk.

Comments

comments powered by Disqus