Simple Blackjack game design¶
Requirements :
- Player should be able to place a bet
- Dealer should be able to deal cards to player
- Player can choose hit or stay only
- Dealer can take more cards if sum <= 16 and declares the outcome
Actors and main use cases¶
- Dealer
- deal card
- take bet
- payout
- declare outcome
- Player
- place bet
- decide action
- take payout
Classes in the system what responsibilities they have and how they interact¶
- Card : represents a normal card
- BlackJackCard : its a card but it know how it behaves in Blackjack game
- Dealer : deals card, etc
- Player : place bet, etc
- Deck : a deck of cards
- Hand : a set of cards
- Game : orchestrates all of the objects, startes the game, asks player to place bet, makes dealer deal cards, asks player for action, asks dealer for action, declares outcome via dealer
You can play this game here : https://colab.research.google.com/drive/1AA9wKauLnu3ikjI3951zeljo5cI95qWT
In [69]:
# K.I.S.S - keep things "very simple" in the beginning
# First code basic interfaces of the classes
# Build a client which uses it
# Implement details
# Then try to improve it
# *** Possible Improvements ***
# print/io logic can be put into implemenations like CLIPlayer
# Name can be asked from user at runtime
# Rules can be abstracted out into a separate class
# More actions can he handled like split, insurance
# Dealer actions can be moved one level up and let game do it
import enum
import sys
from random import randint
class BlackJackGame(object):
def __init__(self):
self.player = self._make_player()
self.dealer = self._make_dealer()
self.deck = Deck()
def play(self):
# get bet from player
# dealer deals cards
# player gameplay
# dealer gameplay
# result and decide to replay
while True:
self.deck.shuffle()
self.player.hand = Hand()
self.dealer.hand = Hand()
bet = self.player.get_bet()
self.dealer.accept_bet(bet)
self.dealer.deal_card(self.deck, self.player)
self.dealer.deal_card(self.deck, self.player)
self.dealer.deal_card(self.deck, self.dealer)
# keep track of hole card to later make it visible
hole_card = self.dealer.deal_card(self.deck, self.dealer, True)
self.display()
while not self._is_bust(self.player.hand):
p_action = self.player.get_action(['hit', 'stand'])
if p_action == 'hit':
self.dealer.deal_card(self.deck, self.player)
self.display()
elif p_action == 'stand':
break
if self._is_bust(self.player.hand):
result = 'Dealer wins, player is bust !'
self.dealer.take_money(bet*2)
else:
hole_card.hidden = False
self.display()
while self.dealer.hand.get_score() <= 16:
self.dealer.deal_card(self.deck, self.dealer)
self.display()
if self._is_bust(self.dealer.hand):
result = 'Player wins, dealer is bust !'
self.player.take_money(bet*2)
elif self.dealer.hand.get_score() == self.player.hand.get_score():
result = 'Push, its a draw !'
self.player.take_money(bet)
self.dealer.take_money(bet)
elif self.dealer.did_player_win(self.dealer.hand, self.player.hand):
result = 'Player wins'
self.player.take_money(bet*2)
else:
result = 'Dealer wins'
self.dealer.take_money(bet*2)
self.publish_scores()
self._publish_result(result)
if not self.player.wants_to_play():
break
def put_back_cards(self):
all_cards = [].append(self.player.hand.cards).append(self.dealer.hand.cards)
for card in all_cards:
if card.hidden:
card.hidden = False
self.deck.add_card(card)
def publish_scores(self):
print('Dealer score : %s'%self.dealer.hand.get_score())
print('Player score : %s'%self.player.hand.get_score())
def _publish_result(self, res):
print("**************************\n")
print(res)
print("\n")
print("**************************\n")
def display(self):
self.dealer.display()
self.player.display()
def _is_bust(self, hand):
return hand.get_score() > 21
def _make_player(self):
return Player('Shaktiman', 100)
def _make_dealer(self):
return Dealer('Kilvish', 100)
class Card(object):
def __init__(self, suit, number):
self.suit = suit
self.number = number
class BlackJackCard(Card):
def __init__(self, suit, number):
super().__init__(suit, number)
self.hidden = False
def is_ace(self):
return self.number == 1
def value(self):
if self.is_ace():
raise NotImplementedError('Ace is handled separately')
if self.number >= 10 and self.number <=13:
return 10
else:
return self.number
def to_string(self):
if self.hidden:
return 'XXX'
else:
return '%s:%d' % (self.suit, self.number)
class Player(object):
def __init__(self, name, money):
self.name = name
self.money = money
self.hand = Hand()
def take_money(self, money):
self.money = self.money + money
def get_bet(self):
amount = int(input('Enter your bet amount %s : '% self.name))
if amount > self.money:
print('Cant bet more than what you have : %d\n'%self.money)
return self.get_bet()
self.money = self.money - amount
return amount
def wants_to_play(self):
return 'y' == input('Want to play (y/n)?').lower()
def display(self):
print('Hand of %s : ' % self.name)
for card in self.hand.cards:
print('[%s]' % card.to_string())
def get_action(self, options):
action_index = input('Choose your action , press 1 for first , 2 for second etc : ' + ','.join(options))
return options[int(action_index)-1]
class Dealer(Player):
def __init__(self, name, money):
super().__init__(name, money)
def deal_card(self, deck, player, hidden=False):
card = deck.remove_card()
card.hidden = hidden
player.hand.add_card(card)
return card
def accept_bet(self, money):
self.money = self.money - money
def did_player_win(self, dealer_hand, player_hand):
d_score = dealer_hand.get_score()
p_score = player_hand.get_score()
return p_score > d_score
class Deck(object):
def __init__(self):
self.cards = []
for suit in Suit:
for number in range(1,14):
# BlackJackCard can be passed via a strategy later on
self.cards.append(BlackJackCard(suit, number))
def shuffle(self):
L = len(self.cards)
for l in range(L-1):
r = randint(l+1, L-1)
self.cards[r], self.cards[l] = self.cards[l], self.cards[r]
def remove_card(self):
return self.cards.pop()
def add_card(self, card):
self.cards.append(card)
class Suit(enum.Enum):
HEART = 1
SPADE = 2
DIAMOND = 3
CLUB = 4
class Hand(object):
def __init__(self):
self.cards = []
def add_card(self, bj_card):
self.cards.append(bj_card)
def get_score(self):
# calculate the score of hand
possible_scores = [0]
for card in self.cards:
new_possible_scores = []
for score in possible_scores:
if card.is_ace():
new_possible_scores.append(score+1)
new_possible_scores.append(score+11)
else:
new_possible_scores.append(score+card.value())
possible_scores = new_possible_scores
possible_scores_less_than_21 = list(filter(lambda x : x<=21, possible_scores))
if len(possible_scores_less_than_21) > 0 :
return max(possible_scores_less_than_21)
else:
return min(possible_scores)
In [ ]:
game = BlackJackGame()
game.play()
In [ ]:
No comments:
Post a Comment