MIL-002: Adopt the game #24
@@ -0,0 +1,58 @@
|
||||
"""! @file
|
||||
@brief The scoreboard: the text at the top of the screen that shows the score.
|
||||
|
||||
The class follows the day-21 lecture on inheritance: `Scoreboard` is a `Turtle`
|
||||
that writes text. Like `food`, this module needs `turtle` when it is imported;
|
||||
the game imports it only when it starts, and the tests install a fake `turtle`
|
||||
module first.
|
||||
"""
|
||||
|
||||
from turtle import Turtle
|
||||
|
||||
from snake_game.constants import (
|
||||
GAME_OVER_POSITION,
|
||||
GAME_OVER_TEXT,
|
||||
SCORE_LABEL,
|
||||
SCOREBOARD_ALIGNMENT,
|
||||
SCOREBOARD_COLOR,
|
||||
SCOREBOARD_FONT,
|
||||
SCOREBOARD_POSITION,
|
||||
)
|
||||
|
||||
|
||||
class Scoreboard(Turtle):
|
||||
"""! @brief Shows the score as text at the top centre of the screen."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""! @brief Set the scoreboard up and write the score 0."""
|
||||
super().__init__()
|
||||
## @brief The number of foods the snake has eaten in this game.
|
||||
self.score = 0
|
||||
self.color(SCOREBOARD_COLOR)
|
||||
self.penup()
|
||||
self.hideturtle()
|
||||
self.goto(SCOREBOARD_POSITION)
|
||||
self.update_scoreboard()
|
||||
|
||||
def update_scoreboard(self) -> None:
|
||||
"""! @brief Wipe the old text and write the label and the score."""
|
||||
self.clear()
|
||||
self.write(
|
||||
f"{SCORE_LABEL}{self.score}",
|
||||
align=SCOREBOARD_ALIGNMENT,
|
||||
font=SCOREBOARD_FONT,
|
||||
)
|
||||
|
||||
def increase_score(self) -> None:
|
||||
"""! @brief Add 1 to the score and write it."""
|
||||
self.score += 1
|
||||
self.update_scoreboard()
|
||||
|
||||
def game_over(self) -> None:
|
||||
"""! @brief Write the game-over text at the centre of the screen.
|
||||
|
||||
The score stays where it is: the old text is not wiped. Automatic drawing is
|
||||
off in the game, so the screen must be updated after this call.
|
||||
"""
|
||||
self.goto(GAME_OVER_POSITION)
|
||||
self.write(GAME_OVER_TEXT, align=SCOREBOARD_ALIGNMENT, font=SCOREBOARD_FONT)
|
||||
@@ -0,0 +1,85 @@
|
||||
"""Tests of the scoreboard, with a fake `Turtle` as its base class."""
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from fakes import install_fake_turtle, make_scoreboard
|
||||
from snake_game import constants
|
||||
|
||||
START_TEXT = ("write", ("Score: 0", "center", ("Arial", 24, "normal")))
|
||||
|
||||
|
||||
def test_scoreboard_is_white_hidden_and_at_the_top_centre(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
|
||||
assert ("color", ("white",)) in scoreboard.calls
|
||||
assert "penup" in scoreboard.call_names()
|
||||
assert "hideturtle" in scoreboard.call_names()
|
||||
assert ("goto", constants.SCOREBOARD_POSITION) in scoreboard.calls
|
||||
|
||||
|
||||
def test_scoreboard_inherits_from_turtle(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
install_fake_turtle(monkeypatch)
|
||||
module = importlib.import_module("snake_game.scoreboard")
|
||||
|
||||
assert issubclass(module.Scoreboard, sys.modules["turtle"].Turtle)
|
||||
|
||||
|
||||
def test_scoreboard_starts_by_writing_score_0_centred_in_arial_24(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
|
||||
assert scoreboard.calls[-1] == START_TEXT
|
||||
|
||||
|
||||
def test_increase_score_wipes_the_old_text_and_writes_the_new_score(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
|
||||
scoreboard.increase_score()
|
||||
|
||||
assert scoreboard.call_names()[-2:] == ["clear", "write"]
|
||||
assert scoreboard.calls[-1] == (
|
||||
"write",
|
||||
("Score: 1", "center", ("Arial", 24, "normal")),
|
||||
)
|
||||
|
||||
|
||||
def test_every_food_adds_one_to_the_score(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
|
||||
for _ in range(3):
|
||||
scoreboard.increase_score()
|
||||
|
||||
assert scoreboard.calls[-1][1][0] == "Score: 3"
|
||||
|
||||
|
||||
def test_game_over_writes_the_text_at_the_centre_in_the_scoreboard_font(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
|
||||
scoreboard.game_over()
|
||||
|
||||
assert scoreboard.calls[-2:] == [
|
||||
("goto", constants.GAME_OVER_POSITION),
|
||||
("write", ("GAME OVER", "center", ("Arial", 24, "normal"))),
|
||||
]
|
||||
|
||||
|
||||
def test_game_over_leaves_the_score_on_the_screen(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
scoreboard = make_scoreboard(monkeypatch)
|
||||
scoreboard.increase_score()
|
||||
calls_before = len(scoreboard.calls)
|
||||
|
||||
scoreboard.game_over()
|
||||
|
||||
assert "clear" not in scoreboard.call_names()[calls_before:]
|
||||
Reference in New Issue
Block a user