Game of life, what’s wrong?

Asked

Viewed 86 times

0

I’m training Python and recently I’ve been trying to recreate the famous "Game Of Life", I managed to avoid code flaws but it is still not behaving properly.
The program was to behave this way:
https://bitstorm.org/gameoflife/

Note that the game is still running through the console window and is best viewed by the standard console and I haven’t implemented editing functions yet so the grid is randomly generated. https://github.com/jeacom25b/python-game-of-life

def genhash(sizex=10, sizey=10):
    hash = {}
    hash["sizex"] = sizex
    hash["sizey"] = sizey

    for x in range(sizex):
        for y in range(sizey):
            hash[(x, y,)] = 0
    return hash


def hashprint(h, char0="0", char1="1"):
    if "sizex" in list(h.keys()) and "sizey" in list(h.keys()):

        x = h["sizex"]
        y = h["sizey"]
        p = ""

        for xx in range(x):
            for yy in range(y):

                p = p + (str(h[(xx, yy)]))
            p = p + ("\n")
        return p.replace("0", char0).replace("1", char1)
    else:
        raise ValueError("the argument is not a spatial hash")


def randomhash(h, rate):
    if "sizex" in list(h.keys()) and "sizey" in list(h.keys()):
        from random import random

        x = h["sizex"]
        y = h["sizey"]
        newh = {}
        newh["sizex"] = x
        newh["sizey"] = y

        for x1 in range(x):
            for y1 in range(y):

                r = random()
                v = 0
                if r < rate:
                    v = 1
                else:
                    v = 0
                newh[(x1, y1)] = v

        return newh
    else:
        raise ValueError("the argument is not a spatial hash")


def convolute(h, r="b3s23"):
    if "sizex" in list(h.keys()) and "sizey" in list(h.keys()):

        # rules
        rb = r.find("b")
        rs = r.find("s")

        b = [int(rule) for rule in r[rb + 1:rs]]
        s = [int(rule) for rule in r[rs + 1:]]

        # x,y !!
        x = h["sizex"]
        y = h["sizey"]

        for xxx in range(x):

            # torus wrapping
            xp1 = xxx + 1
            xm1 = xxx - 1

            if xp1 > x - 1:
                xp1 -= x

            if xm1 < 0:
                xm1 += x

            for yyy in range(y):

                # torus wrapping
                yp1 = yyy + 1
                ym1 = yyy - 1

                if yp1 > y - 1:
                    yp1 -= y

                if ym1 < 0:
                    ym1 += y

                # compute sum of neighbor cells
                sum = (h[(xm1, yp1)] +
                       h[(xxx, yp1)] +
                       h[(xp1, yp1)] +
                       h[(xm1, yyy)] +
                       h[(xp1, yyy)] +
                       h[(xm1, ym1)] +
                       h[(xxx, ym1)] +
                       h[(xp1, ym1)])

                # apply rules
                if h[(xxx,yyy)] == 0:
                    if sum in b:
                        h[(xxx,yyy)] = 1
                else:
                    if sum not in s:
                        h[(xxx,yyy)] = 0


    else:
        raise ValueError("the argument is not a spatial hash")


if __name__ == "__main__":

    h = genhash(20, 40)
    h = randomhash(h, 0.2)

    import time


    while 1:
        time.sleep(0.1)
        convolute(h)
        print(hashprint(h, " ", "5"),"console preview")
  • I’ll take a look - but the first impression is "it’s bigger than it needs to be" - I remember doing a very concise implementation in 2016.

1 answer

3


OK - Your question is not well formulated in the sense that it does not speak exactly what the error is.

How I’m half a fan of game of life, persisted a little - it was only when I created a function that populated its matrix with a "mover" equal to the example you point out - and then in fact we have a distinct behavior.

So instead of your function randomhash for:

def moverhash(h):
    [h.__setitem__((x,y), 0) for x in range(h['sizex']) for y in range(h['sizey'])]
    h[10, 10] = 1
    h[10, 11] = 1
    h[10, 12] = 1
    h[9, 12] = 1
    h[8, 11] = 1

This draws the specific shape that should go "walking" down. (And part of this, see that you can use tuples (in this case pairs) as dictionary indexes without using a pair of parentheses inside the bracket)

In addition to this function, I put a controlled pause between generations - as your program is console, a simple input() within the while, at the end, allows us to move to the next frame only by tightening <enter>

But anyway, then it’s easy to see that you’re wrong - and then we realize why:
You’re updating each matrix cell while scanning - but you’re consulting the same matrix that is changing. Thus, when you arrive in the column (y + 1), the cells it queries by neighbor in the column (y) are already with the next generation values. (another detail: your "x" is vertical, and your "y" is horizontal, denoting columns)

That is, when computing the neighbors of each cell, you count half the grid in the previous generation, and the other half of the cells already computed for the next generation.

This is easily resolved by changing your convolute to receive the old grid, create and fill a new one, and return the new one to each interaction:

def convolute(h, r="b3s23"):
    if "sizex" not in h or "sizey" not in h:
        raise ValueError("the argument is not a spatial hash")

    # rules
    rb = r.find("b")
    rs = r.find("s")

    b = [int(rule) for rule in r[rb + 1:rs]]
    s = [int(rule) for rule in r[rs + 1:]]

    new_grid = {}
    # x,y !!
    x = new_grid['sizex'] = h["sizex"]
    y = new_grid['sizey'] = h["sizey"]

    for xxx in range(x):

        # torus wrapping
        xp1 = xxx + 1
        xm1 = xxx - 1

        if xp1 > x - 1:
            xp1 -= x

        if xm1 < 0:
            xm1 += x

        for yyy in range(y):

            # torus wrapping
            yp1 = yyy + 1
            ym1 = yyy - 1

            if yp1 > y - 1:
                yp1 -= y

            if ym1 < 0:
                ym1 += y

            # compute sum of neighbor cells
            sum_ = sum((
                    h[xm1, yp1],
                    h[xxx, yp1],
                    h[xp1, yp1],
                    h[xm1, yyy],
                    h[xp1, yyy],
                    h[xm1, ym1],
                    h[xxx, ym1],
                    h[xp1, ym1]))

            # apply rules
            if not h[(xxx,yyy)]:
                new_grid[xxx,yyy] = int(sum_ in b)
            else:
                new_grid[xxx,yyy] = int(sum_ in s)
    return new_grid

And obviously change the function call:

if __name__ == "__main__":

    h = genhash(20, 40)
    #h = randomhash(h, 0.1)
    moverhash(h)

    while True:
        print(hashprint(h, " ", "5"),"console preview")
        h = convolute(h)
        input()
  • Thank you, I will improve my questions, but as you can see, I have difficulty even to give good names to the functions.

  • i did not xtendi myself on this - but in general you call the maximum limits at x and y of width and height (letters w and d) and leave x and y for the coordinates at which you are acting. Ai does not need Nomex as "xxx" and "yyy".

  • Yes, it is true, I need to develop this habit, I made the corrections and the code worked perfectly. now the next step is to implement a graphical interface.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.