Глава 19 · Функции игры

Restart / New Game

Новая игра обязана оборвать старую цепочку тиков — иначе на мгновение окажутся две независимые цепочки тиков сразу.

Restart обязан оборвать старую цепочку тиков

Реальное окно: свежая игра с одной головой в центре, табло Счёт: 0 Рекорд: 40
Реальное окно после restart(): свежая змейка, счёт обнулён, рекорд остался.

Restart не просто возвращает змейку в исходную позицию — раздел 19.22 предупреждал: если старая цепочка ontimer() всё ещё тикает, у игры на мгновение оказалось бы две независимые цепочки обновлений одновременно (глава 16 уже разбирала похожую проблему с повторным нажатием кнопки).

restart.py
def restart(self):
    self._generation += 1   # обрывает любую ещё тикающую цепочку прошлой игры
    high_score = self.state.high_score
    self.state = new_game_state(self.rng, high_score=high_score)
    self._clear_overlay()
    self.render()
generation_guard.py
def _schedule_next_tick(self):
    generation = self._generation
    self.screen.ontimer(lambda: self._on_timer(generation), self.state.delay_ms)

def _on_timer(self, generation):
    if generation != self._generation:
        return   # просроченная цепочка от игры до restart() — сама себя гасит
    self.game_tick()
    if self.state.status is GameStatus.RUNNING:
        self._schedule_next_tick()
Поколение (generation) — простой счётчик, не сложная архитектура
Каждый restart() увеличивает _generation на единицу. Любой уже запланированный _on_timer() запомнил СТАРОЕ значение — когда он наконец сработает, число не совпадёт, и он тихо ничего не сделает вместо того, чтобы двигать уже несуществующую змейку прошлой игры.

high_score — единственное поле, которое restart переносит из старого состояния в новое; всё остальное создаётся заново через new_game_state() (раздел 19.25).

Практика: restart и просроченные тики
Автоматическая проверка — функции new_game_state() и restart(): рекорд переживает рестарт, поколение растёт
Открыть практику →