Python Conway's Game of Life Implementation

Can anyone help me implement Conway’s Game of Life in Python? Just to clarify, this is homework, although I believe I am not violating the rules because I am not asking for answers, just help on certain sections. Here are the rules: Wikipedia Link
First, I have separated the game into six (6) functions to organize my code and make it easier for me to implement.

Function 1: create a blank grid

input: nothing

return: a blank grid

Function 2: print a given grid

input: a grid

return: nothing

Function 3: load a pattern

input: a file name, a grid

return: nothing

Function 4: advance a grid one generation

input: a grid

return: a new grid advanced by one generation

Function 5: advance a cell one generation

input: a row, a column, a grid

return: whether the cell is alive or not (True or False)

Function 6: determine the number of living neighbors of a cell

input: a row, a column, a grid

return: the number of living neighbors of the cell

If anyone could implement this for me I would be very, very grateful.

I already have the following code and questions:

Here is most of my own code:

living_cell = "O"
dead_cell = "-"


def create_blank_grid():
    line = [dead_cell for i in range(59)]
    # grid = [["-"] * 30 for i in range(60)]
    line.append("\n")
    grid = [line for i in range(30)]
    
    line = [dead_cell for i in range(59)]
    line.append("\n")
    grid = [line[:] for j in range(30)] 

    
    
    return grid
    

    
# print(create_blank_grid())

def print_grid(grid):
    for i in grid:
      # print(len(i))
      for j in i:
        if j == "\n":
          print(" " * (69-59))
        else:
          print(j, end="")
            
            
            
    
# print_grid(create_blank_grid())




def load_design(file_name, grid):
    myFile = open(file_name, "r")
    coordinates = []
    for line in myFile:
        real_line = line.split()
        row, col = real_line[0], real_line[1]
        coordinates.append((row, col))
    myFile.close()

    # turn on certain cells here
    
    for row, col in coordinates:
      row, col = int(row), int(col)
      # print(row, col)
      grid[row][col] = living_cell
      # print_grid(grid)
      
    for i in grid:
    # print(len(i))
      for j in i:
        print(j, end="")


# load_design("square.in", create_blank_grid()) # this file has this: 
""" 11 34
    11 35
    12 34
    12 35 """

I showed you this code because I have a question right now. Please bear this because I know I am probably violating some rules. If you can just attempt to answer it, I would appreciate it very much.

Function 1 in my plan: create_blank_grid
Function 2 in my plan: print_grid
Function 3 in my plan: load_design

Question: I have absolutely no idea on where to start with function 4, (by the way, functions 5 and 6 are helpers to function 4.) is there any starting point you could give me with this along with a little code to begin the 4th function? I guess really, what I am asking is help to start 5 and 6 since they are parts of function 4. Thanks!

1 Like

I am new here and I really am sorry for asking the way I did. Please forgive me a take a look at my revised and improved question.

1 Like

In function four you would have to iterate through each cell and check it’s neighbors. I suggest take courses on data structures where you will learn about data structures related to this like graphs. They will help you keep track of the neighboring cells. Or you can implement a linked list where each cell has a link to its neighbor cells, So when you iterate through each cell you can check the links of the cell. Also, as i see for cells there is a property alive which is required to distinguish if its dead or alive.
I fear I’m not able to provide you the code:

  1. I don’t know how many cells you want in your grid.
  2. It’s against the forums policy that you can’t provide solution code, that would be categorized cheating.

P.S. I suggest creating a class for Cell!

The thing is, this is a homework project and I can’t use classes.

I fear you’ll not be able to complete this project without learning atleast about modules like numpy and matplotlib which are required to show visualization of your cells.

Hey @gigawhiz78703, I did some research work and find this article on how to make conway’s Game of Life. This article will guide you, how you can break down each problem and perform it via only FUNCTIONS. I hope you’ll find helpful and this will make it easier for you to implement the game.
Link
Happy Coding.

Those are methods, and the cells are class instances.

1 Like

I have mistankenly shared the wrong link and I’m sorry about that.
Here is the right one
You can see the solution and explanation given under The way of python heading.
After that 2 more solution are provided for the same game which use numpy.

1 Like

Hi Armaan!
I have been working on this project for some time now, and I am on function 4, the one that iterates a whole grid one generation forward. I really need some help with this. So far, this is my function 4 (all others are done):

living_cell = "O"
dead_cell = "-"


def create_blank_grid():


    line = []
    for i in range(60):
        line.append(dead_cell)
        
    grid = []
    for j in range(30):
        grid.append(line[:])
    
     return grid


# print(create_blank_grid())

def print_grid(grid):
  for i in grid:
      for j in i:
        print(j, end="")
      print()
   
  return grid


# print_grid(create_blank_grid())


def load_design(file_name, grid):
    myFile = open(file_name, "r")
    coordinates = []
    for line in myFile:
        real_line = line.split()
        row, col = real_line[0], real_line[1]
        coordinates.append((row, col))
    myFile.close()

    # turn on certain cells here

    for row, col in coordinates:
        row, col = int(row), int(col)
        grid[row][col] = living_cell


            
    return print_grid(grid)
    

# load_design("square.in", create_blank_grid())
# square just has coordinates in it like "12 35" on each line




def num_living_neighbors(row, col, grid):
    # just to make sure
    row = int(row)
    col = int(col)
    
    living_neighbors_count = 0

    if (col + 1) < len(grid[row]) and grid[row][col + 1] == living_cell:
      living_neighbors_count += 1
      
    if (col - 1) >= 0 and grid[row][col - 1] == living_cell:
      living_neighbors_count += 1

    if (row + 1) < len(grid) and grid[row + 1][col] == living_cell:
        living_neighbors_count += 1

    if (row - 1) >= 0 and grid[row - 1][col] == living_cell:
        living_neighbors_count += 1

    if (((col + 1) < len(grid[row])) and (row + 1) < len(grid)) and grid[row + 1][col + 1] == living_cell:
        living_neighbors_count += 1

    if (((row + 1) < len(grid)) and (col - 1) >= 0) and grid[row + 1][col - 1] == living_cell:
        living_neighbors_count += 1

    if (((row - 1) >= 0) and (col + 1) < len(grid[row])) and grid[row - 1][col + 1] == living_cell:
        living_neighbors_count += 1

    if (((row - 1) >= 0) and (col - 1) >= 0) and grid[row - 1][col - 1] == living_cell:
        living_neighbors_count += 1
    


    return living_neighbors_count





def adv_cell_one_gen(row, col, grid):

    
    # is alive, less than 2 alive neighbors
    if grid[row][col] == living_cell and num_living_neighbors(row, col, grid) < 2:
        return False

    # is alive, 2 or 3 alive neighbors
    if grid[row][col] == living_cell and (num_living_neighbors(row, col, grid) == 2 or num_living_neighbors(row, col, grid) == 3)
        return True

    # is alive, more than 4 alive neighbors
    if grid[row][col] == living_cell and num_living_neighbors(row, col, grid) > 3:
        return False
    # is dead, has 3 alive neighbors
    if grid[row][col] == dead_cell and num_living_neighbors(row, col, grid) == 3:
        return True

    
    
    

def adv_grid_one_gen(grid):
   
  for i in range(len(grid)):
      for j in range(len(grid[i])):
          if adv_cell_one_gen(i, j, grid) == True:
              grid[i][j] = living_cell
          else:
              grid[i][j] = dead_cell
              
              
  return grid


adv_grid_one_gen(load_design("f-pentomino.in", create_blank_grid()))

The problem with this is that it just runs, but it doesn’t advance the whole grid one generation, it just keeps the same grid. This is homework and it is due very, very soon, so any help would be appreciated Armaan! Thanks! I really appreciate it! I hope to keep in touch and finish this ASAP, as I will be available 24/7 this week, and it is due on 11/18

Maybe I’ve only read a little bit about this game and have only a naive take on it… It seems imperative that we are able to obtain information about the eight neighboring cells.

What makes sense to me is a function to generate a sequence, even a string would do that consists of the eight cells. Heck we can do this with eight bits.

Let’s map it out in that perspective,

0 1 0
0 ? 1
0 0 0

0b01001000

If there are two live cells adjacent what is the outcome for that cell?

Any live cell with fewer than two live neighbours dies, as if by needs caused by underpopulation.
Any live cell with more than three live neighbours dies, as if by overcrowding.
Any live cell with two or three live neighbours lives, unchanged, to the next generation.
Any dead cell with exactly three live neighbours becomes a live cell.

@mtf and @armaan_barak
update: I can make the grid update once, but after that, it doesn’t change (just prints the 2nd gen over and over again, and it doesn’t go on to 3rd gen). I don’t know what to do about this.
Here is my latest code:

"""
Function 1: create a blank grid
input: nothing
return: a blank grid

Function 2: print a given grid
input: a grid
return: nothing

Function 3: load a pattern
input: a file name, a grid
return: nothing

Function 4: advance a grid one generation
input: a grid
return: a new grid advanced by one generation

Function 5: advance a cell one generation
input: a row, a column, a grid
return: whether the cell is alive or not (True or False)

Function 6: determine the number of living neighbors of a cell
input: a row, a column, a grid
return: the number of living neighbors of the cell

"""

living_cell = "O"
dead_cell = "-"


def create_blank_grid():
    
    line = []
    for i in range(60):
        line.append(dead_cell)
        
    grid = []
    for j in range(25):
        grid.append(line[:])
    
    
    grid[5][5] = living_cell
    grid[4][5] = living_cell
    grid[6][5] = living_cell
    grid[4][6] = living_cell
    grid[5][4] = living_cell
    
    
    

# 11 31
# 11 32
# 12 31
# 12 33
# 13 32


    return grid


# print(create_blank_grid())

def print_grid(grid):
  for i in grid:
      for j in i:
        print(j, end="")
      print()
      
      
      
  # for i in grid:
  #     # print(len(i))
  #     for j in i:
  #         # if j == "\n":
  #         #     print(" " * (69 - 59))
  #         # else:
  #             print(j, end="")
  

    
  return grid


# print_grid(create_blank_grid())


def load_design(file_name, grid):
    myFile = open(file_name, "r")
    coordinates = []
    for line in myFile:
        real_line = line.split()
        row, col = real_line[0], real_line[1]
        coordinates.append((row, col))
    myFile.close()

    # turn on certain cells here

    for row, col in coordinates:
        row, col = int(row), int(col)
        # print(row, col)
        grid[row][col] = living_cell
        # print_grid(grid)

    # for i in grid:
    #     # print(len(i))
    #     for j in i:
    #         print(j, end="")
            
            
    # return print_grid(grid)
    return grid
    

# load_design("hertz-oscillator.in", create_blank_grid())




def num_living_neighbors(row, col, grid):
    # just to make sure
    row = int(row)
    col = int(col)
    
    living_neighbors_count = 0

    # if grid[row][col + 1] == living_cell:
    #     living_neighbors_count += 1

    # if grid[row][col - 1] == living_cell:
    #     living_neighbors_count += 1

    # if grid[(row + 1) % len(grid)][col] == living_cell:
    #     living_neighbors_count += 1

    # if grid[row - 1][col] == living_cell:
    #     living_neighbors_count += 1

    # if grid[(row + 1) % len(grid)][(col + 1) % len(grid)] == living_cell:
    #     living_neighbors_count += 1

    # if grid[(row + 1) % len(grid)][(col - 1) % len(grid)] == living_cell:
    #     living_neighbors_count += 1

    # if grid[row - 1][col + 1] == living_cell:
    #     living_neighbors_count += 1

    # if grid[row - 1][col - 1] == living_cell:
    #     living_neighbors_count += 1
    
    
    if (col + 1) < len(grid[row]) and grid[row][col + 1] == living_cell:
      living_neighbors_count += 1
      
    if (col - 1) >= 0 and grid[row][col - 1] == living_cell:
      living_neighbors_count += 1

    if (row + 1) < len(grid) and grid[row + 1][col] == living_cell:
        living_neighbors_count += 1

    if (row - 1) >= 0 and grid[row - 1][col] == living_cell:
        living_neighbors_count += 1

    if (((col + 1) < len(grid[row])) and (row + 1) < len(grid)) and grid[row + 1][col + 1] == living_cell:
        living_neighbors_count += 1

    if (((row + 1) < len(grid)) and (col - 1) >= 0) and grid[row + 1][col - 1] == living_cell:
        living_neighbors_count += 1

    if (((row - 1) >= 0) and (col + 1) < len(grid[row])) and grid[row - 1][col + 1] == living_cell:
        living_neighbors_count += 1

    if (((row - 1) >= 0) and (col - 1) >= 0) and grid[row - 1][col - 1] == living_cell:
        living_neighbors_count += 1
    


    return living_neighbors_count





def adv_cell_one_gen(row, col, grid):

    
    # is alive, less than 2 alive neighbors
    if grid[row][col] == living_cell and num_living_neighbors(row, col, grid) < 2:
        return False

    # is alive, 2 or 3 alive neighbors
    if grid[row][col] == living_cell and (
            num_living_neighbors(row, col, grid) == 2 or num_living_neighbors(row, col, grid) == 3):
        return True

    # is alive, more than 4 alive neighbors
    if grid[row][col] == living_cell and num_living_neighbors(row, col, grid) > 3:
        return False
        
    # is dead, has 3 alive neighbors
    if grid[row][col] == dead_cell and num_living_neighbors(row, col, grid) == 3:

        return True
    
    
    
    
    
    # if (grid[row][col] == living_cell and (num_living_neighbors == 2 or num_living_neighbors == 3)) or (grid[row][col] == dead_cell and num_living_neighbors == 3):
    #   return True
    # return False

    

def adv_grid_one_gen(grid):
  
  new_line = []
  # for i in range(60):
  #     new_line.append(dead_cell)
        
  new_grid = []
  for j in range(30):
      new_grid.append(new_line[:])
      
  for i in range(len(grid)):
    for j in range(len(grid[i])):
      if adv_cell_one_gen(i, j, grid) == True:
        new_grid[i].append(living_cell)
      else:
        new_grid[i].append(dead_cell)
        
  grid, new_grid = new_grid, grid
  # newBoardList = [x[:] for x in grid]
  return print_grid(grid)
      
      
      
      
  
        
        
        
        
        


# def adv_grid_one_gen(grid):
  
#   # new_grid = [[] for i in range(30)]
  
  
#   # for i in range(len(grid)):
#   #   for j in range(i):
#   #     if adv_cell_one_gen(i, j, grid):
#   #       new_grid[i-1].append(living_cell)
#   #     else:
#   #       new_grid[i-1].append(dead_cell)
  
  
#   # print_grid(grid)
  
  
  
        
    


    
#   for i in range(len(grid)):
#       for j in range(len(grid[i])):
#           if adv_cell_one_gen(i, j, grid) == True:
#               grid[i][j] = living_cell
#           else:
#               grid[i][j] = dead_cell
              
              
#   # return print_grid(grid)
#   return grid







def main():

  boardList = create_blank_grid()
  newBoardList = [x[:] for x in boardList]

  while True:
      print("\n\n")
      print_grid(boardList)
      adv_grid_one_gen(boardList)
      # adv_grid_one_gen(load_design("f-pentomino.in", create_blank_grid()))
      boardList, newBoardList = newBoardList, boardList



main()

Again, if you could please run this code, or at least help me make it better, I would greatly appreciate it! Thanks!

Can you mount a REPL or CodePen so we can actually run it? I don’t run code on my local machine.

@mtf I haven’t used REPL (do you mean repl.it by this?) before, but I can try. I have just one thing to tell you before that. I’ll admit this is homework although I genuinely need help and I am using Galax’s answer here as reference code (I don’t really use Stack Overflow, and this is my first time), so please feel free to look at it, although my version won’t have inputs and it will be less fancy. By the way, there is a lot of commented-out code in the REPL, which you don’t have to look at thoroughly, but I would appreciate it if you do! Here is the REPL link, and I hope you can help! Right now, I am at the part in Galax’s answer where the “main()” function is used and I don’t know how to recreate that part in my code. Thanks, @armaan_barak and @mtf!

TBH, I’m more interested in how we analyze a cell, than anything. We’re going to do that a lot so we need to optimize that process. You can go on but I’m stuck here in my own thoughts which I’ll share if anything springs forth.

Yes, repl.it, or any sandbox that supports Python.

Bear in mind that we are working with probability here. nCr is in play. There are 8C2 scenarios where having two neighbors saves our bacon.

     8!
-----------
(8-2)! * 2!

@mtf I thank you for helping me, and although analyzing cells is the most important part of this (it happens 1800 times for a 30x60 grid), it is just not part of this assignment (due in <24 hours), and the part I and stuck on implementing is the part in Galax’s answer where he defines the function “main().” Do you have any way for me to implement this in a way that is specific to my code as shown in the REPL link I provided in my last reply? Thanks!

What I can decipher from any of this in the next 24hrs is not going to help you. Sorry. Us old farts just don’t work that way. Give us an intuitive problem that we can treat like philosophy and there might be some gains made.

@mtf essentially, the problem is that I can advance the grid 1 generation, but not more than that no matter how I try. I know that a loop must be used, but I’m not sure how to implement it. Thoughts?

If you wish to advance or recede a three by three object then it will need a five by five grid containment to permit movement in all directions. Think on this, as it is the first thing to come to mind that I can share.