Files
025-us-states-game/src/us_states_game/game.py
T
Tirsvad 02b537e538 Add Turtle screen, game loop and entry point (MIL-003)
Task: MIL-003#1
Task: MIL-003#2
Task: MIL-003#3
Task: MIL-003#4
Task: MIL-003#5
Task: MIL-003#6
2026-10-13 15:50:45 +08:00

82 lines
2.7 KiB
Python

"""Game loop of the U.S. States Game, independent of any screen technology.
@file game.py
@brief Guess loop and the end-of-game export of the states to learn.
The loop talks to the player through the `GameScreen` port, so it runs under
test with a fake screen and no display.
"""
from collections.abc import Sequence
from pathlib import Path
from typing import Protocol
from us_states_game.constants import (
ANSWER_PROMPT,
EXIT_COMMAND,
NUMBER_OF_STATES,
STATES_TO_LEARN_FILENAME,
TITLE_FORMAT,
)
from us_states_game.states import State, find_state, get_missing_states
from us_states_game.storage import save_states_to_learn
class GameScreen(Protocol):
"""What the game loop needs from the screen."""
def ask_state_name(self, title: str, prompt: str) -> str | None:
"""Ask the player for a state name.
@param title Title of the dialog, showing the progress.
@param prompt Question shown to the player.
@return The typed text, or None if the player cancelled.
"""
...
def write_state(self, state: State) -> None:
"""Write the name of a state at its coordinates on the map.
@param state The state to write.
"""
...
def play(screen: GameScreen, states: Sequence[State]) -> list[str]:
"""Run the guess loop until all states are guessed or the player exits.
@param screen Screen used to ask for answers and to write names.
@param states All states of the game.
@return Names of the states guessed, in guessing order.
"""
guessed_states: list[str] = []
while len(guessed_states) < NUMBER_OF_STATES:
title = TITLE_FORMAT.format(guessed=len(guessed_states), total=NUMBER_OF_STATES)
answer = screen.ask_state_name(title, ANSWER_PROMPT)
if answer is None or answer.strip().casefold() == EXIT_COMMAND:
break
state = find_state(answer, states)
if state is None or state.name in guessed_states:
continue
screen.write_state(state)
guessed_states.append(state.name)
return guessed_states
def run_game(
screen: GameScreen,
states: Sequence[State],
output_path: Path = Path(STATES_TO_LEARN_FILENAME),
) -> list[str]:
"""Play the game and save the states the player missed.
@param screen Screen used to ask for answers and to write names.
@param states All states of the game.
@param output_path File that receives the states to learn.
@return Names of the states not guessed.
"""
guessed_states = play(screen, states)
missing_states = get_missing_states(states, guessed_states)
save_states_to_learn(missing_states, output_path)
return missing_states