Part 14

Завершение игры

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

Счетчик ходов

Счетчик ходов у нижнего края игрового окна показывает количество ходов, сделанных игроком к текущему моменту. Его можно использовать, чтобы найти решение с наименьшим количеством ходов.

Счетчик требует некоторых изменений в коде. Сначала изменим конструктор так, чтобы для счетчика было достаточно места и чтобы в нашем распоряжении был подходящий шрифт для рисования текста:

    def __init__(self):
        ...
        self.window = pygame.display.set_mode((window_width, window_height + self.scale))

        self.game_font = pygame.font.SysFont("Arial", 24)
        ...

Счетчик ходов инициализируется нулем в начале игры. Каждый ход увеличивает его на единицу:

    def new_game(self):
        ...
        self.moves = 0
    def move(self, move_y, move_x):
        ...
        self.moves += 1

Каждый раз при обновлении содержимого окна на экране также должно обновляться количество сделанных ходов:

    def draw_window(self):
        ...
        game_text = self.game_font.render("Moves: " + str(self.moves), True, (255, 0, 0))
        self.window.blit(game_text, (25, self.height * self.scale + 10))
        ...

Новая игра и выход из игры

Теперь добавим клавиатурные команды для начала новой игры с F2 и выхода из игры с Esc. Обе довольно легко реализовать:

    def check_events(self):
        ...
                if event.key == pygame.K_F2:
                    self.new_game()
                if event.key == pygame.K_ESCAPE:
                    exit()
        ...

Также стоит добавить информацию об этой функциональности, чтобы игрок ее видел:

    def draw_window(self):
        ...
        game_text = self.game_font.render("F2 = new game", True, (255, 0, 0))
        self.window.blit(game_text, (200, self.height * self.scale + 10))

        game_text = self.game_font.render("Esc = exit game", True, (255, 0, 0))
        self.window.blit(game_text, (400, self.height * self.scale + 10))
        ...

Решение игры

Игрок решил игру, когда каждая коробка находится на одной из целевых клеток. Следующий метод отвечает за эту проверку:

    def game_solved(self):
        for y in range(self.height):
            for x in range(self.width):
                if self.map[y][x] in [2, 6]:
                    return False
        return True

Метод проходит по всем клеткам игровой сетки. Если какая-либо клетка равна 2 (пустая целевая клетка) или 6 (робот на целевой клетке), игра еще не решена, поэтому метод возвращает False. Если такой клетки в сетке нет, все целевые клетки должны быть заняты коробками, игра решена, и метод возвращает True.

Если игрок решает игру, следует показать подходящее сообщение с помощью метода draw_window:

    def draw_window(self):
        ...
        if self.game_solved():
            game_text = self.game_font.render("Congratulations, you solved the game!", True, (255, 0, 0))
            game_text_x = self.scale * self.width / 2 - game_text.get_width() / 2
            game_text_y = self.scale * self.height / 2 - game_text.get_height() / 2
            pygame.draw.rect(self.window, (0, 0, 0), (game_text_x, game_text_y, game_text.get_width(), game_text.get_height()))
            self.window.blit(game_text, (game_text_x, game_text_y))
        ...

Для полноты также изменим метод move так, чтобы игрок больше не мог двигаться после решения игры:

    def move(self, move_y, move_x):
        if self.game_solved():
            return
        ...

Однако игрок все еще может видеть игровую сетку и финальное состояние игры.

Подсказка для тестирования

При разработке игр часто бывает, что нужно проверить, что происходит в какой-нибудь более поздней ситуации в игре. Например, в этой игре такой ситуацией является момент, когда игра решена.

Проверить правильную работу такой ситуации может быть трудно, потому что обычно нужно решить игру, чтобы добраться до этой точки. Как программисты, мы можем временно упростить игру, чтобы ее было легче тестировать. Например, можно добавить следующее, чтобы временно упростить решение игры:

    def game_solved(self):
        return True

Теперь метод всегда возвращает True, что означает, что игра «решена» с самого начала. Это позволяет легко проверить, что уведомление в конце выглядит хорошо и что игрок больше не может двигаться по сетке после решения. Когда эта функциональность тщательно протестирована, изменения можно отменить.

Ваша игра на GitHub?

Игра теперь завершена. Если вы хотите простой способ поэкспериментировать с кодом и изображениями, можно получить исходный код с GitHub:

GitHub — популярное место для множества видов программных проектов. Его можно использовать для хранения исходного кода и других материалов всех ваших собственных программных проектов, а затем ваша программа будет поддерживаться с помощью системы контроля версий git, и ею можно будет легко делиться с другими. Вы очень хорошо познакомитесь с git и GitHub, если продолжите проходить другие курсы программирования mooc.fi.

Сколько ходов требуется?

Сетка в этой игре довольно маленькая, но сама игра не такая уж простая. Первая задача — просто пройти игру, но следующий этап — попытаться сделать это за как можно меньшее количество ходов. Насколько короток самый короткий путь к решению?

Поиск самого короткого возможного решения — совсем не простая задача, но для этого тоже существуют вычислительные решения. Они являются одной из тем курса Data Structures and Algorithms.

Вы дошли до конца этого раздела! Перейти к следующему разделу:

Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.

В этой части:
  1. 1. Игровой проект

  2. 2. Робот и коробки

  3. 3. Завершение игры

  4. 4. Ваша собственная игра