Глава 19 · Архитектура
GameState с dataclass
Одна структура данных описывает всю игру целиком — без единого объекта Turtle внутри.
Одна структура вместо восьми отдельных переменных
Собираем все поля, разобранные по частям — змейку, направление, еду, счёт, статус,
скорость — в один @dataclass, ту же идею, что и
DrawingState в главе 18:
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()