Add Snake.reset

Refs #19
Task: MIL-003#4
This commit is contained in:
2026-10-13 14:33:56 +08:00
parent 518133b688
commit 19bc5c1431
2 changed files with 731 additions and 657 deletions
+18 -2
View File
@@ -3,9 +3,10 @@
The class follows the lectures "Create a Snake Class & Move to OOP", "Animating
the Snake Segments on Screen" and "Controlling the Snake with Keypresses", and
the day-21 steps in which the snake grows and the game ends. It keeps the lectures'
the day-21 steps in which the snake grows, and the day-24 step in which it is reset
after a collision. It keeps the lectures'
names: `Snake`, `segments`, `create_snake`, `add_segment`, `extend`, `move`, `head`,
`up`, `down`, `left` and `right`. The only differences are that the snake gets
`reset`, `up`, `down`, `left` and `right`. The only differences are that the snake gets
the function that makes a segment as a parameter, so that a test can pass a fake
and needs no window, and how a reversal is refused (see Snake).
"""
@@ -20,6 +21,7 @@ from snake_game.constants import (
LEFT,
MOVE_DISTANCE,
RIGHT,
SEGMENT_PARKING_POSITION,
SEGMENT_SHAPE,
STARTING_POSITIONS,
TAIL_COLLISION_DISTANCE,
@@ -204,6 +206,20 @@ class Snake:
self.head.forward(MOVE_DISTANCE)
self._direction_of_travel = self.head.heading()
def reset(self) -> None:
"""! @brief Start again with a new snake of three segments.
Every old segment is sent far outside the screen, so it can no longer be
seen, the list is emptied, and the snake is created again at the starting
positions. The new head is the first new segment, and it points right.
"""
for segment in self.segments:
segment.goto(*SEGMENT_PARKING_POSITION)
self.segments.clear()
self.create_snake()
self.head = self.segments[0]
self._direction_of_travel = self.head.heading()
def hits_wall(self) -> bool:
"""! @brief Tell whether the head has passed the wall on any side.
+58
View File
@@ -407,3 +407,61 @@ def test_a_snake_of_four_segments_cannot_touch_its_tail_in_a_small_square() -> N
snake.move()
assert not snake.hits_tail()
def test_reset_sends_every_old_segment_off_the_screen() -> None:
snake, created = make_snake()
snake.extend()
old = list(created)
snake.reset()
assert len(old) == len(constants.STARTING_POSITIONS) + 1
assert all(
segment.position() == constants.SEGMENT_PARKING_POSITION for segment in old
)
def test_reset_makes_the_snake_again_at_the_starting_positions() -> None:
snake, created = make_snake()
snake.extend()
snake.extend()
old_count = len(created)
snake.reset()
assert len(snake.segments) == len(constants.STARTING_POSITIONS)
assert positions(snake.segments) == [
(float(x), float(y)) for x, y in constants.STARTING_POSITIONS
]
assert snake.segments == created[old_count:]
def test_reset_makes_the_first_new_segment_the_head() -> None:
snake, created = make_snake()
old_head = snake.head
snake.reset()
new_head = created[len(constants.STARTING_POSITIONS)]
assert snake.head is snake.segments[0] is new_head
assert snake.head is not old_head
assert ("shape", (constants.HEAD_SHAPE,)) in new_head.calls
def test_reset_snake_moves_right_from_the_starting_positions() -> None:
snake, _ = make_snake_moving(constants.UP)
snake.reset()
snake.move()
assert snake.head.position() == (constants.MOVE_DISTANCE, 0)
def test_reset_forgets_the_direction_of_the_last_move() -> None:
snake, _ = make_snake_moving(constants.DOWN)
snake.reset()
snake.up() # allowed again: the new snake moves right, not down
assert snake.head.heading() == constants.UP