shashtheash shashtheash - 25 days ago 13
Python Question

Game of LIfe: Modifying array values in numpy while iterating with conditions

I am a Physics student with very basic knowledge of Python and I have been trying to look for a simple solution to this problem, but I have only been running around in circles. I am writing my own rendition for a program called "The Game of Life" (WikiPage). The rules are straightforward:


The Game consists of so called cells that live on a 2 dimensional grid. These cells evolve in cycles. The initial pattern of dead and live cells is the first generation. The second generation evolves from applying some simple rules simultaneously to all cells. The rules are:

Cells die if they dont have enough company (less than 2 cells neighboring)

Cells die of overpopulation if there are 4 or more cells neighboring.

A dead cell is reborn if there are (exactly) 3 cells neighboring.


This is a part of an assignment, but I am not looking for a spoon-fed solution. We are required to use numpy and arrays.

My approach was to generate an array
board
with an initial feed of random 1's and 0's. Then, in a function, I create another array with one row and one column greater than
board
which will store the new values and then feed it back. I iterate (I know its not pythonic to iterate over an array, so I am open to suggestions and alternatives) over the elements
i
and
j
and make a sum (
np.sum
) of the subarray
nBlock[i-1:i+1,j-1:j+1]
. If the sum is greater than 4 or less than 4, then the element
nBlock[np.array([i,j])]
becomes zero (cell dies), otherwise, it is 1 (cell lives, or is reborn).

The problem is, this doesn't work. The code is as follows:

import numpy as np
import matplotlib.pyplot as plot
import time as t
%matplotlib inline

def printBoard(board):
im = plot.imshow(board, cmap='Greys', interpolation='none')
plot.show()
plot.close()

def initializeBoard():
board = np.random.randint(2, size=(100, 100))
return board

board = initializeBoard()
printBoard(board)

def checkAlive():
if 1 not in board:
print("All cells are dead! Life is over!")
return 0
else:
return board

def calculateNeighbours(board):
nBlock = np.zeros((102, 102))
nBlock[1:101, 1:101] = board

for i in range(1, 100):
for j in range(1, 100):
checksum = np.sum(nBlock[i-1:i+2, j-1:j+2])

nBlock[:,0] = 0
nBlock[0,:] = 0

if checksum == 4:
nBlock[np.array([i,j])] = 1
elif checksum > 4 or checksum < 4:
nBlock[np.array([i,j])] = 0

return nBlock

def play(board):
for i in range(1, 10):
neighbours = calculateNeighbours(board)

board = neighbours[1:101, 1:101]

board = checkAlive()

printBoard(board)

play(board)


The array
board
doesn't get modified. It returns the same
board
.

Another odd thing I noticed is that if I remove the conditions in my
for
loop, the array is successfully modified. For example,

for i in range(0, 100):
for j in range(0, 100):
board[np.array([i,j])] = 1


gives me a matrix which has all elements changed to 1. So there is something about the
if...else
conditions which is not allowing me to manipulate the data in my array.

Any and all help is appreciated :)

Answer

Error:

Your major error is in your interface logic for checkAlive. You have no parameters to the routine; when you do find a live cell, you return the symbiont board, which play immediately assigns to its parameter board.

The board in play is a local variable: changing this does not change the global board you initialized. In contrast, since checkAlive has no local variable, what it returns is the global variable board, which is never changed, anywhere in the program.

Solution:

Since checkAlive doesn't alter the board, don't return it. Instead, return a simple boolean. play can check this and terminate the program, if needed.

Also:

Your cell health algorithm is wrong in a couple of respects. Walk through the logic and repair. Most notably, you use a value of 4 to let a cell live; this is massively incorrect. 2 neighbours leave the cell in its current state; 3 neighbours make the cell live; any other number kills it.

Your current logic includes the cell itself in the count. This does not differentiate between a live cell with 3 friends and a dead cell with 4. As a result, you're creating some cells incorrectly, and you're killing off viable cells (live cell with 2 friends).

Comments