MIL-003: Continuous play and high score #37

Merged
Tirsvad merged 7 commits from mil-003-continuous-play-and-high-score into main 2026-10-13 08:35:54 +02:00
2 changed files with 198 additions and 143 deletions
Showing only changes of commit 518133b688 - Show all commits
+14 -11
View File
@@ -10,8 +10,7 @@ module first.
from turtle import Turtle
from snake_game.constants import (
GAME_OVER_POSITION,
GAME_OVER_TEXT,
HIGH_SCORE_LABEL,
SCORE_LABEL,
SCOREBOARD_ALIGNMENT,
SCOREBOARD_COLOR,
@@ -26,8 +25,10 @@ class Scoreboard(Turtle):
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.
## @brief The number of foods the snake has eaten since the last reset.
self.score = 0
## @brief The largest score reached so far.
self.high_score = 0
self.color(SCOREBOARD_COLOR)
self.penup()
self.hideturtle()
@@ -35,10 +36,10 @@ class Scoreboard(Turtle):
self.update_scoreboard()
def update_scoreboard(self) -> None:
"""! @brief Wipe the old text and write the label and the score."""
"""! @brief Wipe the old text and write the score and the high score."""
self.clear()
self.write(
f"{SCORE_LABEL}{self.score}",
f"{SCORE_LABEL}{self.score} {HIGH_SCORE_LABEL}{self.high_score}",
align=SCOREBOARD_ALIGNMENT,
font=SCOREBOARD_FONT,
)
@@ -48,11 +49,13 @@ class Scoreboard(Turtle):
self.score += 1
self.update_scoreboard()
def game_over(self) -> None:
"""! @brief Write the game-over text at the centre of the screen.
def reset(self) -> None:
"""! @brief Keep the best score, start a new game and write the scoreboard.
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.
When the score is higher than the high score, it becomes the new high
score. The score then goes back to 0.
"""
self.goto(GAME_OVER_POSITION)
self.write(GAME_OVER_TEXT, align=SCOREBOARD_ALIGNMENT, font=SCOREBOARD_FONT)
if self.score > self.high_score:
self.high_score = self.score
self.score = 0
self.update_scoreboard()
+66 -14
View File
@@ -8,7 +8,10 @@ import pytest
from fakes import install_fake_turtle, make_scoreboard
from snake_game import constants
START_TEXT = ("write", ("Score: 0", "center", ("Arial", 24, "normal")))
START_TEXT = (
"write",
("Score: 0 High Score: 0", "center", ("Arial", 24, "normal")),
)
def test_scoreboard_is_white_hidden_and_at_the_top_centre(
@@ -29,7 +32,7 @@ def test_scoreboard_inherits_from_turtle(monkeypatch: pytest.MonkeyPatch) -> Non
assert issubclass(module.Scoreboard, sys.modules["turtle"].Turtle)
def test_scoreboard_starts_by_writing_score_0_centred_in_arial_24(
def test_scoreboard_starts_by_writing_both_scores_0_centred_in_arial_24(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
@@ -47,7 +50,7 @@ def test_increase_score_wipes_the_old_text_and_writes_the_new_score(
assert scoreboard.call_names()[-2:] == ["clear", "write"]
assert scoreboard.calls[-1] == (
"write",
("Score: 1", "center", ("Arial", 24, "normal")),
("Score: 1 High Score: 0", "center", ("Arial", 24, "normal")),
)
@@ -57,29 +60,78 @@ def test_every_food_adds_one_to_the_score(monkeypatch: pytest.MonkeyPatch) -> No
for _ in range(3):
scoreboard.increase_score()
assert scoreboard.calls[-1][1][0] == "Score: 3"
assert scoreboard.calls[-1][1][0] == "Score: 3 High Score: 0"
def test_game_over_writes_the_text_at_the_centre_in_the_scoreboard_font(
def test_high_score_starts_at_0(monkeypatch: pytest.MonkeyPatch) -> None:
scoreboard = make_scoreboard(monkeypatch)
assert scoreboard.high_score == 0
assert scoreboard.score == 0
def test_reset_keeps_a_higher_score_as_the_high_score_and_starts_again(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
for _ in range(3):
scoreboard.increase_score()
scoreboard.game_over()
scoreboard.reset()
assert scoreboard.calls[-2:] == [
("goto", constants.GAME_OVER_POSITION),
("write", ("GAME OVER", "center", ("Arial", 24, "normal"))),
]
assert scoreboard.high_score == 3
assert scoreboard.score == 0
assert scoreboard.call_names()[-2:] == ["clear", "write"]
assert scoreboard.calls[-1] == (
"write",
("Score: 0 High Score: 3", "center", ("Arial", 24, "normal")),
)
def test_game_over_leaves_the_score_on_the_screen(
def test_reset_keeps_the_high_score_when_the_score_is_lower(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
for _ in range(3):
scoreboard.increase_score()
scoreboard.reset()
scoreboard.increase_score()
scoreboard.reset()
assert scoreboard.high_score == 3
assert scoreboard.calls[-1][1][0] == "Score: 0 High Score: 3"
def test_reset_keeps_the_high_score_when_the_score_is_equal(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
scoreboard.increase_score()
calls_before = len(scoreboard.calls)
scoreboard.reset()
scoreboard.increase_score()
scoreboard.game_over()
scoreboard.reset()
assert "clear" not in scoreboard.call_names()[calls_before:]
assert scoreboard.high_score == 1
assert scoreboard.score == 0
def test_the_score_text_shows_both_scores_while_playing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
scoreboard.increase_score()
scoreboard.increase_score()
scoreboard.reset()
scoreboard.increase_score()
assert scoreboard.calls[-1][1][0] == "Score: 1 High Score: 2"
def test_scoreboard_has_no_game_over_any_more(
monkeypatch: pytest.MonkeyPatch,
) -> None:
scoreboard = make_scoreboard(monkeypatch)
assert not hasattr(scoreboard, "game_over")