[Solution]Problem 67: Designing a Card Game Class [Facebook/Meta]
Object Oriented Design, Logic, Programming
To learn more about the newsletter, check our detailed About Page + FAQs
To help me understand you better, please fill out this anonymous, 2-min survey. If you liked this post, make sure you hit the heart icon in this email.
Recommend this publication to Substack over here
Take the next step by subscribing here
How did you do yesterday?
This is one of the better Coding Interview Questions asked. No gimmicks, directly related to the kinds of work you’ll do. Working through these questions is a great way to develop your coding ability and establish your ability to build code for the future. Remember, as this Microsoft Study into great software engineers showed, these skills are crucial. Building your solution the right way can be your ticket into an amazing career.
Problem
Assume you’re designing a card game using the traditional 52-card deck. Design a Game
class representing the game and these following functions associated with the class.
add_card(suit, value)
: Creates a new card object with a suit from one of the following strings:Hearts
,Spades
,Clubs
,Diamonds
, and a value from one of the following strings:A
,2
~10
,J
,Q
,K
. This card is represented byi
, wherei
is an integer indicating how many cards have been created before.card_string(card)
: Returns the string representation of the card represented byi
. It follows the format<value> of <suit>
. For example, a card created byadd_card("Spades", "3")
should have a string representation of3 of Spades
.card_beats(card_a, card_b)
: Check if the card represented bycard_a
beats the one represented bycard_b
. A card beats another card if and only if it has a greater value. The value of the cards are ordered fromA
toK
.
You may implement these however you like. However, preferably this should be easily expandable to accommodate new requirements.
This kind of a problem might seem too easy for the coders amongst you, but building things up the right way will go a long way.
Step 1: Building the Skeleton
The question is nice enough to give you a stub. Don’t be a fool. Use it. We know that the template of this class will look like the following-
class Game:
def __init__(self):
# Initialize here
pass
"""
add_card(suit, value): Creates a new card object with a suit from one of the following strings: Hearts, Spades, Clubs, Diamonds, and a value from one of the following strings: A, 2~10, J, Q, K. This card is represented by i, where i is an integer indicating how many cards have been created before.
"""
def add_card(self, suit: str, value: str) -> None:
# TODO
pass
"""
card_string(card): Returns the string representation of the card represented by i. It follows the format <value> of <suit>. For example, a card created by add_card("Spades", "3") should have a string representation of 3 of Spades.
"""
def card_string(self, card: int) -> str:
# TODO
return ""
"""
card_beats(card_a, card_b): Check if the card represented by card_a beats the one represented by card_b. A card beats another card if and only if it has a greater value. The value of the cards are ordered from A to K.
"""
def card_beats(self, card_a: int, card_b: int) -> bool:
# TODO
return False
The statement- “Returns the string representation of the card represented by i
” is an extremely important detail that you want to pay attention to. This implies that we want to generate our cards in an orderly way since otherwise, we might cause issues. Important to take note of, and definitely important to communicate with your interviewer. Get those low-hanging fruit where you can. I’ve also copied the requirements above the stubs, so I don’t have to keep going all the way to the top. We will clean this up in the final solution.
So what do we do next? Implement one of the functions? Tempting. But notice they refer to a Card as a class. Implementing it, will be extremely helpful to us. Even if they didn’t directly mention a card class, using a class would be a must for good design. It cleans up your code a lot.
Step 2: Implementing The Card Class
Based on add_card, we know that a card has two properties- a suit and a value.
This gives us a pretty simple init
def __init__(self, suit: str, value: str):
self.suit = suit
self.value = value
Next is our comparison function. This will be needed for one of the requirements (and comparators are generally good to have)
def __lt__(self, other):
return self.card_value < other.card_value
Next is where we will get slightly more advanced. Time to bring in a mainstay of Object Oriented Programming.
Step 3: The Almighty Enums
Enums are amazing when designing programming classes. They help make code more readable, by allowing programmers to refer to int values as the strings to represent. This article is a great piece on them. When we have a fixed collection of strings/names, we can pull up Enums. Do we see anything here?
Well, the suits are an obvious contender. We know how many there are, and referring to them by the name instead of the int makes life easier.
Let’s implement that.
from enum import Enum, auto
class Suit(Enum):
CLUBS = auto()
DIAMONDS = auto()
HEARTS = auto()
SPADES = auto()
The auto is a little thing that makes our code easier in Python. In a nutshell, it automatically assigns int values to our enum. I use this because if we had to change the suits, we’d just need to make the changes here. Auto would handle the int assignment for us. Python Devs, read more about Auto here. To the other language users, check out if your language has this functionality. Otherwise, it’s not a huge deal. It’s a small change to hardcode enum values.
We can integrate the enums into the Card Class. This is how I did it.
class Card:
SUITS = {
"Clubs": Suit.CLUBS,
"Diamonds": Suit.DIAMONDS,
"Hearts": Suit.HEARTS,
"Spades": Suit.SPADES,
}
SUIT_NAMES = {e: n for n, e in SUITS.items()}
VALUES = {
"A": 1,
**{str(i): i for i in range(2, 11)},
"J": 11,
"Q": 12,
"K": 13,
}
VALUE_NAMES = {e: n for n, e in VALUES.items()}
This again, makes the whole process easier. To those not familiar, **{str(i): i …
} might be confusing. It is a Python Concept called kwargs, which allows us to pass a variable number of arguments as a string. So that piece of code gives us {‘2’: 2, ‘3’: 3, … ‘10’: 10}. It’s just a neat little way to save some time and show off programming knowledge. Kwargs are a pretty useful concept, so you should definitely understand them. Some examples for you are here.
Now that we’ve done the leg work, the implementation should not be a problem. Let’s put everything together, and implement the functions they asked us to.
Step 4: Combining Everything
If you’ve followed along, this should not be too hard. Our game class becomes relatively trivial.
from enum import Enum, auto
class Suit(Enum):
CLUBS = auto()
DIAMONDS = auto()
HEARTS = auto()
SPADES = auto()
class Card:
SUITS = {
"Clubs": Suit.CLUBS,
"Diamonds": Suit.DIAMONDS,
"Hearts": Suit.HEARTS,
"Spades": Suit.SPADES,
}
SUIT_NAMES = {e: n for n, e in SUITS.items()}
VALUES = {
"A": 1,
**{str(i): i for i in range(2, 11)},
"J": 11,
"Q": 12,
"K": 13,
}
VALUE_NAMES = {e: n for n, e in VALUES.items()}
def __init__(self, suit: str, value: str):
self.suit = self.SUITS[suit]
self.value = self.VALUES[value]
@property
def card_value(self) -> int:
return self.value
@property
def card_suit(self) -> str:
return self.suit
def __lt__(self, other):
return self.card_value < other.card_value
def __str__(self) -> str:
value = self.VALUE_NAMES[self.value]
suit = self.SUIT_NAMES[self.suit]
return f"{value} of {suit}"
class Game:
def __init__(self):
self.cards: list[Card] = []
def add_card(self, suit: str, value: str) -> None:
self.cards.append(Card(suit, value))
def card_string(self, card: int) -> str:
return str(self.cards[card])
def card_beats(self, card_a: Card, card_b: Card) -> bool:
return self.cards[card_a] > self.cards[card_b]
How did you do it? Share it with me. Exceptional/Interesting solutions get free stuff.
Loved the solution? Hate it? Want to talk to me about your crypto portfolio? Get my predictions on the UCL or MMA PPVs? Want an in-depth analysis of the best chocolate milk brands? Reach out to me by replying to this email, in the comments, or using the links below.
Stay Woke.
Go kill all,
Devansh <3
Reach out to me on:
Instagram: https://www.instagram.com/iseethings404/
Message me on Twitter: https://twitter.com/Machine01776819
My LinkedIn: https://www.linkedin.com/in/devansh-devansh-516004168/
My content:
Read my articles: https://rb.gy/zn1aiu
My YouTube: https://rb.gy/88iwdd
Get a free stock on Robinhood. No risk to you, so not using the link is losing free money: https://join.robinhood.com/fnud75