class Table(object): #BLIND_INCREMENTS = [[10,25],[25,50],[50,100],[75,150],[100,200],[150,300],[200,400],[300,600],[400,800],[500,10000],[600,1200],[800,1600],[1000,2000]] BLIND_INCREMENTS = [[3, 6], [25, 50], [50, 100], [75, 150], [100, 200], [150, 300], [200, 400], [300, 600], [400, 800], [500, 10000], [600, 1200], [800, 1600], [1000, 2000]] def __init__(self, seats=8, quiet=False, training=False): self._blind_index = 0 [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[0] self._deck = Deck() self._evaluator = Evaluator() self.community = [] self._round = 0 self._button = 0 self._discard = [] self._side_pots = [0] * seats self._current_sidepot = 0 # index of _side_pots self._totalpot = 0 self._tocall = 0 self._lastraise = 0 self._number_of_hands = 0 # fill seats with dummy players self._seats = [ Player(-1, -1, 0, 'empty', 0, True) for _ in range(seats) ] self.emptyseats = seats self._player_dict = {} self.teacher = xmlrpc.client.ServerProxy('http://0.0.0.0:8080') self._quiet = quiet self._training = training self._run_thread = Thread(target=self.run, args=()) self._run_thread.daemon = True def start(self): self._run_thread.start() def run(self): while True: self.run_game() def run_game(self): self.ready_players() # for p in self._seats: # print('Player ',p.playerID, ' playing hand: ', p.playing_hand, 'sitting out', p.sitting_out) players = [ player for player in self._seats if not player.emptyplayer and not player.sitting_out ] self._number_of_hands = 1 # start hand if table full # if len(players) == len(self._seats): [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[0] # keep playing until there's a single player (shotgun style) while (self.emptyseats < len(self._seats) - 1): # answer = input('Press [enter] to start a game:') # if not answer: self.start_hand(players) self._number_of_hands += 1 if not self._quiet: print('Starting game number: ', self._number_of_hands) for p in self._seats: if p.playing_hand: print('Player ', p.playerID, ' stack size: ', p.stack) # increment blinds every 15 hands (based on avg hands/hour of 30) # if (self._number_of_hands % 15) == 0 and self._number_of_hands < 60: # self.increment_blinds() if len([p for p in players if p.playing_hand]) == 1: winner = [p for p in players if p.playing_hand][0] if self._training: self.teacher.add_winner(winner.server.get_ai_id()) break if self._number_of_hands == 200: print('no winner in 200 hands') break def start_hand(self, players): players = [p for p in players if p.playing_hand] assert sum([p.stack for p in players]) == 2000 * len(self._seats) self.new_round() self._round = 0 player = self._first_to_act(players) self.post_smallblind(player) player = self._next(players, player) self.post_bigblind(player) player = self._next(players, player) self._tocall = self._bigblind # rounds self._round = 0 while self._round < 4 and len(players) > 1: if self._round == 0: self.deal() elif self._round == 1: self.flop() elif self._round == 2: self.turn() elif self._round == 3: self.river() folded_players = [] while not player.playedthisround and len( [p for p in players if not p.isallin]) >= 1: if player.isallin: # print('player ', player.playerID, 'is all in, skipping their turn') player = self._next(players, player) continue # print('requesting move from ',player.playerID) move = player.server.player_move( self.output_state(player)) # MAKE PLAYER MOVE! if move[0] == 'call': self.player_bet(player, self._tocall) if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) elif move[0] == 'check': self.player_bet(player, player.currentbet) if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) elif move[0] == 'raise': self.player_bet(player, move[1] + player.currentbet) if not self._quiet: print('Player', player.playerID, move) for p in players: if p != player: p.playedthisround = False player = self._next(players, player) elif move[0] == 'fold': player.playing_hand = False folded_player = player if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) players.remove(folded_player) folded_players.append(folded_player) # break if a single player left if len(players) == 1: break player = self._first_to_act(players) self.resolve_sidepots(players + folded_players) self.new_round() if not self._quiet: print('totalpot', self._totalpot) assert sum([p.stack for p in self._seats ]) + self._totalpot == 2000 * len(self._seats) self.resolve_game(players) self.reset() def increment_blinds(self): self._blind_index = min(self._blind_index + 1, len(Table.BLIND_INCREMENTS) - 1) [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[self._blind_index] def post_smallblind(self, player): if not self._quiet: print('player ', player.playerID, 'small blind', self._smallblind) self.player_bet(player, self._smallblind) player.playedthisround = False def post_bigblind(self, player): if not self._quiet: print('player ', player.playerID, 'big blind', self._bigblind) self.player_bet(player, self._bigblind) player.playedthisround = False self._lastraise = self._bigblind def player_bet(self, player, total_bet): # relative_bet is how much _additional_ money is the player betting this turn, on top of what they have already contributed # total_bet is the total contribution by player to pot in this round relative_bet = min(player.stack, total_bet - player.currentbet) player.bet(relative_bet + player.currentbet) self._totalpot += relative_bet self._tocall = max(self._tocall, total_bet) if self._tocall > 0: self._tocall = max(self._tocall, self._bigblind) self._lastraise = max(self._lastraise, relative_bet - self._lastraise) def _first_to_act(self, players): if self._round == 0 and len(players) == 2: return self._next( sorted(players + [self._seats[self._button]], key=lambda x: x.get_seat()), self._seats[self._button]) try: first = [ player for player in players if player.get_seat() > self._button ][0] except IndexError: first = players[0] return first def _next(self, players, current_player): idx = players.index(current_player) return players[(idx + 1) % len(players)] def deal(self): for player in self._seats: if player.playing_hand: player.hand = self._deck.draw(2) def flop(self): self._discard.append(self._deck.draw(1)) #burn self.community = self._deck.draw(3) def turn(self): self._discard.append(self._deck.draw(1)) #burn self.community.append(self._deck.draw(1)) def river(self): self._discard.append(self._deck.draw(1)) #burn self.community.append(self._deck.draw(1)) def add_player(self, host, port, playerID, name, stack): if playerID not in self._player_dict: new_player = Player(host, port, playerID, name, stack) for i, player in enumerate(self._seats): if player.emptyplayer: self._seats[i] = new_player new_player.set_seat(i) break self._player_dict[playerID] = new_player self.emptyseats -= 1 def ready_players(self): if len([p for p in self._seats if not p.emptyplayer and p.sitting_out]) == len(self._seats): for p in self._seats: if not p.emptyplayer: p.sitting_out = False p.playing_hand = True def remove_player(self, playerID): try: idx = self._seats.index(self._player_dict[playerID]) self._seats[idx] = Player(-1, -1, 0, 'empty', 0, True) del self._player_dict[playerID] self.emptyseats += 1 except ValueError: pass def resolve_sidepots(self, players_playing): players = [p for p in players_playing if p.currentbet] if not self._quiet: print('current bets: ', [p.currentbet for p in players]) print('playing hand: ', [p.playing_hand for p in players]) if not players: return try: smallest_bet = min( [p.currentbet for p in players if p.playing_hand]) except ValueError: for p in players: self._side_pots[self._current_sidepot] += p.currentbet p.currentbet = 0 return smallest_players_allin = [ p for p, bet in zip(players, [p.currentbet for p in players]) if bet == smallest_bet and p.isallin ] for p in players: self._side_pots[self._current_sidepot] += min( smallest_bet, p.currentbet) p.currentbet -= min(smallest_bet, p.currentbet) p.lastsidepot = self._current_sidepot if smallest_players_allin: self._current_sidepot += 1 self.resolve_sidepots(players) if not self._quiet: print('sidepots: ', self._side_pots) def new_round(self): for player in self._player_dict.values(): player.currentbet = 0 player.playedthisround = False self._round += 1 self._tocall = 0 self._lastraise = 0 def resolve_game(self, players): # print('Community cards: ', end='') # Card.print_pretty_cards(self.community) if len(players) == 1: players[0].refund(sum(self._side_pots)) # print('Player', players[0].playerID, 'wins the pot (',sum(self._side_pots),')') self._totalpot = 0 else: # compute hand ranks print("Board: ", Card.print_pretty_cards(self.community)) for player in players: player.handrank = self._evaluator.evaluate( player.hand, self.community) print("Player: {}".format(player.playerID), Card.print_pretty_cards(player.hand)) # trim side_pots to only include the non-empty side pots temp_pots = [pot for pot in self._side_pots if pot > 0] # compute who wins each side pot and pay winners for pot_idx, _ in enumerate(temp_pots): # print('players last pots', [(p.playerID, p.lastsidepot) for p in players]) # find players involved in given side_pot, compute the winner(s) pot_contributors = [ p for p in players if p.lastsidepot >= pot_idx ] winning_rank = min([p.handrank for p in pot_contributors]) winning_players = [ p for p in pot_contributors if p.handrank == winning_rank ] for player in winning_players: split_amount = int(self._side_pots[pot_idx] / len(winning_players)) if not self._quiet: print( 'Player', player.playerID, 'wins side pot (', int(self._side_pots[pot_idx] / len(winning_players)), ')') player.refund(split_amount) self._side_pots[pot_idx] -= split_amount # any remaining chips after splitting go to the winner in the earliest position if self._side_pots[pot_idx]: earliest = self._first_to_act( [player for player in winning_players]) earliest.refund(self._side_pots[pot_idx]) def reset(self): for player in self._seats: if not player.emptyplayer and not player.sitting_out: player.reset_hand() self.community = [] self._current_sidepot = 0 self._totalpot = 0 self._side_pots = [0] * len(self._seats) self._deck.shuffle() self._button = (self._button + 1) % len(self._seats) while not self._seats[self._button].playing_hand: self._button = (self._button + 1) % len(self._seats) def output_state(self, current_player): return { 'players': [player.player_state() for player in self._seats], 'community': self.community, 'my_seat': current_player.get_seat(), 'pocket_cards': current_player.hand, 'pot': self._totalpot, 'button': self._button, 'tocall': (self._tocall - current_player.currentbet), 'stack': current_player.stack, 'bigblind': self._bigblind, 'playerID': current_player.playerID, 'lastraise': self._lastraise, 'minraise': max(self._bigblind, self._lastraise + self._tocall) }
if __name__ = '__main__': a = Analyzer() a.monte_carlo_rounds = 2000 nn = NeuralNetwork([206,206,100,1], 'analyzer_network', 0.1) deck = Deck() hand = [] community = [] opponents = 0 game_stage = [0,3,4,5] for i in range(1000000): community = [] deck.shuffle() a.reset() hand = deck.draw(2) for _ in range(game_stage[random.randint(0,3)]): community.append(deck.draw(1)) opponents = random.randint(1,7) a.set_num_opponents(opponents) a.set_pocket_cards(*hand) for card in community: a.community_card(card) mc_response = a.analyze() # print('mc response', mc_response)
class Analyzer: def __init__(self): self.deck = Deck() self.reset() self._evaluator = Evaluator() self.monte_carlo_rounds = 1000 def reset(self): self.deck.shuffle() self.hole_cards = [] self.community_cards = [] def set_num_opponents(self, n): self.num_opponents = n def set_monte_carlo_rounds(self, n): self.monte_carlo_rounds = n def set_pocket_cards(self, card1, card2): self.deck.remove(card1) self.deck.remove(card2) self.hole_cards.append(card1) self.hole_cards.append(card2) def community_card(self, card): self.deck.remove(card) self.community_cards.append(card) def analyze(self): wins = 0 ties = 0 to_flop = 5 - len(self.community_cards) to_draw = to_flop + 2 * self.num_opponents total_rounds = self.monte_carlo_rounds total_opponent_hands = total_rounds * self.num_opponents # Monte Carlo simulation for _ in range(self.monte_carlo_rounds): # Draw a uniformly random combination of unseen cards (remaining # community cards + 2 hole cards per opponent) drawn_cards = self.deck.sample(to_draw) all_comms = self.community_cards + drawn_cards[:to_flop] my_ranking = self._evaluator.evaluate(self.hole_cards, all_comms) # 2: win, 1: tie, 0: loss winner = 2 for i in range(self.num_opponents): their_cards = drawn_cards[to_flop + 2 * i:to_flop + 2 * i + 2] their_ranking = self._evaluator.evaluate( their_cards, all_comms) if my_ranking > their_ranking: winner = 0 elif my_ranking == their_ranking: winner = min(winner, 1) if winner == 2: wins += 1 elif winner == 1: ties += 1 win_ratio = wins / total_rounds return win_ratio
class Table(object): BLIND_INCREMENTS = [[10,25],[25,50],[50,100],[75,150],[100,200],[150,300],[200,400],[300,600],[400,800],[500,10000],[600,1200],[800,1600],[1000,2000]] def __init__(self, seats = 8, quiet = False, training = False): self._blind_index = 0 [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[0] self._deck = Deck() self._evaluator = Evaluator() self.community = [] self._round = 0 self._button = 0 self._discard = [] self._side_pots = [0]*seats self._current_sidepot = 0 # index of _side_pots self._totalpot = 0 self._tocall = 0 self._lastraise = 0 self._number_of_hands = 0 # fill seats with dummy players self._seats = [Player(-1,-1,0,'empty',0,True) for _ in range(seats)] self.emptyseats = seats self._player_dict = {} self.teacher = xmlrpc.client.ServerProxy('http://0.0.0.0:8080') self._quiet = quiet self._training = training self._run_thread = Thread(target = self.run, args=()) self._run_thread.daemon = True def start(self): self._run_thread.start() def run(self): while True: self.run_game() def run_game(self): self.ready_players() # for p in self._seats: # print('Player ',p.playerID, ' playing hand: ', p.playing_hand, 'sitting out', p.sitting_out) players = [player for player in self._seats if not player.emptyplayer and not player.sitting_out] self._number_of_hands = 1 # start hand if table full # if len(players) == len(self._seats): [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[0] # keep playing until there's a single player (shotgun style) while(self.emptyseats < len(self._seats)-1): # answer = input('Press [enter] to start a game:') # if not answer: self.start_hand(players) self._number_of_hands += 1 if not self._quiet: print('Starting game number: ', self._number_of_hands) for p in self._seats: if p.playing_hand: print('Player ',p.playerID, ' stack size: ', p.stack) # increment blinds every 15 hands (based on avg hands/hour of 30) if (self._number_of_hands % 15) == 0 and self._number_of_hands < 60: self.increment_blinds() if len([p for p in players if p.playing_hand]) == 1: winner = [p for p in players if p.playing_hand][0] if self._training: self.teacher.add_winner(winner.server.get_ai_id()) break if self._number_of_hands == 200: print('no winner in 200 hands') break def start_hand(self, players): players = [p for p in players if p.playing_hand] assert sum([p.stack for p in players]) == 2000*len(self._seats) self.new_round() self._round=0 player = self._first_to_act(players) self.post_smallblind(player) player = self._next(players, player) self.post_bigblind(player) player = self._next(players, player) self._tocall = self._bigblind # rounds self._round = 0 while self._round<4 and len(players)>1: if self._round == 0: self.deal() elif self._round == 1: self.flop() elif self._round == 2: self.turn() elif self._round ==3: self.river() folded_players = [] while not player.playedthisround and len([p for p in players if not p.isallin]) >=1: if player.isallin: # print('player ', player.playerID, 'is all in, skipping their turn') player = self._next(players, player) continue # print('requesting move from ',player.playerID) move = player.server.player_move(self.output_state(player)) if move[0] == 'call': self.player_bet(player, self._tocall) if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) elif move[0] == 'check': self.player_bet(player, player.currentbet) if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) elif move[0] == 'raise': self.player_bet(player, move[1]+player.currentbet) if not self._quiet: print('Player', player.playerID, move) for p in players: if p != player: p.playedthisround = False player = self._next(players, player) elif move[0] == 'fold': player.playing_hand = False folded_player = player if not self._quiet: print('Player', player.playerID, move) player = self._next(players, player) players.remove(folded_player) folded_players.append(folded_player) # break if a single player left if len(players) ==1: break player = self._first_to_act(players) self.resolve_sidepots(players + folded_players) self.new_round() if not self._quiet: print('totalpot', self._totalpot) assert sum([p.stack for p in self._seats]) + self._totalpot == 2000*len(self._seats) self.resolve_game(players) self.reset() def increment_blinds(self): self._blind_index = min(self._blind_index+1,len(Table.BLIND_INCREMENTS)-1) [self._smallblind, self._bigblind] = Table.BLIND_INCREMENTS[self._blind_index] def post_smallblind(self, player): if not self._quiet: print('player ', player.playerID, 'small blind', self._smallblind) self.player_bet(player, self._smallblind) player.playedthisround = False def post_bigblind(self, player): if not self._quiet: print('player ', player.playerID, 'big blind', self._bigblind) self.player_bet(player, self._bigblind) player.playedthisround = False self._lastraise = self._bigblind def player_bet(self, player, total_bet): # relative_bet is how much _additional_ money is the player betting this turn, on top of what they have already contributed # total_bet is the total contribution by player to pot in this round relative_bet = min(player.stack, total_bet - player.currentbet) player.bet(relative_bet + player.currentbet) self._totalpot += relative_bet self._tocall = max(self._tocall, total_bet) if self._tocall >0: self._tocall = max(self._tocall, self._bigblind) self._lastraise = max(self._lastraise, relative_bet - self._lastraise) def _first_to_act(self, players): if self._round == 0 and len(players) == 2: return self._next(sorted(players + [self._seats[self._button]], key=lambda x:x.get_seat()), self._seats[self._button]) try: first = [player for player in players if player.get_seat() > self._button][0] except IndexError: first = players[0] return first def _next(self, players, current_player): idx = players.index(current_player) return players[(idx+1) % len(players)] def deal(self): for player in self._seats: if player.playing_hand: player.hand = self._deck.draw(2) def flop(self): self._discard.append(self._deck.draw(1)) #burn self.community = self._deck.draw(3) def turn(self): self._discard.append(self._deck.draw(1)) #burn self.community.append(self._deck.draw(1)) def river(self): self._discard.append(self._deck.draw(1)) #burn self.community.append(self._deck.draw(1)) def add_player(self, host, port, playerID, name, stack): if playerID not in self._player_dict: new_player = Player(host, port, playerID, name, stack) for i,player in enumerate(self._seats): if player.emptyplayer: self._seats[i] = new_player new_player.set_seat(i) break self._player_dict[playerID] = new_player self.emptyseats -= 1 def ready_players(self): if len([p for p in self._seats if not p.emptyplayer and p.sitting_out]) == len(self._seats): for p in self._seats: if not p.emptyplayer: p.sitting_out = False p.playing_hand = True def remove_player(self, playerID): try: idx = self._seats.index(self._player_dict[playerID]) self._seats[idx] = Player(-1,-1,0,'empty',0,True) del self._player_dict[playerID] self.emptyseats += 1 except ValueError: pass def resolve_sidepots(self, players_playing): players = [p for p in players_playing if p.currentbet] if not self._quiet: print('current bets: ', [p.currentbet for p in players]) print('playing hand: ', [p.playing_hand for p in players]) if not players: return try: smallest_bet = min([p.currentbet for p in players if p.playing_hand]) except ValueError: for p in players: self._side_pots[self._current_sidepot] += p.currentbet p.currentbet = 0 return smallest_players_allin = [p for p,bet in zip(players, [p.currentbet for p in players]) if bet == smallest_bet and p.isallin] for p in players: self._side_pots[self._current_sidepot] += min(smallest_bet, p.currentbet) p.currentbet -= min(smallest_bet, p.currentbet) p.lastsidepot = self._current_sidepot if smallest_players_allin: self._current_sidepot += 1 self.resolve_sidepots(players) if not self._quiet: print('sidepots: ', self._side_pots) def new_round(self): for player in self._player_dict.values(): player.currentbet = 0 player.playedthisround = False self._round += 1 self._tocall = 0 self._lastraise = 0 def resolve_game(self, players): # print('Community cards: ', end='') # Card.print_pretty_cards(self.community) if len(players)==1: players[0].refund(sum(self._side_pots)) # print('Player', players[0].playerID, 'wins the pot (',sum(self._side_pots),')') self._totalpot = 0 else: # compute hand ranks for player in players: player.handrank = self._evaluator.evaluate(player.hand, self.community) # trim side_pots to only include the non-empty side pots temp_pots = [pot for pot in self._side_pots if pot>0] # compute who wins each side pot and pay winners for pot_idx,_ in enumerate(temp_pots): # print('players last pots', [(p.playerID, p.lastsidepot) for p in players]) # find players involved in given side_pot, compute the winner(s) pot_contributors = [p for p in players if p.lastsidepot >= pot_idx] winning_rank = min([p.handrank for p in pot_contributors]) winning_players = [p for p in pot_contributors if p.handrank == winning_rank] for player in winning_players: split_amount = int(self._side_pots[pot_idx]/len(winning_players)) if not self._quiet: print('Player', player.playerID, 'wins side pot (',int(self._side_pots[pot_idx]/len(winning_players)),')') player.refund(split_amount) self._side_pots[pot_idx] -= split_amount # any remaining chips after splitting go to the winner in the earliest position if self._side_pots[pot_idx]: earliest = self._first_to_act([player for player in winning_players]) earliest.refund(self._side_pots[pot_idx]) def reset(self): for player in self._seats: if not player.emptyplayer and not player.sitting_out: player.reset_hand() self.community = [] self._current_sidepot = 0 self._totalpot = 0 self._side_pots = [0]*len(self._seats) self._deck.shuffle() self._button = (self._button + 1) % len(self._seats) while not self._seats[self._button].playing_hand: self._button = (self._button + 1) % len(self._seats) def output_state(self, current_player): return {'players':[player.player_state() for player in self._seats], 'community':self.community, 'my_seat':current_player.get_seat(), 'pocket_cards':current_player.hand, 'pot':self._totalpot, 'button':self._button, 'tocall':(self._tocall-current_player.currentbet), 'stack':current_player.stack, 'bigblind':self._bigblind, 'playerID':current_player.playerID, 'lastraise':self._lastraise, 'minraise':max(self._bigblind, self._lastraise + self._tocall)}