import pygame
from typing import Tuple, Optional
from settings import *
class Actor:
A class that represents all the actors in the game. This class includes any
attributes/methods that are common between the actors
=== Public Attributes ===
x:
x coordinate of this actors location on the stage
y:
y coordinate of this actors location on the stage
image:
the image of the actor
=== Private Attributes ===
_is_stop:
Flag to keep track of whether this object cannot be moved through
_is_push:
Flag to keep track of whether this object is pushable
Representation Invariant: x,y must be greater or equal to 0
x: int
y: int
_is_stop: bool
_is_push: bool
image: pygame.Surface
def __init__(self, x: int, y: int) -> None:
self.x, self.y = x, y
self._is_stop = False
self._is_push = False
self.image = pygame.Surface((TILESIZE, TILESIZE))
def is_stop(self) -> bool:
Getter for _is_stop
return self._is_stop
def is_push(self) -> bool:
Getter for _is_push
return self._is_push
def copy(self) -> Actor:
Creates an identical copy of self and returns the new copy
To be implemented in the subclasses
raise NotImplementedError
def move(self, game_: Game, dx: int, dy: int) -> bool:
Function to move an Actor on the screen, to the direction
indicated by dx and dy
game_: the Game object
dx: the offset in the x coordinate
dx: the offset in the y coordinate
Returns whether
Note: this method is different from the player_move method in the
Character class. A player_move is trigger by key pressed directly.
This more general move can be a move caused by a push. In fact, this
move method is used in the implementation of player_move.
Things to think about in this method:
The object cannot go off the screen boundaries
The move may push other objects to move as well.
The move might not happen because its blocked by an unmovable object,
in which case this method should return False
Recall how push works: you may push and move a line of multiple
objects as long as the move is not blocked by something.
# TODO Task 2: Complete this method
return True
class Character(Actor):
A class that represents non-Blocks/Bushes on the screen
i.e., Meepo, Wall, Rock, Flag
A Character could potentially be the player that is controlled by the
key presses
=== Additional Private Attributes ===
_is_player:
Whether the character is the player, i.e.,
_is_lose:
Whether the rules contains
_is_win:
Whether the rules contains
_is_player: bool
_is_lose: bool
_is_win: bool
def __init__(self, x: int, y: int) -> None:
Initializes the Character
super().__init__(x, y)
self._is_player = False
self._is_lose = False
self._is_win = False
def is_win(self) -> bool:
Getter for _is_win
return self._is_win
def is_lose(self) -> bool:
Getter for _is_lose
return self._is_lose
def is_player(self) -> bool:
Getter for _is_player
return self._is_player
def set_player(self) -> None:
Sets flag to make this actor the player.
self._is_player = True
self._is_stop = False
self._is_push = False
def unset_player(self) -> None:
Unsets the flag to make the actor not the player.
self._is_player = False
def set_stop(self) -> None:
Sets flag to make actor incapable of being moved through or pushed.
self._is_stop = True
self._is_push = False
self._is_player = False
def unset_stop(self) -> None:
Unsets the flag that prevents actor from being moved through or pushed.
self._is_stop = False
def set_push(self) -> None:
Sets the flag that allows the actor to be pushable
self._is_push = True
self._is_stop = False
self._is_player = False
def unset_push(self) -> None:
Unsets the flag that allows the actor to be pushable
self._is_push = False
def set_win(self) -> None:
Sets this actor to be the win Condition.
self._is_win = True
self._is_lose = False
def unset_win(self) -> None:
Unsets this actor from being the win Condition.
self._is_win = False
def set_lose(self) -> None:
Sets this flag to be the lose condition.
self._is_lose = True
self._is_win = False
def unset_lose(self) -> None:
Unsets this flag from being the lose condition.
self._is_lose = False
def copy_flags(self, other: Character) -> None:
Copy the boolean flags to the
This is a helper method that should be used by the copy methods
implemented in the subclasses.
other._is_player = self._is_player
other._is_push = self._is_push
other._is_stop = self._is_stop
other._is_lose = self._is_lose
other._is_win = self._is_win
def copy(self) -> Character:
Returns a copy of this object itself.
Need to be implemented in the subclasses
raise NotImplementedError
def handle_key_press(self, game_: Game) -> Tuple[int, int]:
Process the key press input and
return (dx, dy), the offsets on the x and y directions.
key_pressed = game_.keys_pressed
dx, dy = 0, 0
if key_pressed[pygame.K_LEFT]:
dx -= 1
elif key_pressed[pygame.K_RIGHT]:
dx += 1
elif key_pressed[pygame.K_UP]:
dy -= 1
elif key_pressed[pygame.K_DOWN]:
dy += 1
return dx, dy
def player_move(self, game_: Game) -> bool:
Detects input from the keyboard and moves the Player on the game stage
based on directional key presses.
Also, after the move, check if we have won or lost the game,
and call the win() and lose() methods in Game accordingly
dx, dy = self.handle_key_press(game_)
if dx == 0 and dy == 0:
return False
return self.move(game_, dx, dy)
class Meepo(Character):
Class representing Ms. Meepo in the game.
Meepo is a special Character because we want to change her image as
she moves in different directions. We also want to see the movement of
her arms as she moves.
=== Additional Public Attributes ===
walk_right:
Image for walking right
walk_left:
Image for walking left
walk_up:
Image for walking up
walk_down:
Image for walking down
walk_left: list
walk_right: list
walk_down: list
walk_up: list
def __init__(self, x: int, y: int) -> None:
Initializes the Meepo Class
Load the images for displaying Ms. Meepos movement.
super().__init__(x, y)
# Add motion images
self.walk_right = [load_image(PLAYER_SPRITE_R1),
load_image(PLAYER_SPRITE_R2)]
self.walk_left = [
pygame.transform.flip(load_image(PLAYER_SPRITE_R1), True, False),
pygame.transform.flip(load_image(PLAYER_SPRITE_R2), True, False)
]
self.walk_up = [load_image(PLAYER_SPRITE_U1),
load_image(PLAYER_SPRITE_U2)]
self.walk_down = [load_image(PLAYER_SPRITE_B1),
load_image(PLAYER_SPRITE_B2)]
self.image = self.walk_down[1]
# TODO Task 1: Add any missing method thats necessary here.
# You may also leave this for now and revisit it when you work on Task 4
# missing method
def handle_key_press(self, game_: Game) -> Tuple[int, int]:
Overriding the same method in the base class, adding the modification
of the image depending on the direction of the move.
# TODO Task 2: Override this method for Meepo
# We want to update the image of Meepo as she moves in different
# directions. Check the __init__ method to see the images that are
# available for use.
# Watch the video demo carefully too see how Meepo moves. Note the
# movement of her arms and tail.
return (0, 0)
# TODO Task 1: add the Wall, Rock, and Flag classes
# Keep the amount of code your write to a minimal for each class (it is
# supposed to be quite short), i.e., inherit and use existing classes/methods
# when appropriate, write ONLY whats special about the new class.
#
# Hint: use the load_image() function to load the image of the character
#
# class Wall():
#
# class Rock():
#
# class Flag():
#
class Bush(Actor):
Class representing the edges and unmovable objects in the game.
def __init__(self, x: int, y: int) -> None:
super().__init__(x, y)
self.image = load_image(BUSH_SPRITE)
# Bush is always unmovable and cannot be moved through
self._is_stop = True
self._is_push = False
def copy(self) -> Bush:
Returns a copy of the Bush object
return Bush(self.x, self.y)
class Block(Actor):
Class for words in the game such as
Meepo, you, is, rock, lose, victor, flag, push, and stop.
Blocks are used for indicating rules in the game.
================
Additional public attribute:
word: the word on this block
word: str
def __init__(self, x: int, y: int, word_: str) -> None:
super().__init__(x, y)
self.word = word_
# Blocks are always pushable and cannot be moved through.
self._is_push = True
self._is_stop = True
def copy(self) -> Block:
Creates an identical copy of self and returns the new copy.
To be implemented in the subclasses
raise NotImplementedError
# TODO Task 1: Implement the Subject and Attribute classes
# Keep the amount of code your write to a minimal for each class (it is
# supposed to be quite short), i.e., inherit and use existing classes/methods
# when appropriate, write ONLY whats special about the new class.
#
# Hint: use the load_image() function to load the image of the character
#
class Subject(Block):
Class representing the Subject blocks in the game, e.g.,
Meepo, Wall, Flag, Rock (see SUBJECTS in settings.py)
# TODO Task 1: Add the initializer and any other necessary method
pass
class Attribute(Block):
Class representing the Attribute blocks in the game, e.g.,
Push, Stop, Victory, Lose, You
# TODO Task 1: Add the initializer and any other necessary method
pass
class Is(Block):
Class representing the Is blocks in the game.
def __init__(self, x: int, y: int) -> None:
super().__init__(x, y, is)# Note the space in is
self.image = load_image(IS_PURPLE)
# TODO Task 1: Add any missing methods that is necessary
# You may also leave this for now and revisit it when you work on Task 4
# missing method
def update(self, up: Optional[Actor],
down: Optional[Actor],
left: Optional[Actor],
right: Optional[Actor]) -> Tuple[str, str]:
Detect horizontally and vertically if a new rule has been created in
the format of a string Subject isAttribute.
up, down, left, right: the Actors that are adjacent (in the four
directions) to this IS block
Return a tuple of (horizontal, vertical) rules if a rule is detected
in either direction, otherwise put an empty string at the tuple index.
Some example return values:
(Wall isPush, Flag isWin)
(, Rock isYou)
(, )
Also, use IS images with different colours:
if no rule is detected on this IS block, use IS_PURPLE
if one rule is detected on this IS block, use IS_LIGHT_BLUE
if two rules are detected on this IS block, use IS_DARK_BLUE
Note: We always read the rule left-to-right or up-to-down, e.g.,
if it reads Push is Wall from left to right, or from bottom to top,
it is NOT a valid rule.
Hint: you may use the built-in method isinstance() to check the class
type of an object.
# TODO Task 3: Complete this method.
return ,
def load_image(img_name: str, width: int = TILESIZE,
height: int = TILESIZE) -> pygame.image:
Return a pygame img of the PNG img_name that has been scaled according
to the given width and size
img = pygame.image.load(img_name).convert_alpha()
return pygame.transform.scale(img, (width, height))
if __name__ == __main__:
import python_ta
python_ta.check_all(config={
extra-imports: [settings, stack, actor, pygame]
})
Reviews
There are no reviews yet.