Created
April 7, 2016 08:05
-
-
Save rbtcollins/16b6c9a447c564bab57e100d86f57c98 to your computer and use it in GitHub Desktop.
This file contains 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 operator import methodcaller | |
import random | |
import itertools | |
def card_values(card): | |
"""Get the possible values of a card in a blackjack game. | |
e.g. card_values(0) -> (1, 11) | |
:return: A tuple of the possible values. | |
""" | |
c = card % 13 | |
if c == 0: | |
return (1, 11) | |
if c >= 9: | |
return (10,) | |
return (c + 1,) | |
def showcard(c): | |
suite = c // 13 | |
card = c % 13 | |
suites = {0: 'H', 1: 'D', 2:'S', 3:'C'} | |
cards = {0:'A', 1:'2', 2:'3', 3:'4', 4:'5', 5:'6', 6:'7', 7:'8', 8:'9', 9:'10', 10:'J', 11:'Q', 12:'K'} | |
return "%s%s" % (suites[suite], cards[card]) | |
def shuffle(): | |
"""Return a shuffled deck of 8 regular decks. | |
Each card is modelled by a number between 0 and 51. | |
""" | |
singledeck = list(range(52)) | |
alldecks = singledeck * 8 | |
random.shuffle(alldecks) | |
return iter(alldecks) | |
class Hand: | |
def __init__(self): | |
self.cards = [] | |
self.held = False | |
def __repr__(self): | |
return "%s %s" % (list(map(showcard,self.cards)), self.held) | |
def _possible_values(self): | |
possible_values = set() | |
for combination in itertools.product(*map(card_values, self.cards)): | |
value = sum(combination) | |
possible_values.add(value) | |
return possible_values | |
def winning(self): | |
possible_values = self._possible_values() | |
if 21 in possible_values: | |
return True | |
if min(possible_values) > 21: | |
return False | |
return None | |
def best_value(self): | |
try: | |
return max(filter(lambda x:x<=21, self._possible_values())) | |
except ValueError: | |
return 0 | |
def finished(self): | |
return self.held or self.winning() is not None | |
class Game: | |
def __init__(self, playercount): | |
self.dealer = Hand() | |
self.players = [] | |
self.deck = shuffle() | |
self.nextplayer = 0 | |
for pos in range(playercount): | |
self.players.append(Hand()) | |
self.players[pos].cards.append(next(self.deck)) | |
self.players[pos].cards.append(next(self.deck)) | |
self.dealer.cards.append(next(self.deck)) | |
self.dealer.cards.append(next(self.deck)) | |
def __repr__(self): | |
return repr(self.__dict__) | |
def turn(self, take): | |
"""Take a turn. | |
:param take: True to take a card, False to hold. | |
""" | |
if not take: | |
self.players[self.nextplayer].held = True | |
self.players[self.nextplayer].cards.append(next(self.deck)) | |
self.nextplayer = (self.nextplayer + 1) % len(self.players) | |
def finished(self): | |
return all(map(methodcaller(finished), self.players)) | |
def dealer_turn(self): | |
to_beat = list(map(methodcaller("best_value"), self.players)) | |
target = max(to_beat) | |
if target == 21: | |
print("someone won") | |
return | |
if self.dealer.best_value() > target: | |
print("dealer wins") | |
return | |
self.dealer.cards.append(next(self.deck)) |
This file contains 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 collections import defaultdict | |
from testtools import TestCase | |
from testtools.matchers import HasLength | |
from blackjack.deck import Hand, card_values, shuffle, Game | |
class TestDeck(TestCase): | |
def test_shuffle(self): | |
seq1 = list(shuffle()) | |
seq2 = list(shuffle()) | |
self.assertNotEqual(seq1, seq2) | |
self.expectThat(seq1, HasLength(52*8)) | |
self.expectThat(seq2, HasLength(52*8)) | |
seen = defaultdict(list) | |
for card in seq1: | |
seen[card].append(card) | |
for card, occurences in seen.items(): | |
self.expectThat(occurences, HasLength(8)) | |
def test_card_values(self): | |
# 0-> 12: H A, 2, ... K | |
# 13 -> 25: D A... | |
# 26 -> 38 | |
# 39 -> 51 | |
for offset in range(0, 52, 13): | |
self.assertEqual((1,11), card_values(0 + offset)) | |
for c in range(1,10): | |
self.assertEqual((c + 1,), card_values(c + offset)) | |
for c in range(11,13): | |
self.assertEqual((10,), card_values(c + offset)) | |
class TestHand(TestCase): | |
def test_init(self): | |
h = Hand() | |
self.assertEqual([], h.cards) | |
def test_winning_state(self): | |
h = Hand() | |
hands = [[9, 0], [9, 10, 0]] | |
for hand in hands: | |
h = Hand() | |
h.cards = hand | |
self.assertEqual(True, h.winning()) | |
def test_losing_state(self): | |
h = Hand() | |
hands = [[7, 8 ,9], [9, 8, 0, 1]] | |
for hand in hands: | |
h = Hand() | |
h.cards = hand | |
self.assertEqual(False, h.winning()) | |
def test_neutral_state(self): | |
h = Hand() | |
hands = [[7, 8], [9, 8, 0]] | |
for hand in hands: | |
h = Hand() | |
h.cards = hand | |
self.assertEqual(None, h.winning(), "hand %r" % (hand, )) | |
class TestGame(TestCase): | |
def test_init(self): | |
g = Game(2) | |
self.expectThat(g.players, HasLength(2)) | |
self.assertIsInstance(g.players[0], Hand) | |
self.assertIsInstance(g.players[1], Hand) | |
self.assertIsInstance(g.dealer, Hand) | |
def check_initial(hand): | |
self.expectThat(hand.cards, HasLength(2)) | |
check_initial(g.dealer) | |
check_initial(g.players[0]) | |
check_initial(g.players[1]) | |
self.assertEqual(0, g.nextplayer) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment