Minimalistic Game of Life simulator in Python

For scripts to aid with computation or simulation in cellular automata.
Gustavo6046
Posts: 646
Joined: December 7th, 2013, 6:26 pm
Location: South of Brazil.

Minimalistic Game of Life simulator in Python

Hello pals, I'm back! I don't remember how far have I gone in this community, but I have been doing a minimalistic, extendable and quick Game of Life simulator, in Python.

Code:

Code: Select all

``````import re
import os
import time
import sys
import operator

default_rle = """
x = 5, y = -2
24bo11b\$22bobo11b\$12b2o6b2o12b2o\$11bo3bo4b2o12b2o\$2o8bo5bo3b2o14b\$2o8b
o3bob2o4bobo11b\$10bo5bo7bo11b\$11bo3bo20b\$12b2o!
"""

born = re.compile(r"^b(\d+)")
surv = re.compile(r"^s(\d+)")

neighbors = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, 1),
(1, 1),
(1, 0),
(1, -1),
(0, -1),
]

def repeat(i, n):
return [i[a:a+n] for a in xrange(0, len(i), n)]

def rle_decode(text):
return re.sub(r'(\d+)([^\d])', lambda m: m.group(2) * int(m.group(1)), text)

def encode(text):
return re.sub(r'(.)\1*', lambda m: str(len(m.group(0))) + m.group(1), text)

class GolTable(object):
rle_header = re.compile(r"^x\s*=\s*(\d+),?\s*y\s*=\s*(\d+)")

def __init__(self, rule="b3/s23", char=[".", "+", "@", "P", "~", "#"]): # LifeHistory-ready charmap.
self.coords = {}
self.bounds = [None, None, None, None]  # top, left, bottom, right
self.max_bounds = self.bounds

# Display setup
self.char = char
self.no_char = char[0]
self.unknown_char = "?"

self.rulestring = rule

if type(rule) is str:
b = set()
s = set()

rset = rule.split("/")

for a in rset:
if born.match(a) is not None:
for x in a[1:]:
b.add(int(x))

elif surv.match(a) is not None:
for x in a[1:]:
s.add(int(x))

self.rules = {
"born": b,
"surv": s,
}

else:
self.rules = dict(rules)

def set_cell(self, x, y, state):
if state == 0:
if (x, y) in self.coords:
del self.coords[(x, y)]
self.get_bounds()

return True

return False

else:
if (x, y) in self.coords and self.coords[(x, y)] == state:
return False

self.coords[(x, y)] = state

self.get_bounds()

return True

def get_cell(self, x, y):
if (x, y) in self.coords:
return self.coords[(x, y)]

return 0

def update_bounds(self, top, left, bottom, right):
self.bounds = [top, left, bottom, right]

def _bound(self, c):
x = c[0]
y = c[1]

if self.bounds[1] is None or x <= self.bounds[1]:
self.bounds[1] = x

if self.bounds[3] is None or x > self.bounds[3]:
self.bounds[3] = x

if self.bounds[0] is None or y <= self.bounds[0]:
self.bounds[0] = y

if self.bounds[2] is None or y > self.bounds[2]:
self.bounds[2] = y

def _max_bound(self, c):
x = c[0]
y = c[1]

if self.max_bounds[1] is None or x <= self.max_bounds[1]:
self.max_bounds[1] = x

if self.max_bounds[3] is None or x > self.max_bounds[3]:
self.max_bounds[3] = x

if self.max_bounds[0] is None or y <= self.max_bounds[0]:
self.max_bounds[0] = y

if self.max_bounds[2] is None or y > self.max_bounds[2]:
self.max_bounds[2] = y

def get_bounds(self, bias=0):
self.bounds = [None, None, None, None]

for c, s in self.coords.items():
if s > bias:
self._bound(c)
self._max_bound(c)

def copy(self, other, rules=False):
other.bounds = self.bounds
other.coords = self.coords

if rules:
other.rules = self.rules

def step(self, b=None):
if self.population == 0:
return False

if not b:
b = self.bounds

self.get_bounds()
other = GolTable()

for y in xrange(b[0] - 1, b[2] + 2):
for x in xrange(b[1] - 1, b[3] + 2):
self._step_cell(x, y, other)

other.copy(self)
del other

return True

def neighbors(self, x, y):
r = 0

surrounding = [map(operator.add, (x, y), nc) for nc in neighbors]
assert len(surrounding) == 8

for s in surrounding:
r += self.get_cell(*s)

return r

def _step_cell(self, x, y, other=None):
n = self.neighbors(x, y)

if not other:
other = self

if n in self.rules["born"] and self.get_cell(x, y) == 0:
if type(self.rules["born"]) in (set, list, tuple):
other.set_cell(x, y, 1)

else:
other.set_cell(x, y, self.rules["born"][n])

elif n in self.rules["surv"] and self.get_cell(x, y) == 1:
other.set_cell(x, y, 1)

def to_rle(self):
out = ""

for y in xrange(b[0], b[2] + 1):
for x in xrange(b[1], b[3] + 1):
out += self.char[self.get_coords[(x, y)]]

out += "\n"

return rle_encode(out)

@classmethod
def from_rle(cls, rle, rule="b3/s23", char=[".", "+", "@", "P", "~", ";"]):
inp = rle_decode(rle)

new = cls(rule, char)

x = 0
y = 0

for c in inp:
if c == "\n":
y += 1
continue

new.set_cell(x, y, char.index(c))

x += 1

return new

@classmethod
def from_rle_standard(cls, rle, rule="b3/s23", char=[".", "+", "@", "P", "~", ";"]):
rle = "\n".join(l for l in rle.splitlines() if not l.startswith("#") and l not in ("", " "))
root = [0, 0]

lines = rle.splitlines()
m = cls.rle_header.match(lines[0])

if m is not None:
rle = "\n".join(lines[1:])
root = [int(x) for x in m.groups()]

inp = rle_decode(rle)

new = cls(rule, char)

x = root[0]
y = root[1]

for c in inp:
if c == "!":
return new

if c == "\$":
y += 1
x = root[0]
continue

if c not in ("o, b"):
continue

new.set_cell(x, y, (1 if c == "o" else 0))

if c in ("o", "b"):
x += 1

return new

def display(self, b=None):
disp = ""

self.get_bounds()

if not b:
b = self.bounds

for y in xrange(b[0], b[2] + 1):
for x in xrange(b[1], b[3] + 1):
if (x, y) in self.coords:
try:
disp += self.char[self.coords[(x, y)]]

except IndexError:
disp += self.unknown_char

else:
disp += self.no_char

disp += "\n"

return disp

def _print(self, b=None):
print self.display(b)

def population(self, bias=0):
return len([x for x in self.coords.values() if x > bias])

if __name__ == "__main__": # test
try:
t = GolTable.from_rle_standard(open(sys.argv[1]).read())

except IndexError:
t = GolTable.from_rle_standard(default_rle)

print_time = 1
overhead = 1

while True:
overhead += 1
if overhead % print_time == 0:
t._print(t.max_bounds)

try:
t.step()

except ValueError:
print "End of simulation!"

time.sleep(.001)

if overhead % print_time == 0:
os.system("cls")
``````
Instructions:
a) Paste into a file e.g. main.py
b) Make sure you have Python 2.7 (tested-with version) installed! (report if it works with lower versions)
c) Get your command line, `cd` to the folder you put the file with the code and do:

Code: Select all

``````python <filename>.py <rle filename>.rle
``````
It should run the RLE in the command-line, in a Golly-like environment.

You can also import the file and use it as a Game of Life library!

I have recorded a short video, available here.
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times.

Billabob
Posts: 146
Joined: April 2nd, 2015, 5:28 pm

Re: Minimalistic Game of Life simulator in Python

Hunting wrote:Wow!
Please don’t post single-word replies on threads that have been dead for over a year. It needlessly clutters up the forums.
▄▀
▀▀▀

Gustavo6046
Posts: 646
Joined: December 7th, 2013, 6:26 pm
Location: South of Brazil.

Re: Minimalistic Game of Life simulator in Python

I checked my Gmail, and surprise surprise! I believe I'll just change my avatar and other things. I want to look decent in here.

On topic, I believe I'll remake the CGOL player.
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times.

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

Billabob wrote:
Hunting wrote:Wow!
Please don’t post single-word replies on threads that have been dead for over a year. It needlessly clutters up the forums.
Okay... I just want to check if Gustavo is alive or something. I'll delete that.
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

Gustavo6046 wrote:I checked my Gmail, and surprise surprise! I believe I'll just change my avatar and other things. I want to look decent in here.

On topic, I believe I'll remake the CGOL player.
Hi, Gustavo! There's a bunch of MINIMALISTIC GoL simulator (not in Python) on the Internet. I'll show you one: GoL simulator in JS1K. (That simulator didn't use your RLE copy/paste init-state input method. In that simulator, you can see a big grid just like Golly, but without many options.)
http://js1k.com/2012-love/demo/1111
(Your script looks awesome, but my Python level is like a beginner.)
By the way, I will PM(Private Message) you about GoL tech.
(You are in South of Brazil?)
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife

77topaz
Posts: 1497
Joined: January 12th, 2018, 9:19 pm

Re: Minimalistic Game of Life simulator in Python

Hunting wrote:Okay... I just want to check if Gustavo is alive or something. I'll delete that.
Replacing the contents of the post with the word "DELETED" doesn't help anyone. Just don't do this again in the future...

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

77topaz wrote:
Hunting wrote:Okay... I just want to check if Gustavo is alive or something. I'll delete that.
Replacing the contents of the post with the word "DELETED" doesn't help anyone. Just don't do this again in the future...
Okay.
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife

Gustavo6046
Posts: 646
Joined: December 7th, 2013, 6:26 pm
Location: South of Brazil.

Re: Minimalistic Game of Life simulator in Python

(a) I've gathered a LOT of Python knowledge since then, and I may guarantee you, that is bulky and inefficient. I can do a much faster method (considering I don't usually use NumPy). I will create a Golly-independent Python script (probably with wxWidgets) that will search for combinations of input patterns Aₓ that generate the desired result pattern B. (with multiple configurations for position ranges, etc). I'm already working on it.

This will probably use a brute force method, won't be as fast as an actual CGoL algorithm (especially, it will be MUCH slower than HashLife), one of the reasons being it will be in Python.

(b) Yes, I live in Brazil.
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times.

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

Gustavo6046 wrote:(a) I've gathered a LOT of Python knowledge since then, and I may guarantee you, that is bulky and inefficient. I can do a much faster method (considering I don't usually use NumPy). I will create a Golly-independent Python script (probably with wxWidgets) that will search for combinations of input patterns Aₓ that generate the desired result pattern B. (with multiple configurations for position ranges, etc). I'm already working on it.

This will probably use a brute force method, won't be as fast as an actual CGoL algorithm (especially, it will be MUCH slower than HashLife), one of the reasons being it will be in Python.

(b) Yes, I live in Brazil.
(a) Wow.
(b) you are the first person who lived in brazil ive everseen.
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

(c) check your private message, then.
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife

Entity Valkyrie
Posts: 247
Joined: November 30th, 2017, 3:30 am

Re: Minimalistic Game of Life simulator in Python

By a different entity, Yizientity:

https://repl.it/@YuzhiEntity/Behaulken

Hunting
Posts: 3891
Joined: September 11th, 2017, 2:54 am

Re: Minimalistic Game of Life simulator in Python

Gustavo6046 wrote:
September 11th, 2018, 7:21 pm
I checked my Gmail, and surprise surprise! I believe I'll just change my avatar and other things. I want to look decent in here.

On topic, I believe I'll remake the CGOL player.
I just want to bump this thread, to remind Gustavo of this thread, this community, and this everything.

Sorry everyone.

I was like Gustavo when I joined this community.
I'm against Golly 4.x and Python 3.x

This one is entirely James' fault. --xkcd

Anyone want to bet what will be the next list to appear in b2n3s23-q/C1 census?

LeapLife - DirtyLife - LispLife