Skip to content

Instantly share code, notes, and snippets.

@ap-Codkelden
Last active April 5, 2023 21:23
Show Gist options
  • Save ap-Codkelden/100b659dbe41de7e60118988ac421110 to your computer and use it in GitHub Desktop.
Save ap-Codkelden/100b659dbe41de7e60118988ac421110 to your computer and use it in GitHub Desktop.
A simple John Conway Life game
#! /usr/bin/env python3
# More about Conway's Game of Life you can read here:
# < https://conwaylife.com/wiki/Conway%27s_Game_of_Life>
import random
import time
import sys
import os
from typing import Final, List, Tuple
# Ширина, висота сітки
width: int = 70
height: int = 35
# Кольори у вигляді керуючих послідовностей ANSI
GREEN: Final = '\033[32m'
BLACK: Final = '\033[30m'
DARKGREY: Final = '\033[90m'
LIGHTGREY: Final = '\033[97m'
def make_grid(neighbors_data: List[List[Tuple[int]]]) -> List[List[int]]:
"""
Будує сітку, виходячи з даних про клітину та її сусідів, за правилами
Дж. Конвея
Аргументи:
neighbors_data: кортеж із даними про клітину та кількість її сусідів
Повертає:
Список списків з клітинами
"""
new_grid: List[List[int]] = []
# Перебираємо всі рядки, по кожному рядку народжуємо нову клітину, або ж
# знищуємо стару.
# Клітина повертається функцією `raise_cell_or_die()`
for row in neighbors_data:
new_row = [raise_cell_or_die(*c) for c in row]
new_grid.append(new_row)
return new_grid
def raise_cell_or_die(cell_state: int, neighbors_count: int) -> int:
"""
Повертає стан клітини (0 або 1) у відповідності до даних про кількість її
сусідів за правилами Дж. Конвея
Аргументи:
cell_state: клітина (0 або 1)
neighbors_count: загальна кількість сусідів у сміжних клітинах з нею
Повертає:
клітину живу (1) або ні (0)
"""
if cell_state == 0:
return 1 if neighbors_count == 3 else 0
if cell_state == 1:
if neighbors_count <= 1 or neighbors_count >= 4:
return 0
return 1
def display_grid(grid: List[List[int]], gen: int = None) -> None:
"""
Виводить на екран колонію клітин.
Аргументи:
grid: сітка з клітинами
gen: номер покоління. Необовʼязковий параметр. Якщо не вказаний,
виводиться 'Undefined'
"""
if gen is None:
gen = 'Undefined'
k = ["".join([GREEN+' @' if x else DARKGREY+' .' for x in row]) for row in grid]
at_grid = "\n".join(k)
print(BLACK + f"Generation: {gen}\n" + at_grid)
return
def get_neighbor_cell_positions(x: int, y: int) -> List[Tuple[int, int]]:
"""
Отримання координат навколишніх клітин у сітці для клітини з координатами
x та y.
Аргументи:
x: x-координата
y: y-координата
Повертає:
Список кортежів вигляду [(x1, y1), ... (xn, yn)] для координат кожної з
оточуючих клітин, які лежать в межах сітки.
"""
pos_box: List[Tuple[int, int]] = [] # порожній список для повернення
k: List[int] = [-1, 0, 1] # відносний зсув по координатах у всі боки
# Розрахунок зсуву для поточних координат
# Координати додаються у список для повернення
for i in k:
for j in k:
pos_x: int = x + i
pos_y: int = y + j
# за винятком координат самої клітини, координат, що містять
# X або Y, які менші за 0 або ж більші за розмір сітки (а значить
# лежать за межами сітки)
if (i == j == 0) or (-1 in (pos_x, pos_y)) or pos_y >= width \
or pos_x >= height:
# наступний крок
continue
# додавання у список
pos_box.append((pos_x, pos_y),)
return pos_box
def calculate_neigbors(grid: List[List[int]]) -> List[List[Tuple[int]]]:
"""
Підрахунок кількості живих клітинок навколо кожної з клітин колонії.
Аргументи:
grid: сітка
Повертає:
Список списків по кількості рядків. Кожен список рядка містить
відповідну клітинам кількість кортежів. Кожен кортеж в свою чергу
містить саму клітину (0 чи 1), та кількість живих сусідів навколо неї.
"""
new_grid: List = [] # порожній список для списків рядків
for i in range(height):
new_row: List = [] # порожній рядок
for j in range(width):
# отримуємо координати x та y
neigbor_positions = get_neighbor_cell_positions(i, j)
try:
# для кожної пари координат отримуємо клітину з цієї пари
# координат, живу чи мертву (0 або 1)
# В результаті отримуємо список з нулів та одиниць,
# в подальшому сума цього списку дасть нам кількість
try:
cells: List[int] = [grid[x][y] for (x, y) in neigbor_positions]
except Exception as _:
raise
except IndexError:
# В разі помилки буде виведено повідомлення, і робота
# завершиться
print(f"Out of boundaries for cell at {i}, {j}!\n")
sys.exit()
# Додоаємо саму клітину та кількість живих сусідів у рядок,
new_row.append((grid[i][j], sum(cells)))
# а рядок -- у сітку
new_grid.append(new_row)
return new_grid
def clear() -> None:
"""
Процедура очистки терміналу шляхом надіслання відповідної команди
у термінал
"""
platform = sys.platform
if platform.startswith('darwin'):
os.system("clear")
elif platform.startswith('win'):
os.system("cls")
return
def resize_terminal() -> None:
"""
Змінює розмір терміналу відповідно до кожного типу операційної системи
"""
if sys.platform.startswith('darwin'):
command = "\x1b[8;{rows};{cols}t".format(rows=height + 4, cols=width + width)
sys.stdout.write(command)
elif sys.platform.startswith('win'):
command = "mode con: cols={0} lines={1}".format(width + width, height + 5)
os.system(command)
elif sys.platform.startswith("linux"):
command = '\x1b[8;{rows};{cols}t'.format(rows=height + 4, cols=width + width)
os.system(command)
else:
print("Your operating system is not supported.\n\r")
sys.exit()
return
def run():
"""
Рушій
"""
c = 0 # лічильник поколінь
resize_terminal()
# початкова сітка з віпадковою генерацією живих клітин
grid = [[0 if random.randint(0, 1) == 0 else 1 for _ in range(width)] for _ in range(height)]
# показуємо початкову сітку
display_grid(grid)
try:
while True:
# очищаємо екран від сітки, показуємо сітку, потім
time.sleep(.85) # тримаємо сітку на екрані 0,85 с
# розраховуємо сусідів клітин
# і на основі цих даних створюємо нову сітку
neighbors = calculate_neigbors(grid)
grid = make_grid(neighbors)
# очищаємо екран і показуємо нову сітку
clear()
display_grid(grid, gen=c)
# збільшуємо лічильник поколінь
c += 1
# Якщо натиснута комбінація Ctrl+C, очищаємо екран і пишемо інформацію про
# кількість поколінь
except KeyboardInterrupt:
clear()
print(BLACK + f"End of Life Game. {c} generations.")
if __name__ == "__main__":
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment