The Real GTA Grand Theft Auto (Bonus, 20 Points)
0. ONU
Welcome to ONU! ONU is a card game played with a specially printed deck. The objective of ONU is to be the first player to get rid of all your cards. Firstly, let’s go over the basic rules of ONU :
0.1 Gameplay
By default, the number of players is set to 7, and the initial hand card count is also set to 7. Users can customize the number of players and the initial hand card count based on their needs. The rest of the cards are placed in a deck.
The game begins. The first player could play any card he/she wants.
There are numeric cards and special cards in the deck. The numeric cards contain color and number, while the special cards have unique effects that can be placed on the next player.
The players take turns to play their cards. They must match the card played by the last player either by number, color, or effect.
If a player is not prevented from playing cards by special cards, but there is no card to play due to the different color and number, they must draw a card from the deck, and then his/her turn ends without playing any cards.
Whenaplayerhasplayedallhis/herhand,he/shewinsthegame.However,ifthedeckisemptiedbefore any player has played all of their cards, the scores are calculated based on the remaining cards in each player’s hand. The player with the lowest score wins the game.
0.2 Card Types
- Numeric Cards: These cards have a number on the card and also have a color. They can only be played on a card that matches by color or number.
- Change Color Cards: These cards can be placed on any card and change the current color to the color specified by the Change Color Cards.
- Ban Cards: When the player places these cards, the next player has to skip his/her turn. The Ban Cards have a color and can only be played on a card that matches by the color, or on another Ban card.
- Plus-Two Cards: When the player place these cards, the next player will have to pick up two cards and skip his/her turn. The Plus-Two Cards have a color and can only be played on a card that matches by color, or on another Plus-Two.
Special rule: If the last player plays a Plus-Two Card, and the current player also has a Plus-Two Card, the current player can play the card instead of drawing and the next player has to either draw 4 cards or also play a Plus-Two card, and so on. That is, the Plus-Two Card can be accumulated. It would be a good choice to record the cumulative value with a variable.
0.3 General description
In this assignment, you will be tasked with implementing an ONU game. The assignment involves the following classes: Card
, CardSet
, Player
and Game
. Your objective is to complete some functions within these classes.
- The
Card
class is the basic class in the game. There are two types of cards,NumericCard
, which includes the Numeric Cards andSpecialCard
, which includes Change Color Cards, Ban Cards and Plus-Two Cards. They are all inherited fromCard
. - The
CardSet
class is used to represent a set of cards and provides the operations to manipulate card sets. It has two subclasses:Hand
andDeck
. TheHand
class is used for operations related to a player’s hand, while theDeck
class is used for operations related to the game’s deck. - The
Player
class represents the player agent. The only thing one player could do is to make an action and choose a card from his/her hand to play if possible. - The
Game
class represents the ONU game itself and is responsible for managing the entire game process, including handling player actions, maintaining the deck, and detecting game termination.
Notes: Write your code in the designated areas and avoid modifying other parts of the code. You may add additional helper functions or attributes as needed.
Good luck with your implementation of ONU !
1. Card (20 Points)
1.1 Description
In this programming assignment, we will start by implementing the most fundamental unit of the game, which is the Card
class. The __init__
method for Card
is written for you. It has one member variable:_color
, to store the color of each individual card object created from the Card
class. The color
is given by an Enum Color
.
class Color(IntEnum):
RED = 0
YELLOW = 1
GREEN = 2
BLUE = 3
CYAN = 4
ORANGE = 5
PURPLE = 6
WHITE = 7
BLACK = 8
VIOLET = 9
By assigning the parameter color to the member variable self._color
, you ensure that each individual card object has its own color value stored as part of its state. Now, it’s your task to override some methods in the subclass NumericCard
and SpecialCard
.
You will encounter a function called __repr__
in this part. It is a special method used to return a string representation of an object. When you use the print function or directly input an object in the interactive environment in Python, you are actually invoking the object’s __repr__
method.
1.2 Methods
Card.get_color()
: Return the color of the card.NumericCard.__init__(color, number)
: Initialize the necessary parameters forNumericCard
, which is the number of the card. The input would be the Enum color and an integer number. Since thecolor
variable is set byCard
fromsuper().__init__()
, you don’t need to worry about it.NumericCard.__repr__()
: Return a string for printing the information of card. For numeric cards, the function should return ”Numeric card” followed by their color name and number. For example, card ”red 4” gets ”Numeric card RED 4”. The color name is defined in EnumColor
.NumericCard.__lt__(other)
: Compare the card with another cardother
. First, the color is com- pared based on the order in EnumColor
. Next, if they are in the same color, compare their numbers.
When it comes to different types in the comparison of numbers, the Numeric Cards are smaller than Special Cards. Return True
if the card is less than other
and return False
otherwise.
NumericCard.__eq__(other)
: Compare the card with another cardother
to see whether they are the same. ReturnTrue
if their parameters are equal and returnFalse
otherwise.NumericCard.get_number()
: Return the number of the card.SpecialCard.__init__(color, effect)
: Initialize the necessary parameters forSpecialCard
, which is the type of the Special Card. This is given by EnumEffect
, which is defined below:
class Effect(Enum):
CHANGE_COLOR = 0
BAN = 1
PLUS_TWO = 2
SpecialCard.__repr__()
: Return a string for printing the information of card. For special cards, it should return the name of the special card , according to Enum Effect, followed by its color name according to Enum Color similarly. An example is given by ”BAN card YELLOW.”SpecialCard.__lt__(other)
: Compare the card with another Cardother
. First, the color is compared based on the order in EnumColor
. When they are in the same color, ifother
is a Numeric Card, it is smaller than the Special Card. Otherwise, the Special Cards are compared based on the order inEffect
. ReturnTrue
if the card is less thanother
and returnFalse
otherwise.SpecialCard.__eq__(other)
: Compare the card with another cardother
to see whether they are the same. ReturnTrue
if their parameters are equal and returnFalse
otherwise.SpecialCard.get_effect()
: Return the effect of the Special Card.
By completing this task, you will have the basic building block of the game, allowing you to represent and visualize the different cards used in gameplay.
1.3 Example
Input 1:
card1 = NumericCard(Color.GREEN, 1)
card2 = NumericCard(Color.YELLOW, 7)
card3 = SpecialCard(Color.YELLOW, Effect.BAN)
print(card1 < card2)
print(card1 < card3)
print(card2 < card3)
Output 1:
False
False
True
Input 2:
print(NumericCard(Color.RED, 0))
print(SpecialCard(Color.YELLOW, Effect.CHANGE_COLOR))
Output 2:
Numeric card RED 0
CHANGE_COLOR card YELLOW
2. CardSet (20 Points)
2.1 Description
Next, let’s implement the CardSet
, Hand
and Deck
. The CardSet
class is the base class that represents a set of cards. It provides common functionality and operations that can be performed on a collection of cards.
The Hand
class is a subclass of CardSet
and represents a player’s hand cards. It provides additional methods to manipulate a player’s hand. This includes actions such as adding cards to the hand and removing cards from the hand.
The Deck
class is also a subclass of CardSet
and represents a game deck. In addition to the common functionality inherited from CardSet
, it provides the functionality to retrieve the top card from the deck.
Note that we do not need to override the __init__
method for Hand
and Deck
because they do not have any differences from the base class CardSet
in initialization. Therefore, the subclasses inherit the initialization and other methods from the base class directly.
2.2 Methods
CardSet.__init__(cards)
: Save thecards
given by the list cards in the variable_cards
.CardSet.__repr__()
: Return a string that prints all the cards in the cardset. It would be a list that containing every card.
Here is an example:
card1 = NumericCard(Color.BLUE, 1)
card2 = SpecialCard(Color.BLACK, Effect.CHANGE_COLOR)
card3 = NumericCard(Color.GREEN, 1)
Cards = CardSet([card1, card2, card3])
print(Cards)
The output would be
[Numeric card BLUE 1, CHANGE_COLOR card BLACK, Numeric card GREEN 1]
CardSet.is_empty()
: ReturnTrue
if there is no cards left in the cardset, and returnFalse
other- wise.Hand.add_card(card)
: Add the givencard
into the cardset.Hand.remove_card(card)
: Remove the givencard
from the cardset.Deck.get_next_card()
: Return the top of the deck, which would be the first card in the list. After this card is returned, it is no longer in the deck.
3. Player (30 Points)
3.1 Description
Next, let’s implement the Player
class. In our game, players are considered to be willing to do anything for victory, including cheating. Therefore, their behavior is strictly controlled by the game. The player’s hand is managed by the game system, and they can only make choices based on their hand and the previous player’s actions. The actual gameplay actions, such as playing cards or drawing cards, are all executed by the game system. Also due to this reason, players do not have their own attributes, so the __init__
method does not perform any operations. Your task is to assist the players in making their choices.
3.2 Methods
Player.sort_cards(cards)
: Return a sorted version of the input listcards
in descending order, which means from largest to smallest.Player.action(cards, last_card, is_last_player_drop)
: The inputs consist of the player’s hand, the card last played, and whether the last player has played any card. They are helpful when making the decisions. The returned value is a tuple with two items to store the actions. The first element of the tuple is an EnumActionType
representing the actions. The second element represents the card to play. If the player could not play any card, the second element would beNone
.
class ActionType(Enum):
DRAW = 0
DROP = 1
PASS = 2
To be detailed, the player should first check the status based on the actions of the previous player.
If the previous card played was a Ban Card, the player could do nothing, so return ActionType.PASS
and None
.
If the card played was a Plus-Two Card, and the player has no Plus-Two Card to play, similarly, the player could do nothing and return ActionType.PASS
and None
. If the player has one or more Plus-Two Cards, just choose a larger one to play.
If a player has no valid card in his hand to play, he/she needs to draw a card instead. Return ActionType.DRAW
and None
.
If a player can play a card, return ActionType.DROP
and the chosen card. To choose a card to play, you always play the largest card that is available. Here the definition of ”largest” is the same as the method __lt__
in the Cards.
One may be curious about why Player
needs to know is_last_player_drop
. For example, if the previous player played a Ban card, the current player is prevented from playing any cards, and the variable is_last_player_drop
would be set to False
. On the next player’s turn, he/she knows that the last_card
Ban card has taken effect through this variable, and he/she can play a normal game turn. You should consider all similar situations.
4. Game Start! (30 Points)
4.1 Description
Next, let’s implement some methods in the Game
class to prepare for the start of the game. Here are some attributes defined in Game.__init__()
.
Game._deck
represents the deck of cards used in the game. It is initialized with the Deck
object, which is created from the provided cards list.
The players are initialized as a list Game._players
. The number of players is set in num_player
and it defaults to 7 if not provided.
Game._current_player_id
represents the index of the current player. It is initialized with the value of dealer_id
parameter minus 1. This assumes that the dealer_id
is a zero-based index representing the starting player. The subtraction of one (dealer_id - 1) is done to return the index of the player when the next player is calculated for the first time.
Game._last_card
represents the last card played in the game. It is initially set to None and will be updated whenever a player plays a card.
Game._is_last_player_drop
represents whether the last player had played any card. It is initially set to False
. It can be used to determine whether the last card played was played by the last player.
Game._plus_two_cnt
represents the count of Plus-Two Cards played and may be helpful when treating the accumulation of Plus-Two Cards. It is initially set to 0 and can be incremented whenever a player plays a Plus-Two Card.
After all the basic components are built, our game is ready to start! Players take turns playing cards or drawing cards until a player has no cards left in his/her hand, indicating victory. If the deck is empty, the game is also stopped. All these operations are implemented in the method Game.turn()
.
4.2 Methods
Game.__init__(cards, num_player, hand_card_num, dealer_id)
: Most of the initialization is done by the template. The only thing you need to do is to dealhand_card_num
cards to each player as their initial hand. After one player has finished taking the initial hand, it is then the next player’s turn to take. The hand are recorded inGame._player_hands
. The deck of cards contains more cards than the initial hand size, so there is no need to worry about the deck running out of cards before the game starts.Game.is_end()
: This function is used to check whether the game is ended. ReturnTrue
if there is any player with no hand cards, or if the deck is empty. Otherwise, returnFalse
. You may notice that there is a methodGame.is_not_end()
. It is used for the return inGame.turn()
to improve code readability.Game.current_player_drop_card(card)
: To handle the process of a player playing a card, re- move the played card from the player’s hand. Besides, update the relevant attributes based on the played card.Game.get_scores()
: Calculate the scores for each player and return the scores as a list.
Solution
Card
为了解决这个编程任务,我们需要根据题目要求实现几个 Python类。这些类分别是Card
、NumericCard
、SpecialCard
,并且还涉及到两个枚举类Color
和Effect
。下面是根据题目要求实现的这些类的代码。
基本枚举类
首先,我们定义枚举类Color
和Effect
。
from enum import IntEnum, Enum
class Color(IntEnum):
RED = 0
YELLOW = 1
GREEN = 2
BLUE = 3
CYAN = 4
ORANGE = 5
PURPLE = 6
WHITE = 7
BLACK = 8
VIOLET = 9
class Effect(Enum):
CHANGE_COLOR = 0
BAN = 1
PLUS_TWO = 2
Card 类
然后是Card
基类和它的子类。
class Card:
def __init__(self, color):
self._color = color
def get_color(self):
return self._color
class NumericCard(Card):
def __init__(self, color, number):
super().__init__(color)
self._number = number
def __repr__(self):
return f"Numeric card {self._color.name} {self._number}"
def __lt__(self, other):
if self._color != other.get_color():
return self._color < other.get_color()
if isinstance(other, NumericCard):
return self._number < other._number
return True # NumericCards are smaller than SpecialCards
def __eq__(self, other):
return self._color == other.get_color() and self._number == other._number
def get_number(self):
return self._number
class SpecialCard(Card):
def __init__(self, color, effect):
super().__init__(color)
self._effect = effect
def __repr__(self):
return f"{self._effect.name} card {self._color.name}"
def __lt__(self, other):
if self._color != other.get_color():
return self._color < other.get_color()
if isinstance(other, NumericCard):
return False # SpecialCards are greater than NumericCards
return self._effect < other._effect
def __eq__(self, other):
return self._color == other.get_color() and self._effect == other._effect
def get_effect(self):
return self._effect
这些类实现了题目中所要求的功能。Card
类包含颜色信息,NumericCard
类包含数字和颜色信息,SpecialCard
类包含特殊效果和颜色信息。重写的__repr__
、__lt__
和 __eq__
方法分别用于打印卡片信息、比较卡片大小和判断卡片是否相同。
示例代码测试
最后,按照题目中的示例,我们可以测试这些类的实现是否正确。
# 示例输入 1
card1 = NumericCard(Color.GREEN, 1)
card2 = NumericCard(Color.YELLOW, 7)
card3 = SpecialCard(Color.YELLOW, Effect.BAN)
print(card1 < card2) # 应该输出 False
print(card1 < card3) # 应该输出 False
print(card2 < card3) # 应该输出 True
# 示例输入 2
print(NumericCard(Color.RED, 0)) # 应该输出 Numeric card RED 0
print(SpecialCard(Color.YELLOW, Effect.CHANGE_COLOR)) # 应该输出 CHANGE_COLOR card YELLOW
这些代码实现了题目要求的功能,可以用于进一步开发完整的ONU游戏。
当然,我将为上面的代码添加详细的注释,以便更好地理解每一行代码的作用。
# 引入 Enum 模块,用于创建枚举类
from enum import IntEnum, Enum
# 定义颜色枚举类,每种颜色对应一个整数值
class Color(IntEnum):
RED = 0
YELLOW = 1
GREEN = 2
BLUE = 3
CYAN = 4
ORANGE = 5
PURPLE = 6
WHITE = 7
BLACK = 8
VIOLET = 9
# 定义特效枚举类,每种特效对应一个整数值
class Effect(Enum):
CHANGE_COLOR = 0
BAN = 1
PLUS_TWO = 2
# 定义 Card 基类
class Card:
# 初始化方法,每张卡片都有一个颜色
def __init__(self, color):
self._color = color # 将传入的颜色赋值给卡片
# 获取卡片颜色的方法
def get_color(self):
return self._color # 返回卡片颜色
# 定义 NumericCard 类,继承自 Card 类
class NumericCard(Card):
# 初始化方法,每张数值卡片除了颜色还有一个数值
def __init__(self, color, number):
super().__init__(color) # 调用父类的初始化方法设置颜色
self._number = number # 设置数值卡片的数值
# 重写 __repr__ 方法,用于打印卡片信息
def __repr__(self):
return f"Numeric card {self._color.name} {self._number}"
# 重写 __lt__ 方法,用于比较两张卡片的大小
def __lt__(self, other):
if self._color != other.get_color():
return self._color < other.get_color()
if isinstance(other, NumericCard):
return self._number < other._number
return True # 数值卡片被认为小于特效卡片
# 重写 __eq__ 方法,用于比较两张卡片是否相等
def __eq__(self, other):
return self._color == other.get_color() and self._number == other._number
# 获取数值卡片的数值
def get_number(self):
return self._number
# 定义 SpecialCard 类,继承自 Card 类
class SpecialCard(Card):
# 初始化方法,每张特效卡片除了颜色还有一个特效
def __init__(self, color, effect):
super().__init__(color) # 调用父类的初始化方法设置颜色
self._effect = effect # 设置特效卡片的特效
# 重写 __repr__ 方法,用于打印卡片信息
def __repr__(self):
return f"{self._effect.name} card {self._color.name}"
# 重写 __lt__ 方法,用于比较两张卡片的大小
def __lt__(self, other):
if self._color != other.get_color():
return self._color < other.get_color()
if isinstance(other, NumericCard):
return False # 特效卡片被认为大于数值卡片
return self._effect < other._effect
# 重写 __eq__ 方法,用于比较两张卡片是否相等
def __eq__(self, other):
return self._color == other.get_color() and self._effect == other._effect
# 获取特效卡片的特效
def get_effect(self):
return self._effect
在这段代码中,我们首先定义了两个枚举类Color
和Effect
来表示卡片的颜色和特效。接着定义了一个Card
基类和两个子类NumericCard
和SpecialCard
。这些类都包含了各自的特性(如颜色、数值或特效),以及用于比较和展示
卡片信息的方法。这是实现ONU游戏的基础,使我们能够创建、比较和打印游戏中的卡片。
公众号:AI悦创【二维码】
AI悦创·编程一对一
AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Web、Linux」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh
C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh
方法一:QQ
方法二:微信:Jiabcdefh
- 0
- 0
- 0
- 0
- 0
- 0