Conway's Game of Life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

conway.py 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import argparse
  2. import copy
  3. import os
  4. import sys
  5. import time
  6. DEFAULT_GRID = [
  7. [0, 0, 0, 0, 0],
  8. [0, 0, 1, 0, 0],
  9. [0, 0, 1, 0, 0],
  10. [0, 0, 1, 0, 0],
  11. [0, 0, 0, 0, 0],
  12. ]
  13. DEFAULT_SPEED = 500
  14. class GameOfLife:
  15. def __init__(self, speed: int = DEFAULT_SPEED, grid: list[list[int]] = DEFAULT_GRID):
  16. self.grid = grid.copy()
  17. self.height = len(self.grid)
  18. self.width = len(self.grid[0])
  19. self.speed = speed
  20. @classmethod
  21. def from_string(cls, template: str, speed: int = DEFAULT_SPEED):
  22. lines = template.strip().split('\n')
  23. for line in lines:
  24. print("line is: ", line, "+++")
  25. grid = [[int(col) for col in line] for line in lines]
  26. return GameOfLife(grid=grid, speed=speed)
  27. def play(self):
  28. while True:
  29. self.iterate()
  30. self.show()
  31. time.sleep(self.speed / 1000)
  32. def iterate(self):
  33. new_grid = copy.deepcopy(self.grid)
  34. for y, row in enumerate(self.grid):
  35. for x, current_value in enumerate(row):
  36. is_alive = current_value == 1
  37. living_neighbors = self.get_living_neighbors(y, x)
  38. if is_alive:
  39. if living_neighbors < 2:
  40. new_grid[y][x] = 0
  41. elif living_neighbors == 2 or living_neighbors == 3:
  42. new_grid[y][x] = 1
  43. else:
  44. new_grid[y][x] = 0
  45. else:
  46. if living_neighbors == 3:
  47. new_grid[y][x] = 1
  48. self.grid = new_grid
  49. def get_living_neighbors(self, y: int, x: int):
  50. neighbors = self.get_neighbors(y, x)
  51. living = [n for n in neighbors if n == 1]
  52. return len(living)
  53. def get_neighbors(self, y: int, x: int):
  54. neighbors = []
  55. if y > 0:
  56. neighbors.append(self.grid[y - 1][x]) # N
  57. if x > 0:
  58. neighbors.append(self.grid[y - 1][x - 1]) # NW
  59. if x < self.width - 1:
  60. neighbors.append(self.grid[y - 1][x + 1]) # NE
  61. if x > 0:
  62. neighbors.append(self.grid[y][x - 1]) # W
  63. if x < self.width - 1:
  64. neighbors.append(self.grid[y][x + 1]) # E
  65. if y < self.height - 1:
  66. neighbors.append(self.grid[y + 1][x]) # S
  67. if x > 0:
  68. neighbors.append(self.grid[y + 1][x - 1]) # SW
  69. if x < self.width - 1:
  70. neighbors.append(self.grid[y + 1][x + 1]) # SE
  71. return neighbors
  72. def show(self):
  73. self.clear()
  74. for row in self.grid:
  75. for col in row:
  76. if col == 0:
  77. # print('□', end='')
  78. print('·', end='')
  79. else:
  80. print('■', end='')
  81. print('\n')
  82. def clear(self):
  83. os.system('cls' if os.name == 'nt' else 'clear')
  84. if __name__ == "__main__":
  85. parser = argparse.ArgumentParser(description='Conway\'s Game of Life')
  86. parser.add_argument(
  87. '-f', '--file', help='The path to a file containing a starting grid. The grid file should use 0 for dead cells and 1 for living cells.')
  88. parser.add_argument(
  89. '-s', '--speed', help='The delay between iterations in milliseconds (default: 500, minimum: 100)', default=500, type=int),
  90. args = parser.parse_args()
  91. if args.speed < 100:
  92. print('Speed must be at least 100 milliseconds')
  93. exit(1)
  94. if args.file:
  95. with open(args.file, 'r') as f:
  96. contents = f.read()
  97. GameOfLife.from_string(contents, args.speed).play()
  98. else:
  99. GameOfLife(args.speed).play()