8
\$\begingroup\$

It's my implementation. Can I get some opinions on it?

import random
import os
import time

class Game:

 def __init__(self, turns, w, h):
    self.board = []

    for x in range(0, h):
        self.board.append([])
        for y in range(0, w):
            self.board[x].append(random.random())

    self.turns = turns

 def next_gen(self):
    self.board_next = []

    for x in range(0, len(self.board)):
        self.board_next.append([])

    for x in range(0, len(self.board)):
        for y in range(0, len(self.board[x])):
            n = 0

            for dx in range(-1, 2):
                for dy in range(-1, 2):
                    if (dx == 0 and dy == 0):
                        pass
                    else:
                        try:
                            if (x + dx >= 0 and y + dy >= 0):
                                if (self.board[x + dx][y + dy]):
                                    n += 1
                        except IndexError:
                            pass

            c = self.board[x][y]

            if (n == 1 or n == 0):
                c = 0
            elif (n == 3):
                c = 1
            elif (n == 2):
                pass
            else:
                c = 0

            self.board_next[x].append(c)

    self.board = self.board_next[:]

 def show(self):
    for x in range(0, len(self.board)):
        l = ""
        for y in range(0, len(self.board[x])):
            if (self.board[x][y]):
                l += "X"
            else:
                l += "#"
        print(l)
    time.sleep(0.5)

 def start(self):
    for x in range(0, self.turns):
        print("\n+Gen {0}".format(x + 1))
        self.show()
        if (x + 1 != self.turns):
            os.system('clear')
        self.next_gen()

os.system('clear')
game = Game(3, 3, 3)
game.board = [
      [0, 0, 0],
      [1, 1, 1],
      [0, 0, 0]]
game.start()

And output:

+Gen 1
###
XXX
###

+Gen 2 
#X# 
#X#
#X#

+Gen 3
###
XXX
###
\$\endgroup\$
4
\$\begingroup\$

First three general remarks:

  1. range does not need an explicit start argument, it defaults to 0. range is basically range(start=0, stop, step=1), only stop is needed
  2. if takes an expression as argument which just has to evaluate truthy or falsey. There is no need for parenthesis, unless chaining multiple expressions over multiple lines or doing some complicated and/or logic.
  3. you should consistently use 4 spaces per indentation level (your class only has 2 in the outer level).

Your setting up of the board can be simplified with a list comprehension:

self.board = [[random.random() for _ in range(w)] for _ in range(h)]

Here I used _ as an unused loop variable, as customary in Python.

I would also save h and w in the class for later use.


In Game.next_gen:

if (n == 1 or n == 0):
    c = 0
elif (n == 3):
    c = 1
elif (n == 2):
    pass
else:
    c = 0

is equivalent to:

if n == 3:
    c = 1
elif n != 2:
    c = 0

I would make two helper functions calculating neighbours that are alive:

def neighbours(self, x, y):
    p = -1, 0, 1
    for dx, dy in itertools.product(p, p)
        if dx == dy == 0:
            continue
        _x, _y = x + dx, y + dy
        if 0 <= _x < self.h and 0 <= _y < self.w:
            yield _x, _y

def alive_neighbours(self, x, y):
    return sum(self.board[n_x][n_y] > 0 for n_x, n_y in self.neighbours(x, y))

Here I used the boundaries to make sure that neighbours only serves points inside the grid (and used itertools.product to make it easier to construct dx and dy). Then getting the sum of all alive neighbours just sums over the values, since in self.board empty cells have a value of zero, while alive cells a value greater than zero.

This simplifies next_gen to:

def next_gen(self):
   board_next = [[0 for _ in range(self.w)] * self.h]
   for x in range(len(self.board)):
       for y in range(len(self.board[x])):
           n = self.alive_neighbours(x, y)
           cell = self.board[x][y]
           if n == 3:
              cell = 1
           elif n != 2:
              cell = 0
           board_next[x][y] = cell
   self.board = board_next[:]

Your show function can be simplified using str.join:

def show(self):
   for row in self.board:
       print("".join("X" if cell else "#" for cell in row))

The time.sleep(0.5) I would put in start to make it possible to show the generation without delay if necessary.


I would put the code calling your class into a if __name__ == "__main__": guard to allow importing your class from another module:

if __name__ == "__main__":
    os.system('clear')
    game = Game(3, 3, 3)
    game.board = [
      [0, 0, 0],
      [1, 1, 1],
      [0, 0, 0]]
    game.start()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.