Глава 19 · Архитектура

GameState с dataclass

Одна структура данных описывает всю игру целиком — без единого объекта Turtle внутри.

Одна структура вместо восьми отдельных переменных

Собираем все поля, разобранные по частям — змейку, направление, еду, счёт, статус, скорость — в один @dataclass, ту же идею, что и DrawingState в главе 18:

GameStatesnake: list[Position]direction: Directionnext_direction:Directionfood: Position | Nonescore: inthigh_score: intstatus: GameStatusdelay_ms: int
Ни одного объекта Turtle — только данные, которые полностью описывают текущую игру.
gamestate.py
@dataclass
class GameState:
    snake: list[Position] = field(default_factory=lambda: [(0, 0)])
    direction: Direction = Direction.RIGHT
    next_direction: Direction = Direction.RIGHT
    food: Position | None = (0, 0)
    score: int = 0
    high_score: int = 0
    status: GameStatus = GameStatus.READY
    delay_ms: int = BASE_DELAY_MS
Почему food вообще может быть None
На девяноста девяти играх из ста food — обычная клетка. Но раздел 19.18 показывает честный крайний случай: если змейка займёт буквально всё поле, свободной клетки для новой еды не останется. Тип Position | None делает этот случай видимым в самой сигнатуре, а не спрятанным — код, который читает state.food, обязан явно решить, что делать, если еды нет.
Соблазн сохранить сюда Turtle-объект — частая ошибка
Если положить head_turtle внутрь GameState, тесты из раздела 19.30 перестанут работать без настоящего окна, а «модель» перестанет быть моделью — она снова срастётся с отображением, как в первом прототипе. Раздел 19.31 разбирает эту ошибку как отдельный Debug Lab.

next_direction — отдельное от direction поле неспроста: раздел 19.31 (Debug Labs 5–6) объясняет, зачем клавиатура запрашивает направление, а не меняет его напрямую.

Практика: конструируем и сравниваем GameState
Автоматическая проверка — класс GameState: создание, сравнение и dataclasses.replace()
Открыть практику →