Глава 19 · Функции игры
Restart / New Game
Новая игра обязана оборвать старую цепочку тиков — иначе на мгновение окажутся две независимые цепочки тиков сразу.
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(): рекорд переживает рестарт, поколение растёт