Page 1 of 1

Minimalistic Game of Life simulator in Python

Posted: April 2nd, 2017, 10:40 am
by Gustavo6046
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! :D

I have recorded a short video, available here.

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 6:50 pm
by Billabob
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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 7:21 pm
by Gustavo6046
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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 9:00 pm
by Hunting
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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 9:08 pm
by Hunting
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?)

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 9:22 pm
by 77topaz
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...

Re: Minimalistic Game of Life simulator in Python

Posted: September 11th, 2018, 9:40 pm
by Hunting
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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 14th, 2018, 9:16 pm
by Gustavo6046
(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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 20th, 2018, 11:28 am
by Hunting
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.

Re: Minimalistic Game of Life simulator in Python

Posted: September 20th, 2018, 11:48 am
by Hunting
(c) check your private message, then.

Re: Minimalistic Game of Life simulator in Python

Posted: December 1st, 2018, 2:17 am
by Entity Valkyrie
By a different entity, Yizientity:

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

Re: Minimalistic Game of Life simulator in Python

Posted: February 4th, 2020, 10:08 am
by Hunting
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.