def getHandStrength(hand, board, iters=1000): """ Uses pbots calc library to get the hand strength of a given hand against a given board. hand: a list of 2 Card objects board: a list of Card objects (could be [] or contain up to 5 cards) With iters=500: 95% of data within 3.8% of actual With iters=1000: 95% of data within 2.7% of actual With iters=2000: 95% of data within 2.2% of actual With iters=4000: 95% of data within 1.7% of actual """ if ((type(hand) == str) and (type(board) == str)): #print hand, board res = calc(hand+":xx", board, "", iters) else: assert len(board) <= 5, "Error: board must have 3-5 cards" assert (len(hand)==2), "Error: hand must contain exactly 2 cards" # the "xx" tells pbots_calc to compare hand against a random opponent hand handstr = convertSyntax(hand)+":xx" boardstr = convertSyntax(board) #note the empty "" parameter is telling pbots_calc not to remove any extra cards from the deck res = calc(handstr, boardstr, "", iters) return res.ev[0] # return the EV of our hand
def determineBestCardToKeep(hand, board, iters=1000, highcard_weight=0.03): """ A helper function for determineDiscardFast(). """ originalHandStr = convertSyntax(hand) boardStr = convertSyntax(board) # create a neutral deck that does not contain any cards from the hand or board neutralDeck = [] for card in FULL_DECK: if card in board or card in hand: continue else: neutralDeck.append(card) # can't let comparison card be within +/- of value of cards in the hand forbiddenRanks = [hand[0].rank, hand[1].rank, hand[0].rank-1, hand[1].rank-1, hand[0].rank+1, hand[1].rank+1] # shuffle so that we can pop random cards off of top shuffle(neutralDeck) # get two comparison cards that will be swapped cmpCard1 = neutralDeck.pop(0) # note: we make sure that these comparison cards are not the same rank as either card in our hand! while cmpCard1.rank in forbiddenRanks or cmpCard1.suit==hand[0].suit or cmpCard1.suit==hand[1].suit: cmpCard1 = neutralDeck.pop(0) cmpCard2 = neutralDeck.pop(0) while cmpCard2.rank in forbiddenRanks or cmpCard2.suit==hand[0].suit or cmpCard2.suit == hand[1].suit: cmpCard2 = neutralDeck.pop(0) # now we have 2 "neutral" comparison cards cmpCard1Str = convertSyntax(cmpCard1) cmpCard2Str = convertSyntax(cmpCard2) #print "Cmp Cards:", cmpCard1Str, cmpCard2Str # play [C_1, C1*] against [C_2, C2*] res1 = calc("%s%s:%s%s" % (originalHandStr[0:2], cmpCard1Str, originalHandStr[2:4], cmpCard2Str), boardStr, "", iters) # play [C_1, C2*] against [C_2, C1*] res2 = calc("%s%s:%s%s" % (originalHandStr[0:2], cmpCard2Str, originalHandStr[2:4], cmpCard1Str), boardStr, "", iters) # determine which card is stronger, and should be kept C1_scores = [res1.ev[0], res2.ev[0]] # the EVs for hands where we KEPT C1 C2_scores = [res1.ev[1], res2.ev[1]] # the EVs for hands where we KEPT C2 #print C1_scores #print C2_scores avgC1Score = mean(C1_scores) + highcard_weight * (hand[0].rank-hand[1].rank) avgC2Score = mean(C2_scores) + highcard_weight * (hand[1].rank-hand[0].rank) if avgC1Score > avgC2Score: # C1 is better to keep keepCard = 0 else: keepCard = 1 return keepCard
def setHandRankRiver(self): if self.numberOfOpponents == 2: r = pbots_calc.calc(''+self.currentHand+':xx:xx', self.currentBoard, '', 5000) if r: self.currentHandStrength = zip(r.hands, r.ev)[0][1] else: r = pbots_calc.calc(''+self.currentHand+':xx', self.currentBoard, '', 5000) if r: self.currentHandStrength = zip(r.hands, r.ev)[0][1]
def determineBestDiscard(hand, board, min_improvement=0.05, iters=100): """ Given a hand and either a flop or turn board, returns whether the player should discard, and if so, which card they should choose. hand: a list of Card objects board: a list of Card objects """ originalHandStr = convertSyntax(hand)+":xx" boardstr = convertSyntax(board) originalHandEV = calc(originalHandStr, boardstr, "", 1000).ev[0] swapFirstEVSum = 0 swapSecondEVSum = 0 numCards = 0 for card in FULL_DECK: if card in board or card in hand: continue else: # increment numCards += 1 cardstr = convertSyntax(card) # compare original hand to the hand made by replacing first card with CARD swapFirstStr = "%s%s%s" % (cardstr,originalHandStr[2:4],":xx") # compare original hand to the hand made by replacing second card with CARD swapSecondStr = "%s%s%s" % (originalHandStr[0:2],cardstr,":xx") # print("First:", swapFirstStr) # print("Second:", swapSecondStr) swap_first_res = calc(swapFirstStr, boardstr, "", iters) swap_second_res = calc(swapSecondStr, boardstr, "", iters) swapFirstEVSum += swap_first_res.ev[0] swapSecondEVSum += swap_second_res.ev[0] # calculate the average EV of swapping first and second when we play them against the original hand avgSwapFirstEV = float(swapFirstEVSum) / numCards avgSwapSecondEV = float(swapSecondEVSum) / numCards # if either swap increases EV by more than 5% if avgSwapFirstEV > originalHandEV+min_improvement or avgSwapSecondEV > originalHandEV+min_improvement: if avgSwapFirstEV > avgSwapSecondEV: return (True, 0, avgSwapFirstEV, originalHandEV) else: return (True, 1, avgSwapSecondEV, originalHandEV) else: return (False, None, 0, originalHandEV)
def get_best_action(self, received_packet, avail_actions = []): equity = None # determine if we are still in the same betting round to prevent raising to infinity problem if received_packet['num_boardcards'] == self.num_boardcards: self.is_new_round = False else: self.is_new_round = True self.num_boardcards = received_packet['num_boardcards'] if received_packet['num_active_players'] == 3: self.fold_thres = THREE_FOLD_THRES self.raise_thres = THREE_RAISE_THRES self.reraise_thres = THREE_RERAISE_THRES equity = pbots_calc.calc(':'.join([self.my_hand, 'xx', 'xx']), ''.join(received_packet['boardcards']), "", ITER_TABLE[self.num_boardcards]) else: self.fold_thres = TWO_FOLD_THRES self.raise_thres = TWO_RAISE_THRES self.reraise_thres = TWO_RERAISE_THRES equity = pbots_calc.calc(':'.join([self.my_hand, 'xx']), ''.join(received_packet['boardcards']), "", ITER_TABLE[self.num_boardcards]) equity = equity.ev[0] do_reraise = random.random() > self.reraise_thres if equity < self.fold_thres: # TODO: Implement bluffing / call here if CHECK in avail_actions: return CHECK return FOLD elif equity > self.raise_thres and (self.is_new_round or do_reraise): winning_factor = ((equity - self.fold_thres) / (1 - self.fold_thres))**POWER if BET in avail_actions: amount = self.bet_handler(winning_factor) return BET + ":" + str(amount) elif RAISE in avail_actions: amount = self.raise_handler(winning_factor) return RAISE + ":" + str(amount) if CALL in avail_actions: if self.should_call(equity): return CALL + ":" + str(self.call_amount) else: return FOLD return CHECK
def _calc(self, hands, board, dead, iters): key = (hands, board, dead, iters) if key in self.cache: return self.cache[key] strength = calc(hands, board, dead, iters).ev[0] self.cache[key] = strength return strength
def index(): if request.method == 'GET': return render_template('index.html') else: board = get_cards(request.form['board']) hole = get_cards(request.form['hole'], all=True) opponent = get_cards(request.form['opponent'], all=True) discard = get_cards(request.form['discard']) result = calc(hole + ":" + opponent, board, discard, 100000) if result is None: return render_template('index.html', result='fail', hole=hole, opponent=opponent, board=board, discard=discard) return render_template('index.html', result='success', evs=result.ev, hole=hole, opponent=opponent, board=board, discard=discard)
def bet(self, potSize, boardCards, lastActions, legalActions, timeBank): #LOCAL MACROS# CALC_ITERS = 1000 #MAIN CODE# #Initial equity calculation hole_cards_str = self.to_str(self.holeCards) + ':xxx' board_cards_str = self.to_str(boardCards) discard_str = self.to_str(self.discards) e = calc(hole_cards_str, board_cards_str, discard_str, CALC_ITERS).ev[0] #Gathers variables for input into threshold and bet functions opponent = self.opponent button = self.button if len(boardCards) == 0: betting_round = 0 else: betting_round = len(boardCards) - 2 current_bet = self.normalize_bet(self.current_bet) r1, r2 = random(), random() print 'time:', timeBank print 'betting round:', betting_round print 'your_equity', e #Updates current opponent bet opponent.update_bet_sequence(potSize, self.current_bet, betting_round) #Applies threshold function if e < self.threshold_function(opponent, self.current_bet, button, betting_round, r1): return self.try_fold(legalActions) #If threshold is met, makes bet else: bet_amount = self.denormalize_bet(self.bet_function(opponent, e, button, betting_round, r2)) return self.try_bet(bet_amount, potSize, legalActions)
def add_hand_data_pt(self, hand, board, bet_seq, button): #LOCAL MACROS# CALC_ITERS = 1000 POLY_DEGREE = 1 #MAIN CODE# #Makes hand inut for equity calculation h = hand + ':xx' #For each betting phase with board cards for i in xrange(0,4): #Stores total bet made on that phase self.play_history[button][i]['bet'].append(bet_seq[i]) #Stores equity of the hand that the opponent possesed at the time of the bet if i == 0: b = '' else: b = self.to_str(board[:3+i]) d = '' e = calc(h,b,d,CALC_ITERS).ev[0] self.play_history[button][i]['equity'].append(e) #print '_____' #print 'PLAY_HISTORY', i, button, self.play_history[button][i] #print '_____' #Performs polynomial regression to determine the equity to bet function and a bet to equity function l1 = polyfit(self.play_history[button][i]['equity'], self.play_history[button][i]['bet'], POLY_DEGREE) for k in range(POLY_DEGREE + 1): l1[k] = float(l1[k]) self.e_to_b_lib[button][i] = l1 l2 = polyfit(self.play_history[button][i]['bet'], self.play_history[button][i]['equity'], POLY_DEGREE) for k in range(POLY_DEGREE + 1): l2[k] = float(l2[k]) self.b_to_e_lib[button][i] = l2 #Checks to see if equity threshold should change based on new betting evidence. if e < self.equity_floor_lib[button][i]: self.equity_floor_lib[button][i] = e
def evaluator(request): try: # TODO: need to check if hand range (request body) is null, and also insert ALL hands for second range result = pbots_calc.calc(request.body + ":xx","","",1000000) if result: return HttpResponse(str(result)) else: return HttpResponse("Error") except: return HttpResponse("Error")
def calculateEquityStat (self, name, hand, numActivePlayersBeforeFold, boardcards, iter): for i in range(len(numActivePlayersBeforeFold)): numBc = I_TO_NUM_BC[i] if numActivePlayersBeforeFold[i] == 2: eq = 0 if numBc == 0: eq = self.equity_table_2[hand] else: eq = pbots_calc.calc(':'.join([hand, 'xx']), ''.join(boardcards[:numBc]), "", iter).ev[0] self.minEquityTwo[name][numBc] = min(eq, self.minEquityTwo[name][numBc]) self.averageEquityTwo[name][numBc] = calcEWMA (self.alpha, eq, self.averageEquityTwo[name][numBc]) elif numActivePlayersBeforeFold[i] == 3: eq = 0 if i == 0: eq = self.equity_table_3[hand] else: eq = pbots_calc.calc(':'.join([hand, 'xx', 'xx']), ''.join(boardcards[:numBc]), "", iter).ev[0] self.minEquityThree[name][numBc] = min(eq, self.minEquityThree[name][numBc]) self.averageEquityThree[name][numBc] = calcEWMA (self.alpha, eq, self.averageEquityThree[name][numBc])
def determineBestDiscardFast(hand, board, min_improvement=0.02, iters=100): """ min_improvement: the EV % that swapping must increase our hand by in order to go for the swap Returns: -whether we should swap -if so, the index of the card in our hand we should swap (0 or 1) -the avg. EV of swapping that card -the original EV of our hand as is """ # if we were to keep a card, determine which one is best keepCard = determineBestCardToKeep(hand, board) #print "Playoff thinks we should discard:", 1-keepCard boardStr = convertSyntax(board) originalHandEV = calc("%s:xx" % convertSyntax(hand), boardStr, "", 1000).ev[0] numCards = 0 swapEVSum = 0 for card in FULL_DECK: if card in board or card in hand: continue else: # increment numCards += 1 cardstr = convertSyntax(card) # remember, we want to KEEP our keepCard and swap the other card!!! swapStr = "%s%s%s" % (cardstr,convertSyntax(hand[keepCard]),":xx") swapRes = calc(swapStr, boardStr, "", iters) swapEVSum += swapRes.ev[0] # calculate the average EV of swapping the card besides our keepCard avgSwapEV = float(swapEVSum) / numCards # if either swap increases EV by more than 5% if avgSwapEV > originalHandEV + min_improvement: return (True, 1-keepCard, avgSwapEV, originalHandEV) else: return (False, None, 0, originalHandEV)
def _calc(self, hands, board, dead, iters): key = (hands, board, dead, iters) if key in self.cache: return self.cache[key] result = calc(hands, board, dead, iters) if result is not None: strength = result.ev[0] self.cache[key] = strength else: print "Warning: calc returned None" strength = 0.5 return strength
def determine_winner(p0_hand, p1_hand, board): assert len(board) == FLOP_SIZE + TURN_SIZE + RIVER_SIZE, "Error: cannot determine winner on incomplete board" handStr = "%s:%s" % (convert_card_syntax(p0_hand), convert_card_syntax(p1_hand)) boardStr = convert_card_syntax(board) res = pbots_calc.calc(handStr, boardStr, "", 1) # hand against board, no dead cards, 1 iteration if abs(res.ev[0] - 1.0) < 0.01: return 0 # player 0 wins elif abs(res.ev[1] - 1.0) < 0.01: return 1 # player 1 wins else: return 3 # represents a tie
def generate_equity_points(combos_filepath): if os.path.isfile('./jsons/points.csv'): print( "DO NOT OVERWRITE points.csv, takes forever to generate. Proceeding." ) return # Need the RANGE_TO_HANDS_MAP.json range_to_hands = json.load(open('./jsons/RANGE_TO_HANDS_MAP.json', 'r')) i = 0 points = pd.DataFrame() for combos in pd.read_csv(combos_filepath, header=0, chunksize=500000): # FLAG: EMERGENCY DATA THINNING TO MEET DEADLINE combos = combos.sample(frac=0.1) print("Chunk: ", i) # Convert these to lists print("Converting to lists") combos['hand'] = combos.hand.apply( lambda x: [int(s) for s in x.strip('][').split(', ')]) combos['board'] = combos.board.apply( lambda x: [int(s) for s in x.strip('][').split(', ')]) # Convert these to strings print("Converting to strings") combos['hand'] = combos.hand.apply( lambda x: mechanics.convert_card_syntax(_map_integer_to_card(x))) combos['board'] = combos.board.apply( lambda x: mechanics.convert_card_syntax(_map_integer_to_card(x))) print("Calculating equities") p = pd.DataFrame() # For each hand/board combo, calculate equity of hand vs each of 8 ranges against the board to create an 8-D point. Add these 8-D points to a list) for j in range(8): # This line is a nested set of functions p['equity_%x' % j] = combos.apply(lambda row: int(100 * round( pbots_calc.calc( row['hand'] + ":" + ",".join(range_to_hands[str(j)]), row[ 'board'], "", 100).ev[0], 2)), axis=1) print("Writing") points.append(p) if i == 0: p.to_csv('./jsons/points.csv', mode='w', header=True) else: p.to_csv('./jsons/points.csv', mode='a', header=False) i += 1
def get_calc_hand_strength(self, cards, board_cards, discarded_cards): """ Gets the effective hand strength of your cards at any stage of the game using the pbots_calc library. """ if calc is not None: result = calc(''.join(cards) + ':xx', ''.join(board_cards), ''.join(discarded_cards), 1000) if result is not None: strength = result.ev[0] else: strength = random.random() else: strength = random.random() return strength
def updateEquities(self): # consider num of active players # group by boolean profiles # find all combinations of boolean profiles # query equity calculator # generate full list if self.oppAProbDist == None: combinedList = self.oppBProbDist.distribution.keys() elif self.oppBProbDist == None: combinedList = self.oppAProbDist.distribution.keys() else: combinedList = list(set(list(self.oppAProbDist.distribution.keys()) + list(self.oppBProbDist.distribution.keys()))) # without boolean check self.equities = {} for keyA in combinedList: # should be combined distribution eq = pbots_calc.calc([(self.holeCard1,self.holeCard2),keyA], self.boardCards, '', 1000).ev[0] self.equities[keyA] = eq
def abstract_hand_postflop(hand, board, nodes, range_to_hands): """ Abstracts a NLH hand to one of 100 hand buckets, depending on the board @hand: a List of exactly 2 Card objects @board: a List of 3, 4, or 5 Card objects @nodes: a list of arrays representing centroid coordinates to abstract to @range_to_hands: a dictinoary that maps a range [0, 7] to the corresponding list of hands in that range Returns a number [0, 99] representing the cluster index of abstraction """ assert len( hand) == 2, "Error in abstraction: incorrectly formatted hand length" assert len(board) in [ 3, 4, 5 ], "Error in abstraction: incorrectly formatted board length" # Get the sorted, string form of the hand sorted_cards = sort_cards(hand) hand_str = mechanics.convert_card_syntax(sorted_cards) # Abstract the board (i.e. sort the streets by rank then by suit). # Since we are using imperfect recall, we do not need to maintain distinction between streets. ordered_board = sorted(board, key=lambda c: (c.rank, c.suit)) board_str = mechanics.convert_card_syntax(ordered_board) # Calculate equities to get 8_D point equities = [None] * 8 for j in range(8): opp_hands = range_to_hands[str(j)] opp_hands_str = ",".join(opp_hands) equities[j] = int(100 * round( pbots_calc.calc(hand_str + ":" + opp_hands_str, board_str, "", 100000).ev[0], 2)) equities = np.asarray(equities) def closest_node(node, nodes): nodes = np.asarray(nodes) dist_2 = np.sum((nodes - node)**2, axis=1) return np.argmin(dist_2) closest = closest_node(equities, nodes) return str(closest)
def pbots_calc_clean(handlist,boardlist,discarded): # set for 100 Monte Carlo iterations oddslist = str(pbots_calc.calc(handlist,boardlist,discarded,100)) oddslist = list(oddslist) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) oddslist=''.join(oddslist) oddslist = oddslist.split("'") oddslist.remove('') return oddslist
def discard(self, potSize, boardCards, lastActions, legalActions, timeBank): #LOCAL MACROS# CALC_ITERS = 1000 #MAIN CODE# #Seeks to keep the cards that afford the maximum equity against an arbitrary pair of cards #by computing the equity of 2 card subsets of hand print 'time:', timeBank v, l, discard = 0, list(self.holeCards), None for i in xrange(3): j = (i + 1) % 3 k = (i + 2) % 3 h = l[i] + l[j] + ':xx' b = self.to_str(boardCards) d = l[k] c = calc(h,b,d,CALC_ITERS) if c.ev[0] > v: discard = l[k] self.holeCards.remove(discard) self.discards.add(discard) return 'DISCARD:' + discard
def determineWinner(p1_hand, p2_hand, board): """ Given a board with 5 cards, and two hands, determines the winner on the river. 0: Player 1 wins 1: Player 2 wins 3: Players tie """ assert len(board)==5, "Error: cannot determine winner on a board with less than 5 cards" handstr = "%s:%s" % (convertSyntax(p1_hand), convertSyntax(p2_hand)) boardstr = convertSyntax(board) res = calc(handstr, boardstr, "", 1) if abs(res.ev[0]-1.0) < 0.01: return 0 elif abs(res.ev[1]-1.0) < 0.01: return 1 else: # print " ----------- Tie?--------------" # print "P1:%f P2: %f" % (res.ev[0], res.ev[1]) return 3 # tie player
def pbots_calc_clean(self,handlist,boardlist,discarded): # set for 10000 Monte Carlo iterations #d1 = datetime.now() oddslist = str(pbots_calc.calc(handlist,boardlist,discarded,10000)) #d2 = datetime.now() #print d2-d1 oddslist = list(oddslist) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) for char in oddslist: if char in ["[","]","(",")",","," "]: oddslist.remove(char) oddslist=''.join(oddslist) oddslist = oddslist.split("'") oddslist.remove('') return oddslist
def test3(): range_to_hands = json.load(open('./jsons/RANGE_TO_HANDS_MAP.json', 'r')) combos = [['AdKs', 'TsJs4d'], ['ThJd', 'TsJs4d'], ['Qs8c', 'TsJs4d'], ['Jc3d', 'TsJs4d']] points = [] for combo in combos: equities = [None] * 8 hand_str = combo[0] board_str = combo[1] for j in range(8): opp_hands = range_to_hands[str(j)] opp_hands_str = ",".join(opp_hands) equities[j] = int(100 * round( pbots_calc.calc(hand_str + ":" + opp_hands_str, board_str, "", 100000).ev[0], 2)) points.append(equities) points_matrix = np.matrix(points) print(points_matrix) print(type(points_matrix)) kmeans = KMeans(n_clusters=2).fit(points_matrix) print(type(kmeans.cluster_centers_))
# pbots_calc is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # pbots_calc in a file in teh toplevel directory called "GPLv3". If not, see # <http://www.gnu.org/licenses/>. # import pbots_calc import sys """ Simple command line tool for equity calculations. Demonstrates how to use the pbots_calc library python wrapper. """ if __name__ == "__main__": if len(sys.argv) < 2: print("usage: %s hand1:hand2:hand... [board [dead]]" % sys.argv[0]) sys.exit(0) board = "" dead = "" if len(sys.argv) >= 3: board = sys.argv[2] if len(sys.argv) >= 4: sys.argv[3] argument = sys.argv[1] r = pbots_calc.calc(argument, board, dead, 1000000) if r: print(set(zip(r.hands, r.ev)))
def index(request): r = pbots_calc.calc("ATs:89s","","",1000000) if r: return HttpResponse(str(r)) else: return HttpResponse("error")
def get_action(self, game, round, pot, cards, board_cards, legal_moves, cost_func, move_history, time_left, min_amount=None, max_amount=None): ''' Where the magic happens - your code should implement this function. Called any time the server needs an action from your bot. Arguments: game: the pokerbots.Game object. round: the pokerbots.Round object. pot: the pokerbots.Pot object. cards: an array of your cards, in common format. board_cards: an array of cards on the board. This list has length 0, 3, 4, or 5. legal_moves: a set of the move classes that are legal to make. cost_func: a function that takes a move, and returns additional cost of that move. Your returned move will raise your pot.contribution by this amount. move_history: a list of moves that have occurred during this round so far, earliest moves first. time_left: a float of the number of seconds your bot has remaining in this match (not round). min_amount: if BetAction or RaiseAction is valid, the smallest amount you can bet or raise to (i.e. the smallest you can increase your pip). max_amount: if BetAction or RaiseAction is valid, the largest amount you can bet or raise to (i.e. the largest you can increase your pip). ''' if self.check_player and round.hand_num == 1: self.check_player = False if len(move_history) % 2 == 0: self.opp = 'B' else: self.opp = 'A' print("we think opponent is player " + self.opp) last_move = move_history[-1] if last_move == 'CHECK:' + self.opp: self.opp_check += 1 # print(last_move + ", opponent check count = " + str(self.opp_check)) if self.stop and FoldAction in legal_moves: return FoldAction() elif self.stop and CheckAction in legal_moves: return CheckAction() if round.hand_num == 50: if self.fold_before_flop > 15: self.opp_bluff_before_flop = True print("over 15 self.fold_before_flop") if self.fold_before_turn > 20: self.opp_bluff_before_turn = True print("over 15 self.fold_before_turn") if calc is not None: result = calc(''.join(cards) + ':xx', ''.join(board_cards), ''.join(self.discarded_cards), 1000) if result is not None: strength = result.ev[0] else: print "Warning: calc returned None" strength = random.random() else: strength = random.random() #sophia # anti bluff block if min_amount == 160: strength = 0.95 if CallAction in legal_moves: return CallAction() if len(board_cards) == 5 and FoldAction in legal_moves: # check if last move was opponent bet last_move = move_history[-1] if "BET" in last_move and last_move[-1] == self.opp: if len(self.opp_river_bet) > 3: if round.hand_num < 400: best_fit = stats.linregress( np.array(self.opp_river_bet), np.array(self.opp_river_strength)) self.slope = best_fit[0] self.intercept = best_fit[1] self.predicted_strength = min( 1, int(last_move.split(':')[1]) * self.slope + self.intercept) print("slope = " + str(self.slope) + ", intercept = " + str(self.intercept)) print("predicted strength: " + str(self.predicted_strength)) print("our strength: " + str(strength)) if self.predicted_strength >= strength + 0.05: print("folded bc opponent's bet indicates stronger") if round.hand_num <= 20: if min_amount <= 60: self.fold += 1 return FoldAction() if ExchangeAction in legal_moves and self.exchange_count < 3 and len( board_cards) >= 3: # decision to exchange # exchange logic # if we exchange, we should update self.discarded_cards exchange_cost = cost_func(ExchangeAction()) exchange_ev = 0.6 * pot.opponent_total - 0.4 * (pot.total + exchange_cost) check_ev = strength * pot.opponent_total - (1. - strength) * pot.total if exchange_ev > check_ev and len( board_cards ) > 3 and strength <= 0.75: # exchanging is worth it self.discarded_cards |= set( cards) # keep track of the cards we discarded self.exchange_count += 1 return ExchangeAction() return CheckAction() if self.fold >= 11 and len(board_cards) == 3 and min_amount <= 40: if CallAction in legal_moves: print("antibluff 1") return CallAction() if self.fold >= 11 and len(board_cards) == 4 and min_amount <= 50: if CallAction in legal_moves: print("antibluff 1") return CallAction() if self.fold >= 11 and len(board_cards) == 5 and min_amount <= 60: if CallAction in legal_moves: print("antibluff 1") return CallAction() if len(board_cards) == 0 and strength * 0.5 > random.random(): if RaiseAction in legal_moves and min_amount < 10: raise_amt = min_amount + 5 * (random.random()) raise_amt = max(raise_amt, min_amount) raise_amt = min(raise_amt, max_amount) return RaiseAction(raise_amt) if len(board_cards) == 3 and strength * 0.5 > random.random(): if RaiseAction in legal_moves and min_amount < 10: raise_amt = min_amount + 5 * (random.random()) raise_amt = max(raise_amt, min_amount) raise_amt = min(raise_amt, max_amount) return RaiseAction(raise_amt) if not (ExchangeAction in legal_moves and self.exchange_count < 3 and len(board_cards) >= 3 ): # decision to commit resources to the pot continue_cost = cost_func( CallAction()) if CallAction in legal_moves else cost_func( CheckAction()) # figure out how to raise the stakes commit_amount = 0 if len(board_cards) == 0: commit_amount = int(pot.pip + continue_cost + (strength * random.random()) * (pot.grand_total + continue_cost)) elif len(board_cards) == 3: commit_amount = int(pot.pip + continue_cost + (1.5 * strength * random.random()) * (pot.grand_total + continue_cost)) elif len(board_cards) == 4: # should be between 0.5 and 0.75 commit_amount = int(pot.pip + continue_cost + (2 * strength * random.random()) * (pot.grand_total + continue_cost)) elif len(board_cards) == 5: commit_amount = int(pot.pip + continue_cost + (2.25 * strength * random.random()) * (pot.grand_total + continue_cost)) if min_amount is not None: commit_amount = max(commit_amount, min_amount) commit_amount = min(commit_amount, max_amount) if continue_cost == 0: if BetAction in legal_moves and strength > 0.95 and len( board_cards) >= 4: commit_action = BetAction(max_amount) else: commit_action = CheckAction() if continue_cost > 0: if len(board_cards) != 0: #1 self.num_of_bets += 1 if self.num_of_bets == 2 and strength < 0.9: #2 strength -= 0.03 if self.num_of_bets == 3 and strength < 0.9: #3 strength -= 0.05 if continue_cost > 20 and strength < 1: #4.1 strength -= 0.05 if strength < 0.8 and len(board_cards) >= 4: #4.2 strength -= 0.05 if continue_cost > 50 and strength < 0.95: #5 strength -= 0.11 if pot.opponent_num_exchanges > 0: #6 strength -= 0.05 if continue_cost < 5 and len(board_cards) == 5: #7 strength += 0.1 if continue_cost > 0: if RaiseAction in legal_moves and len( board_cards) == 5 and strength >= 0.95: commit_action = RaiseAction(commit_amount) elif CallAction in legal_moves: # we are contemplating an all-in call commit_action = CallAction() else: # only legal action return CheckAction() pot_odds = float(continue_cost) / (pot.grand_total + continue_cost) + 0.1 if len(board_cards) == 0 and strength > 0.5: return commit_action if strength >= pot_odds or (self.opp_bluff_before_turn and len(board_cards) == 3) or ( (len(board_cards) == 0) and self.opp_bluff_before_flop): if strength >= 0.7: # commit more sometimes return commit_action last_move = move_history[-1] if 'BET' in last_move and len( board_cards) == 3 and last_move[-1] == self.opp: if int(last_move.split(':') [1]) > 300 and FoldAction in legal_moves: print(last_move) print('sophia flop all in fold') if round.hand_num <= 20: if min_amount <= 60: self.fold += 1 return FoldAction() return CallAction() else: # staying in the game has negative EV if round.hand_num <= 20: if min_amount <= 60: self.fold += 1 print("hand" + str(round.hand_num) + ": " + str(self.fold)) return FoldAction() elif continue_cost == 0: if len(board_cards) == 3: strength += 0.02 if len(board_cards) == 4: strength += 0.05 if len(board_cards) == 5: strength += 0.1 if self.opp_check >= 3 and strength > 0.6 and pot.grand_total >= 10 and pot.grand_total <= 20: return commit_action if self.opp_check > 4: strength += 0.1 if strength >= 0.6 and BetAction in legal_moves or RaiseAction in legal_moves: # commit more sometimes return commit_action elif BetAction in legal_moves or RaiseAction in legal_moves and random.random( ) < strength: # balance bluffs with value bets return commit_action return CheckAction() if self.fold >= 11 and len(board_cards) >= 3 and min_amount <= 40: if CallAction in legal_moves: print("antibluff 2") return CallAction() if CheckAction in legal_moves: return CheckAction() elif CallAction in legal_moves and strength >= 0.7: return CallAction() elif FoldAction in legal_moves: if round.hand_num <= 20: if min_amount <= 60: self.fold += 1 print("hand" + str(round.hand_num) + ": " + str(self.fold)) return FoldAction()
def handle_round_over(self, game, round, pot, cards, opponent_cards, board_cards, result, new_bankroll, new_opponent_bankroll, move_history): ''' Called when a round ends. Called Game.num_rounds times. Arguments: game: the pokerbots.Game object. round: the pokerbots.Round object. pot: the pokerbots.Pot object. cards: the cards you held when the round ended. opponent_cards: the cards your opponent held when the round ended, or None if they never showed. board_cards: the cards on the board when the round ended. result: 'win', 'loss' or 'tie' new_bankroll: your total bankroll at the end of this round. new_opponent_bankroll: your opponent's total bankroll at the end of this round. move_history: a list of moves that occurred during this round, earliest moves first. Returns: Nothing. ''' self.num_of_bets = 0 if new_bankroll > (1000 - round.hand_num) * 1.5 + 10: self.stop = True self.games[result] += 1 if round.hand_num <= 50: flop = False turn = False for moves in move_history: if "FLOP" in moves: flop = True if "TURN" in moves: turn = True if self.opp == 'B': if moves == "FOLD:A": if not flop: self.fold_before_flop += 1 elif not turn: self.fold_before_turn += 1 if moves == "FOLD:" + self.opp: if not flop: self.bluff_before_flop += 1 elif not turn: self.bluff_before_turn += 1 else: # self.opp == 'A' if moves == "FOLD:B": if not flop: self.fold_before_flop += 1 elif not turn: self.fold_before_turn += 1 if moves == "FOLD:A": if not flop: self.bluff_before_flop += 1 elif not turn: self.bluff_before_turn += 1 # print(self.fold_before_flop, self.fold_before_turn, self.bluff_before_turn, self.bluff_before_flop) # save info on how much B bet and the strength of their hand if len( board_cards ) == 5 and opponent_cards != None: # make sure after river and opponent revealed cards # iterate through move history to look for opponent's first bet after river river = False for element in move_history: if "RIVER" in element: river = True if river and "BET" in element: # only look at moves that are bets after river # move is in the form "BET:bet_amt:player" so cut first four chars of move and splice the rest by the colon info = element[4:].split(':') if info[1] == self.opp: # only care about B's bet # add key info[0] with value strength of opponent_cards if calc is not None: opp_result = calc(''.join(opponent_cards) + ':xx', ''.join(board_cards), '', 1000) self.opp_river_bet.append(int(info[0])) self.opp_river_strength.append(opp_result.ev[0]) else: opp_result = random.random()
# pbots_calc is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # pbots_calc in a file in teh toplevel directory called "GPLv3". If not, see # <http://www.gnu.org/licenses/>. # import pbots_calc import sys """ Simple command line tool for equity calculations. Demonstrates how to use the pbots_calc library python wrapper. """ if __name__ == "__main__": if len(sys.argv) < 2: print "usage: %s hand1:hand2:hand... [board [dead]]" % sys.argv[0] sys.exit(0) board = "" dead = "" if len(sys.argv) >= 3: board = sys.argv[2] if len(sys.argv) >= 4: dead = sys.argv[3] r = pbots_calc.calc(sys.argv[1], board, dead, 1000000) if r: print zip(r.hands, r.ev)
def get_best_action(self, received_packet, avail_actions = []): equity = None # set thresholds # if not self.one_out: # self.fold_thres = THREE_FOLD_THRES_TABLE[self.num_boardcards] # self.raise_thres = THREE_RAISE_THRES_TABLE[self.num_boardcards] # self.opp_fold_thres = THREE_opp_fold_thres # else: # self.fold_thres = TWO_FOLD_THRES_TABLE[self.num_boardcards] # self.raise_thres = TWO_RAISE_THRES_TABLE[self.num_boardcards] # self.opp_fold_thres = TWO_opp_fold_thres # see if we could use precomputed equity if self.num_active_players == 2 or self.one_folded == True: self.fold_thres = TWO_FOLD_THRES_TABLE[self.num_boardcards] self.raise_thres = TWO_RAISE_THRES_TABLE[self.num_boardcards] self.opp_fold_thres = self.STATS.getFoldPercent(self.guy_active) if self.num_boardcards == 0: equity = self.equity_table_2[self.my_hand] else: equity = pbots_calc.calc(':'.join([self.my_hand, 'xx']), ''.join(received_packet['boardcards']), "", self.monte_carlo_iter) equity = equity.ev[0] else: self.fold_thres = THREE_FOLD_THRES_TABLE[self.num_boardcards] self.raise_thres = THREE_RAISE_THRES_TABLE[self.num_boardcards] self.opp_fold_thres = self.opp1_skill * self.STATS.getFoldPercent(self.opponent_1_name) + self.opp2_skill * self.STATS.getFoldPercent(self.opponent_2_name) if self.num_boardcards == 0: equity = self.equity_table_3[self.my_hand] else: equity = pbots_calc.calc(':'.join([self.my_hand, 'xx', 'xx']), ''.join(received_packet['boardcards']), "", self.monte_carlo_iter) equity = equity.ev[0] do_reraise = random.random() < self.opp_fold_thres # print 'FOLD T: ' + str(self.opp_fold_thres) # print 'EQUITY: ' + str(equity) own_a_lot_of_chips = self.my_stacksize > LOTS_OF_CHIPS winning_factor = ((equity - self.fold_thres) / (1 - self.fold_thres))**POWER # TODO: Raise a little if they checked last turn # if equity < self.fold_thres and ((own_a_lot_of_chips != True) or (self.num_boardcards != 0)): # # TODO: Implement bluffing / call here # if CHECK in avail_actions: # # bet a little bit if possible to exploit bots who fold to small raises: (but dont do this all the time) # # if BET in avail_actions and self.my_stacksize > BET_SMALL_LIKELIHOOD[self.num_boardcards]: # if False: # random_nig = random.random() # # foldPerc = 0.0 # # for name in self.opp_dict: # # if self.opp_dict[name].status == ACTIVE: # # foldPerc += self.STATS.foldPercentage[name] # # if not self.one_folded: # # foldPerc = foldPerc / 2 # if random_nig < self.opp_fold_thres: # print "MINBET: " + str(self.minBet) # return BET + ":" + str(self.minBet) # return CHECK # return FOLD # Subroutines we use to make our lives easier: ##################################################################################################### def do_call (): if self.should_call(equity): self.highestRaiseAmtThisRnd = self.call_amount return CALL + ":" + str(self.call_amount) else: return FOLD def do_raise_preflop (): if self.my_hand in self.boss_class1: if self.lastRaiser in [TYPE_STA] and self.my_stacksize > SUFFICIENT_CHIPS: return CALL + ":" + str(self.call_amount) # raise a lot self.highestRaiseAmtThisRnd = self.maxRaise return RAISE + ":" + str(self.maxRaise) elif self.my_hand in self.boss_class2: if self.num_active_players == 3 and self.one_folded == False: if (self.opp_dict[self.opponent_1_name].playerType not in [TYPE_STA, TYPE_TA, TYPE_LP]) and \ (self.opp_dict[self.opponent_2_name].playerType not in [TYPE_STA, TYPE_TA, TYPE_LP]): # raise a lot self.highestRaiseAmtThisRnd = self.maxRaise return RAISE + ":" + str(self.maxRaise) else: self.highestRaiseAmtThisRnd = self.maxRaise return RAISE + ":" + str(self.minRaise) elif self.opp_dict[self.guy_active].playerType not in [TYPE_STA, TYPE_TA, TYPE_LP]: # raise a lot self.highestRaiseAmtThisRnd = self.maxRaise return RAISE + ":" + str(self.maxRaise) else: self.highestRaiseAmtThisRnd = self.minRaise return RAISE + ":" + str(self.minRaise) # bet a little in general self.highestRaiseAmtThisRnd = self.minRaise return RAISE + ":" + str(self.minRaise) def do_call_preflop (): raiserType = self.opp_dict[self.lastRaiser].playerType if raiserType in [TYPE_STA, TYPE_TA, TYPE_LP]: if self.my_hand in self.boss_class1: return do_call() else: # do not engage return FOLD elif raiserType != TYPE_LA: if self.my_hand in self.boss_class1 or self.my_hand in self.boss_class2: return do_call() else: return FOLD else: return do_call() ##################################################################################################### if equity < self.fold_thres: if self.isPreflop: if self.handPosition == BUTTON: if self.opp_dict[self.opponent_1_name].playerType == TYPE_LA or self.opp_dict[self.opponent_2_name].playerType == TYPE_LA: if (self.fold_thres - equity) < TOLERANCE_FOR_LA: if CALL in avail_actions: if self.call_amount == self.bigBlind and RAISE in avail_actions: self.highestRaiseAmtThisRnd = self.minRaise return RAISE + ":" + str(self.minRaise) return FOLD if BET in avail_actions and self.minBet <= self.bigBlind: if self.oppCheckedThisRound: random_nig = random.random() if random_nig < self.opp_fold_thres: # print "MINBET: " + str(self.minBet) self.highestRaiseAmtThisRnd = self.minBet return BET + ":" + str(self.minBet) if CHECK in avail_actions: return CHECK if CALL in avail_actions: # if relatively smalll: if self.inc_call_amount <= self.bigBlind or \ (self.isPreflop == False and (self.inc_call_amount < 2 * self.bigBlind or self.inc_call_amount < 0.07 * self.potsize)): return do_call() return FOLD # elif equity > self.raise_thres and (self.is_new_round or do_reraise): elif equity > self.raise_thres: if self.isPreflop: # fun time! if self.handPosition == BUTTON: if self.opp_dict[self.opponent_1_name].playerType == TYPE_LA or self.opp_dict[self.opponent_2_name].playerType == TYPE_LA: if self.num_active_players == 2 or self.one_folded: if self.opp_dict[self.guy_active].playerType == TYPE_LA: # n***a will handle raising return do_call() else: # n****s will handle the raising for us return do_call() if CALL in avail_actions: if self.potsize > 5 * self.bigBlind: return do_call_preflop() else: if RAISE in avail_actions: return do_raise_preflop() else: return do_call_preflop() elif self.handPosition == BB: if CALL in avail_actions: # someone raised return do_call_preflop() elif RAISE in avail_actions and self.potsize < 7: return do_raise_preflop() return CHECK elif self.handPosition == SB: if CALL in avail_actions: if self.call_amount == self.bigBlind and RAISE in avail_actions: return do_raise_preflop() else: # someone raised return do_call_preflop() else: if BET in avail_actions: amount = self.bet_handler(winning_factor) return BET + ":" + str(amount) elif RAISE in avail_actions and do_reraise: # if they raise a little, either counter raise or call if self.should_call(equity): amount = self.raise_handler(winning_factor) return RAISE + ":" + str(amount) else: return FOLD # not in very good equity range if CHECK in avail_actions or BET in avail_actions: if self.oppCheckedThisRound: random_nig = random.random() if random_nig < self.opp_fold_thres: # print "MINBET: " + str(self.minBet * 4) self.highestRaiseAmtThisRnd = self.minBet * 4 return BET + ":" + str(self.minBet * 4) if CALL in avail_actions: # TODO: do preflop logic here if self.isPreflop: if self.handPosition == BUTTON or (self.handPosition == SB and self.num_active_players == 2): if self.opp_dict[self.guy_active].playerType == TYPE_LA: # n***a will handle raising return do_call() if self.potsize > 5 * self.bigBlind: return do_call_preflop() else: if RAISE in avail_actions: return do_raise_preflop() else: return do_call_preflop() elif self.handPosition == SB: if self.call_amount == self.bigBlind: return do_call() else: # someone raised return FOLD elif self.handPosition == BB: # someone raised if self.potsize <= 3 * self.bigBlind: return do_call_preflop() return FOLD else: return do_call () # return do_call() # if CHECK in avail_actions: # random_nig = random.random() # if random_nig < self.opp_fold_thres: # print "MINBET: " + str(self.minBet) # return BET + ":" + str(self.minBet) return CHECK
import pbots_calc suits = ['h', 's', 'd', 'c'] numbers = ['2', '3', '4', '5', '6', '7', '8', '9', 't', 'j', 'q', 'k', 'a'] cards = [] for i in range(4): for j in range(13): cards.append(numbers[j] + suits[i]) for c1 in range(52): for c2 in range(51 - c1): c2 += c1 + 1 for c3 in range(51 - c2): c3 += c2 + 1 hand = cards[c1] + cards[c2] + cards[c3] equity = pbots_calc.calc(hand + ":xxx", '', '', 10000).ev[0] print hand + "=" + str(equity)
def test(): board = "" dead = "" r = pbots_calc.calc("4qo:jts+", "", "", 1000000) if r: print(list(zip(r.hands, r.ev)))
import numpy as np from pbots_calc import calc RANKS = list('23456789TJQKA') SUITS = list('cdhs') DECK = [r + s for r in RANKS for s in SUITS] preflop_strengths = [] for _ in range(100000): np.random.shuffle(DECK) preflop_strengths.append(calc(''.join(DECK[:2]) + ':xx', '', '', 1300).ev[0]) preflop = np.percentile(preflop_strengths, np.arange(1, 100)) np.savetxt('preflop.csv', preflop, delimiter=',')
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # pbots_calc in a file in teh toplevel directory called "GPLv3". If not, see # <http://www.gnu.org/licenses/>. # import pbots_calc import sys """ Simple command line tool for equity calculations. Demonstrates how to use the pbots_calc library python wrapper. """ if __name__ == "__main__": if len(sys.argv) < 2: print "usage: %s hand1:hand2:hand... [board [dead]]" % sys.argv[0] sys.exit(0) board = "" dead = "" if len(sys.argv) >= 3: board = sys.argv[2] if len(sys.argv) >=4: dead = sys.argv[3] r = pbots_calc.calc(sys.argv[1], board, dead, 1000000) if r: print zip(r.hands, r.ev)
def get_action(self, game, round, pot, cards, board_cards, legal_moves, cost_func, move_history, time_left, min_amount=None, max_amount=None): ''' Where the magic happens - your code should implement this function. Called any time the server needs an action from your bot. Arguments: game: the pokerbots.Game object. round: the pokerbots.Round object. pot: the pokerbots.Pot object. cards: an array of your cards, in common format. board_cards: an array of cards on the board. This list has len 0, 3, 4, or 5. legal_moves: a set of the move classes that are legal to make. cost_func: a function that takes a move, and returns additional cost of that move. Your returned move will raise your pot.contribution by this amount. move_history: a list of moves that have occurred during this round so far, earliest moves first. time_left: a float of the number of seconds your bot has remaining in this match (not round). min_amount: if BetAction or RaiseAction is valid, the smallest amount you can bet or raise to (i.e. the smallest you can increase your pip). max_amount: if BetAction or RaiseAction is valid, the largest amount you can bet or raise to (i.e. the largest you can increase your pip). ''' print time_left if len(self.opp_range_all) == 1326: self.removeSeenFromOppRange(cards) while (self.moves < len(move_history)): self.handleMove(move_history[self.moves], game, board_cards) self.moves += 1 if calc is not None: result = calc(''.join(cards) + ':' + ','.join(self.opp_range), ''.join(board_cards), ''.join(self.discarded_cards), 1000) if result is not None: strength = result.ev[0] else: print "Warning: calc returned None" strength = random.random() else: strength = random.random() margin = game.big_blind / 2.0 + 1.0 if round.bankroll > 0: margin *= 1 elif round.bankroll < 0: margin *= -1 else: margin *= 0 bb_per_hand_ahead = (round.bankroll - margin) / (game.num_hands - round.hand_num + 1.0) aggression_factor = min( max((1.5 - bb_per_hand_ahead) / 3.0 + np.random.normal(0.0, 0.05), 0.0), 1.0) # 0 no aggression, 1 full aggression if ExchangeAction in legal_moves: # decision to exchange # exchange logic # if we exchange, we should update self.discarded_cards if len(board_cards) == 0: return CheckAction() # Never exchange pre-flop exchange_cost = cost_func(ExchangeAction()) outs = 0 if len(board_cards) == 4 and (aggression_factor > 0.5 or strength < 0.4): flush_outs = checkFlushDraw(cards, board_cards, self.discarded_cards) straight_outs = checkStraightDraw(cards, board_cards, self.discarded_cards) threekind_outs = check3ofKindDraw(cards, board_cards, self.discarded_cards) if flush_outs > 0: outs = flush_outs elif not straight_outs > 0 and not flush_outs == -2: outs = straight_outs elif not threekind_outs > 0 and not flush_outs == -2 and not straight_outs == -2: outs = threekind_outs if not outs == 0: stack_rem = game.round_stack - pot.total exchange_cost_cum = 0 prob_no_hit_cum = 1.0 j = 1 while (stack_rem >= ((2**j) - 1) * exchange_cost): cards_rem = 52 - len(cards) - len(board_cards) - len( self.discarded_cards) - 2 * (j - 1) exchange_cost_cum += prob_no_hit_cum * (2**( j - 1)) * exchange_cost prob_no_hit_cum *= (1.0 - outs * 1.0 / cards_rem) * ( 1.0 - outs * 1.0 / (cards_rem - 1.0)) j += 1 exchange_ev = pot.grand_total * ( 1 - prob_no_hit_cum) - prob_no_hit_cum * exchange_cost_cum check_ev = strength * pot.grand_total if exchange_ev > check_ev: # exchanging is worth it self.discarded_cards |= set( cards) # keep track of the cards we discarded print "EXCHANGE TO HIT DRAW" return ExchangeAction() strength_exchange = calc( 'xx:' + ','.join(self.opp_range), ''.join(board_cards), ''.join(self.discarded_cards) + ''.join(cards), 1000).ev[0] exchange_ev = strength_exchange * (pot.grand_total + exchange_cost) - exchange_cost check_ev = strength * pot.grand_total if exchange_ev > check_ev: # exchanging is worth it self.discarded_cards |= set( cards) # keep track of the cards we discarded return ExchangeAction() return CheckAction() else: # decision to commit resources to the pot if bb_per_hand_ahead > 1.5: if not self.check_fold: print 'WIN SECURE. START CHECK FOLDING. ROUND #' + str( round.hand_num) self.check_fold = True if CheckAction in legal_moves: return CheckAction() else: return FoldAction() continue_cost = cost_func( CallAction()) if CallAction in legal_moves else cost_func( CheckAction()) # figure out how to raise the stakes if aggression_factor < 0.25: commit_amount = int(pot.pip + continue_cost + 4 * math.sqrt(max(strength - 0.6, 0.0)) * aggression_factor * (pot.grand_total + continue_cost)) elif aggression_factor > 0.6: commit_amount = int(pot.pip + continue_cost + 2 * math.sqrt(strength) * aggression_factor * (pot.grand_total + continue_cost)) else: commit_amount = int(pot.pip + continue_cost + 2 * math.sqrt(max(strength - 0.3, 0.0)) * aggression_factor * (pot.grand_total + continue_cost)) if min_amount is not None: commit_amount = max(commit_amount, min_amount) if max_amount is not None: commit_amount = min(commit_amount, max_amount) if RaiseAction in legal_moves: commit_action = RaiseAction(commit_amount) elif BetAction in legal_moves: commit_action = BetAction(commit_amount) elif CallAction in legal_moves: # we are contemplating an all-in call commit_action = CallAction() else: # only legal action return CheckAction() if continue_cost > 0: # our opponent has raised the stakes print strength if continue_cost > 1 and strength < 1: # tight-aggressive playstyle strength -= (max( min( 0.75, continue_cost * 1.0 / (pot.grand_total - continue_cost)), 0.25 ) - 0.25) * max( min( -1.212 * aggression_factor**2 + 0.5152 * aggression_factor + 0.5455, 0.6), 0.25 ) # intimidation factor print strength # calculate pot odds: is it worth it to stay in the game? pot_odds = float(continue_cost) / (pot.grand_total + continue_cost) if strength >= pot_odds: # staying in the game has positive EV if len( board_cards ) == 0 and continue_cost > 1 and strength < 0.8 * max( min(1.0 - aggression_factor, 0.5), 0.8): return FoldAction() if strength > 1.15 - aggression_factor and 2 * random.random( ) * (1.0 - aggression_factor ) < strength: # commit more sometimes print[strength, aggression_factor] return commit_action return CallAction() else: # staying in the game has negative EV return FoldAction() elif continue_cost == 0: if len(board_cards) == 0: if 2 * 0.63 * np.random.normal(1.0, 0.1) * ( 1.0 - aggression_factor ) < strength: # balance bluffs with value bets return commit_action else: if np.random.normal(1.0, 0.2) * ( 1.0 - aggression_factor ) < strength: # balance bluffs with value bets return commit_action return CheckAction() # Default to checkcall if CallAction in legal_moves: return CallAction() else: return CheckAction()
1, 4)] and vals_dict[card4[0]] >= vals_dict[c[0]] and not c in [card1, card2, card3, card4] ]: card_keys5.append(card1 + card2 + card3 + card4 + card5) print len(card_keys5) probdict = {} start_time = time.time() i = 0 for key in card_keys5: cards = key[0:4].replace('a', 's').replace('b', 'h') # c maps to c, d maps to d board_cards = key[4:10].replace('a', 's').replace( 'b', 'h') # c maps to c, d maps to d probdict[key] = calc(cards + ':xx', board_cards, '', 1000).ev[0] i += 1 if i % int(len(card_keys5) * 0.005) == 0: print str( i * 100.0 / len(card_keys5)) + '% complete. Estimated time remaining: ' + str( (len(card_keys5) - i) * ((time.time() - start_time) / i) * (1 / 60.0)) + ' minutes' pickle.dump(probdict, open("postflop_odds.pkl", "wb")) #print hand_list #len(set([card1[1],card2[1]]))+1
def get_best_action(self, received_packet, avail_actions = []): equity = None # determine if we are still in the same betting round to prevent raising to infinity problem if received_packet['num_boardcards'] == self.num_boardcards: self.is_new_round = False else: self.is_new_round = True self.num_boardcards = received_packet['num_boardcards'] # set thresholds # if not self.one_out: # self.fold_thres = THREE_FOLD_THRES_TABLE[self.num_boardcards] # self.raise_thres = THREE_RAISE_THRES_TABLE[self.num_boardcards] # self.reraise_thres = THREE_RERAISE_THRES # else: # self.fold_thres = TWO_FOLD_THRES_TABLE[self.num_boardcards] # self.raise_thres = TWO_RAISE_THRES_TABLE[self.num_boardcards] # self.reraise_thres = TWO_RERAISE_THRES # see if we could use precomputed equity if self.num_active_players == 2 or self.one_folded == True: self.fold_thres = TWO_FOLD_THRES_TABLE[self.num_boardcards] self.raise_thres = TWO_RAISE_THRES_TABLE[self.num_boardcards] self.reraise_thres = TWO_RERAISE_THRES if self.num_boardcards == 0: equity = self.equity_table_2[self.my_hand] else: equity = pbots_calc.calc(':'.join([self.my_hand, 'xx']), ''.join(received_packet['boardcards']), "", self.monte_carlo_iter) equity = equity.ev[0] else: self.fold_thres = THREE_FOLD_THRES_TABLE[self.num_boardcards] self.raise_thres = THREE_RAISE_THRES_TABLE[self.num_boardcards] self.reraise_thres = THREE_RERAISE_THRES if self.num_boardcards == 0: equity = self.equity_table_3[self.my_hand] else: equity = pbots_calc.calc(':'.join([self.my_hand, 'xx', 'xx']), ''.join(received_packet['boardcards']), "", self.monte_carlo_iter) equity = equity.ev[0] do_reraise = random.random() > self.reraise_thres print 'EQUITY: ' + str(equity) if equity < self.fold_thres: # TODO: Implement bluffing / call here if CHECK in avail_actions: return CHECK return FOLD elif equity > self.raise_thres and (self.is_new_round or do_reraise): winning_factor = ((equity - self.fold_thres) / (1 - self.fold_thres))**POWER if BET in avail_actions: amount = self.bet_handler(winning_factor) return BET + ":" + str(amount) elif RAISE in avail_actions: amount = self.raise_handler(winning_factor) return RAISE + ":" + str(amount) if CALL in avail_actions: if self.should_call(equity): return CALL + ":" + str(self.call_amount) else: return FOLD return CHECK