Печать

fsd

ssdg

from tkinter import *       # импортируем всё из графической библиотеки tkinter
import types                # для динамического добавления метода show к каждой кнопке

cols = rows = 12            # столбцы и строки (определяют размер игрового поля)
playground = [False] * cols * rows  # список состояний клеток:
                                    # False = мертва (чёрная), True = жива (зелёная)
colors = ['black', 'green'] # Индекс для colors берём из playground
                            # и таким образом определяем цвет кнопки
power = False               # флаг, запущена игра или включён режим редактирования

def start(event):
    ''' обработчик нажатия на клавишу пробел — запуск/останов игры '''
    global power
    power = not power       # переключаем состояние (игра или режим редактирования)
    if power:               # если игра запущена, вызываем функцию life()
        life()

def roomie(n):
    """Подсчитывает количество живых соседей для клетки с индексом n."""
    s = 0                   # счётчик соседей
    for i in (-1, 0, 1):    # перебираем номера 8-ми кнопок, соседей кнопки n
        for j in (-cols, 0, cols):
            # исключаем из соседей клетку с номером n и соседей за пределами поля
            if i+j != 0 and n+i+j >= 0 and n+i+j < cols*rows:
                s += playground[n+i+j]
    return s

def life():                 # Conway Game Life
    """
    Выполняет один шаг игры «Жизнь» и планирует следующий шаг через 500 мс.
    Клетка становится живой, если у неё 3 соседа (рождение)
    или она уже жива и у неё есть 2 соседа (выживание)
    """
    global playground
    pg = [False] * (cols * rows)    # временное поле для нового поколения
    for i in range(cols * rows):
        s = roomie(i)               # количество живых соседей
        pg[i] = s == 3 or (playground[i] and s == 2)    # правила Конвея
    playground = pg
    if power:                       # если игра запущена,
        tk.after(500, life)         # планируем следующий шаг

def play(n):                        # обработчик нажатия на кнопку-клетку
    playground[n] = not playground[n]   # инвертируем состояние клетки

def show(self):
    """
    Метод, добавляемый каждой кнопке. Обновляет цвет кнопки
    в соответствии с состоянием клетки (состояние клетки в playground).
    """
    self.config(bg=colors[playground[self.n]],
                activebackground=colors[playground[self.n]])
    self.after(400, self.show)      # повторяем обновление каждые 400 мс

tk = Tk()                           # создаём главное окно программы
tk.geometry('400x400')              # устанавливаем размер окна
tk.title("Conway Game")             # заголовок окна программы
tk.bind('<space>', start)           # привязываем пробел к функции start

# Создаём игровое поле из кнопок, расположенных в rows рядов по cols штук в каждом
for n in range(cols * rows):        # цикл для перебора всех клеток
    if n % cols == 0:               # если n кратно количеству колонок, начинаем новый ряд
        f = Frame()                 # создаём фрейм (контейнер для нового ряда)
        f.pack(expand=YES, fill=BOTH)   # размещаем фрейм в окне, создавая новый ряд

    btn = Button(f, font=('mono', 1))   # создаём маленькую кнопку
    btn.pack(expand=YES, fill=BOTH, side=LEFT)  # отрисовываем кнопку
    btn.config(command=lambda n=n: play(n))     # привязываем функцию к кнопке
    btn.n = n                       # сохраняем номер клетки в атрибуте кнопки
    btn.show = types.MethodType(show, btn) # добавляем метод show() к каждой кнопке
    btn.show()                      # запускаем бесконечное обновление цвета кнопки

mainloop()

Лист. 1

Рис. 1