def get_bucket_number(hole_cards, community_cards=None):
    """	
	Evaluate handstrength base on Chen's Formula if only hole cards are drawn,
    because it values both hand potential as well as relative value of card rank.
    Otherwise, evaluate handstrength using Deuces Monte-carlo Look-up table.
    Divide the bucket with equal probability based on the number of buckets.
    Using the handstrength value drawn earlier, find the right bucket.
    
    :param hole_cards: List(str) in format of 'CA' for hole cards belong to the current player
    :param community_cards: List(str) in format of 'S3' for community cards   
    """
    if not community_cards:
        points = starting_hand_evaluator(hole_cards)
        bucket_number = int(
            math.ceil((points + 1.5) / 21.5 * constants.BUCKET_NUM) - 1)
    else:
        hole_cards = list(
            map(lambda x: DeucesCard.new(x[1] + x[0].lower()), hole_cards))
        community_cards = list(
            map(lambda x: DeucesCard.new(x[1] + x[0].lower()),
                community_cards))

        evaluator = DeucesEvaluator()
        five_cards_ranking = evaluator.evaluate(hole_cards, community_cards)
        strength = 1.0 - evaluator.get_five_card_rank_percentage(
            five_cards_ranking)
        bucket_number = int(math.ceil(strength * constants.BUCKET_NUM) - 1)

    return 0 if bucket_number == -1 else bucket_number
Example #2
0
 def __init__(self):
     self.playerJustMoved = 2
     self.deck = Deck()
     self.board = self.deck.draw(3)
     self.player1 = self.deck.draw(2)
     self.player2 = self.deck.draw(2)
     self.evaluator = Evaluator()
Example #3
0
 def __init__(self):
     self.ALL_CARDS = np.array([i for i in range(52)])
     self.hands = []
     self.flop = (-1, -1, -1)
     self.turn = -1
     self.river = -1
     self.eval = Evaluator()
Example #4
0
async def evaluateHand(whichServer):
    global table
    global hands
    global board
    global isRound
    global firstGame
    global players
    global pot
    finalBoard = []
    finalHands = []
    handWinner = []
    max = 100000
    eval = Evaluator()
    for card in board[whichServer]:
        temp1 = card[1].replace('10', 'T')
        temp2 = card[0].lower()
        temp3 = Card.new(temp1[0] + temp2[0])
        finalBoard.append(temp3)

    for p in table[whichServer]:
        for card in hands[whichServer][p]:
            temp1 = card[1].replace('10', 'T')
            temp2 = card[0].lower()
            temp3 = Card.new(temp1[0] + temp2[0])
            finalHands.append(temp3)

        valueOfHand = eval.evaluate(finalBoard, finalHands)

        if valueOfHand < max:
            max = valueOfHand
            handWinner = []
            handWinner.append(p)
        elif valueOfHand == max:
            handWinner.append(p)

        print(eval.evaluate(finalBoard, finalHands))
        finalHands = []

    pot[whichServer] /= len(handWinner)
    for winners in handWinner:
        players[whichServer][winners] += pot[whichServer]
        await bot.send_message(
            pokerChannel[whichServer], winners.display_name +
            " won the round! They won " + str(pot[whichServer]) +
            "$. Use command startPoker to start another one!")
    firstGame[whichServer] = 0
    pot[whichServer] = 0
    isRound[whichServer] = 0
Example #5
0
class PokerState:
    def __init__(self):
        self.playerJustMoved = 2
        self.deck = Deck()
        self.board = self.deck.draw(3)
        self.player1 = self.deck.draw(2)
        self.player2 = self.deck.draw(2)
        self.evaluator = Evaluator()

    def Clone(self):
        st = PokerState()
        st.playerJustMoved = self.playerJustMoved
        st.deck = self.deck
        st.board = self.board
        st.player1 = self.player1
        st.player2 = self.player2
        return st

    def DoMove(self, move):
        """ Update a state by carrying out the given move.
            Must update playerJustMoved.
        """

        self.playerJustMoved = 3 - self.playerJustMoved

    def GetMoves(self):
        return ["aposta", "sai"]

    def GetResult(self, player1):
        p1 = self.evaluator.evaluate(self.player1, self.board)
        p2 = self.evaluator.evaluate(self.player2, self.board)
        if p1 < p2:
            return 1.0
Example #6
0
    def evaluate_hand(board, hand):
        if board is None or len(board) < 3:
            raise ValueError("Missing board cards")
        if hand is None or len(hand) < 2:
            raise ValueError("Missing hole cards")

        deuces_board = [Card.new(board[0]), Card.new(board[1]), Card.new(board[2]),
                        Card.new(board[3]), Card.new(board[4])]
        deuces_hand = [Card.new(hand[0]), Card.new(hand[1])]

        evaluator = Evaluator()
        score = evaluator.evaluate(deuces_board, deuces_hand)
        hand_rank = evaluator.get_rank_class(score)
        hand_rank_str = evaluator.class_to_string(hand_rank)

        return hand_rank_str, score
Example #7
0
    def calculateHand(self, hole_card, round_state):
        """ 
        Passes what's on table and in your hand to deuces,
        to calculate hand strength,
        if the flop hasnt been played yet, return -1
        """
        if (round_state["community_card"] == []):
            return -1

        board = list(
            map(lambda x: Card.new(x[-1] + x[0].lower()),
                round_state["community_card"]))

        hand = list(map(lambda x: Card.new(x[-1] + x[0].lower()), hole_card))
        evaluator = Evaluator()
        #Return as a fraction, lower is better
        return (evaluator.evaluate(board, hand) / 7462)
Example #8
0
class poker_engine():
    def __init__(self):
        self.ALL_CARDS = np.array([i for i in range(52)])
        self.hands = []
        self.flop = (-1, -1, -1)
        self.turn = -1
        self.river = -1
        self.eval = Evaluator()

    def init_game_for(self, num_players):
        total_cards_in_use = num_players * 2 + 5
        cards = np.random.choice(self.ALL_CARDS,
                                 total_cards_in_use,
                                 replace=False)
        self.hands = []
        for i in range(num_players):
            self.hands.append((cards[i * 2], cards[i * 2 + 1]))
        self.river = cards[-1]
        self.turn = cards[-2]
        self.flop = (cards[-3], cards[-4], cards[-5])

    def get_hand_for_player(self, i):
        return self.hands[i]

    def _get_deuces_card(self, card):
        card_str = ""
        val = card % 13 + 1
        if val < 10 and val > 1:
            card_str = str(val)
        else:
            if val == 1:
                card_str = "A"
            if val == 10:
                card_str = "T"
            if val == 11:
                card_str = "J"
            if val == 12:
                card_str = "Q"
            if val == 13:
                card_str = "K"

        if card < 13:
            card_str += "h"
        elif card < 26:
            card_str += "d"
        elif card < 39:
            card_str += "s"
        else:
            card_str += "c"
        return Card.new(card_str)

    def str_to_card(self, s):
        val = s[0]
        card = 0
        if val == "a":
            card = 0
        elif val == "t":
            card = 9
        elif val == "j":
            card = 10
        elif val == "q":
            card = 11
        elif val == "k":
            card = 12
        else:
            card = int(val) - 1
        suit = s[1]
        if suit == "d":
            card += 13
        if suit == "s":
            card += 26
        if suit == "c":
            card += 39
        return card

    def get_winner(self, folded):
        board = [self._get_deuces_card(i) for i in self.flop]
        board.append(self._get_deuces_card(self.turn))
        board.append(self._get_deuces_card(self.river))
        hands = [[
            self._get_deuces_card(self.hands[h][0]),
            self._get_deuces_card(self.hands[h][1])
        ] for h in range(len(self.hands)) if h not in folded]
        scores = [self.eval.evaluate(h, board) for h in hands]
        winner = np.argmin(scores)
        return winner
Example #9
0
        st = PokerState()
        st.playerJustMoved = self.playerJustMoved
        st.deck = self.deck
        st.board = self.board
        st.player1 = self.player1
        st.player2 = self.player2
        return st

    def DoMove(self, move):
        """ Update a state by carrying out the given move.
            Must update playerJustMoved.
        """

        self.playerJustMoved = 3 - self.playerJustMoved

    def GetMoves(self):
        return ["aposta", "sai"]

    def GetResult(self, player1):
        p1 = self.evaluator.evaluate(self.player1, self.board)
        p2 = self.evaluator.evaluate(self.player2, self.board)
        if p1 < p2:
            return 1.0


board = [Card.new('Th'), Card.new('Kh'), Card.new('Qh'), Card.new('Jh')]
Card.print_pretty_cards(board)
p1 = [Card.new('8c'), Card.new('9c')]
e = Evaluator()

print e.evaluate(board, p1)
Example #10
0
from deuces.card import Card
from deuces.evaluator import Evaluator
from deuces.deck import Deck

# create a card
card = Card.new('Qh')

# create a board and hole cards
board = [Card.new('2h'), Card.new('2s'), Card.new('Jc')]
hand = [Card.new('Qs'), Card.new('Th')]

# pretty print cards to console
Card.print_pretty_cards(board + hand)

# create an evaluator
evaluator = Evaluator()

# and rank your hand
rank = evaluator.evaluate(board, hand)
print("Rank for your hand is: %d" % rank)

# or for random cards or games, create a deck
print("Dealing a new hand...")
deck = Deck()
board = deck.draw(5)
player1_hand = deck.draw(2)
player2_hand = deck.draw(2)

print("The board:")
Card.print_pretty_cards(board)
Example #11
0
 def __init__(self, smallBlind=1):
     self.deck = Deck()
     self.players = []
     self.smallBlind = smallBlind
     self.bigBlind = self.smallBlind * 2
     self.evaluator = Evaluator()
Example #12
0
class Game:
    def __init__(self, smallBlind=1):
        self.deck = Deck()
        self.players = []
        self.smallBlind = smallBlind
        self.bigBlind = self.smallBlind * 2
        self.evaluator = Evaluator()

    def runGame(self):
        self.dealerPosition = randint(0, len(self.players))
        round = 0
        while len(self.players) > 1:
            self.runRound(round)
            self.removePlayerIfBankrupt()
            round += 1

    def runRound(self, round):
        self.resetGame(round)
        self.makePlayersPayBlinds()
        self.doRound(self.bigBlind, True)

        if (self.moreThanOnePlayerInRound()):
            self.drawCardsAndDoRound(3)
            if (self.moreThanOnePlayerInRound()):
                self.drawCardsAndDoRound(1)
                if (self.moreThanOnePlayerInRound()):
                    self.drawCardsAndDoRound(1)
        self.makeWinner()

    def removePlayerIfBankrupt(self):
        newPlayers = []
        for player in self.players:
            if player.stack >= self.bigBlind:
                newPlayers.append(player)
        self.players = newPlayers

    def getPlayersInRoundNotInAllIn(self):
        return [
            player for player in self.playersInRound
            if player not in self.playerIsAllIn
        ]

    def moreThanOnePlayerInRound(self):
        return len(self.playersInRound) > 1

    def resetGame(self, round):
        print("\n\n\n ROUND", str(round + 1), "\n\n\n")
        self.startNewRound()
        self.board = []
        for player in self.players:
            player.print()
        print("dealer is " + str(self.players[self.dealerPosition].name))
        self.pot = 0
        self.playersInRound = []
        self.playersBettedCurrentRound = {}
        self.playerIsAllIn = set()

    def increaseDealerPosition(self):
        self.dealerPosition = (self.dealerPosition + 1) % len(self.players)

    def drawCardsAndDoRound(self, cardsToDraw):
        for player in self.playersInRound:
            self.playersBettedCurrentRound[player] = 0
        print("\n\nPot total is", self.pot)
        print("\n Flop:")
        self.board += self.deck.draw(cardsToDraw)
        Card.print_pretty_cards(self.board)
        if (len(self.getPlayersInRoundNotInAllIn()) > 1):
            self.doRound()

    def makeWinner(self):
        # Todo: side pot if someone is all-in
        winner = None
        lowestScore = 999999
        for player in self.playersInRound:
            score = self.evaluator.evaluate(player.getHand(), self.board)
            if (score < lowestScore):
                winner = player
                lowestScore = score + 0
        winner.stack += self.pot
        print("\n\nWinner is:", winner.name, " with +", self.pot,
              "in pot.\n\n")
        self.pot = 0

    def addPlayerToGame(self, player):
        self.players.append(player)

    def startNewRound(self):
        self.increaseDealerPosition()
        self.deck.shuffle()
        self.dealCardsToPlayers()

    def doRound(self, roundValue=0, preflop=False):
        self.roundValue = roundValue
        roundNr = 0
        while True:
            for i in range(len(self.players)):
                i += self.dealerPosition + preflop * 3
                i = i % len(self.players)
                player = self.players[i]
                if (player in self.playersInRound):
                    if (roundNr != 0 and self.allBettedTheSame()
                            or not self.moreThanOnePlayerInRound()):
                        return
                    betAmount = player.doAction(
                        self, self.playersBettedCurrentRound[player])
                    self.doPlayerAction(player, betAmount)
            roundNr += 1

    def makePlayersPayBlinds(self):
        for player in self.players:
            if (player.stack > self.smallBlind):
                self.playersBettedCurrentRound[player] = 0
                self.playersInRound.append(player)
        smallBlindPLayer = self.players[(self.dealerPosition + 1) %
                                        len(self.players)]
        bigBlindPLayer = self.players[(self.dealerPosition + 2) %
                                      len(self.players)]
        self.makePlayerBet(smallBlindPLayer, self.smallBlind)
        self.makePlayerBet(bigBlindPLayer, self.bigBlind)

    def makePlayerBet(self, player, amount):
        actualBet = player.bet(amount)
        self.playersBettedCurrentRound[player] += actualBet
        self.pot += actualBet
        return actualBet

    def allBettedTheSame(self):
        playersToCheck = [
            player for player in self.playersBettedCurrentRound.keys()
            if player not in self.playerIsAllIn
        ]
        if (len(playersToCheck) > 0):
            firstVal = float(self.playersBettedCurrentRound[playersToCheck[0]])
            for player in playersToCheck:
                if (float(self.playersBettedCurrentRound[player]) != firstVal):
                    return False
        return True

    def doPlayerAction(self, player, betAmount):
        if (betAmount == -1):
            self.playersBettedCurrentRound.pop(player, None)
            self.playersInRound.remove(player)
            print(player.name, "folded.")
        elif (betAmount > 0):
            self.makePlayerBet(player, betAmount)
            self.roundValue = max(self.roundValue,
                                  self.playersBettedCurrentRound[player])
            self.updateAllInPlayerList(player)
            print(player.name, "in with",
                  self.playersBettedCurrentRound[player], "in this round")

    def updateAllInPlayerList(self, player):
        if (player.stack == 0):
            self.playerIsAllIn.add(player)

    def dealCardsToPlayers(self):
        for i in range(self.dealerPosition,
                       len(self.players) + self.dealerPosition):
            index = i % len(self.players)
            player = self.players[index]
            card1 = self.deck.draw()[0]
            card2 = self.deck.draw()[0]
            player.giveCards(card1, card2)
Example #13
0
from __future__ import division
from __future__ import print_function

import random
from collections import defaultdict, Counter
from itertools import combinations
from subprocess import call

from deuces import Card
from deuces.deck import Deck
from deuces.evaluator import Evaluator

evaluator = Evaluator()

# class Outcome:
#     def __init__(self):
#         self.occurrences = 0
#         self.probability = None
#         self.relations = {
#             'wins_to': list(),
#             'loses_to': list(),
#             'ties_to': list(),
#         }
#
#     def get_most_likely(self):
#         return
#
#
# class ResultSet:
#     def __init__(self):
#         self.outcomes = defaultdict(Outcome)
Example #14
0
    deck = Deck()

    boards = []
    hands = []

    for i in range(n):
        boards.append(deck.draw(m))
        hands.append(deck.draw(2))
        deck.shuffle()

    return boards, hands


n = 10000
cumtime = 0.0
evaluator = Evaluator()
boards, hands = setup(n, 5)
for i in range(len(boards)):
    start = time.time()
    evaluator.evaluate(boards[i], hands[i])
    cumtime += (time.time() - start)

avg = float(cumtime / n)
print("7 card evaluation:")
print("[*] Deuces: Average time per evaluation: %f" % avg)
print("[*] Decues: Evaluations per second = %f" % (1.0 / avg))

###

cumtime = 0.0
boards, hands = setup(n, 4)
Example #15
0
    def create_alsm(self):

        # the table containing all ranks where the Ace could be in a straight, depending on the lowest card
        # we'll call it, ace-low-straight-map
        # this will be the mapping:
        # A3456 -> 23456
        # A4567 -> 34567
        # A5678 -> 45678
        # A6789 -> 56789
        # A789T -> 6789T
        # A89TJ -> 789TJ
        # A9TJQ -> 89TJQ
        # However, we should obtain the rank of the hands through calculation, 
        # rather than store their values
        evaluator = Evaluator()
        offsuit_A3456_rank = evaluator.evaluate(create_cards(["Ah","3h","4h","5h","6h"]),[])
        offsuit_A4567_rank = evaluator.evaluate(create_cards(["Ah","4h","5h","6h","7h"]),[])
        offsuit_A5678_rank = evaluator.evaluate(create_cards(["Ah","5h","6h","7h","8h"]),[])
        offsuit_A6789_rank = evaluator.evaluate(create_cards(["Ah","6h","7h","8h","9h"]),[])
        offsuit_A789T_rank = evaluator.evaluate(create_cards(["Ah","7h","8h","9h","Th"]),[])
        offsuit_A89TJ_rank = evaluator.evaluate(create_cards(["Ah","8h","9h","Th","Jh"]),[])
        offsuit_A9TJQ_rank = evaluator.evaluate(create_cards(["Ah","9h","Th","Jh","Qh"]),[])

        offsuit_23456_rank = evaluator.evaluate(create_cards(["2h","3h","4h","5h","6h"]),[])
        offsuit_34567_rank = evaluator.evaluate(create_cards(["3h","4h","5h","6h","7h"]),[])
        offsuit_45678_rank = evaluator.evaluate(create_cards(["4h","5h","6h","7h","8h"]),[])
        offsuit_56789_rank = evaluator.evaluate(create_cards(["5h","6h","7h","8h","9h"]),[])
        offsuit_6789T_rank = evaluator.evaluate(create_cards(["6h","7h","8h","9h","Th"]),[])
        offsuit_789TJ_rank = evaluator.evaluate(create_cards(["7h","8h","9h","Th","Jh"]),[])
        offsuit_89TJQ_rank = evaluator.evaluate(create_cards(["8h","9h","Th","Jh","Qh"]),[])

        onsuit_A3456_rank  = evaluator.evaluate(create_cards(["Ah","3h","4h","5h","6c"]),[])
        onsuit_A4567_rank  = evaluator.evaluate(create_cards(["Ah","4h","5h","6h","7c"]),[])
        onsuit_A5678_rank  = evaluator.evaluate(create_cards(["Ah","5h","6h","7h","8c"]),[])
        onsuit_A6789_rank  = evaluator.evaluate(create_cards(["Ah","6h","7h","8h","9c"]),[])
        onsuit_A789T_rank  = evaluator.evaluate(create_cards(["Ah","7h","8h","9h","Tc"]),[])
        onsuit_A89TJ_rank  = evaluator.evaluate(create_cards(["Ah","8h","9h","Th","Jc"]),[])
        onsuit_A9TJQ_rank  = evaluator.evaluate(create_cards(["Ah","9h","Th","Jh","Qc"]),[])
        
        onsuit_23456_rank  = evaluator.evaluate(create_cards(["2h","3h","4h","5h","6c"]),[])
        onsuit_34567_rank  = evaluator.evaluate(create_cards(["3h","4h","5h","6h","7c"]),[])
        onsuit_45678_rank  = evaluator.evaluate(create_cards(["4h","5h","6h","7h","8c"]),[])
        onsuit_56789_rank  = evaluator.evaluate(create_cards(["5h","6h","7h","8h","9c"]),[])
        onsuit_6789T_rank  = evaluator.evaluate(create_cards(["6h","7h","8h","9h","Tc"]),[])
        onsuit_789TJ_rank  = evaluator.evaluate(create_cards(["7h","8h","9h","Th","Jc"]),[])
        onsuit_89TJQ_rank  = evaluator.evaluate(create_cards(["8h","9h","Th","Jh","Qc"]),[])

        # the dictionary defining the necessary mappings, # ace-low-straight-map
        alsm = {
                    (offsuit_A3456_rank,3) : offsuit_23456_rank,
                    (offsuit_A4567_rank,4) : offsuit_34567_rank,
                    (offsuit_A5678_rank,5) : offsuit_45678_rank,
                    (offsuit_A6789_rank,6) : offsuit_56789_rank,
                    (offsuit_A789T_rank,7) : offsuit_6789T_rank,
                    (offsuit_A89TJ_rank,8) : offsuit_789TJ_rank,
                    (offsuit_A9TJQ_rank,9) : offsuit_89TJQ_rank,

                    (onsuit_A3456_rank,3)  : onsuit_23456_rank,
                    (onsuit_A4567_rank,4)  : onsuit_34567_rank,
                    (onsuit_A5678_rank,5)  : onsuit_45678_rank,
                    (onsuit_A6789_rank,6)  : onsuit_56789_rank,
                    (onsuit_A789T_rank,7)  : onsuit_6789T_rank,
                    (onsuit_A89TJ_rank,8)  : onsuit_789TJ_rank,
                    (onsuit_A9TJQ_rank,9)  : onsuit_89TJQ_rank
               }
        return alsm
    deck = Deck()
    for card in hole_card + board:
        deck.remove_card(card)
    board_full = board + deck.draw(5 - len(board))
    opponents_hole = [deck.draw(2) for i in range(nb_player - 1)
                      ]  #[unused_cards[2 * i:2 * i + 2]
    opponents_score = [
        evaluator.evaluate(board_full, hole) for hole in opponents_hole
    ]  #[HandEvaluator.eval_hand(hole, community_card) for hole in opponents_hole]
    my_score = evaluator.evaluate(
        board_full,
        hole_card)  #HandEvaluator.eval_hand(hole_card, community_card)
    return 1 if my_score < min(opponents_score) else 0


evaluator = Evaluator()
hole_card = [Card.new('9s'), Card.new('Ks')]
board = [Card.new('8d'), Card.new('Ah'), Card.new('Kh')]
nb_players = 6
nb_simulations = 10000

time_1 = time.time()
hand_equity = deuces_estimate_win_rate(nb_simulations, nb_players, hole_card,
                                       board)
print("The equity is: " + str(hand_equity * 100) + "%")
time_2 = time.time()
print("The total time taken is: " + str((time_2 - time_1) * 1000) + " [ms]")

### Getting evals/s
evaluator = Evaluator()
hole_card = [Card.new('9s'), Card.new('Ks')]
    def GenerateMultipliers(self,
                            hands,
                            hand_values,
                            num_players=4,
                            data_dir=None):
        """
        Generates an array full of multipliers used to calculate the results of gameplay
        
        hands =         Array of hands (rounds*all_hands_per_round, 3) with format:
                        [[dealer_Cards], [player1_cards], [player2_cards], [player3_cards], ...]
            
        hand_values =   Array of the associated hand ranks returned from evaluate_hands
        
        num_players =   Number of players per round (excluding the player/dealer)
        
        
        Returns array of multipliers (num_rounds,num_players,4) specifying the following for each hand:

            multipliers[round,hand,0]:    Play multiplier -- determines whether hand should be played
                                                             and whether it wins, loses, or pushes
                                                             (1 = win, -1 = loss, 0 = no play or push)
                                                            
            multipliers[round,hand,1]:    Ante multiplier -- 0 = ante pushes, 1 = ante wins, -1 = ante loses
            
            multipliers[round,hand,2]:    Pair Plus multiplier -- -1 if Pair Plus loses, otherwise the bonus multiple
            
            multipliers[round,hand,3]:    6-Card Multiplier -- -1 if 6-Card bonus loses, otherwise the bonus multiple
        """

        if data_dir is None:
            data_dir = self.tmp_dir

        all_hands = num_players + 1
        num_rounds = len(hands) / all_hands

        multipliers = np.memmap(path.join(data_dir, 'multipliers'),
                                dtype='int32',
                                mode='w+',
                                shape=(num_rounds, num_players, 4))

        play_multipliers = multipliers[:, :, 0]
        ante_multipliers = multipliers[:, :, 1]
        pair_plus_multipliers = multipliers[:, :, 2]
        six_card_multipliers = multipliers[:, :, 3]

        #group hand values into sets of 1 dealer hand + related player hands
        #Split the dealer values into one group and player values into another
        splittable_values = hand_values.reshape(-1, all_hands)
        dealer_values = splittable_values.T[0].reshape(-1, 1)
        player_values = splittable_values.T[1:].T

        #Lower valued hands beat higher valued hands
        #Win_lose = -1 for player hands that lose, 1 for wins, and 0 for ties
        win_lose = np.memmap(path.join(data_dir, 'win_lose'),
                             dtype='int32',
                             mode='w+',
                             shape=(num_rounds, num_players))
        win_lose[:] = np.copysign(1, dealer_values - player_values)

        #Hand and card ranks needed to determine when play bet should be made for each hand
        #The suits are arbitrary as long as these are unsuited hands
        ACE_HIGH = self.evaluate_hand(Card.hand_to_binary(['Ac', '2d', '4d']))
        KING_HIGH = self.evaluate_hand(Card.hand_to_binary(['Kc', '2d', '3d']))
        QUEEN_HIGH = self.evaluate_hand(Card.hand_to_binary(['Qc', '2d',
                                                             '3d']))
        ACE = Card.get_rank_int(Card.new('Ac'))
        KING = Card.get_rank_int(Card.new('Kc'))
        QUEEN = Card.get_rank_int(Card.new('Qc'))

        #In each round, one dealer card is dealt face up
        #Here we build an array view containing the first dealer card for each round as our face up card
        #hands.reshape(1,-1)[0] is equivalent to hands.flatten() without copying into a new array

        dealer_face_up = hands.reshape(1,
                                       -1)[0][0::all_hands * 3].reshape(-1, 1)

        #Face up dealer cards are converted to int ranks
        #face_up_rank = (dealer_face_up>>8)&0xFF

        #Hand plays based on House Way:
        #    Always play A high or better
        #    Play K high if dealer face up card is K or worse
        #    Play Q high if dealer face up card is Q or worse
        #    Never play hands less than Q high

        #Play the hand = 1, don't play = 0

        play_multipliers[:] = np.where(
            player_values <= ACE_HIGH, 1,
            np.where(
                player_values <= KING_HIGH,
                np.where((dealer_face_up >> 8) & 0xF < ACE, 1, 0),
                np.where(player_values <= QUEEN_HIGH,
                         np.where((dealer_face_up >> 8) & 0xF < KING, 1, 0),
                         0)))

        #Pair Plus bonus loses (-1) when a player's hand is less than a pair
        #Otherwise it pays a multiple of the bet depending on the hand strength for pairs and better
        #The bet plays independently of the hand winning or losing,
        #but if the player does not make a play bet, the Pair Plus bonus is forfeit (-1)

        #Pay chart
        #    Worse than one pair:  -1
        #    Pair: 1x
        #    Flush: 3x
        #    Straight: 6x
        #    Straight flush: 30x
        #    Royal flush: 50x

        pair_plus_multipliers[:] = np.where(
            play_multipliers == 0, -1,
            np.where(
                player_values > self.tc_lookup.MAX_PAIR, -1,
                np.where(
                    player_values > self.tc_lookup.MAX_FLUSH, 1,
                    np.where(
                        player_values > self.tc_lookup.MAX_STRAIGHT, 3,
                        np.where(
                            player_values > self.tc_lookup.MAX_TRIPS, 6,
                            np.where(
                                player_values >
                                self.tc_lookup.MAX_STRAIGHT_FLUSH, 30,
                                np.where(player_values > 1, 40, 50)))))))

        #Calculate Ante wins/losses
        #If a play bet is not made, ante loses (-1) automatically
        #If a play bet is made:
        #    Hand wins:  ante wins (+1)
        #    Hand loses: ante loses (-1) if dealer has Q high or better, otherwise pushes (0)

        ante_multipliers[:] = np.where(
            play_multipliers == 0, -1,
            np.where(win_lose > -1, win_lose,
                     np.where(dealer_values <= QUEEN_HIGH, -1, 0)))

        #Calculate Play wins/losses
        #We need to do this after calculating the Pair Plus bonus
        #so we don't confuse ties with forfeit hands
        #If the dealer hand is worse than Q high, the Play bet pushes (0)

        play_multipliers[:] = np.where(dealer_values <= QUEEN_HIGH,
                                       play_multipliers * win_lose, 0)

        del win_lose

        #Now we need to compute the 6-card bonuses
        #These are based on the best 5-card hand that can be made from the player's 3 card and the dealer's 3

        #The first out of each group of hands is the dealer hand for that group
        #The rest are the player hands
        #we want groups like: [dcard1, dcard2, dcard3, pcard1, pcard2, pcard3]
        #to pass to deuces' hand evaluator
        #This requires some slicing arobatics to avoid building new arrays

        #Hands are arranged by round such as dealer_cards, player1_cards, player2_cards, etc.
        #Then stepped slices are taken and truncated to form slices for each combo of dealer and player cards

        #eg. If hands = [D01, D02, D03, Pa1, Pa2, Pa3, Pb1, Pb2, Pb3,
        #                D11, D12, D13, Pc1, Pc2, Pc3, Pd1, Pd2, Pd3]
        #
        #Then eval_hands[0] = [[D01, D02, D03, Pa1, Pa2, Pa3],
        #                      [D11, D12, D13, Pc1, Pc2, Pc3]]
        #
        #     eval_hands[1] = [[D01, D02, D03, Pb1, Pb2, Pb3],
        #                      [D11, D12, D13, Pd1, Pd2, Pd3]]

        eval_hands = [
            hands.reshape(-1, all_hands, 3)[:, ::i + 1][:, :2].reshape(-1, 6)
            for i in xrange(num_players)
        ]

        #calculate 6card bonuses
        evaluator = Evaluator()

        #This is the main bottleneck in the simulator
        #The only real way to resolve it is to rebuild the deuces library's
        #hand evaluator to process arrays of hands rather than one at a time,
        #which is currently beyond the scope of this project

        for i in xrange(num_players):
            six_card_multipliers[:, i] = np.apply_along_axis(
                evaluator._six, 1, eval_hands[i])

        #Map hand values to bonus payouts

        SC_MAX_STRAIGHT_FLUSH = evaluator.table.MAX_STRAIGHT_FLUSH
        SC_MAX_QUADS = evaluator.table.MAX_FOUR_OF_A_KIND
        SC_MAX_FULL_HOUSE = evaluator.table.MAX_FULL_HOUSE
        SC_MAX_FLUSH = evaluator.table.MAX_FLUSH
        SC_MAX_STRAIGHT = evaluator.table.MAX_STRAIGHT
        SC_MAX_TRIPS = evaluator.table.MAX_THREE_OF_A_KIND

        #6-Card Bonus Payout table
        #Worse than trips  -1
        #Trips              5
        #Straight           10
        #Flush              15
        #Full House         25
        #Quads              50
        #Straight Flush     200
        #Royal Flush        1000

        six_card_multipliers[:] = np.where(
            six_card_multipliers > SC_MAX_TRIPS, -1,
            np.where(
                six_card_multipliers > SC_MAX_STRAIGHT, 5,
                np.where(
                    six_card_multipliers > SC_MAX_FLUSH, 10,
                    np.where(
                        six_card_multipliers > SC_MAX_FULL_HOUSE, 15,
                        np.where(
                            six_card_multipliers > SC_MAX_QUADS, 25,
                            np.where(
                                six_card_multipliers > SC_MAX_STRAIGHT_FLUSH,
                                50,
                                np.where(six_card_multipliers > 1, 200,
                                         1000)))))))

        return multipliers