Conway's Game of Life using Sparse Matrix (Python)

1.5k Views Asked by At

I am writing the game of life in python using a Sparse Matrix. My program takes coordinates from user input and sets the cells at the chosen coordinates to be alive. I have it to where it will print the first generation, but I can't seem to figure out why it wont print any subsequent generations. Any help would be appreciated.

class SparseLifeGrid:

generations = list()

def __init__(self):
    """
    "pass" just allows this to run w/o crashing.
    Replace it with your own code in each method.
    """
    self._mat = list()
    self.col = []
    self.row = []

def minRange(self):
    """
    Return the minimum row & column as a tuple.
    """
    for i in range(len(self.row)):

        if self.row[i] < self.minRow:
            self.minRow = self.row[i]

        if self.col[i] < self.minCol:
            self.minCol = self.col[i]

    min_Range = [self.minRow,self.minCol]

    return min_Range

def maxRange(self):
    """
    Returns the maximum row & column as a tuple.
    """
    for i in range(len(self.row)):

        if self.row[i] > self.maxRow:
            self.maxRow = self.row[i]

        if self.col[i] > self.maxCol:
            self.maxCol = self.col[i]

    max_Range = [self.maxRow,self.maxCol]

    return max_Range

def configure(self,coordList):
    """
    Set up the initial board position.
    "coordlist" is a list of coordinates to make alive.
    """
   # for i in coordList:
   #     self.setCell(i[0],i[1])

    self._mat = list()
    self.coordList = coordList

    for i in range(len(self.coordList)):
        spot = self.coordList[i]
        self.row += [spot[0]]
        self.col += [spot[1]]
        self._mat += [[self.row[i],self.col[i]]]

    self.maxRow = self.minRow = self.row[0]
    self.maxCol = self.minCol = self.col[0]


def clearCell(self,row, col):
    """
    Set the cell to "dead" (False)
    """
    self[row,col] = 0

def setCell(self,row, col):
    """
    Set the cell to "live" (True") and if necessary, expand the
    minimum or maximum range.
    """
    self[row,col] = 1


def isLiveCell(self,row,col):
    n = len(self.coordList)

    for i in range(n):

        if (self._mat[i] == [row,col]):
            return True

    return False


def numLiveNeighbors(self, row,col):
    """
    Returns the number of live neighbors a cell has.
    """
    neighbors = 0

    if self.isLiveCell(row+1,col): #checks below the current cell
        neighbors += 1

    if self.isLiveCell(row-1,col): #checks above the current cell
        neighbors += 1

    if self.isLiveCell(row,col+1): #checks to the right of the current cell
        neighbors += 1

    if self.isLiveCell(row,col-1): #checks to the left of the current cell
        neighbors += 1

    if self.isLiveCell(row+1,col+1): #checks downwards diagonally to the right of the current cell
        neighbors += 1

    if self.isLiveCell(row+1,col-1): #checks downwards diagonally to the left of the current cell
        neighbors += 1

    if self.isLiveCell(row-1,col+1): #checks upwards diagonally to the right of the current cell
        neighbors += 1

    if self.isLiveCell(row-1,col-1): #checks upawards diagonally to the left of the current cell
        neighbors += 1

    return neighbors


def __getitem__(self,ndxTuple):
    row = ndxTuple[0]
    col = ndxTuple[1]

    if(self.isLiveCell(row,col)==1):
        return 1

    else:
        return 0

def __setitem__(self,ndxTuple, life):
    """
    The possible values are only true or false:
    True says alive, False for dead.
    Also, check to see if this cell is outside of the maximum row and/or
    column. If it is, modify the maximum row and/or maximum column.
    """
    ndx = self._findPosition(ndxTuple[0],ndxTuple[1])

    if ndx != None:

        if life != True:
            self._mat[ndx].value = life

        else:
            self._mat.pop[ndx]

    else:

        if life != True:
            element = _GoLMatrixElement(ndxTuple[0],ndxTuple[1],life)
            self._mat.append(element)

def _findPosition(self,row,col):
    ''' Does a search through the matrix when given the row&col and
        returns the index of the element if found
    '''
    n = len(self._mat)

    for i in range(n):

        if (row == self._mat[i]) and (col == self._mat[i]):
            return i

    return None

def __str__(self):
    """
    Print a column before and after the live cells
    """
    s=""
    maxRange=self.maxRange()
    minRange=self.minRange()

    for i in range(minRange[0]-1,maxRange[0]+2):
        for j in range(minRange[1]-1,maxRange[1]+2):
            s+=" "+str(self[i,j])
        s+="\n"

    return s

def getCopy(self):
    """
    Return a copy of the current board object, including the max and min
    values, etc.
    """
    return SparseLifeGrid()

def evolve(self):
    """
    Save the current state to the "generations" list.
    Based on the current generation, return the next generation state.
    """
    self.generations.append(self._mat)

    for row in range(len(self.row)):
        for col in range(len(self.col)):

            if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) == 2)):
                self.setCell(row,col)

            if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) == 3)):
                self.setCell(row,col)

            if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) < 2)):
                self.clearCell(row,col)

            if ((self[row,col] == True) and (self.numLiveNeighbors(row,col) > 3)):
                self.clearCell(row,col)

            if ((self[row,col] == False) and (self.numLiveNeighbors(row,col) == 3)):
                self.setCell(row,col)

    self.generations.append(self._mat)
    return self._mat

def hasOccurred(self):
    """
    Check whether  this current state has already occured.
    If not, return False.  If true, return which generation number (1-10).
    """
    for i in range(len(self.generations)):

        if len(self.generations) > 0:
            print("This is generation",len(self.generations))
            return self.generations[i]

        else:
            print("No Generations")
            return False

def __eq__(self,other):
    """
    This is good method if we want to compare two sparse matrices.
    You can just use "sparseMatrixA == sparseMatrixB" once this method
    is working.
    """
    pass

class _GoLMatrixElement:
    """
    Storage class for one cell
    """
    def __init__(self,row,col):
        self.row = row
        self.col = col
        self.next = None  #
        # Since this node exists, this cell is now alive!
        # To kill it, we just delete this node from the lists.
from SparseLifeGrid import SparseLifeGrid
import sys

def readPoints(lifeGrid):
    """
    Reads the locations of life and set to the SparseMatrix
    """
    print("1. Enter positions of life with row,col format (e.g., 2,3).")
    print("2. Enter empty line to stop.")

    life=input()
    coordList=[]
    while life:
        points=life.split(",")
        try:
            coord=[int(points[0]),int(points[1])]
            coordList.append(coord)
        except ValueError:
            print("Ignored input:" + life+ ", row, col not valid numbers")
        except:
            print("Unexpected error:", sys.exc_info()[0])
        print("added, keep entering or enter empty line to stop.")
        life=input()
    print("Thanks, finished entering live cells")
    lifeGrid.configure(coordList)

def main():
    """
    Runs for ten generations if a stable (repeating) state is not found.
    """
    lifeGrid= SparseLifeGrid()
    readPoints(lifeGrid)
    patterns=0
    i=0 
    while i <10 :
        """
        Evolve to the next generation
        """
        lifeGrid.evolve()
        print(lifeGrid)
        """
        Check whether this generation is a repetition of any of the
        previous states.
        If yes return the previous matching generation (1-10).
        """
        patterns=lifeGrid.hasOccurred()
        if patterns != -1:
            break
        i+=1

    if i==10:
        print("No pattern found")
    else:

        print("Pattern found at: " + str(i)+ " of type: " + str(patterns))

main()
1

There are 1 best solutions below

0
On

The loop only executes once because of this:

patterns=lifeGrid.hasOccurred()
if patterns != -1:
    break

patterns will never equal -1 under any circumstance, since hasOccurred can only return False or a member of self.generations or None. As a result, the loop will always break in the first iteration, and no subsequent generations will be printed.

Incidentally, your hasOccurred logic is strange. if len(self.generations) equals zero, then the loop will not get executed at all, and none of your return statements will be evaluated, so the result will be None. If the length is greater than zero, then the condition within the loop is always True, so self.generations[i] is always returned in the first iteration. Perhaps you meant to do:

#changed the name from `hasOcurrence`, since that implies the method can only return True or False.
def get_last_occurrence(self):
    for i in range(len(self.generations)):
        #todo: somehow compare self.generations[i] to the current generation
        if the generations match:
            return i
    #the loop ended without finding a match!
    return False

Then, within main:

patterns=lifeGrid.hasOccurred()
if patterns != False:
    break