Last active
December 10, 2015 04:58
-
-
Save SegFaultAX/4384790 to your computer and use it in GitHub Desktop.
Simple blackjack implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
from random import shuffle | |
from pprint import pformat | |
def group_by(keyfn, l): | |
groups = {} | |
for e in l: | |
key = keyfn(e) | |
groups.setdefault(key, []) | |
groups[key].append(e) | |
return groups | |
### Cards ### | |
SUITS = { s[0]: s for s in ["spades", "hearts", "clubs", "diamonds"] } | |
RANKS = { 1: "ace", 11: "jack", 12: "queen", 13: "king" } | |
SCORES = { 1: 11, 11: 10, 12: 10, 13: 10 } | |
STATES = { | |
"busted": "You've busted!", | |
"blackjack": "You hit blackjack!", | |
"twentyone": "You hit 21!", | |
} | |
def new_deck(): | |
ranks = range(1, 14) | |
return [(suit, rank) for suit in SUITS.keys() for rank in ranks] | |
def shuffle_deck(deck=None, times=1): | |
if deck is None: | |
deck = new_deck() | |
for _ in range(times): | |
shuffle(deck) | |
return deck | |
def format_card(card): | |
suit, rank = card | |
suit_name = str(SUITS.get(suit)) | |
rank_name = str(RANKS.get(rank, rank)) | |
return "%s of %s" % (rank_name.capitalize(), suit_name.capitalize()) | |
def format_hand(hand, sep="\n"): | |
return sep.join(format_card(card) for card in hand) | |
### Blackjack ### | |
DEALER = "_dealer_" | |
def deal_cards(game, per_player): | |
deck = game["deck"] | |
hands = game["hands"] | |
for _ in range(per_player): | |
for player in game["players"]: | |
hands[player].append(deck.pop(0)) | |
game["dealer"].append(deck.pop(0)) | |
game.update({"deck": deck, "hands": hands}) | |
return game | |
def score_card(card): | |
suit, rank = card | |
return SCORES.get(rank, rank) | |
def is_ace(card): | |
suit, rank = card | |
return rank == 1 | |
def score_hand(hand): | |
aces = len([c for c in hand if is_ace(c)]) | |
score = sum(score_card(card) for card in hand) | |
while score > 21 and aces: | |
score -= 10 | |
aces -= 1 | |
return score | |
def new_game(players): | |
game = { "deck": shuffle_deck(times=7), "players": players, "hands": {}, | |
"dealer": [] } | |
for player in players: | |
game["hands"][player] = [] | |
return game | |
def hand_type(hand): | |
score = score_hand(hand) | |
cards = len(hand) | |
if score == 21 and cards == 2: | |
return "blackjack" | |
elif score == 21: | |
return "twentyone" | |
elif score > 21: | |
return "busted" | |
return "normal" | |
WINNING = ["twentyone", "blackjack"] | |
LOSING = ["busted"] | |
COMPLETED = WINNING + LOSING | |
def normalize_hand(hand): | |
scoring = {"blackjack": 100, "twentyone": 50, "busted": 0} | |
return scoring.get(hand_type(hand), score_hand(hand)) | |
def compare_hands(hand1, hand2): | |
return cmp(normalize_hand(hand1), normalize_hand(hand2)) | |
def determine_winners(game): | |
dealer, players = game["dealer"], game["hands"].items() | |
comp = lambda p: compare_hands(p[1], dealer) | |
stats = group_by(comp, players) | |
winners = stats.get(1, []) | |
losers = stats.get(-1, []) | |
pushes = stats.get(0, []) | |
if hand_type(dealer) == "busted": | |
losers += pushes | |
pushes = [] | |
return (winners, losers, pushes) | |
### UI ### | |
def get_input(prompt, options=None): | |
while True: | |
val = raw_input(prompt) | |
if options is None or val in options: | |
return val | |
def print_menu(player): | |
print "It's %s's turn" % player | |
print "Press 'h' to hit" | |
print "Press 's' to stay" | |
print "Press 'q' to quit" | |
def print_hand(player, hand): | |
print "\n%s's hand:" % player | |
print format_hand(hand) | |
def player_turn(player, game): | |
deck, hand = game["deck"], game["hands"][player] | |
print_menu(player) | |
print_hand(player, hand) | |
while True: | |
print "\nScore: %d" % score_hand(hand) | |
htype = hand_type(hand) | |
if htype in COMPLETED: | |
print STATES.get(htype) | |
break | |
move = get_input("> ", ["h", "s", "q"]) | |
if move == "h": | |
card = deck.pop(0) | |
hand.append(card) | |
print "Hit: %s" % format_card(card) | |
elif move == "s": | |
print "Staying" | |
break | |
elif move == "q": | |
print "Goodbye!" | |
sys.exit() | |
return htype | |
def dealer_turn(game): | |
deck, hand = game["deck"], game["dealer"] | |
print "It's the dealers turn" | |
print "\nDealer's hand:" | |
print format_hand(hand) | |
while score_hand(hand) < 17: | |
card = deck.pop(0) | |
hand.append(card) | |
print "Dealer hits: %s" % format_card(card) | |
score = score_hand(hand) | |
print "\nScore: %d" % score | |
return score | |
def print_scores(game): | |
print "\nScore\n" | |
print "Dealer has:" | |
print format_hand(game["dealer"]) | |
print "with a score of %d\n" % score_hand(game["dealer"]) | |
winners, losers, pushes = determine_winners(game) | |
print "Winners: " | |
for winner in winners: | |
name, hand = winner | |
print " %s - %d" % (name, score_hand(hand)) | |
print "Losers: " | |
for loser in losers: | |
name, hand = loser | |
print " %s - %d" % (name, score_hand(hand)) | |
print "Pushes: " | |
for push in pushes: | |
name, hand = push | |
print " %s - %d" % (name, score_hand(hand)) | |
def main(): | |
game = new_game([chr(n) for n in range(65, 75)]) | |
deal_cards(game, 2) | |
for player in game["players"]: | |
player_turn(player, game) | |
dealer_turn(game) | |
print_scores(game) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment