0) Problem Restatement
Design Blackjack in an object-oriented way (asked at Goldman Sachs). The tricky part candidates often miss: an Ace counts as 1 or 11, whichever is better for the hand. Also detect a blackjack (Ace + a 10-value card as the first two cards) and a bust (over 21).
Rules (simplified): each player and the dealer get 2 cards. Players choose hit (take a card) or stand. Going over 21 = bust (you lose). Then the dealer draws until reaching at least 17. Closest to 21 without busting wins, and a natural blackjack beats a regular 21.1) Classes
Architecture Diagram
classDiagram
class Card { +Rank rank +Suit suit +value() int }
class Shoe { -List cards +draw() Card +shuffle(seed) void }
class Hand { +List cards +add(card) +bestValue() int +isBlackjack() bool +isBust() bool }
class Participant { +Hand hand }
class Player { +String name +int bet +decide(dealerUpCard) Action }
class Dealer { +shouldHit() bool }
class Game { -Shoe shoe -List players -Dealer dealer +playRound() Map }
Participant <|-- Player
Participant <|-- Dealer
Game --> Shoe
Game --> Player
Game --> Dealer
Participant --> Hand
Hand --> Card2) The Ace Logic (core algorithm)
Count every Ace as 1 first, then turn one Ace into 11 (adding 10) if that doesn't exceed 21. Two Aces as 11 would be 22, so at most one Ace can ever count as 11.
import random
RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
SUITS = ['♠', '♥', '♦', '♣']
class Card:
def __init__(self, rank, suit): self.rank, self.suit = rank, suit
def base_value(self):
if self.rank == 'A': return 1
if self.rank in ('J', 'Q', 'K'): return 10
return int(self.rank)
class Hand:
def __init__(self): self.cards = []
def add(self, card): self.cards.append(card)
def best_value(self):
total = sum(c.base_value() for c in self.cards)
if any(c.rank == 'A' for c in self.cards) and total + 10 <= 21:
total += 10 # one Ace counts as 11
return total
def is_soft(self): # an Ace is currently counted as 11
hard = sum(c.base_value() for c in self.cards)
return any(c.rank == 'A' for c in self.cards) and hard + 10 <= 21
def is_blackjack(self): return len(self.cards) == 2 and self.best_value() == 21
def is_bust(self): return self.best_value() > 21
class Shoe:
def __init__(self, decks=6, seed=None):
self.cards = [Card(r, s) for _ in range(decks) for s in SUITS for r in RANKS]
random.Random(seed).shuffle(self.cards)
def draw(self): return self.cards.pop()
def settle(player, dealer):
if player.is_bust(): return "lose"
if player.is_blackjack() and not dealer.is_blackjack(): return "blackjack" # usually pays 3:2
if dealer.is_bust(): return "win"
p, d = player.best_value(), dealer.best_value()
return "win" if p > d else "lose" if p < d else "push"
def dealer_play(shoe, dealer_hand, hit_soft_17=False):
while dealer_hand.best_value() < 17 or (hit_soft_17 and dealer_hand.best_value() == 17 and dealer_hand.is_soft()):
dealer_hand.add(shoe.draw())
Examples: A + K = 21 (blackjack). A + A + 9 = 1 + 1 + 9 = 11, then +10 = 21. A + 7 + 9 = 17 (the Ace must be 1, since 27 would bust).
3) Game Flow
- Players place bets. Deal 2 cards to each player and the dealer (one dealer card face down).
- If the dealer shows an Ace, optionally offer insurance. Check for dealer blackjack.
- Each player acts: hit or stand (later: double down, split) until they stand or bust.
- The dealer reveals and draws by rule (hit below 17; house rule for "soft 17").
- Settle each player (win, lose, push, blackjack 3:2) and update chips.
- Reshuffle when the shoe falls below a cut card (e.g., 25% remaining).
4) Design Notes
- Rules as configuration (number of decks, dealer hits soft 17, blackjack payout) keep the engine reusable.
- Player decisions go through a strategy interface: a human (UI input) or a bot (basic strategy table). That's great for testing.
- Seeded shuffle makes games reproducible in tests.
- Extensions:
splitcreates two hands for one player (so a Player has a list of Hands), anddoubledoubles the bet and draws exactly one card.
5) Wrap-Up
Model Card, Shoe, Hand, Player, Dealer and Game. The key algorithm is best_value: count Aces as 1, then add 10 once if it doesn't bust, which also defines soft hands. Detect blackjack (2 cards totaling 21) and busts, let the dealer draw to 17 by configurable rules, and settle outcomes, keeping rules and player strategies pluggable for extensions like split and double down.