The goal for this lab is to write a program that will manage cards for a card game. All cards have a suit and a rank, which can be used to determine the value of cards in relation to each other. All cards will be managed by a Binary Search Tree (BST) where the best card is the maximum and the worst card is the minimum.
In order to manage cards for this lab, you will define aCard
andPlayerHand
classes that organizes the cards in a BST data structure.
You will also write pytests intestFile.py
illustrating your behavior works correctly. This lab writeup will provide some test cases for clarity, but the Gradescope autograder will run different tests shown here.
Instructions
You will need to create three files:
Card.py
- Defines a Card class. For simplicity, this class will assume all Cards have asuit
and arank
PlayerHand.py
- Defines a PlayerHand (BST) class that is an ordered collection of a Player’s Cards. You can adapt the BST implementation shown in the textbook supporting the specifications in this lab
testFile.py
- This file will contain your pytest functions that tests the overall correctness of your class definitions
There will be no starter code for this assignment, but rather class descriptions and required methods are defined in the specification below.
You should organize your lab work in its own directory. This way all files for a lab are located in a single folder. Also, this will be easy to import various files into your code using theimport / from
technique shown in lecture.
Card.py
TheCard.py
file will contain the definition of aCard
class. TheCard
class will hold information about the cards (suit
andrank
), and for simplicity, it will also double as a node in ourPlayerHand
BST. We will define the Card attributes as follows:
suit
- string value that distinguishes what suit that card is:C
(club),D
(diamond),H
(heart), orS
(spade)
rank
- string value to distinguish the rank of the card (in ascending value):A
(ace),2
,3
,4
,5
,6
,7
,8
,9
,10
,J
(Jack),Q
(Queen),K
(King). Assume there is no Joker
parent
- a reference to the parent node of a card in the BST,None
if it has no parent (it is the root)
left
- a reference to the left child of a card in the BST,None
if it has no left child
right
- a reference to the right child of a card in the BST,None
if it has no right child
count
- an integer representing the amount of times this card appears in the BST.1
by default, but it can be greater since your implementation should support duplicate cards
You will write a constructor that allows the user to construct aCard
object by passing in values for the suit and rank. Your constructor should also create thecount
attribute and initialize it to1
, as well as create theparent
,left
, andright
attributes initialized toNone
.
__init__(self, suit, rank)
Your Card class definition should also support the following “getter” and “setter” methods:
getSuit(self)
setSuit(self, suit)
getRank(self)
setRank(self, rank)
getCount(self)
setCount(self, count)
getParent(self)
setParent(self, parent)
getLeft(self)
setLeft(self, left)
getRight(self)
setRight(self, right)
__str__(self)
- the overloaded to-string operator. For example, it should return the string"S A | 1\n"
if theCard
is an Ace of Spades and has no duplicates
Lastly, your Card classcanoverload the>
,
, and==
operators. This is optional, but it can be helpful when inserting cards into their proper position within thePlayerHand
BST. In this context, aCard
should first be compared by itsrank
. For our purposes, we treatA
(Ace) as the smallest, andK
(King) as the largest. If therank
is equal, we then compare thesuit
of the cards, whereC
(Club)
D
(Diamond)
H
(Heart)
S
(Spade). By this logic,==
should only returnTrue
if both the suit and the rank are equal.Note:you should also make sure that you handle thesuit
andrank
of yourCard
case-insensitively, meaning thatCard('s', 'a')
,Card('S', 'A')
, orCard('s', 'A')
are all valid inputs and should be handled as the same card.
PlayerHand.py
ThePlayerHand.py
file will contain the definition of aPlayerHand
class. This will keep track of the cards a player has in their hand, implemented as a BST. ThePlayerHand
will manageCard
objects based on theirsuit
andrank
.
__init__(self)
- the constructor for thePlayerHand
will simply initialize the empty BST.
In addition to the construction of the BST in this class, the following methods are required to be implemented:
getTotalCards(self)
- returns the total number of cards in hand
getMin(self)
- returns the card with the lowest value from the player’s hand. ReturnsNone
if there is no card in the hand
getSuccessor(self, suit, rank)
- attempts to finds the Card with thesuit
andrank
, and returns the card with the next greatest value ReturnsNone
if there is no card with the specifiedsuit
andrank
, or if the Card is the maximum and has no successor
put(self, suit, rank)
- this adds a card with the specifiedsuit
andrank
to the BST. If that Card already exists in the BST, increment thecount
for that Card
delete(self, suit, rank)
- attempts to find the Card with the specifiedsuit
andrank
, and decrements the Cardcount
. If the count is0
after decrementing thecount
, remove the node from the BST entirely. ReturnsTrue
if the Card was successfully removed or decremented, andFalse
if the card is not present in the BST
isEmpty(self)
- returnsTrue
if there are no cards in the BST and returnsFalse
otherwise
get(self, suit, rank)
- attempts to find the Card with the specifiedsuit
andrank
, and returns the Card object if it exists. Otherwise, returnNone
inOrder(self)
- returns a string with the in-order traversal of the BST. Printing the in-order traversal should help check that the cards are in the correct order in the tree
preOrder(self)
- returns a string with the pre-order traversal of the BST. BSTs with the same structure should always have the same pre-order traversal, so this can be used to verify that everything was inserted correctly
An example of theinOrder()
string format is given below:
hand = PlayerHand() hand.put('D', 'A') hand.put('S', 'K') hand.put('S', '2') hand.put('C', 'Q') hand.put('H', '7') hand.put('S', 'K') hand.put('C', 'K') assert hand.inOrder() == \ "D A | 1\n\ S 2 | 1\n\ H 7 | 1\n\ C Q | 1\n\ C K | 1\n\ S K | 2\n"
An example of thepreOrder()
string format is given below:
hand = PlayerHand() hand.put('D', 'A') hand.put('S', 'K') hand.put('S', '2') hand.put('C', 'Q') hand.put('H', '7') hand.put('S', 'K') hand.put('C', 'K') assert hand.preOrder() == \ "D A | 1\n\ S K | 2\n\ S 2 | 1\n\ C Q | 1\n\ H 7 | 1\n\ C K | 1\n"
Other than the required methods, feel free to implement any helper methods that you think are useful in your implementation. The automated tests will test only your implementation of the required methods by creating aPlayerHand
containing variousCards
with differentsuit
andrank
attributes. Thedelete()
andput()
methods will be run, withget()
,inOrder()
, andpreOrder()
being used to verify that thePlayerHand
is fully functional. You should write similar tests to confirm your BST is working properly.
testFile.py
This file should test all of your classes using pytest. Think of various scenarios and edge cases when testing your code according to the given descriptions. You should test every class’ method functionality (except for getters / setters). Even though Gradescope will not use this file when running automated tests (there are separate tests defined for this), it is important to provide this file with various test cases (testing is important!!).