-
Notifications
You must be signed in to change notification settings - Fork 0
/
gol.py
115 lines (102 loc) · 5.13 KB
/
gol.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from collections import deque
import argparse
import numpy as np
import matplotlib.pyplot as plt
class Field:
def __init__(self):
# очередь для проверки на стабильность поля
self.same = deque([_ for _ in range(5)], maxlen=10)
# Создаем одномерный массив из 0/1 с вероятностями p
# Вероятность мертвой клетки 1 - LIFE_PRT (то есть клетка будет иметь значение 0,
# с вероятностью 95%). Живой - соостветственно наоборот
self.fieldmap = np.array([np.random.choice([0, 1], size=FIELD_SIZE ** 2,
p=[1 - LIFE_PRT / 100, LIFE_PRT / 100])])
# решейпим поле в 2D
self.fieldmap.shape = (self.fieldmap.size // FIELD_SIZE, FIELD_SIZE)
def new_turn(self):
# Итерируемся по полю
for ix, iy in np.ndindex(self.fieldmap.shape[0], self.fieldmap.shape[1]):
# сохраняем значение клетки и сумму ее окружения
cell = self.fieldmap[ix, iy]
# берем 9 клеток (8 из окружения и сама клетка), суммируем (так как значения 0, 1)
# вычитаем исследуемую клетку
arnd = self.fieldmap[ix - 1: ix + 2, iy - 1: iy + 2].sum() - cell
# если клетка мертвая и вокруг 3 живых - создаем жизнь
if arnd == 3 and cell == 0:
self.fieldmap[ix, iy] = 1
# иначе если вокруг живой клетки меньше 2х или больше 3х клеток - убиваем
# итого получается, что не охваченные условиями клетки остаются в прежних состояниях
elif cell and (arnd < 2 or arnd > 3):
self.fieldmap[ix, iy] = 0
def cells_alive(self):
population = self.fieldmap.sum()
self.same.append(population)
return population
def field_change(self):
return len(set(self.same)) > 1
def parse_args():
parser = argparse.ArgumentParser(description="Conway's Game of Life yet another implementation")
parser.add_argument(
'-f',
'--field-size',
type=int,
default=128,
help="Field size in cells. You need to specify just one side, since it's square anyway... Default is 128x128"
)
parser.add_argument(
'-l',
'--life-proc',
type=int,
default=5,
help="Percent of alive cells. Default value is 5"
)
parser.add_argument(
'-t',
'--time',
type=int,
default=40,
help="Pause between ticks in milliseconds. Default value is 40"
)
parser.add_argument(
'-s',
'--seed',
type=int,
default=None,
help="Seed for random number generator. Default value is None, for random start every time"
)
return parser.parse_args()
if __name__ == "__main__":
# ####### initial values ###########
args = parse_args()
# Размер поля
FIELD_SIZE = args.field_size
# Процент живых клеток на поле
LIFE_PRT = args.life_proc
# seed для генератора случайных чисел
SEED = args.seed
# cколько времени стоим на паузе, переводим из миллисекунда в секунды
PAUSE = args.time / 1000
# выбираем сид, с которым будем инициализироваться
np.random.seed(SEED)
# ##################################
new_field = Field()
# Цикл до тех пор пока на поле есть живые и живых менее 39% (эмпирическим путем выясненная величина,
# стабильное состояние игрового поля). Кроме того, заканчиваем, если в течение 10 ходов ничего не
# поменялось
# Вынес из условия цикла, чтобы не плодить ненужные расчеты
overpopulated = FIELD_SIZE ** 2 * .39
current_cells = new_field.cells_alive()
while current_cells and current_cells < overpopulated and new_field.field_change():
# spy - предназначен для отрисовки разреженных матриц, но сгодится и для нас
plt.spy(new_field.fieldmap, markersize=2)
plt.title(f'{(current_cells * 100 / FIELD_SIZE ** 2):.2f}%')
# рисуем то, что вытащили на spy
plt.draw()
# пауза
plt.pause(PAUSE)
# чистим холст
plt.clf()
# считаем новое состояние
new_field.new_turn()
# считаем количество оставшихся в живых на поле
current_cells = new_field.cells_alive()