Лабораторная работа №3. Игра "Жизнь"

В этой лабораторной работе вашей задачей является написать симулятор игры «Жизнь».

Игру «Жизнь» изобрел математик Джон Хортон Конвей в 1970 году. Она пользовалась популярностью не только среди его коллег. Об увлекательности игры «Жизнь» свидетельствуют результаты множества интересных исследований и многочисленные компьютерные реализации. При этом она имеет непосредственное отношение к перспективной области математики - теории клеточных автоматов.

Правила игры «Жизнь» достаточно простые:

  • «Жизнь» разыгрывается на бесконечном клеточном поле.
  • У каждой клетки 8 соседних клеток.
  • В каждой клетке может жить существо.
  • Существо с двумя или тремя соседями выживает в следующем поколении, иначе погибает от одиночества или перенаселённости.
  • В пустой клетке с тремя соседями в следующем поколении рождается существо [1].

Подробнее про игру «Жизнь» можно прочитать в журнале Квант.

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

Для начала нам нужно создать игровое поле, на котором будет разворачиваться действие игры «Жизнь». Поэтому напишем класс GameOfLife, который принимает следующие параметры:

  • width - ширина окна (по умолчанию 640px);
  • height - высота окна (по умолчанию 480px);
  • cell_size - высота и ширина клетки (по умолчанию 10px).

Ниже приведен пример создания трех игровых полей одинаковых размеров, но с разным рамзером клеток (10, 20 и 40, соответственно):

Далее представлен листинг, который позволяет создать игровое поле:

import pygame
from pygame.locals import *


class GameOfLife:
    def __init__(self, width = 640, height = 480, cell_size = 10, speed = 10):
        self.width = width
        self.height = height
        self.cell_size = cell_size

        # Устанавливаем размер окна
        self.screen_size = width, height
        # Создание нового окна
        self.screen = pygame.display.set_mode(self.screen_size)

        # Вычисляем количество ячеек по вертикали и горизонтали
        self.cell_width = self.width // self.cell_size
        self.cell_height = self.height // self.cell_size

        # Скорость протекания игры
        self.speed = speed


   def draw_grid(self):
        # http://www.pygame.org/docs/ref/draw.html#pygame.draw.line
        for x in range(0, self.width, self.cell_size):
            pygame.draw.line(self.screen, pygame.Color('black'), 
                (x, 0), (x, self.height))
        for y in range(0, self.height, self.cell_size):
            pygame.draw.line(self.screen, pygame.Color('black'), 
                (0, y), (self.width, y))


   def run(self):
        pygame.init()
        clock = pygame.time.Clock()
        pygame.display.set_caption('Game of Life')
        self.screen.fill(pygame.Color('white'))
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == QUIT:
                    running = False
            self.draw_grid()
            pygame.display.flip()
            clock.tick(self.speed)
        pygame.quit()


if __name__ == '__main__':
    game = GameOfLife(320, 240, 20)
    game.run()
Подробное описание всех используемых функций из pygame читайте в официальной документации.

Каждая клетка может находиться в двух состояниях: "живая" или "мертвая". Нам нужно создать список (матрицу) клеток размером cell_height ×\times cell_width, которые в дальнейшем мы будем отображать на нашем поле, окрашивая их в зеленый и белый цвета, для "живых" и "мертвых" клеток соответственно.

def cell_list(self, randomize=False):
    """
    Создание списка клеток.

    Клетка считается живой, если ее значение равно 1. 
    В противном случае клетка считается мертвой, то 
    есть ее значение равно 0.
    Если параметр randomize = True, то создается список, где
    каждая клетка может быть равновероятно живой или мертвой.
    """
    pass

Пример использования:

>>> from pprint import pprint as pp
>>> game = GameOfLife(320, 240, 40)
>>> clist = game.cell_list(randomize=True)
>>> pp(clist)
[[1, 1, 0, 0, 1, 1, 1, 1],
 [0, 1, 1, 1, 1, 1, 1, 0],
 [1, 0, 1, 1, 0, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 1],
 [1, 0, 1, 1, 1, 1, 0, 0],
 [1, 1, 1, 1, 0, 1, 1, 1]]

Теперь нам нужно окрашивать клетки в зеленый или белый цвета в зависимости от того живая клетка или мертвая. Каждая клетка на поле представлена прямоугольником размера cell_size ×\times cell_size, который можно закрасить с помощью функции pygame.draw.rect. Эта функция принимает следующие параметры:

  • Surface - где нужно отрисовать прямоугольник (в нашем случае это screen);
  • color - цвет, которым следует закрасить прямоугольник (pygame.Color('white') или pygame.Color('green'));
  • Rect - координаты прямоугольника в формате (x, y, длина стороны a, длина стороны b).
def draw_cell_list(self, rects):
    """
    Отображение списка клеток 'rects' с закрашиванием их в 
    соответствующе цвета
    """
    pass
Добавьте вызовы метода draw_cell_list() в метод run() перед обновлением поля pygame.display.flip().

Теперь осталось написать метод для обновления поля, чтобы состояния клеток менялись по описанным в начале правилам. Чтобы определить как должно измениться состояние клетки необходимо получить состояние ее соседей. Напишите функцию, которая получает для клетки список ее соседей:

def get_neighbours(self, cell):
    """
    Вернуть список соседних клеток для клетки cell.

    Соседними считаются клетки по горизонтали,
    вертикали и диагоналям, то есть во всех
    направлениях.
    """
    pass

Теперь нужно написать функцию для обновления состояния всех клеток:

def update_cell_list(self, cell_list):
    """
    Обновление состояния клеток
    """
    pass
Важно помнить, что обновление всего поля должно происходить за один раз, если состояние клеток менять последовательно, то это повлияет на результат игры.

Теперь у вас должна быть полностью рабочая игра.

Изменим дизайн нашей игры. Вы могли заметить, что клетка это объект, который имеет атрибут (поле) - состояние; список клеток также является объектом, над которым можно выполнять такие действия как заполнение или обновление. Вашей задачей является написать два класса Cell CellList, которые представляют собой клетку и список ячеек (список объектов типа Cell). Ниже приведен пример интерфейса (возможных методов):

class Cell:

    def __init__(self, row, col, state=False):
        pass

    def is_alive(self):
        pass


class CellList:

    def __init__(self, nrows, ncols, randomize=False):
        pass

    def update(self):
        pass

    def get_neighbours(self, cell):
        pass

    @classmethod
    def from_file(cls, filename):
        pass

    def __iter__(self):
        pass

    def __next__(self):
        pass

    def __str__(self):
        pass

Класс CellList должен быть итерируемым (реализовывать iterator protocol), например, так можно пройтись по списку клеток и оживить случайные клетки:

>>> cell_list = CellList(nrow = 6, ncol = 8)
>>> for cell in cell_list:
        cell.state = random.randint(0,1)
>>> print(cell_list)
[[1, 1, 0, 0, 1, 1, 1, 1],
 [0, 1, 1, 1, 1, 1, 1, 0],
 [1, 0, 1, 1, 0, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 1],
 [1, 0, 1, 1, 1, 1, 0, 0],
 [1, 1, 1, 1, 0, 1, 1, 1]]

Также в классе CellList должно быть предусмотрено заполнение списка клеток данными из файла:

>>> cell_list = CellList.from_file(filename = 'game.txt')
>>> print(cell_list)
[[1, 1, 0, 0, 1, 1, 1, 1],
 [0, 1, 1, 1, 1, 1, 1, 0],
 [1, 0, 1, 1, 0, 0, 0, 0],
 [1, 0, 0, 0, 0, 0, 0, 1],
 [1, 0, 1, 1, 1, 1, 0, 0],
 [1, 1, 1, 1, 0, 1, 1, 1]]

[1] https://life.written.ru - программа для моделирования игры «Жизнь»

results matching ""

    No results matching ""