Skip to content

Commit 12fcaf9

Browse files
alien-gammagregcorbett
alien-gamma
authored andcommitted
Adds support for multi-sprite Goals and BeeBots
- all Components now take a list of sprites/images rather than a single sprite/image. sprites in future scenarios. - this is backwards compatiable with all 1.X scenarios - update test cases to use lists for Component sprites - Add complete method to Goal that completes goal - so that it can handle the sprite change - rework has_been_met to is_complete, as complete() is a better method name than met() for this method. - update test cases due to Goal changes and update comments
1 parent ebbad61 commit 12fcaf9

13 files changed

+405
-221
lines changed

src/BeeBot.py

+24-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class BeeBot(Component):
2424
def __init__(self, scenario):
2525
"""Create a BeeBot."""
2626
# Read the sprite and assume it is the "NORTH" sprite.
27-
self.original_sprite = scenario.get_beebot_sprite()
27+
self.original_sprite_list = scenario.get_beebot_sprite()
2828

2929
# The amount the BeeBot moves.
3030
self.step = scenario.get_board_step()
@@ -33,15 +33,12 @@ def __init__(self, scenario):
3333
self.start_logical_position = Point(
3434
scenario.get_beebot_start_position())
3535

36-
# Call the superclass constructor
37-
super().__init__(self.original_sprite,
36+
# Call the superclass constructor.
37+
# Use copy() here to copy the objects not just the reference.
38+
super().__init__(self.original_sprite_list.copy(),
3839
self.start_logical_position.copy(),
3940
self.step)
4041

41-
# Read the sprite and assume it is the "NORTH" sprite.
42-
self.original_sprite = scenario.get_beebot_sprite()
43-
self.sprite = self.original_sprite
44-
4542
# Which way is the BeeBot facing.
4643
self.start_heading = scenario.get_beebot_heading()
4744
self.heading = self.start_heading
@@ -63,6 +60,24 @@ def __init__(self, scenario):
6360

6461
self.running = False
6562

63+
def increment_sprite(self):
64+
"""
65+
Increment the sprite to display for this BeeBot.
66+
67+
Also ensures the visual Heading of the sprite is consistent before and
68+
after this method is called.
69+
"""
70+
super().increment_sprite()
71+
# The new sprite will be facing Heading.NORTH regardless of
72+
# self.heading, so fix that.
73+
if self.heading == Heading.EAST:
74+
self.sprite = self.rotate(self.sprite, 270)
75+
elif self.heading == Heading.WEST:
76+
self.sprite = self.rotate(self.sprite, 90)
77+
elif self.heading == Heading.SOUTH:
78+
self.sprite = self.rotate(self.sprite, 180)
79+
80+
6681
def move(self, event):
6782
"""Move the BeeBot."""
6883
if event.type == CustomEvent.MOVE_BEEBOT_UP:
@@ -208,7 +223,8 @@ def reset_position(self):
208223
self.screen_location = self.start_logical_position.scale(self.step)
209224

210225
# Reset BeeBot sprite
211-
self.sprite = self.original_sprite
226+
self._sprite_list = self.original_sprite_list.copy()
227+
self._sprite_list_index = 0
212228

213229
# From the original sprite, rotate to the start_heading
214230
if self.start_heading == Heading.EAST:

src/Component.py

+37-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ class Component(pygame.sprite.Sprite):
1212
"""This class defines an individual Component."""
1313

1414
def __init__(self,
15-
sprite, # The image to display (can be None)
15+
sprite_list, # The list of sprites to display.
1616
start_logical_position, # The starting point of the Component
1717
step): # The 'size' of the Component
1818
"""Create a Component."""
19-
# The sprite to display on screen for this Component
20-
self.sprite = sprite
19+
# The sprite(s) to display on screen for this Component.
20+
self._sprite_list = sprite_list
21+
22+
# Which sprite in self._sprite_list to display.
23+
self._sprite_list_index = 0
2124

2225
# The position of the Component in terms of squares on the screen
2326
self.logical_position = start_logical_position.copy()
@@ -28,12 +31,42 @@ def __init__(self,
2831
# calling superclass constructor
2932
pygame.sprite.Sprite.__init__(self)
3033

34+
@property
35+
def sprite(self):
36+
"""A "public" getter for the current sprite."""
37+
return self._sprite_list[self._sprite_list_index]
38+
39+
@sprite.setter
40+
def sprite(self, new_sprite):
41+
"""
42+
A "public" getter for the current sprite.
43+
44+
This method is needed for Components that rotate, like the BeeBot.
45+
"""
46+
self._sprite_list[self._sprite_list_index] = new_sprite
47+
48+
@property
49+
def sprite_list(self):
50+
"""A "public" getter for self._sprite_list."""
51+
return self._sprite_list
52+
53+
@property
54+
def sprite_list_index(self):
55+
"""A "public" getter for self._sprite_list_index."""
56+
return self._sprite_list_index
57+
58+
def increment_sprite(self):
59+
"""Increment the sprite to display for this Component."""
60+
if self._sprite_list_index < len(self._sprite_list) - 1:
61+
self._sprite_list_index = self._sprite_list_index + 1
62+
3163
def display(self, screen):
3264
"""Draw the Component object on screen, if it has a sprite."""
3365
if self.sprite is not None:
3466
screen.blit(self.sprite, self.screen_location)
3567

3668
def is_equal_to(self, other_component):
3769
"""Compare this Component for equality with other_component."""
38-
return (self.sprite == other_component.sprite and
70+
return (self.sprite_list == other_component.sprite_list and
71+
self.sprite_list_index == other_component.sprite_list_index and
3972
self.screen_location.is_equal_to(other_component.screen_location))

src/GameWindow.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -599,13 +599,17 @@ def check_for_goal_collisions(self):
599599
if self.board.goal_group.is_ordered:
600600
goal = self.board.goal_group.get_current_goal()
601601
if self.robot.logical_position.is_equal_to(goal.logical_position):
602-
goal.has_been_met = True
602+
goal.complete()
603+
if goal.should_increment_beebot_sprite:
604+
self.robot.increment_sprite()
603605
self.board.goal_group.increment_pointer()
604606

605607
else:
606608
for goal in self.board.goal_group.components:
607609
if self.robot.logical_position.is_equal_to(goal.logical_position):
608-
goal.has_been_met = True
610+
goal.complete()
611+
if goal.should_increment_beebot_sprite:
612+
self.robot.increment_sprite()
609613

610614
if self.board.goal_group.have_all_goals_been_met():
611615
# clear any remaining events
@@ -730,7 +734,7 @@ def handle_button_press(self, button):
730734
self.store_movement('Backward')
731735

732736
if button.text == 'Reset':
733-
# Reset the BeeBots position and the met status of the goals
737+
# Reset the BeeBots position and the completed status of the Goals.
734738
self.robot.reset_position()
735739
self.board.goal_group.reset_all_goals()
736740

@@ -760,7 +764,7 @@ def handle_key_press(self, event):
760764
if event.key == pygame.K_RIGHT:
761765
self.store_movement('Right')
762766
# if the event is the space bar,
763-
# reset the BeeBot's position and clears any met goals.
767+
# reset the BeeBot's position and clears any completed Goals.
764768
# it doesn't clear the memory!
765769
if event.key == pygame.K_SPACE:
766770
self.robot.reset_position()

src/Goal.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,40 @@ class Goal(Component):
66
"""This class defines an individual Goal."""
77

88
def __init__(self,
9-
sprite, # The image to display (can be None)
9+
sprite_list, # The list of sprites to display.
1010
start_logical_position, # The positon of the Goal
11-
step): # Should be the same as the BeeBot step
11+
step, # Should be the same as the BeeBot step
12+
should_increment_beebot_sprite=False):
1213
"""Create a Goal."""
1314
# Call superclass constructor
14-
super().__init__(sprite, start_logical_position, step)
15+
super().__init__(sprite_list, start_logical_position, step)
1516

16-
# True if Goal has been met
17-
self.has_been_met = False
17+
# True if Goal is currently completed.
18+
self._is_complete = False
19+
# A marker to indicate that the BeeBot sprite should be incremented
20+
# after the completion of this Goal.
21+
self.should_increment_beebot_sprite = should_increment_beebot_sprite
22+
23+
@property
24+
def is_complete(self):
25+
"""A "public" getter for the self._is_complete."""
26+
return self._is_complete
27+
28+
def complete(self):
29+
"""Mark this Goal as complete and increment the sprite if possible."""
30+
self._is_complete = True
31+
if self._sprite_list_index < len(self._sprite_list) - 1:
32+
self.increment_sprite()
33+
34+
def reset(self):
35+
"""Reset this Goal."""
36+
self._is_complete = False
37+
self._sprite_list_index = 0
1838

1939
def is_equal_to(self, other_component):
2040
"""Compare this Goal for equality with other_goal."""
2141
if not isinstance(other_component, Goal):
2242
# A Goal can obviously never be equal to a non Goal
2343
return False
24-
return (self.has_been_met == other_component.has_been_met and
44+
return (self.is_complete == other_component.is_complete and
2545
super().is_equal_to(other_component))

src/GoalGroup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ def increment_pointer(self):
3737
def have_all_goals_been_met(self):
3838
"""True iff all the Goal objects in the GoalGroup have been met."""
3939
for goal in self.components:
40-
if not goal.has_been_met:
40+
if not goal.is_complete:
4141
return False
4242
return True
4343

4444
def reset_all_goals(self):
4545
"""Set all Goals to not been met."""
4646
for goal in self.components:
47-
goal.has_been_met = False
47+
goal.reset()
4848
# if Goals are ordered, move ptr back to start of GoalGroup
4949
if self.is_ordered:
5050
self._goal_ptr = 0

0 commit comments

Comments
 (0)