Skip to content

Instantly share code, notes, and snippets.

@abhayraw1
Last active March 16, 2018 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abhayraw1/f2a45cfcb0179f5bd4da7e6520c3be07 to your computer and use it in GitHub Desktop.
Save abhayraw1/f2a45cfcb0179f5bd4da7e6520c3be07 to your computer and use it in GitHub Desktop.
Conway's Game of Life Python 3 implementation.

Conway's Game of Life

Running the script
python3 game_of_life.py game_of_life.json
Running different configurations

There area number of different starting configurations to choose from in the configuration file: game_of_life.json You can specify any starting configurations given in the file by changing the value of the "Preset" key

"Preset": "Blinker" /*Blinker preset*/

You can choose from the various specified presets given below:

Glider
Blinker
Toad
Pulsar
GosperGun
Diehard
Boat
Beacon
Acorn
Spaceship

Parameters in configuration file:

  • Rows: number of row in the grid
  • Columns: number of row in the grid
  • TimeStep: time in seconds between two iterations of the game.
  • Preset: initial configuration of the game. Presets can be chosen from the various configurations given in the file itself.
  • CellDimension: dimension of the square cell.
  • BackgroundColor: color of background (denotes dead cells). Given as hex codes
  • CellColor: color of live cells. Given as hex codes

Note: The script depends on tkinter for GUI

{
"Game": {
"Rows": 60,
"Columns": 80,
"TimeStep": 0.05,
"Preset": "Blinker"
},
"GUI": {
"CellDimension": 10,
"BackgroundColor": "#535c68",
"CellColor": "#eb4d4b"
},
"Presets": {
"Glider" : [[1,0,0],[0,1,1],[1,1,0]],
"Blinker" : [[1,1,1]],
"Toad" : [[1, 1, 1, 0],[0, 1, 1, 1]],
"Pulsar" : [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
"GosperGun" : [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]],
"Diehard" : [[0, 0, 0, 0, 0, 0, 1, 0],
[1, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 1, 1]],
"Boat" : [[1, 1, 0],
[1, 0, 1],
[0, 1, 0]],
"Beacon" : [[0, 0, 1, 1],
[0, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 0]],
"Acorn" : [[0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
[1, 1, 0, 0, 1, 1, 1]],
"Spaceship" : [[0, 0, 1, 1, 0],
[1, 1, 0, 1, 1],
[1, 1, 1, 1, 0],
[0, 1, 1, 0, 0]]
}
}
# for Python 2 just uncomment the line below
# from __future__ import print_function
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import numpy as np
import _thread
import time
import json
import sys
class GUI:
def __init__(self, num_rows, num_cols, px_per_box, bg_color, cell_color, line_wid=0):
self.root = Tk()
self.steps = 0
self.line_wid = line_wid
self.px_per_box = px_per_box
self.bg_color = bg_color
self.cell_color = cell_color
self.to_update = True
self.boxes = np.zeros((num_rows, num_cols), dtype=object)
self.init_dimensions(num_rows, num_cols)
self.init_canvas()
self.create_grid()
self.create_rectangles()
self.init_info()
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.root.title("Conway's Game of life")
def on_closing(self):
self.to_update = False
if messagebox.askokcancel("Quit", "Do you want to quit?"):
self.root.destroy()
else:
self.to_update = True
def init_info(self):
self.info = Label(self.root, text="", height=2, width=self.num_cols)
self.info.pack(side=BOTTOM)
def init_dimensions(self, num_rows, num_cols):
self.num_rows = num_rows
self.num_cols = num_cols
self.width = self.num_cols*self.px_per_box + (self.num_cols + 1)*self.line_wid
self.height = self.num_rows*self.px_per_box + (self.num_rows + 1)*self.line_wid
def init_canvas(self):
self.canvas = Canvas(self.root, width=self.width, height=self.height)
self.canvas.pack()
def create_grid(self):
for i in range(self.num_cols+1):
col = (self.line_wid + self.px_per_box)*i + self.line_wid/2
self.canvas.create_line(col, 0, col, self.height, width=self.line_wid)
for i in range(self.num_rows+1):
row = (self.line_wid + self.px_per_box)*i + self.line_wid/2
self.canvas.create_line(0, row, self.width, row, width=self.line_wid)
def create_rectangles(self):
for i in range(self.num_rows):
for j in range(self.num_cols):
y1 = i*self.px_per_box + (i + 1)*self.line_wid
x1 = j*self.px_per_box + (j + 1)*self.line_wid
self.boxes[i,j] = self.canvas.create_rectangle(x1, y1, \
x1+self.px_per_box, y1+self.px_per_box, fill=self.bg_color)
def update(self, config):
self.steps += 1
self.update_canvas(config)
self.update_info(config)
if self.to_update:
self.root.update()
def make_info(self, config):
population = len(np.nonzero(config)[0])
str = "Population:\t{}\nTime Steps:\t{}"
str = str.format(population, self.steps)
return str
def update_info(self, config):
info = self.make_info(config)
self.info.configure(text=info)
def update_canvas(self, config):
for i in range(self.num_rows):
for j in range(self.num_cols):
color = self.bg_color
if config[i,j]:
color = self.cell_color
self.canvas.itemconfig(self.boxes[i,j], fill=color)
class Game:
def __init__(self, num_rows, num_cols, config):
self.num_rows = num_rows
self.num_cols = num_cols
self.init_config()
self.load_config(config)
def init_config(self):
self.next_config = np.zeros((self.num_rows, self.num_cols), dtype=bool)
self.prev_config = np.zeros((self.num_rows, self.num_cols), dtype=bool)
def load_config(self, config):
init_state = config
st_r = (self.prev_config.shape[0] - init_state.shape[0])//2
en_r = (self.prev_config.shape[0] + init_state.shape[0])//2
st_c = (self.prev_config.shape[1] - init_state.shape[1])//2
en_c = (self.prev_config.shape[1] + init_state.shape[1])//2
self.prev_config[st_r:en_r, st_c: en_c] = init_state
def get_neighbours(self, state):
rolls = [np.roll(np.roll(state,i,0),j,1) \
for i in [-1,0,1] for j in [-1,0,1] if i!=0 or j!=0]
return sum(rolls)
def gen_next_state(self):
num_nbrs = self.get_neighbours(self.prev_config)
statis = self.prev_config*(num_nbrs > 1)*(num_nbrs < 4)
newgen = np.logical_not(self.prev_config)*(num_nbrs > 2)*(num_nbrs < 4)
self.next_config = statis + newgen
def step(self):
self.gen_next_state()
self.prev_config[:,:] = self.next_config
def get_config(file):
f = open(file, 'r')
try:
return json.loads(f.read())
except:
print('Exception in JSON file')
return
def load_preset(config, preset):
try:
init_state = config['Presets'][preset]
return np.array(init_state)
except KeyError as e:
print("No Preset {} in config file.".format(e), file=sys.stderr)
sys.exit(-1)
if __name__ == '__main__':
config_file = sys.argv[1]
config = get_config(config_file)
if isinstance(config, type(None)):
sys.exit(-1)
num_rows = config['Game']['Rows']
num_cols = config['Game']['Columns']
time_step = config['Game']['TimeStep']
preset = config['Game']['Preset']
px_per_box = config['GUI']['CellDimension']
bg_color = config['GUI']['BackgroundColor']
cell_color = config['GUI']['CellColor']
init_state = load_preset(config, preset)
if np.any(np.array(init_state.shape) > np.array([num_rows, num_cols])):
print("Preset {} shape {} is greater than grid {}"\
.format(preset, init_state.shape,(num_rows, num_cols)))
sys.exit(-1)
game = Game(num_rows, num_cols, init_state)
gui = GUI(num_rows, num_cols, px_per_box, bg_color, cell_color)
gui.update(game.prev_config)
while gui.to_update:
time.sleep(time_step)
game.step()
gui.update(game.prev_config)
@abhayraw1
Copy link
Author

For python 2 just uncomment line #2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment