Rozdział 19 · Architektura

GameState z dataclass

Jedna struktura danych opisuje całą grę, bez żadnego obiektu Turtle środku.

Jedna struktura zamiast ośmiu oddzielnych zmiennych

Zebrać wszystkie pola podzielone na części — węża, kierunek, jedzenie, wynik, status, Prędkość to jeden. @dataclass, ten sam pomysł co DrawingState w rozdziale 18:

GameStatesnake: list[Position]direction: Directionnext_direction:Directionfood: Position | Nonescore: inthigh_score: intstatus: GameStatusdelay_ms: int
Żadnych obiektów Turtle — tylko dane w pełni opisujące aktualną grę.
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
Dlaczego food w ogóle można None
Dziewięćdziesiąt dziewięć meczów na sto food jest zwykłą klatką. Ale sekcja 19.18 pokazuje szczery, skrajny przypadek: jeśli wąż zajmuje dosłownie całe pole, nie będzie wolnej klatki na nowe jedzenie. Rodzaj Model Position | None sprawia, że ten przypadek jest widoczny w samej sygnaturze, a nie ukryty — kod, który czyta state.foodjest zobowiązany jasno zdecydować, co zrobić, jeśli nie ma jedzenia.
Pokusa, aby zapisać tutaj obiekt Turtle- — częsty błąd
Jeśli wpiszesz head_turtle w środku GameState, testy z rozdziału 19.30 przestaną działać bez prawdziwego okna, a „model” przestanie być modelem — znowu połączy się z wyświetlaniem, jak w pierwszym prototypie. Rozdział 19.31 omawia ten błąd jako osobny Debug Lab.

next_direction — oddzielny od direction pole z jakiegoś powodu: Rozdział 19.31 (Debug Labs 5-6) wyjaśnia, dlaczego klawiatura żąda kierunek i nie zmienia go bezpośrednio.

Praktyka: projektujemy i porównujemy GameState
Automatyczna weryfikacja — klasa GameState: tworzenie, porównanie i dataclasses.replace()
Otwórz praktykę →