-
Notifications
You must be signed in to change notification settings - Fork 0
/
convolve.py
165 lines (139 loc) · 6.42 KB
/
convolve.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# -*- coding: utf-8 -*-
"""
The Game of Life (GoL) module named in honour of John Conway
This module defines the classes required for the GoL simulation.
Created on Tue Jan 15 12:21:17 2019
@author: shakes
"""
import numpy as np
from scipy import signal
import rle
class GameOfLife:
'''
Object for computing Conway's Game of Life (GoL) cellular machine/automata
'''
def __init__(self, N=256, finite=False, fastMode=False):
self.grid = np.zeros((N,N), np.int)
self.neighborhood = np.ones((3,3), np.int) # 8 connected kernel
self.neighborhood[1,1] = 0 #do not count centre pixel
self.finite = finite
self.fastMode = fastMode
self.aliveValue = 1
self.deadValue = 0
def getStates(self):
'''
Returns the current states of the cells
'''
return self.grid
def getGrid(self):
'''
Same as getStates()
'''
return self.getStates()
def evolve(self):
'''
Given the current states of the cells, apply the GoL rules:
- Any live cell with fewer than two live neighbors dies, as if by underpopulation.
- Any live cell with two or three live neighbors lives on to the next generation.
- Any live cell with more than three live neighbors dies, as if by overpopulation.
- Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction
'''
# get weighted sum of neighbours
grid = self.getGrid()
rows, cols = grid.shape
weightingGrid = signal.convolve(grid, self.neighborhood, mode="same")
# implement the GoL rules by thresholding the weights
evolvedGrid = (
((grid == 1) & (weightingGrid > 1) & (weightingGrid < 4)) # survival
| ((grid == 0) & (weightingGrid == 3)) # reproduction
).astype(np.int)
# update the grid
self.grid = evolvedGrid
def insertBlinker(self, index=(0,0)):
'''
Insert a blinker oscillator construct at the index position
'''
self.grid[index[0], index[1]+1] = self.aliveValue
self.grid[index[0]+1, index[1]+1] = self.aliveValue
self.grid[index[0]+2, index[1]+1] = self.aliveValue
def insertGlider(self, index=(0,0)):
'''
Insert a glider construct at the index position
'''
self.grid[index[0], index[1]+1] = self.aliveValue
self.grid[index[0]+1, index[1]+2] = self.aliveValue
self.grid[index[0]+2, index[1]] = self.aliveValue
self.grid[index[0]+2, index[1]+1] = self.aliveValue
self.grid[index[0]+2, index[1]+2] = self.aliveValue
def insertGliderGun(self, index=(0,0)):
'''
Insert a glider construct at the index position
'''
self.grid[index[0]+1, index[1]+25] = self.aliveValue
self.grid[index[0]+2, index[1]+23] = self.aliveValue
self.grid[index[0]+2, index[1]+25] = self.aliveValue
self.grid[index[0]+3, index[1]+13] = self.aliveValue
self.grid[index[0]+3, index[1]+14] = self.aliveValue
self.grid[index[0]+3, index[1]+21] = self.aliveValue
self.grid[index[0]+3, index[1]+22] = self.aliveValue
self.grid[index[0]+3, index[1]+35] = self.aliveValue
self.grid[index[0]+3, index[1]+36] = self.aliveValue
self.grid[index[0]+4, index[1]+12] = self.aliveValue
self.grid[index[0]+4, index[1]+16] = self.aliveValue
self.grid[index[0]+4, index[1]+21] = self.aliveValue
self.grid[index[0]+4, index[1]+22] = self.aliveValue
self.grid[index[0]+4, index[1]+35] = self.aliveValue
self.grid[index[0]+4, index[1]+36] = self.aliveValue
self.grid[index[0]+5, index[1]+1] = self.aliveValue
self.grid[index[0]+5, index[1]+2] = self.aliveValue
self.grid[index[0]+5, index[1]+11] = self.aliveValue
self.grid[index[0]+5, index[1]+17] = self.aliveValue
self.grid[index[0]+5, index[1]+21] = self.aliveValue
self.grid[index[0]+5, index[1]+22] = self.aliveValue
self.grid[index[0]+6, index[1]+1] = self.aliveValue
self.grid[index[0]+6, index[1]+2] = self.aliveValue
self.grid[index[0]+6, index[1]+11] = self.aliveValue
self.grid[index[0]+6, index[1]+15] = self.aliveValue
self.grid[index[0]+6, index[1]+17] = self.aliveValue
#self.grid[index[0]+6, index[1]+17] = self.aliveValue incorrect line
self.grid[index[0]+6, index[1]+18] = self.aliveValue # correct line
self.grid[index[0]+6, index[1]+23] = self.aliveValue
self.grid[index[0]+6, index[1]+25] = self.aliveValue
self.grid[index[0]+7, index[1]+11] = self.aliveValue
self.grid[index[0]+7, index[1]+17] = self.aliveValue
self.grid[index[0]+7, index[1]+25] = self.aliveValue
self.grid[index[0]+8, index[1]+12] = self.aliveValue
self.grid[index[0]+8, index[1]+16] = self.aliveValue
self.grid[index[0]+9, index[1]+13] = self.aliveValue
self.grid[index[0]+9, index[1]+14] = self.aliveValue
def insertFromPlainText(self, txtString, pad=0):
'''
Assumes txtString contains the entire pattern as a human readable pattern without comments
'''
# skip comments
while txtString[0] == "!":
txtString = txtString[txtString.find("\n") + 1:]
width = txtString.find("\n") + 1 # +1 for '\n'
height = (int) (len(txtString) / (width)) + 1
grid = np.zeros((height, width - 1), np.int) # -1 to exclude '\n'
for index, char in enumerate(txtString):
if char == "O":
row = index // width
col = index % width
grid[row][col] = 1
self.grid = grid
def insertFromRLE(self, rleString, pad=0):
'''
Given string loaded from RLE file, populate the game grid
'''
parser = rle.RunLengthEncodedParser(rleString)
charGrid = parser.pattern_2d_array
shape = (len(charGrid), len(charGrid[0]))
grid = np.zeros(shape, int)
for row in range(parser.size_y):
for col in range(parser.size_x):
if charGrid[row][col] == 'b':
grid[row][col] = 0
elif charGrid[row][col] == 'o':
grid[row][col] = 1
self.grid = grid