Построение статических паттернов Game of Life в Python Matplotlib - PullRequest
2 голосов
/ 27 апреля 2019

Существует множество программ на Python для игры в классическую «Игру жизни», и большинство из них предлагают приятную графическую анимацию, которая позволяет следовать фантастическим паттернам, появляющимся в игре.Такие программы хороши для интерактивного использования на компьютере, но мне было очень трудно найти такую, которая обеспечивает статический графический вывод, предназначенный для печатных страниц (например, те, что были показаны в статье Мартина Гарднера 1970 года, в которой миру представили «Игру жизни»: Фантастические комбинации новой пасьянса Джона Конвея "Жизнь" ).Есть много программ, которые предлагают текстовый вывод шаблонов Life, но я не нашел ни одной, способной сделать то же самое с графикой Matplolib.Итак, я начал писать один, как показано в коде ниже:

import matplotlib.pyplot as plt

def iterate(Z):
    shape = len(Z), len(Z[0])
    N = [[0,]*(shape[0]+2)  for i in range(shape[1]+2)]
    # Compute number of neighbours for each cell
    for x in range(1,shape[0]-1):
        for y in range(1,shape[1]-1):
            N[x][y] = Z[x-1][y-1]+Z[x][y-1]+Z[x+1][y-1] \
                    + Z[x-1][y]            +Z[x+1][y]   \
                    + Z[x-1][y+1]+Z[x][y+1]+Z[x+1][y+1]
    # Update cells
    for x in range(1,shape[0]-1):
        for y in range(1,shape[1]-1):
            if Z[x][y] == 0 and N[x][y] == 3:
                Z[x][y] = 1
            elif Z[x][y] == 1 and not N[x][y] in [2,3]:
                Z[x][y] = 0
    return Z

# The 'beehive' pattern
Z = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

n_generations = 10
fig, axes = plt.subplots(2, 5, figsize=(8, 8))
for i in range(n_generations):
    iterate(Z)
    ax = axes.flat[i]
    ax.imshow(Z, interpolation='nearest', cmap=plt.cm.binary)
    ax.set_axis_off()
    ax.set_title('Generation {}'.format(i+1))
plt.grid(True)
plt.tight_layout()
plt.show()

Это работает, но это далеко не хорошо, потому что:

(1) Мне бы хотелось, чтобы каждый график показываллинии сетки, чтобы они могли воспроизвести оригинальные рисунки статьи Гарднера, но я не смог выяснить, как это сделать;

(2) Я бы также хотел иметь возможность использовать сферы вместоквадраты для представления живых клеток (так же, как они появляются в статье Гарднера);

Любая помощь в улучшении этого кода будет принята с благодарностью!

Ответы [ 2 ]

2 голосов
/ 27 апреля 2019

Для создания линий сетки вам нужны галочки в соответствующих позициях. Использование MultipleLocator приведет к тикам, кратным 1.

Круги являются стандартными маркерами рассеяния. Вы можете отобразить данные как разброс вместо изображения.

Вместе это может выглядеть следующим образом, где я также сделал код немного более компактным.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker


def iterate(Z):
    # http://www.labri.fr/perso/nrougier/from-python-to-numpy/code/game_of_life_numpy.py
    N = (Z[0:-2, 0:-2] + Z[0:-2, 1:-1] + Z[0:-2, 2:] +
         Z[1:-1, 0:-2]                 + Z[1:-1, 2:] +
         Z[2:  , 0:-2] + Z[2:  , 1:-1] + Z[2:  , 2:])
    birth = (N == 3) & (Z[1:-1, 1:-1] == 0)
    survive = ((N == 2) | (N == 3)) & (Z[1:-1, 1:-1] == 1)
    Z[...] = 0
    Z[1:-1, 1:-1][birth | survive] = 1
    return Z

# The 'beehive' pattern
Z = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
     [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Z = np.array(Z)

X, Y = np.meshgrid(np.arange(Z.shape[1])+.5, np.arange(Z.shape[0])+.5)

fig, axes = plt.subplots(2, 5, figsize=(8, 4))
for i, ax in enumerate(axes.flat):
    Z = iterate(Z)

    ax.scatter(X[Z > 0], Y[Z > 0], color="k")

    ax.grid(True, color="k")
    ax.xaxis.set_major_locator(mticker.MultipleLocator())
    ax.yaxis.set_major_locator(mticker.MultipleLocator())
    ax.tick_params(size=0, length=0, labelleft=False, labelbottom=False)
    ax.set(xlim=(0, Z.shape[1]), ylim=(Z.shape[0], 0),
           title='Generation {}'.format(i+1), aspect="equal")


plt.tight_layout()
plt.show()

enter image description here

1 голос
/ 27 апреля 2019

Для первой проблемы это потому, что вы отключили оси. Я включил его и сделал несколько настроек:

n_generations = 10
fig, axes = plt.subplots(2, 5, figsize=(16, 8))
for i in range(n_generations):
    iterate(Z)
    ax = axes.flat[i]
    ax.imshow(Z, interpolation='nearest', cmap=plt.cm.binary)
#     ax.set_axis_off()
    ax.set_xticks(np.arange(10)+.5)
    ax.set_yticks(np.arange(10)+.5)
    ax.set_xticklabels('')
    ax.set_yticklabels('')
    ax.set_title('Generation {}'.format(i+1))

plt.tight_layout()
plt.show()

Выход: enter image description here

По другому вопросу. Я не думаю, что это возможно с imshow. Возможно, вам придется написать пользовательскую функцию my_plot(Z, ax).

...