# a5_unocard.py # STUDENTS: PLACE YOUR NAME AND NETID HERE # Sources/people consulted: STUDENTS: FILL IN OR WRITE "None" # STUDENTS: PUT DATE YOU COMPLETED THIS HERE # skeleton by: # Anthony Poon (ATP65) # Victoria Litvinova (VL242) - simple edits # Lillian Lee (LJL2) - simple edits # April 27, 2018 """ This module contains the UnoCard class and sub-classes for the Uno game. """ from a5_card import Card import a5_unogamestate class UnoCard(Card): """ An Uno card is an extension of the standard playing card that provide two additional features. It is capable of determining whether it can be placed on top of another card. And also, it can define an optional action to take on the game state when the card is played. Instance Attributes: specialName [str or None]: A short name of any special ability that this UnoCard has or None if the card doesn't do anything special. """ def __init__(self, suit, rank): """ In addition to defining the suit and rank, also define the special name. By default, the special name is None. Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ super().__init__(suit, rank) self.specialName = None def __str__(self): """ Override the parent str method to append the special name of this card, if any. Returns: A string representation of an UnoCard. """ output = super().__str__() if self.specialName != None: output = output + " (" + self.specialName + ")" return output def isPlaceableOnTop(self, c): """ Determines if the self UnoCard can be placed on top of the given card, c. For example, if the given card is the top card of the pile, this method returns True or False depending on whether this card can be added to the pile. The default implementation of this is that it returns True if the given card is None or either the rank or suit of this card matches the given card. Preconditions: c [Card or None]: A card, which is typically the top card in the game's pile, or None if the game's pile is empty. Returns: A boolean, True if this card can be placed on top of c or False if it cannot. """ if c == None: return True if self.rank == c.rank: return True if self.suit == c.suit: return True else: return False def performAction(self, gameState): """ This method is invoked when the game has determined that this card has been played and so it must perform its action. The method allows the card to perform its action by modifying the provided game state. By default, an UnoCard has a no-op action. Preconditions: gameState [UnoGameState]: A non-None game state for the currently running game of Uno. """ return class ReverseActionCard(UnoCard): """ Cards with the 10 rank are considered Reverse cards, which reverse the order of play. """ def __init__(self, suit): """ Override the parent init method so that the rank is always 10 and the specialName attribute is set to "Reverse". Inside, this should call an init method from a parent class. Requirement: Call a parent class's init method. Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ # TODO: Students, implement this method here! pass def performAction(self, gameState): """ Modify the game state to reverse the order of play. Preconditions: Same preconditions as the parent definition of this method. """ print(">> Play order has been reversed!") # TODO: Students, implement this method here! pass class SkipActionCard(UnoCard): """ Cards with the 11th rank (Jacks) are considered Skip cards, which skip the next player. """ def __init__(self, suit): """ Override the parent init method so that the rank is always 11 and the specialName attribute is set to "Skip". Inside, this should call an init method from a parent class. Requirement: Call a parent class's init method. Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ # TODO: Students, implement this method here! pass def performAction(self, gameState): """ Modify the game state to increment past the next player without actually allowing that player to take their turn. Preconditions: Same preconditions as the parent definition of this method. """ print(">> Next player has been skipped!") # TODO: Students, implement this method here! pass class DrawTwoActionCard(UnoCard): """ Cards with the 12th rank (Queens) are Draw-Two cards, which force the next player to draw two cards. Note that in this version of the game, we do not skip the next player even though they are forced to draw two cards. """ def __init__(self, suit): """ Override the parent init method so that the rank is always 12 and the specialName attribute is set to "Draw Two". Inside, this should call an init method from a parent class. Requirement: Call a parent class's init method. Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ # TODO: Students, implement this method here! pass def performAction(self, gameState): """ Modify the game state to force the next player to draw two cards. Do NOT skip the next player. Preconditions: Same preconditions as the parent definition of this method. """ print(">> Next player must draw two cards!") # TODO: Students, implement this method here! pass class WildUnoCard(UnoCard): """ Cards in the 13th Rank (Kings) are Wild cards. They can be played on top of any card and force the suit to change to this card's suit. """ def __init__(self, suit): """ Override the parent init method so that the rank is always 13 and the specialName attribute is set to "Wild". Inside, this should call an init method from a parent class. Requirement: Call a parent class's init method. Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ # TODO: Students, implement this method here! pass def isPlaceableOnTop(self, c): """ As wild cards can be placed on top of any card, overwrite the parent implementation so that this card is always playable on top of any card regardless of what that card is. Preconditions: Same preconditions as the parent definition of this method. Returns: A boolean, True if this card can be placed on top of c or False if it cannot. """ # TODO: Students, implement this method here! pass class WildDrawFourActionCard(WildUnoCard): """ Cards in the 1st Rank (Aces) are Wild Draw Four cards. They can be played on top of any card, force the suit to change to this card's suit, and also force the next player to draw four cards. Notice that this extends from WildUnoCard, and thus inherits its isPlaceableOnTop behavior. """ def __init__(self, suit): """ Override the parent init method so that the rank is always 1 and the specialName attribute is set to "Wild Draw Four". Inside, this should call an init method from a parent class. Requirement: Call a parent class's init method. Hint: There are ways to call the init of a specific parent class. For example, super() can be called with arguments: https://docs.python.org/3/library/functions.html#super Preconditions: suit [int]: An int in 0..Card.NUM_SUITS-1. """ # TODO: Students, implement this method here! pass def performAction(self, gameState): """ Modify the game state to force the next player to draw four cards. Do NOT skip the next player. Preconditions: Same preconditions as the parent definition of this method. """ print(">> Next player must draw four cards!") # TODO: Students, implement this method here! pass def full_deck(): """ Returns a list of all 52 cards, where the 10s, Jacks, Queens, Kings, and Aces are instantiated as instances of the appropriate UnoCard sub-type and the remaining cards are provided as UnoCard instances. The returned list is in ascending rank order and then suit order, with the 2 of Clubs first and then the Ace of Spades last. Returns: A list of 52 UnoCards. """ output = [] # This includes ranks between 2 and 9, inclusive, which are created as normal # cards. for rank in range(2, 10): for suit in range(Card.NUM_SUITS): output.append(UnoCard(suit, rank)) # Create reverse cards, which are 10s. for suit in range(Card.NUM_SUITS): output.append(ReverseActionCard(suit)) # Create skip cards, which are Jacks. for suit in range(Card.NUM_SUITS): output.append(SkipActionCard(suit)) # Create draw two cards, which are Queens. for suit in range(Card.NUM_SUITS): output.append(DrawTwoActionCard(suit)) # Create wild cards, which are Kings. for suit in range(Card.NUM_SUITS): output.append(WildUnoCard(suit)) # Create wild draw four cards, which are Aces. for suit in range(Card.NUM_SUITS): output.append(WildDrawFourActionCard(suit)) return output