"""Strategy module for anyone to tie in their own strategy"""
import random

CARDS = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9)

STAY = 0      # stay
HIT = 1       # hit
DOUBLE = 2    # double down
SPLIT = 3     # split
TRUECOUNT = ''

BASIC = dict({  #      (A, 2, 3, 4, 5, 6, 7, 8, 9, 10)
                '8':   (1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
                '9':   (1, 1, 2, 2, 2, 2, 1, 1, 1, 1),
                '10':  (1, 2, 2, 2, 2, 2, 2, 2, 2, 1),
                '11':  (1, 2, 2, 2, 2, 2, 2, 2, 2, 2),
                '12':  (1, 1, 1, 0, 0, 0, 1, 1, 1, 1),
                '13':  (1, 0, 0, 0, 0, 0, 1, 1, 1, 1),
                '14':  (1, 0, 0, 0, 0, 0, 1, 1, 1, 1),
                '15':  (1, 0, 0, 0, 0, 0, 1, 1, 1, 1),
                '16':  (1, 0, 0, 0, 0, 0, 1, 1, 1, 1),
                'A2':  (1, 1, 1, 1, 2, 2, 1, 1, 1, 1),
                'A3':  (1, 1, 1, 1, 2, 2, 1, 1, 1, 1),
                'A4':  (1, 1, 1, 2, 2, 2, 1, 1, 1, 1),
                'A5':  (1, 1, 1, 2, 2, 2, 1, 1, 1, 1),
                'A6':  (1, 1, 2, 2, 2, 2, 1, 1, 1, 1),
                'A7':  (1, 0, 2, 2, 2, 2, 0, 0, 1, 1),
                'A8':  (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
                'A9':  (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
                'A10': (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
                'AA':  (3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
                '22':  (1, 1, 1, 3, 3, 3, 3, 1, 1, 1),
                '33':  (1, 1, 1, 3, 3, 3, 3, 1, 1, 1),
                '44':  (1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
                '55':  (1, 2, 2, 2, 2, 2, 2, 2, 2, 1), # never split 5's
                '66':  (1, 1, 3, 3, 3, 3, 1, 1, 1, 1),
                '77':  (1, 3, 3, 3, 3, 3, 3, 1, 1, 1),
                '88':  (3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
                '99':  (0, 3, 3, 3, 3, 3, 0, 3, 3, 0),
                '1010':(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
                })

def b(row, column):
    return lambda : BASIC[row][column]

def d(num, row, column):
    """returns double down for TRUECOUNT >= limit num"""
    return lambda : 2 if TRUECOUNT >= num else BASIC[row][column]

def p(num, row, column):
    """returns split for TRUECOUNT >= num"""
    return lambda : 3 if TRUECOUNT >= num else BASIC[row][column]

def h(num, row, column):
    """returns hit for TRUECOUNT < num"""
    return lambda : 1 if TRUECOUNT < num else BASIC[row][column]

def s(num, row, column):
    """returns stand for TRUECOUNT >= num"""
    return lambda : 0 if TRUECOUNT >= num else BASIC[row][column]

# Revere point count method of counting cards. for futher reference look at
#  the book 'Playing blackjack as a business' by Lawrence Revere
RPC = dict({    #      (A, 2, 3, 4, 5, 6, 7, 8, 9, 10)
            '8':   (b('8',0), b('8',1), b('8',2), d(6,'8',3), d(4,'8',4),
                            d(2,'8',5), b('8',6), b('8',7), b('8',8), b('8',9)),
            '9':   (b('9',0), d(1,'9',1), h(0,'9',2), h(-2,'9',3), h(-4,'9',4),
                            h(-6,'9',5), d(4,'9',6), b('9',7), b('9',8), b('9',9)),
            '10':  (d(4,'10',0), b('10',1), b('10',2), b('10',3), b('10',4),
                            b('10',5), h(-6,'10',6), h(-4,'10',7), h(-1,'10',8), d(4,'10',9)),
            '11':  (d(1,'11',0), b('11',1), b('11',2), b('11',3), b('11',4),
                            b('11',5), b('11',6), h(-7,'11',7), h(-5,'11',8), h(-4,'11',9)),
            '12':  (b('12',0), s(3,'12',1), s(1,'12',2), h(0,'12',3), h(-2,'12',4),
                            h(-1,'12',5), b('12',6), b('12',7), b('12',8), b('12',9)),
            '13':  (b('13',0), h(-1,'13',1), h(-2,'13',2), h(-3,'13',3), h(-5,'13',4),
                            h(-5,'13',5), b('13',6), b('13',7), b('13',8), b('13',9)),
            '14':  (b('14',0), h(-3,'14',1), h(-4,'14',2), h(-5,'14',3), b('14',4),
                            b('14',5), b('14',6), b('14',7), b('14',8), b('14',9)),
            '15':  (b('15',0), h(-6,'15',1), b('15',2), b('15',3), b('15',4),
                            b('15',5), b('15',6), b('15',7), b('15',8), s(4,'15',9)),
            '16':  (b('16',0), b('16',1), b('16',2), b('16',3), b('16',4),
                            b('16',5), b('16',6), b('16',7), s(7,'16',8), s(0,'16',9)),
            'A2':  (b('A2',0), b('A2',1), b('A2',2), d(3,'A2',3), h(0,'A2',4),
                            h(-2,'A2',5), b('A2',6), b('A2',7), b('A2',8), b('A2',9)),
            'A3':  (b('A3',0), b('A3',1), b('A3',2), d(1,'A3',3), h(-2,'A3',4),
                            b('A3',5), b('A3',6), b('A3',7), b('A3',8), b('A3',9)),
            'A4':  (b('A4',0), b('A4',1), d(5,'A4',2), h(0,'A4',3), h(-5,'A4',4),
                            b('A4',5), b('A4',6), b('A4',7), b('A4',8), b('A4',9)),
            'A5':  (b('A5',0), b('A5',1), d(3,'A5',2), h(-2,'A5',3), b('A5',4),
                            b('A5',5), b('A5',6), b('A5',7), b('A5',8), b('A5',9)),
            'A6':  (b('A6',0), d(1,'A6',1), h(-2,'A6',2), h(-6,'A6',3), b('A6',4),
                            b('A6',5), b('A6',6), b('A6',7), b('A6',8), b('A6',9)),
            'A7':  (b('A7',0), d(1,'A7',1), h(-2,'A7',2), h(-5,'A7',3), b('A7',4),
                            b('A7',5), b('A7',6), b('A7',7), b('A7',8), b('A7',9)),
            'A8':  (b('A8',0), b('A8',1), d(5,'A8',2), d(3,'A8',3), d(2,'A8',4),
                            d(1,'A8',5), b('A8',6), b('A8',7), b('A8',8), b('A8',9)),
            'A9':  (b('A9',0), b('A9',1), b('A9',2), d(7,'A9',3), d(6,'A9',4),
                            d(5,'A9',5), b('A9',6), b('A9',7), b('A9',8), b('A9',9)),
            'A10':  (b('A10',0), b('A10',1), b('A10',2), b('A10',3), b('A10',4),
                            b('A10',5), b('A10',6), b('A10',7), b('A10',8), b('A10',9)),
            'AA':  (b('AA',0), b('AA',1), b('AA',2), b('AA',3), b('AA',4),
                            b('AA',5), b('AA',6), b('AA',7), b('AA',8), b('AA',9)),
            '22':  (b('22',0), b('22',1), p(2,'22',2), h(-1,'22',3), h(-4,'22',4),
                            b('22',5), b('22',6), b('22',7), b('22',8), b('22',9)),
            '33':  (b('33',0), b('33',1), p(3,'33',2), h(0,'33',3), b('33',4),
                            b('33',5), b('33',6), b('33',7), b('33',8), b('33',9)),
            '44':  (b('44',0), b('44',1), b('44',2), d(6,'44',3), d(4,'44',4),
                            d(2,'44',5), b('44',6), b('44',7), b('44',8), b('44',9)),
            '55':  (b('55',0), b('55',1), b('55',2), b('55',3), b('55',4),
                            b('55',5),b('55',6), b('55',7), b('55',8), b('55',9)),
            '66':  (b('66',0), p(3,'66',1), h(0,'66',2), h(-2,'66',3), h(-4,'66',4),
                            h(-6,'66',5), b('66',6), b('66',7), b('66',8), b('66',9)),
            '77':  (b('77',0), b('77',1), b('77',2), b('77',3), b('77',4),
                            b('77',5), b('77',6), b('77',7), b('77',8), b('77',9)),
            '88':  (b('88',0), b('88',1), b('88',2), b('88',3), b('88',4),
                            b('88',5), b('88',6), b('88',7), b('88',8), p(5,'88',9)),
            '99':  (p(6,'99',0), h(-1,'99',1), h(-3,'99',2), h(-4,'99',3), h(-6,'99',4),
                            h(-7,'99',5), p(6,'99',6), b('99',7), b('99',8), b('99',9)),
            '1010':(b('1010',0), b('1010',1), b('1010',2), b('1010',3), p(5,'1010',4),
                            p(5,'1010',5), b('1010',6), b('1010',7), b('1010',8), b('1010',9)),
                })

def bet(units, min_bet, max_bet, cash, *strategy):
    """returns bet before a hand starts"""
    if strategy[0] == 'RANDOM':
        cur_bet = max(units * random.randint(1,10), min_bet)
        return min(min(cur_bet, max_bet), cash)
    elif strategy[0] == 'CONSTANT':
        cur_bet = max(units, min_bet)
        return min(min(cur_bet, max_bet), cash)
    elif strategy[0] == 'PMC':
        count = strategy[1][0]
        decks = strategy[1][1]
        cur_bet = max(min_bet , units * int(round(float(count) / decks)))
        return min(min(cur_bet, max_bet), cash)
    elif strategy[0] == 'RPC':
        count = strategy[1][0]
        halfdecks = strategy[1][1]
        truecount = int(round(float(count) / halfdecks))
        cur_bet = max(units * truecount, min_bet)
        return min(min(cur_bet, max_bet), cash)

def move(hand_value, dealers_card, is_hard, splitable, *strategy):
    """returns a move given hand value and other variables"""
    dealers_card = CARDS[dealers_card]
    if strategy[0] == 'BASIC' or strategy[0] == 'PMC':
        if is_hard and not splitable: 
            if hand_value < 8:
                return HIT
            elif hand_value < 17:
                return BASIC[str(hand_value)][dealers_card]
            else:
                return STAY
        elif splitable:
            if is_hard:
                return BASIC[str(hand_value / 2) * 2][dealers_card]
            else: # Aces (always split aces!)
                return SPLIT
        else:
            return BASIC['A' + str(hand_value - 11)][dealers_card]
    elif strategy[0] == 'RPC':
        count = strategy[1][0]
        halfdecks = strategy[1][1]
        global TRUECOUNT
        TRUECOUNT = int(round(float(count) / halfdecks))
        if is_hard and not splitable: 
            if hand_value < 8:
                return HIT
            elif hand_value < 17:
                return RPC[str(hand_value)][dealers_card]()
            else:
                return STAY
        elif splitable:
            if is_hard:
                return RPC[str(hand_value / 2) * 2][dealers_card]()
            else: # Aces (always split aces!)
                return SPLIT
        else:
            return RPC['A' + str(hand_value - 11)][dealers_card]()
    else:
        raise ValueError("Unknow strategy:", strategy[0])

