Skip to main content
Code Review

Return to Question

deleted 220 characters in body; edited tags
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

I've just finished implementing Conway's Game of Life in python-3.6. Here's the code:

To run the code, just invoke python3.6 conway.py. The board shape is fixed; but the configuration is randomly generated with numpyNumPy.

My aim with this project was:

  1. implementImplement a concise and pythonic version of the algorithm
  2. getGet better with python'sPython's OOP constructs
  3. killKill time :-)

I'd appreciate any feedback, criticism and suggestions here.

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!


Changelog:

  1. removed the Cell class, it wasn't necessary.
  2. reorganise code
  3. add comments and type hints

I've just finished implementing Conway's Game of Life in python-3.6. Here's the code:

To run the code, just invoke python3.6 conway.py. The board shape is fixed; but the configuration is randomly generated with numpy.

My aim with this project was

  1. implement a concise and pythonic version of the algorithm
  2. get better with python's OOP constructs
  3. kill time :-)

I'd appreciate any feedback, criticism and suggestions here.

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!


Changelog:

  1. removed the Cell class, it wasn't necessary.
  2. reorganise code
  3. add comments and type hints

I've just finished implementing Conway's Game of Life in python-3.6:

To run the code, just invoke python3.6 conway.py. The board shape is fixed; but the configuration is randomly generated with NumPy.

My aim with this project was:

  1. Implement a concise and pythonic version of the algorithm
  2. Get better with Python's OOP constructs
  3. Kill time :-)

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code.

added 1079 characters in body
Source Link
coldspeed
  • 395
  • 2
  • 11
fromimport enumcurses
import numpy as np
import Enumtime

from collections import defaultdict
importfrom curses
enum import numpyEnum
from astyping np
import timeList

class State(Enum):
 ''' enum class to represent possible cell states '''
  dead = 0
 alive = 1
class Board:
 ''' Board class to represent the game board '''
 def __init__(self, m : int, n : int, init : List[List[int]]):
 self.m = m # the self.nnumber =of nrows
 self.sizen = mn * n # the number of columns
 self.board_ = [
 [State(init[i][j]) for j in range(self.n)] for i in range(self.m)
 ]
 def __str__(self) -> str:
 ''' return the __str__ representation of a Board object
 * represents a live cell, and a space represents a dead one
 '''
  return '\n'.join([
 ''.join(
 ['*' if cell.value else ' ' for cell in row]
 ) for row in self.board_]
 )
 @property
 def population(self):
 ''' population — the number of live cells on the board '''
 return sum(cell.value for row in self.board_ for cell in row)
 def count_live_neighbours(self, x : int, y : int) -> int:
 ''' count the live neighbours of a cell '''
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if (i == x and j == y) or i < 0 or j < 0:
 continue
 # handle IndexErrors raised during invalid indexing operations 
 try:
 count += self.board_[i][j].value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x : int, y : int) -> State:
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y]
 # determine the next state based on the current state and 
 # number of live neighbours 
  if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self) -> List[List[State]]: 
 ''' return board configuration for the next state ''' 
 return [
 [self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 self.board_''' =update self.next_board_state()
the board configuration with defthe has_live_cells(self):
config for the next state '''
 return any(cell.value for row in self.board_ for cell in= rowself.next_board_state()
 @property
  def populationhas_live_cells(self):
 -> bool:
 return sum(cell.value for row in''' self.board_return forwhether cellthere inare row)
any live cells or defnot __str__(self):'''
 return '\n'.join(
  [''.joinany(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
 board_ for cell in row)
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
 step = 0
  board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 step = 0
 while board.has_live_cells():
 step += 1
 print(board)
 print('f{step,} {board.population}')
 board.advance_state()
 time.sleep(.1)

Changelog: removed the Cell class, it wasn't necessary.

  1. removed the Cell class, it wasn't necessary.
  2. reorganise code
  3. add comments and type hints
from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
 dead = 0
 alive = 1
class Board:
 def __init__(self, m, n, init):
 self.m = m self.n = n
 self.size = m * n
 self.board_ = [
 [State(init[i][j]) for j in range(self.n)] for i in range(self.m)
 ]
 def count_live_neighbours(self, x, y):
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if i == x and j == y or i < 0 or j < 0:
 continue
 try:
 count += self.board_[i][j].value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x, y):
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y]
 if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self):
 return [
 [self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 self.board_ = self.next_board_state()
 def has_live_cells(self):
 return any(cell.value for row in self.board_ for cell in row)
 @property
  def population(self):
 return sum(cell.value for row in self.board_ for cell in row)
 def __str__(self):
 return '\n'.join(
  [''.join(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
  )
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
 step = 0
  board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 while board.has_live_cells():
 step += 1
 print(board)
 print(step, board.population)
 board.advance_state()
 time.sleep(.1)

Changelog: removed the Cell class, it wasn't necessary.

import curses
import numpy as np
import time

from collections import defaultdict
from enum import Enum
from typing import List

class State(Enum):
 ''' enum class to represent possible cell states '''
  dead = 0
 alive = 1
class Board:
 ''' Board class to represent the game board '''
 def __init__(self, m : int, n : int, init : List[List[int]]):
 self.m = m # the number of rows
 self.n = n  # the number of columns
 self.board_ = [
 [State(init[i][j]) for j in range(self.n)] for i in range(self.m)
 ]
 def __str__(self) -> str:
 ''' return the __str__ representation of a Board object
 * represents a live cell, and a space represents a dead one
 '''
  return '\n'.join([
 ''.join(
 ['*' if cell.value else ' ' for cell in row]
 ) for row in self.board_]
 )
 @property
 def population(self):
 ''' population — the number of live cells on the board '''
 return sum(cell.value for row in self.board_ for cell in row)
 def count_live_neighbours(self, x : int, y : int) -> int:
 ''' count the live neighbours of a cell '''
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if (i == x and j == y) or i < 0 or j < 0:
 continue
 # handle IndexErrors raised during invalid indexing operations 
 try:
 count += self.board_[i][j].value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x : int, y : int) -> State:
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y]
 # determine the next state based on the current state and 
 # number of live neighbours 
  if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self) -> List[List[State]]: 
 ''' return board configuration for the next state ''' 
 return [
 [self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 ''' update the board configuration with the config for the next state '''
 self.board_ = self.next_board_state()
 def has_live_cells(self) -> bool:
 ''' return whether there are any live cells or not '''
 return any(cell.value for row in self.board_ for cell in row)
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) 
 board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 step = 0
 while board.has_live_cells():
 step += 1
 print(board)
 print('f{step} {board.population}')
 board.advance_state()
 time.sleep(.1)

Changelog:

  1. removed the Cell class, it wasn't necessary.
  2. reorganise code
  3. add comments and type hints
deleted 281 characters in body
Source Link
coldspeed
  • 395
  • 2
  • 11
from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
 dead = 0
 alive = 1
class Cell:
 def __init__(self, x, y, state):
 self.x = x
 self.y = y
 self.state = State.dead if not state else state
 self.visited = False

class Board:
 def __init__(self, m, n, init):
 self.m = m
 self.n = n
 self.size = m * n
 self.board_ = []
[
 for i in range [State(self.minit[i][j]):
  for j in range(self.board_.append([]n)] for ji in range(self.nm):
 self.board_[-1].append(Cell(i, j, state=State(init[i][j])))]
 def count_live_neighbours(self, x, y):
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if i == x and j == y or i < 0 or j < 0:
 continue
 try:
 count += self.board_[i][j].state.value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x, y):
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y].state
 if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self):
 return [
 [Cell(i, j, self[self.next_cell_state(i, j)) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 self.board_ = self.next_board_state()
 def has_live_cells(self):
 return any(cell.state.value for row in self.board_ for cell in row)
 @property
 def population(self):
 return sum(cell.state.value for row in self.board_ for cell in row)
 def __str__(self):
 return '\n'.join(
 [''.join(['*' if cell.state.value else ' ' for cell in row]) for row in self.board_]
 )
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
 step = 0
 board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 while board.has_live_cells():
 print(board)step += 1
 board.advance_stateprint(board)
 print(step +=, 1board.population)
 print(step, board.populationadvance_state()
 time.sleep(.1)

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!


Changelog: removed the Cell class, it wasn't necessary.

from enum import Enum
from collections import defaultdict
import numpy as np
import time
class State(Enum):
 dead = 0
 alive = 1
class Cell:
 def __init__(self, x, y, state):
 self.x = x
 self.y = y
 self.state = State.dead if not state else state
 self.visited = False

class Board:
 def __init__(self, m, n, init):
 self.m = m
 self.n = n
 self.size = m * n
 self.board_ = []

 for i in range(self.m):
  self.board_.append([]) for j in range(self.n):
 self.board_[-1].append(Cell(i, j, state=State(init[i][j])))
 def count_live_neighbours(self, x, y):
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if i == x and j == y or i < 0 or j < 0:
 continue
 try:
 count += self.board_[i][j].state.value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x, y):
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y].state
 if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self):
 return [
 [Cell(i, j, self.next_cell_state(i, j)) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 self.board_ = self.next_board_state()
 def has_live_cells(self):
 return any(cell.state.value for row in self.board_ for cell in row)
 @property
 def population(self):
 return sum(cell.state.value for row in self.board_ for cell in row)
 def __str__(self):
 return '\n'.join(
 [''.join(['*' if cell.state.value else ' ' for cell in row]) for row in self.board_]
 )
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) 
 step = 0
 board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 while board.has_live_cells():
 print(board)
 board.advance_state()
 step += 1
 print(step, board.population)
 time.sleep(.1)

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!

from enum import Enum
from collections import defaultdict
import curses
import numpy as np
import time
class State(Enum):
 dead = 0
 alive = 1
class Board:
 def __init__(self, m, n, init):
 self.m = m
 self.n = n
 self.size = m * n
 self.board_ = [
  [State(init[i][j]) for j in range(self.n)] for i in range(self.m)
 ]
 def count_live_neighbours(self, x, y):
 count = 0
 for i in range(x - 1, x + 2):
 for j in range(y - 1, y + 2):
 if i == x and j == y or i < 0 or j < 0:
 continue
 try:
 count += self.board_[i][j].value
 except IndexError:
 continue
 return count
 def next_cell_state(self, x, y):
 count = self.count_live_neighbours(x, y)
 cur_state = self.board_[x][y]
 if count in {2, 3} and cur_state == State.alive:
 return cur_state
 elif count == 3 and cur_state == State.dead:
 return State.alive
 return State.dead
 def next_board_state(self):
 return [
 [self.next_cell_state(i, j) for j in range(self.n)] for i in range(self.m)
 ]
 
 def advance_state(self):
 self.board_ = self.next_board_state()
 def has_live_cells(self):
 return any(cell.value for row in self.board_ for cell in row)
 @property
 def population(self):
 return sum(cell.value for row in self.board_ for cell in row)
 def __str__(self):
 return '\n'.join(
 [''.join(['*' if cell.value else ' ' for cell in row]) for row in self.board_]
 )
if __name__ == '__main__':
 arr = np.random.choice([0, 1], (20, 100), p=[0.90, 0.1]) #np.loadtxt('glider.dat', dtype=int)
 step = 0
 board = Board(arr.shape[0], arr.shape[1], init=arr.tolist())
 while board.has_live_cells():
 step += 1
 print(board)
 print(step, board.population)
 board.advance_state()
 time.sleep(.1)

My next step with this program would likely be to host it on a server and let people run it on their browser with a Step and Run button. This might influence how I'd need to structure my code. Thanks!


Changelog: removed the Cell class, it wasn't necessary.

added 44 characters in body
Source Link
coldspeed
  • 395
  • 2
  • 11
Loading
Source Link
coldspeed
  • 395
  • 2
  • 11
Loading
lang-py

AltStyle によって変換されたページ (->オリジナル) /