HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Conway's Game of Life without objects

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withoutobjectsconwaygamelife

Problem

I was wondering if anyone could provide some feedback on my solution to Conway's game of life. I tried to solve it without using objects. It returns a dictionary of living points on a grid and points that were alive at some point. I'm wondering if there is a better way to check the surrounding points or if storing the active points in a dictionary is a bad idea.

def list_to_check(x,y):
    return  [(x + 1, y),
             (x - 1, y),
             (x +1, y +1 ),
             (x -1, y -1 ),
             (x +1, y -1 ),
             (x -1, y +1 ),
             (x, y+1),
             (x, y-1)]

def neighbors_count(x,y,cells):
    count = 0
    for item in list_to_check(x,y):     
       if item in cells:
           count += cells[item]
    return count 

def next_state(cells):
    next_state = {}
    for point in cells.keys():
        alive = cells[point] == 1
        neighbors = neighbors_count(point[0],point[1],cells)
        if not alive and neighbors == 3:
            next_state[point] = 1
        elif neighbors > 3 or neighbors >>>>>>>>>>>>>>>>>>>', x + 1

    for item in print_grid(20,test_array_dict)[::-1]:
        print item
    tick(test_array_dict)


Update

I added [ cells.pop(point,None) for point in cells.keys() if cells[point] == 0 ] to the tick function and it really sped up the the code on higher iterations

Solution

You're representing the cells mainly using a dictionary of coordinates, converting to a two-dimensional array only for the purposes of printing the output. That is a good representation for sparse boards, but not so much for crowded boards.

The algorithm that you use, though, is cumbersome, particularly illustrated by these few lines:

for item in cells.keys():
    checking = [ x for x in list_to_check(item[0],item[1]) if x not in to_check and x not in cells ]
    to_check += checking


In other words, make a list of the neighbors of each cell, but make sure that each coordinate is listed just once. Then, once you have that list to_check, what do you do with it?

for item in to_check:
    if neighbors_count(item[0],item[1],cells) == 3:
        to_add[(item[0],item[1])] = 1


You count each cell's neighbors! But the number of neighbors is precisely the amount of overlapped processing that would have occurred had you not bothered to deduplicate the to_check list to begin with! So, instead of drawing a list of interesting cells and counting their neighbors, why not just have each existing cell increment the neighbor count of each neighboring coordinate?

In addition to the change in algorithm, I also recommend:

  • Take advantage of list/set/dict comprehensions more.



  • Represent the board as a set of live cells, rather than a dict.



  • Rename list_to_check() to neighbors().



  • Rename print_grid() to grid(), since it actually doesn't print anything.



  • Construct the next state from scratch rather than by mutation: test_array_dict = tick(test_array_dict)



I'd rewrite three functions as follows (and eliminate neighbors_count(), next_state(), and add_to_grid_alive():

def tick(live_cells):
    """ Takes a set of coordinates of live cells, and returns a set of
    coordinates of the live cells in the next generation. """
    neighbor_count = {}
    for xy in live_cells:
        for neighbor in neighbors(*xy):
            neighbor_count[neighbor] = 1 + neighbor_count.get(neighbor, 0)
    return set(xy for xy in neighbor_count
                   if neighbor_count[xy] == 3
                   or xy in live_cells and neighbor_count[xy] == 2)

def grid(size, cells):
    return [[int((x, y) in cells) for x in range(size)] for y in range(size)]

def load_list(inputs):
    return set(inputs)


Actually, tick() would be even better written using a collections.Counter. I don't know if you consider that "using an object".

Code Snippets

for item in cells.keys():
    checking = [ x for x in list_to_check(item[0],item[1]) if x not in to_check and x not in cells ]
    to_check += checking
for item in to_check:
    if neighbors_count(item[0],item[1],cells) == 3:
        to_add[(item[0],item[1])] = 1
def tick(live_cells):
    """ Takes a set of coordinates of live cells, and returns a set of
    coordinates of the live cells in the next generation. """
    neighbor_count = {}
    for xy in live_cells:
        for neighbor in neighbors(*xy):
            neighbor_count[neighbor] = 1 + neighbor_count.get(neighbor, 0)
    return set(xy for xy in neighbor_count
                   if neighbor_count[xy] == 3
                   or xy in live_cells and neighbor_count[xy] == 2)

def grid(size, cells):
    return [[int((x, y) in cells) for x in range(size)] for y in range(size)]

def load_list(inputs):
    return set(inputs)

Context

StackExchange Code Review Q#70049, answer score: 2

Revisions (0)

No revisions yet.