Игра "Жизнь" на языке 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 
 

© 2024. All rights reserved.

Powered by Hydejack v9.1.6