Лабораторная работа №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
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
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 - программа для моделирования игры «Жизнь»