Rozdział 17 · Ostatnie poprawki
Tic-Tac-Toe Pro — pełny program i wyniki rozdziału
Ta sama gra co w rozdziale 17.6 — ale teraz z modelem, zasadami, testami i architekturą, które wytrzymają rozwój projektu.
Program końcowy
Plik jest kompletny, samodzielny i nie posiada niewidzialnych zależności od innych lekcji:
projects/tkinter/tic-tac-toe/tic_tac_toe.py
tic_tac_toe.py — struktura
WINNING_LINES = (...)
def find_winner(board): ...
def is_draw(board): ...
def load_scores(): ... # opcjonalne zapisywanie wyniku (17.27)
def save_scores(state): ...
@dataclass
class GameState:
...
class TicTacToeApp:
def __init__(self, root, *, persist_scores=False): ...
def build_ui(self): ...
def build_board(self, outer): ...
def attempt_move(self, index): ...
def on_cell_enter(self, index): ...
def on_cell_leave(self, index): ...
def on_key(self, event): ...
def render(self): ...
def new_round(self): ...
def new_match(self): ...
def pulse_winning_line(self, tick=0): ...
def cancel_pulse(self): ... # odwołuje zaplanowany after() (17:30)
def on_close(self): ...
def main():
root = tk.Tk()
app = TicTacToeApp(root)
root.mainloop()
if __name__ == "__main__":
main()
Uruchom grę u siebie
python tic_tac_toe.py w terminalu — albo Run przycisku w VS Code, albo PyCharm. Porównaj z tic_tac_toe_basic.py (Rozdział 17.6) to ten sam wynik dla gracza, z zupełnie inną architekturą wewnętrzną.Lista gotowych gier
MODEL
- pole (
board) jest oddzielna od widgetów - bieżący gracz — jawne pole stanu
- terminal state (game_over/winner) jest polem jawnym, a nie wyjściem z widgetów
RULES
- nieprawidłowe ruchy są odbite i nie zmieniają gracza
- wszystkie 8 linii wypłatnych testowanych na X i O
- losowanie jest sprawdzane PO wygranej
UI
- status tury jest zawsze widoczny
- zwycięzca jest widoczny nie tylko po kolorze
- pole reaguje na zmiany rozmiaru okien
- klawiatura działa
ARCHITEKTURA
- callback- i krótkie: walidacja → model → render
- czyste zasady są testowane bez okna
- render() ustala kanoniczny widok na podstawie modelu; Efekty tymczasowe są nałożone na to
Most do rozdziału 18
Dalej: GDZIE, nie tylko KTO i CO
W tej grze wydarzenia pomogły nam zrozumieć, KTO zareagował i CO powinno się wydarzyć. Ponadto wydarzenia z myszy podają nam WSPÓŁRZĘDNE —
event.x oraz event.y. W następnym rozdziale współrzędne myszy staną się podstawą do rysowania na Canvas.Praktyka: Tic-Tac-Toe Pro całość
Moduł tkinter otwiera natywne okno Python — wykonaj lokalnie w VS Code, PyCharm lub Jupyter
Praktyka kursuje lokalnie
Streszczenie rozdziału 17
Co zbudowaliśmy i czego się nauczyliśmy
- Zdarzenie, callback, command i binding to cztery różne, powiązane pojęcia; callback command= zazwyczaj nie otrzymuje Event; callback zarejestrowany przez bind() go otrzymuje.
- command= opisuje główną akcję Button; bind() jest jawną odpowiedzią na konkretne zdarzenie myszy lub klawiatury. Wbudowane sposoby aktywacji Button zależą od klasy i platformy widgetu, więc command i bind nie powinny być traktowane jako zamienne.
- Stan gry — nie to samo co widżety, które go pokazują: najechanie myszą udowadnia to najlepiej.
- find_winner(board) i is_draw(board) — funkcje czyste, testowane bez żadnego otwartego okna; kolejność „najpierw zwycięstwo, potem remis” — część zasad, a nie szczegół implementacyjny.
- Mysz (command=) i klawiatura (bind na root) zbiegają się do tej samej funkcji attempt_move() – zasady gry istnieją tylko w jednym miejscu.
- render() ustawia kanoniczny wyświetlacz bazy Z modelu. Najechanie myszką i krótki akcent zwycięstwa tymczasowo zmieniają widok na górze bez zmiany GameState; następny render() ponownie całkowicie przywraca widok modelu.
- Subtelne efekty wizualne (podgląd po najechaniu, podświetlenie linii, puls zwycięstwa) opierają się na solidnej architekturze — są możliwe właśnie dlatego, że model jest oddzielony od widoku.