Last active
August 29, 2015 14:03
-
-
Save finbarrtimbers/4d4df708491cbe9d8669 to your computer and use it in GitHub Desktop.
Text-based, one player blackjack game.
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/local/bin/python | |
"text-based Blackjack program. One player, one dealer; dealer hits to 17." | |
import random | |
import sys | |
class Card(object): | |
"Represents a playing card. Used here in DECK." | |
def __init__(self, name, value, suit, index): | |
self.name = name | |
self.value = value | |
self.suit = suit | |
self.index = index | |
def __unicode__(self): | |
temp = unicode(self.name + " of ") + suit_to_unicode(self.suit) | |
temp.encode('utf-8') | |
return temp | |
def __str__(self): | |
if UNICODE: | |
encoding = sys.stdout.encoding or DEFAULT_ENCODING | |
return unicode(self).encode(encoding, 'replace') | |
else: | |
return self.name + " of " + self.suit | |
def suit_to_unicode(suit): | |
"Converts a string representation of a suit to the unicode symbol." | |
if suit == "Spades": | |
return u"\u2660" | |
elif suit == "Hearts": | |
return u"\u2665" | |
elif suit == "Diamonds": | |
return u"\u2666" | |
elif suit == "Clubs": | |
return u"\u2663" | |
def card_suit(card_index): | |
"Converts the index of a card into the appropriate suit (as a string). " | |
if card_index >= 0 and card_index < 13: | |
suit = "Spades" | |
elif card_index >= 13 and card_index < 26: | |
suit = "Clubs" | |
elif card_index >= 26 and card_index < 39: | |
suit = "Diamonds" | |
elif card_index >= 39 and card_index < 52: | |
suit = "Hearts" | |
return suit | |
def card_name(card_index): | |
"Turns card index into a name. e.g. 13th card is a king." | |
if card_index == 0: | |
return 'Ace' | |
elif card_index > 0 and card_index < 10: | |
return str(card_index + 1) | |
elif card_index == 10: | |
return 'Jack' | |
elif card_index == 11: | |
return 'Queen' | |
elif card_index == 12: | |
return 'King' | |
else: | |
print card_index | |
raise ValueError | |
def card_value(card_index): | |
"Converts card_index into the card's value. e.g. King has a value of 10." | |
if card_index == 0: | |
return 11 | |
elif card_index > 0 and card_index < 10: | |
return card_index + 1 | |
elif card_index >= 10 and card_index <= 12: | |
return 10 | |
else: | |
raise ValueError("n must be between 0 and 12") | |
def clear(): | |
"Clears terminal" | |
import os | |
os.system('cls' if os.name == 'nt' else 'clear') | |
def deal_cards(n, cards_played=[]): | |
"deals n cards, while remembering which cards have been dealt." | |
hand = [] | |
deck = {n : Card(card_name(n % 13), card_value(n % 13), card_suit(n), n) | |
for n in xrange(52)} | |
if len(cards_played) >= 52: | |
return deal_cards(n, []) | |
for i in xrange(n): | |
while True: | |
j = random.randrange(52) | |
if j not in cards_played: | |
cards_played.append(j) | |
break | |
hand.append(deck[j]) | |
return hand | |
def get_hand_value(hand): | |
"Returns the total value of the hand." | |
ace_counter = 0 | |
for card in hand: | |
if card.name == 'Ace': | |
ace_counter += 1 | |
total = sum([card.value for card in hand]) | |
if total > 21 and ace_counter > 0: | |
while total > 21 and ace_counter > 0: | |
total -= 10 | |
ace_counter -= 1 | |
return total | |
def print_hand(hand): | |
"Prints the hand to stdout." | |
print '-' * 15 | |
for card in hand: | |
print card | |
print '-' * 15 | |
print 'Total: ' + str(get_hand_value(hand)) + '\n' | |
def play_dealer(dealer_hand, silent=False): | |
"Plays for the dealer by hitting until 17." | |
hand_value = get_hand_value(dealer_hand) | |
if not silent: | |
print "Dealer's hand:" | |
print_hand(dealer_hand) | |
while hand_value < 17: | |
dealer_hand += deal_cards(1) | |
if not silent: | |
print "Dealer hits. Dealer's hand is now:" | |
print_hand(dealer_hand) | |
hand_value = get_hand_value(dealer_hand) | |
return dealer_hand | |
def end_hand(string): | |
"Ends the hand by printing the string and clearing the screen." | |
raw_input(string + "[Enter to continue]") | |
clear() | |
# TODO: add splitting. | |
# When a hand has a pair, then you can split into two hands. | |
# Requires doubling the bet. | |
# TODO: add double down. | |
# On the first turn, players can double their bet, | |
# take a single card, and stand. | |
# TODO: add surrendering. | |
# Player gives up a half-bet and retires from the game. | |
def play_hand(bet): | |
"Plays one hand of blackjack. Returns result of bet." | |
hand = deal_cards(2, []) | |
dealer = deal_cards(2) | |
i = 0 | |
while True: | |
print "The dealer's hand:" | |
print_hand(dealer) | |
print "\nYour hand:" | |
print_hand(hand) | |
hand_value = get_hand_value(hand) | |
dealer_hand_value = get_hand_value(dealer) | |
if hand_value > 21: | |
end_hand("Sorry, you've gone bust.") | |
return -1* bet | |
if i == 0: | |
action_string = "[H]it [S]tand [D]ouble S[u]rrender [Q]uit:" | |
action = raw_input(action_string).lower() | |
else: | |
action = raw_input("[H]it [S]tand [Q]uit:").lower() | |
if action == "h": | |
hand += deal_cards(1) | |
elif action == "s": | |
dealer = play_dealer(dealer) | |
dealer_hand_value = get_hand_value(dealer) | |
if dealer_hand_value > 21 or dealer_hand_value < hand_value: | |
end_hand("You win!") | |
return bet | |
else: | |
end_hand("You lose!") | |
return 0 | |
elif action == "p": | |
print "Not yet implemented, sorry!" | |
elif action == "d": | |
hand += deal_cards(1) | |
bet *= 2 | |
dealer = play_dealer(dealer) | |
dealer_hand_value = get_hand_value(dealer) | |
if dealer_hand_value > 21 or dealer_hand_value < hand_value: | |
end_hand("You win!") | |
return bet | |
else: | |
end_hand("You lose!") | |
return 0 | |
elif action == "u": | |
end_hand("Better luck next round.") | |
bet *= 0.5 | |
return bet | |
else: | |
if action == 'q': | |
sys.exit(0) | |
else: | |
print "Invalid option." | |
i += 1 | |
def main(): | |
"Runs blackjack game." | |
try: | |
chips = 100.0 | |
while chips != 0: | |
if chips == 0: | |
print "Sorry, you ran out of money. Have a nice day!" | |
choice = raw_input("Want to play a hand [y/n]? ") | |
if choice == 'y': | |
bet_message = "Bet [min: " + str(HOUSE_MIN) | |
bet_message += "; you have " + str(chips) + "]:" | |
bet_response = raw_input(bet_message) | |
try: | |
bet = float(bet_response) | |
assert bet >= HOUSE_MIN and bet <= chips | |
chips += play_hand(bet) | |
except (ValueError, AssertionError): | |
print "Invalid input." | |
elif choice == 'n': | |
raise SystemExit(0) | |
else: | |
print "Invalid choice." | |
# The try/except allows me to handle KeyboardInterrupts silently. | |
except (KeyboardInterrupt, EOFError): | |
pass | |
if __name__ == "__main__": | |
# set global variables | |
HOUSE_MIN = 1.0 | |
UNICODE = True | |
DECK = {n : Card(card_name(n % 13), card_value(n % 13), card_suit(n), n) | |
for n in xrange(52)} | |
DEFAULT_ENCODING = 'utf-8' | |
#start playing | |
main() |
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
from blackjack import * | |
import unittest | |
class TestBlackjack(unittest.TestCase): | |
def test_get_value(self): | |
self.assertTrue(len([get_hand_value(deal_cards(i, [])) | |
for i in range(10)]) == 10) | |
def test_deal_cards(self): | |
self.assertTrue(len([play_dealer(deal_cards(2, []), silent=True) | |
for i in range(10)]) == 10) | |
def regression_tests(self): | |
hand1 = [Card(card_name(n % 13), card_value(n % 13), card_suit(n), n) | |
for n in [0, 5, 8, 12]] | |
hand2 = [Card(card_name(n % 13), card_value(n % 13), card_suit(n), n) | |
for n in [0, 13, 26, 39]] | |
self.assertTrue(get_hand_value(hand1) == 26) | |
self.assertTrue(get_hand_value(hand2) == 14) | |
if __name__ == "__main__": | |
suite = unittest.TestLoader().loadTestsFromTestCase(TestBlackjack) | |
unittest.TextTestRunner(verbosity=2).run(suite) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment