Игра "Жизнь" на языке Python
Правила
Дано бесконечное поле, разделенное на ячейки. Ячейка может быть свободной или занятой “живой” клеткой. Клетки на поле образуют колонию. У каждой ячейки есть восемь ближайших смежных (граничащих) с ней ячеек, которые могут быть заняты другими клетками.
Колония изменяется по следующим правилам:
- Если у клетки заняты соседями две или три смежных ячейки, то клетка “выживает” и переходит в следующее поколение, в противном случае она “умирает” от перенаселения или недостатка соседей.
- Если рядом с пустой ячейкой находятся ровно три живые клетки, то на месте этой ячейки появляется новая клетка.
- В процессе смены поколения “умирающие” клетки считаются принадлежащими колонии до определения всех новых клеток и клеток, переходящих в следующее поколение. Также и рождающиеся считаются принадлежащими следующему поколению и не оказывают влияние на количество соседей клеток текущего поколения.
Колония
Колония задана множеством пар координат клеток на бесконечном поле, например:
colony = { (2,2), (3,2), (4,2), (4,3), (3,4) }
Список соседей
Функция, формирующая список пар координат смежных ячеек для клетки с координатами (x,y). Функция создает список координат смежных ячеек, а затем при помощи функции filter оставляет только те ячейки, которые заняты клетками.
def get_neighbours(cell, colony):
# Кортеж смещений координат смежных ячеек
dxdy = ((1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1),(1,0))
# Пары координат 8 смежных ячеек
nearest_pairs = [(cell[0]+dx,cell[1]+dy) for dx,dy in dxdy]
# Оставляет только те из них, которые заняты живыми клетками
neighbours = set(filter(lambda c: c in colony,nearest_pairs))
return neighbours
Например, для колонии
colony = { (2,2), (3,2), (4,2), (4,3), (3,4) }
функция вернет
get_neighbours((3,2),colony)
> {(2, 2), (4, 2), (4, 3)}
Ареал колонии
Ареал колонии это множество пустых ячеек, с каждой из которых граничит хотя бы одна живая клетка (занятая ячейка) колонии. Функция get_areal при помощи списочного выражения формирует список смежных ячеек для каждой клетки колонии, объединяя затем все смежные клетки в одно множество. Из полученного множества вычитается множество ячеек, занятых клетками колонии, чтобы получить только ареал колонии.
def get_areal(colony):
dxdy = [(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1),(1,0)]
areal = set([(cell[0]+dx,cell[1]+dy) for dx,dy in dxdy for cell in colony])
return areal.difference(colony)
Для рассматриваемого примера колонии функция get_areal вернёт:
area = get_areal(colony)
print(area)
> {(4, 4), (2, 4), (1, 2), (2, 1), (4, 1), (3, 1), (5, 4), (1, 1), (5, 1), (2, 3), (4, 5), (3, 3), (5, 3), (2, 5), (1, 3), (3, 5), (5, 2)}
Следующее поколение
def next_generation(colony):
# Рождающиеся клетки (клетка из ареала колонии с 3 соседями)
born = set(filter(lambda c: len(get_neighbours(c, colony))==3,get_areal(colony)))
# Клетка переходящая в следующее поколение (2 или 3 соседа)
live = set(filter(lambda c: len(get_neighbours(c, colony)) in (2,3), colony))
# Объединяем рождающиеся клетки с выжившими клетками в одно множество
next_generation = born.union(live)
return next_generation
Для рассматриваемого примера колонии функция next_generation вернёт:
print(next_generation(colony))
> {(2, 3), (4, 3), (3, 1), (3, 2), (4, 2)}
colony = { (2,2), (3,2), (4,2), (4,3), (3,4) }
for i in range(0,5):
fig,ax = plt.subplots(figsize=[3,3])
[ax.add_patch(Rectangle((x, y), 1, 1)) for x,y in colony]
plt.xlim([-1,7])
plt.ylim([-1,7])
plt.grid(ls=':')
colony = next_generation(colony)
Поколение 1 | Поколение 2 | Поколение 3 |
---|---|---|
![]() | ![]() | ![]() |
Поколение 4 | Поколение 5 | |
![]() | ![]() |