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

Game of Life, separation of logic / GUI

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

Problem

This is one of my first steps with GUI. I've tried an implementation of Conway's Game of Life with TkInter.
I would appreciate any opinions about my code, especially about the separation of GUI and logic.

```
import Tkinter as tk
from copy import deepcopy
from random import randrange

def random_fields(width):
"""
Returns a set of tuples of random integers, eg: set((2,4), (6,1)).

These tuples are meant as coordiantes.
The parameter width is the maximum value of x and y.
"""
fields_alive = width * width // 6
fields = set()
for _ in range(fields_alive):
x = randrange(width)
y = randrange(width)
fields.add((x,y))
return fields

class Field(object):
""" Represents a field in Game of Life. """
def __init__(self, x, y):
""" Constructs a "dead" field at coords x, y """
self.x = x
self.y = y
self.is_alive = False

def __str__(self):
return "Field: \n x: {0}; y: {1}; is_alive: {2}\n".format(self.x, self.y, self.is_alive)

def change(self):
""" Changes from dead to alive and vice-versa. """
if self.is_alive:
self.is_alive = False
else:
self.is_alive = True

class GameOfLifeMatrix(object):
"""
Represents a matrix in Game of Life and contains the logic.

The global STARTFIELDS is a dict, with some well known repeating patterns like:
"blinker" and "toad"
"""
STARTFIELDS = { "blinker": ((2,1), (2,2), (2,3)),
"toad": ((2,2), (3,2), (4,2), (1,3), (2,3), (3,3))
}
def __init__(self, width = 25, start_fields = "random"):
"""
Constructs a new square matrix for Game Of Life.

parameters:
width: integer, the width and height of the matrix
start_fields, you can provide:
- "blinker" or "toad" for the well known patterns
- "random" to create a random matrix
- a set of tuples containing spec

Solution

In terms of your specific question, I think you have done a good job of separating out the simulation and the presentation. However, you have a few slightly awkward bits of code:

def change(self):
    """ Changes from dead to alive and vice-versa. """
    if self.is_alive:
        self.is_alive = False
    else:
        self.is_alive = True


could be simplified to

def change(self):
    """ Changes from dead to alive and vice-versa. """
    self.is_alive = not self.is_alive


and

if not self.STARTFIELDS.get(start_fields) is None:


to

if start_fields in self.STARTFIELDS:


and

self.matrix = list()
    for x in range(width):
        row = list()
        self.matrix.append(row)
        for y in range(width):
            field = Field(x, y)
            if (x, y) in self.start_fields:
                field.change()
            row.append(field)


to

matrix = [[Field(x, y) for y in range(width)] for x in range(width)]
for x, y in self.start_fields:
    matrix[x][y].change()


Your approach to next_generation could be improved; if you made a list of all cells that _should_change, then changed them, you could do it in-place and wouldn't need the deepcopy. Separating out _should_change was a good idea, but you could make more of it.

Code Snippets

def change(self):
    """ Changes from dead to alive and vice-versa. """
    if self.is_alive:
        self.is_alive = False
    else:
        self.is_alive = True
def change(self):
    """ Changes from dead to alive and vice-versa. """
    self.is_alive = not self.is_alive
if not self.STARTFIELDS.get(start_fields) is None:
if start_fields in self.STARTFIELDS:
self.matrix = list()
    for x in range(width):
        row = list()
        self.matrix.append(row)
        for y in range(width):
            field = Field(x, y)
            if (x, y) in self.start_fields:
                field.change()
            row.append(field)

Context

StackExchange Code Review Q#27638, answer score: 2

Revisions (0)

No revisions yet.