bj_objects.py

'''\
Blackjack - Card Deck.

Cards are integer values from 1 (Ace) to 10.

Rules:
    0 - null
    1 - stay
    2 - hit
    3 - double
    4 - split pair
    5 - bust
    6 - push
    7 - win (got 21)
    8 - blackjack (player)
    9 - blackjack (dealer)

Developer@Sonnack.com
March 2016
'''
####################################################################################################
from sys import stdoutstderr
from random import randomshuffle
from bj_rulz import RuleHardRuleSoftRuleSplit
####################################################################################################

CardDeck52 = 4 * [1+card if card < 10 else 10 for card in range(13)] 
shuffle(CardDeck52)

CardDeck4 = 4 * CardDeck52
shuffle(CardDeck4)


##================================================================================================##
def NewDeck (nbr_of_decks):
    suit = [1+card if card < 10 else 10 for card in range(13)] 
    deck = 4 * suit
    decks = nbr_of_decks * deck
    shuffle(decks)
    return decks


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class CardDeck (object):
    '''Card Deck class.'''

    def __init__ (self_nbr_of_decks):
        self.deck = 4 * [1+c if c < 10 else 10 for c in range(13)] 
        self.nd = _nbr_of_decks
        self.reset()

    def reset (self):
        self.cards = self.nd * self.deck
        shuffle(self.cards)
        shuffle(self.cards)

    def __call__ (self):
        if len(self):
            return self.cards.pop()

    def __str__ (self):
        return str(self.cards)

    def __len__ (self):
        return len(self.cards)


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class Cards (object):
    '''Card Hand class.'''

    def __init__ (self):
        self.initialize()

    def initialize (self):
        self.cards = []
        self.hand = 0
        self.counts = [0]

    def blackjack (self):
        '''True if hand is A+10.'''
        if len(self.cards) == 2:
            if len(self.counts) == 2:
                if self.counts[1] == 21:
                    return True
        return False

    def win (self):
        '''True if any count is 21.'''
        for n in self.counts:
            if n == 21:
                return True
        return False

    def bust (self):
        '''True if face value is > 21.'''
        if 21 < self.counts[0]:
            return True
        return False

    def count_cards (self):
        '''count: [face-value, 10+face, 20+face,...]'''
        self.counts = [sum(self.cards)]
        self.hand = self.counts[0]
        a = 10
        for c in self.cards:
            if c == 1:
                t = self.hand + a
                self.counts.append(t)
                if t <= 21:
                    self.hand = t
                a += 10

    def __len__ (self):
        return len(self.cards)

    def __call__ (selfcard):
        '''Add card to hand.'''
        self.cards.append(card)
        self.count_cards()

    def __str__ (self):
        p = len(self) * ' %2d'
        q = len(self.counts) * ', %d'
        s = 'Cards: [%s] {%s}' % (p[1:], q[2:])
        t = tuple(self.cards + self.counts)
        return s % t

    def __repr__ (self):
        s = '{cards:%d, hand:%d, face:%d, bust:%s, win:%s, bj:%s}'
        t = (len(self), self.handself.counts[0], self.bust(), self.win(), self.blackjack())
        return s % t

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class CardHand (Cards):
    '''Participant Card Hand class.'''

    def __init__ (self_bank):
        super(CardHand,self).__init__()
        self.bank = float(_bank)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class Dealer (CardHand):
    '''Dealer's Hand class.'''
    hard_stay = 17
    soft_stay = 17

    def __init__ (selfbank):
        super(Dealer,self).__init__(bank)

    def new_hand (self):
        super(Dealer,self).initialize()

    def play_hand (selfdeckplayerfp):
        # Dealer or Player blackjack...
        if self.blackjack() or player.blackjack() or player.bust():
            return
        # Dealer play...
        while not self.bust():
            if self.win():
                break
            if self.hard_stay <= self.counts[0]:
                break
            if 1 < len(self.counts):
                n = self.counts[1]
                if self.soft_stay <= n <= 21:
                    break
            # Take a card...
            self(deck())

    def __str__ (self):
        r = super(Dealer,self).__str__()
        return 'Dealer %s' % r

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class Player (CardHand):
    '''Player's Hand class.'''

    def __init__ (selfpot):
        super(Player,self).__init__(pot)
        self.bet_level = 1
        self.original_bank = float(pot)
        self.min_bank = float(pot)
        self.max_bank = float(pot)
        self.new_hand()

    def new_hand (self):
        super(Player,self).initialize()
        self.bet = float(0)
        self.win_lose = 0
        self.split_pair_flag = False
        self.p1 = None
        self.p2 = None

    def play_hand (selfdeckdealerfp):
        # Dealer blackjack...
        if dealer.blackjack() or self.blackjack():
            return
        # Split pair...
        if (self.cards[0] == self.cards[1]) and (self.bet <= self.bank):
            # Matching pair and we can afford to double the bet...
            self.split_pair_flag = RuleSplit(fpdealerself)
            if self.split_pair_flag:
                self.split_pair(deckdealerfp)
                return
        # Rulz...
        if 1 < len(self.counts):
            rule = RuleSoft(fpdealerself)
        else:
            rule = RuleHard(fpdealerself)
        # Double-Down...
        if rule == 3:
            self.double_down()
            print >> fp'Double Down: %d  {pot: %d}' % (self.betself.bank)
            # Take one card...
            self(deck())
            return
        # Hit; take cards...
        while (rule == 2or (rule == 3):
            self(deck())
            if 1 < len(self.counts):
                rule = RuleSoft(fpdealerself)
            else:
                rule = RuleHard(fpdealerself)

    def split_pair (selfdeckdealerfp):
        self.p1 = Player(0)
        self.p2 = Player(0)
        self.p1.bet = self.bet
        self.p2.bet = self.bet
        self.bank -= self.bet
        self.p1(self.cards[0])
        self.p2(self.cards[1])
        self.p1(deck())
        self.p2(deck())
        # Rulz 1...
        if 1 < len(self.p1.counts):
            rule = RuleSoft(fpdealerself.p1)
        else:
            rule = RuleHard(fpdealerself.p1)
        # Double-Down if allowed...
        # Hit; take cards...
        while (rule == 2or (rule == 3):
            self.p1(deck())
            if 1 < len(self.p1.counts):
                rule = RuleSoft(fpdealerself.p1)
            else:
                rule = RuleHard(fpdealerself.p1)
        # Rulz 2...
        if 1 < len(self.p2.counts):
            rule = RuleSoft(fpdealerself.p2)
        else:
            rule = RuleHard(fpdealerself.p2)
        # Double-Down if allowed...
        # Hit; take cards...
        while (rule == 2or (rule == 3):
            self.p2(deck())
            if 1 < len(self.p2.counts):
                rule = RuleSoft(fpdealerself.p2)
            else:
                rule = RuleHard(fpdealerself.p2)

    def make_bet (self):
        '''Place a bet according to bet-level.'''
        self._track_min_max()
        self._bet_level()
        self.bet = self.bet_level
        if self.bank < self.bet:
            self.bet = self.bank
        self.bank -= self.bet

    def double_down (self):
        '''Double-Down on bet.'''
        if self.bet <= self.bank:
            self.bet  += self.bet
            self.bank -= self.bet

    def set_bet_level (selfwin_flag):
        '''Set next bet-level.'''
        if win_flag:
            if 5 <= self.bet_level:
                self.bet_level = 5
                return
            if 3 <= self.bet_level:
                self.bet_level = 5
                return
            if 2 <= self.bet_level:
                self.bet_level = 3
                return
            if 1 <= self.bet_level:
                self.bet_level = 2
                return
        self.bet_level = 1

    def _bet_level (self):
        '''Normalize bet-level.'''
        if 5 <= self.bet_level:
            self.bet_level = 5
            return
        if 3 <= self.bet_level:
            self.bet_level = 3
            return
        if 2 <= self.bet_level:
            self.bet_level = 2
            return
        self.bet_level = 1

    def _track_min_max (self):
        '''Track minimum and maximum bank.'''
        if self.bank < self.min_bank:
            self.min_bank = self.bank
        if self.max_bank < self.bank:
            self.max_bank = self.bank

    def __str__ (self):
        r = super(Player,self).__str__()
        return 'Player %s' % r




####################################################################################################
'''eof'''