def __init__(self, players, little_blind, big_blind, deck_seed): """Initialize a new Round. Arguments: players -- a list of Player objects, representing the individual players that will be in the round little_blind -- amount of chips that must be bet by the little blind big_blind -- amount of chips that must be bet by the big blind deck_seed -- the seed which will be used to generate the psuedorandom deck ordering, as well as the round's unique ID which will can be used to verify the deck ordering after a game ends """ self.round_id = self.generate_id(deck_seed) self.logger = self.get_logger() self.logger.info("Initializing Round") self.logger.info("Deck seed: " + binascii.hexlify(deck_seed)) self.logger.info("Players:") self.players = players for player in self.players: self.logger.info(str(player)) player.game_round = self self.table = [] self.pots = [Pot(0, 0)] self.action_position = 0 self.deck = Deck(deck_seed) self.logger.info(str(self.deck)) self.logger.info("Little blind: " + str(little_blind)) self.little_blind = little_blind self.logger.info("Big blind: " + str(big_blind)) self.big_blind = big_blind self.blinds_in = False self.latest_action = None self.logger.info("Round initialized")
class Round: def __init__(self, players, little_blind, big_blind, deck_seed): """Initialize a new Round. Arguments: players -- a list of Player objects, representing the individual players that will be in the round little_blind -- amount of chips that must be bet by the little blind big_blind -- amount of chips that must be bet by the big blind deck_seed -- the seed which will be used to generate the psuedorandom deck ordering, as well as the round's unique ID which will can be used to verify the deck ordering after a game ends """ self.round_id = self.generate_id(deck_seed) self.logger = self.get_logger() self.logger.info("Initializing Round") self.logger.info("Deck seed: " + binascii.hexlify(deck_seed)) self.logger.info("Players:") self.players = players for player in self.players: self.logger.info(str(player)) player.game_round = self self.table = [] self.pots = [Pot(0, 0)] self.action_position = 0 self.deck = Deck(deck_seed) self.logger.info(str(self.deck)) self.logger.info("Little blind: " + str(little_blind)) self.little_blind = little_blind self.logger.info("Big blind: " + str(big_blind)) self.big_blind = big_blind self.blinds_in = False self.latest_action = None self.logger.info("Round initialized") def get_logger(self): """Return the logger which this round will use""" return loggers.get_round_logger(self.round_id[:8]) def generate_id(self, seed): """Create a unique ID for the round based on the round seed Arguments: seed -- the round's unique seed string """ hf = hashlib.sha256() hf.update(seed) return hf.hexdigest() def round_is_over(self): """Return whether the round is over. Returns False if more than one player is still in the round, and True otherwise """ self.logger.debug("Checking if > 1 player is left in the round") players_in_round = 0 for player in self.players: if player.in_round: players_in_round = players_in_round + 1 if players_in_round >= 2: self.logger.debug("There is more than one player in the round") return False else: self.logger.debug("There is not more than one player in the round") return True def betting_is_over(self): """Return whether there are enough players in the round with chips to continue betting""" self.logger.info("Checking if more than one player is in the round" + " and has chips") players_that_can_bet = 0 for player in self.players: if player.in_round and not player.all_in: players_that_can_bet = players_that_can_bet + 1 self.logger.debug("Number of players that can bet: " + str(players_that_can_bet)) if players_that_can_bet >= 2: return False else: return True def get_player_by_id(self, bcc_id): """Return the Player object with the given bcc_id if it exists or else None Arguments: bcc_id -- a string representing a player's unique BCC ID """ self.logger.debug("Trying to find a player with the id %s", bcc_id) for player in self.players: if player.user.bcc_id == bcc_id: self.logger.debug("Found a player with the id %s", bcc_id) return player self.logger.debug("Did not find a player with the id %s", bcc_id) return None def get_top_card(self): """Return the top card from the deck.""" card = self.deck.get_next_card() self.logger.info("Taking a card off the deck: " + str(card)) return card def get_player_with_action(self): """Return the player which the action is to.""" player = self.players[self.action_position] self.logger.debug("Returning the player with action: " + str(player)) return player def player_with_action_id(self): """Return the bcc_id of the player with the action""" return self.get_player_with_action().user.bcc_id def advance_to_next_player(self): """Move the action to the next player on the table.""" self.logger.debug("Moving the action to the next table position") self.action_position = (self.action_position + 1) % len(self.players) def deal_hands(self): """Deal a hand to each player.""" for i in range(2): for player in self.players: self.logger.info("Dealing card to %s", str(player)) player.deal_card(self.get_top_card()) def deal_flop(self): """Deal the flop.""" self.logger.info("Burying a card") self.get_top_card() for i in range(3): self.logger.info("Putting a card on the table") self.table.append(self.get_top_card()) def deal_turn(self): """Deal the turn.""" self.logger.info("Burying a card") self.get_top_card() self.logger.info("Putting a card on the table") self.table.append(self.get_top_card()) def deal_river(self): """Deal the river.""" self.logger.info("Burying a card") self.get_top_card() self.logger.info("Putting a card on the table") self.table.append(self.get_top_card()) def do_betting_round(self): """Do a betting round.""" self.logger.info("Starting a betting round") raised_in_round = 0 # amount each player must put in to stay in chips_in_round = 0 # amount of chips that have been put in round for player in self.players: player.chips_in_round = 0 self.logger.info("Resetting the amount of chips each player has in" + " the round to zero") if not self.blinds_in: little = self.get_player_with_action() little.bet_blind(self.little_blind) self.logger.info("The small blind has been bet") chips_in_round = self.little_blind raised_in_round = self.little_blind self.advance_to_next_player() big = self.get_player_with_action() big.bet_blind(self.big_blind) self.logger.info("The big blind has been bet") chips_in_round = chips_in_round + self.big_blind raised_in_round = self.big_blind self.advance_to_next_player() self.blinds_in = True starting_betting = True betting_ends_on = self.get_player_with_action() self.logger.info("Starting the betting") # starting_betting is true before the first player in the round has # performed an action. # self.betting_is_over is true when only one player can bet. # starting_betting should always mean the loop is entered. # betting_ends_on is initially set to the first player who would # have action if he was in the round, and can also be set to the last # player to have raised. # The betting round should be exited out of early if there is only # one player left who hasn't folded or if there is only one player # left who can play, and no one has raised anything in the round. while starting_betting or ( betting_ends_on != self.get_player_with_action() and (not (self.betting_is_over() and raised_in_round == 0)) and (not self.round_is_over()) ): starting_betting = False player = self.get_player_with_action() self.logger.info("Moving action to " + str(player)) action = player.get_action(raised_in_round, self.big_blind) if action.choice == Action.VOID: self.logger.info("Void action") else: if action.choice == Action.RAISE: new_chips_in = action.amount raised_in_round = player.chips_in_round chips_in_round = chips_in_round + new_chips_in betting_ends_on = self.get_player_with_action() elif action.choice == Action.CALL: new_chips_in = action.amount chips_in_round = chips_in_round + new_chips_in elif action.choice == Action.CHECK: pass elif action.choice == Action.FOLD: pass self.latest_action = action self.logger.info("The action has been applied") self.advance_to_next_player() self.logger.info("Done with this player, moving to the next") self.logger.info("Done with the betting. Move on to sorting out pots.") players_in = filter(lambda p: p.chips_in_round > 0, self.players) ordered = sorted(players_in, key=lambda p: p.chips_in_round) self.logger.info("Sorting players still in by number of chips" + " that were played in this round.") pot = self.pots[len(self.pots) - 1] for p in ordered: if p.all_in and p.chips_in_round > 0: self.logger.info( "Found a player who is all in and has chips" + " in the round. This will cause the current " + "pot to be finalized." ) pot.chips_to_win = p.chips_in_pot self.logger.debug( "Set the minimum number of chips needed to" + " win the current pot to the number of chips " + "this player has put into all of the pots." ) chips_for_pot = p.chips_in_round # chips_for_pot is the number of chips that will be put in this # pot by each player for o in players_in: if o.chips_in_round != 0: if o.chips_in_round < chips_for_pot: # this player has folded and has less chips to # put in than the player who this pot is being # created for has, so they all go into this pot pot.chips = pot.chips + o.chips_in_round chips_in_round = chips_in_round - o.chips_in_round o.chips_in_round = 0 else: o.chips_in_round = o.chips_in_round - chips_for_pot pot.chips = pot.chips + chips_for_pot chips_in_round = chips_in_round - chips_for_pot self.logger.debug("Put %i chips in this pot from" + " %s", chips_for_pot, str(player)) self.logger.info("Finalized the pot, creating the next pot") pot = Pot(0, 0) self.pots.append(pot) self.logger.info("Putting the remaining chips from the round into " + "the current open pot") pot.chips = pot.chips + chips_in_round def reveal_hands(self): """Reveal the hands of all players who are still in the round""" for player in self.players: if player.in_round: player.cards_revealed = True def get_player_best_hand(self, player): """Return the best poker hand from the 7 cards available to the player. Argument: player -- a Player object """ available_cards = [] available_cards.extend(self.table) available_cards.extend(player.cards) self.logger.debug("All cards available to %s: %s", str(player), available_cards) assert len(available_cards) == 7 hands = [] for hand in combinations(available_cards, 5): hands.append(get_category(hand)) sorted_hands = sorted(hands, reverse=True) self.logger.debug("Best hand: %s", sorted_hands[0]) return sorted_hands[0] def rank_players(self): """Return the players still in the round ordered by their hands.""" players_in = [] for player in self.players: if player.in_round: players_in.append((player, self.get_player_best_hand(player))) return sorted(players_in, key=lambda player: player[1], reverse=True) def tie_break_winners(self, winners): """Return the player who wins the tie-breaker from a list of winners. Argument: winners -- a list of Player objects who have the same best hand """ highest_card_hash = 0 player_with_highest_card = None for winner in winners: for card in winner.cards: if card.__hash__ > highest_card_hash: highest_card_hash = card.__hash__ player_with_highest_card = winner return player_with_highest_card def award_chips(self): """Award chips to the players who won the pots.""" ordered_winners = [] # a list of tuples of players with their winning # hands in order, player with best hand at [0] if self.round_is_over(): # game ended with only one player left in, so give all chips to # that player for player in self.players: if player.in_round: assert ordered_winners == [] ordered_winners = [(player, None)] else: ordered_winners = self.rank_players() self.logger.info("Ordered winners by hands:") for player in ordered_winners: self.logger.info(str(player[0].user.bcc_id)) self.logger.info("Handing out chips") for pot in self.pots: self.logger.info("Handing out chips for %s", str(pot)) iterator = iter(ordered_winners) for player, hand in iterator: if player.chips_in_pot >= pot.chips_to_win: winners = [player] self.logger.debug("winner of pot: %s", str(player)) for next_player, next_hand in iterator: if hand == next_hand and next_player.chips_in_pot >= pot.chips_to_win: winners.append(next_player) self.logger.debug("%s has tied", str(next_player)) self.logger.debug("winners of pot: %s", winners) chips_per_winner = pot.chips / len(winners) self.logger.debug("chips per winner: %d", chips_per_winner) for winner in winners: winner.chips = winner.chips + chips_per_winner pot.chips = pot.chips - chips_per_winner assert pot.chips >= 0 self.logger.debug("gave %d chips to %s", chips_per_winner, str(winner)) if pot.chips != 0: self.logger.debug("there are %d chips left over", pot.chips) winner = self.tie_break_winners(winners) self.logger.debug("winner of tie-breaking procedure:" + " %s", winner) winner.chips = winner.chips + pot.chips pot.chips = 0 if pot.chips != 0: self.logger.info( "Chips were left over in this pot" + " for the administrator! How kind." + " Chips left over: %i", pot.chips, ) self.logger.info("finished dealing out chips for this pot") break def play(self): """Play the round.""" self.deal_hands() if not self.round_is_over(): if self.betting_is_over(): self.reveal_hands() else: self.action_position = 0 self.do_betting_round() if not self.round_is_over(): self.deal_flop() if self.betting_is_over(): self.reveal_hands() else: self.action_position = 0 self.do_betting_round() if not self.round_is_over(): self.deal_turn() if self.betting_is_over(): self.reveal_hands() else: self.action_position = 0 self.do_betting_round() if not self.round_is_over(): self.deal_river() if self.betting_is_over(): self.reveal_hands() else: self.action_position = 0 self.do_betting_round() if not self.round_is_over(): self.reveal_hands() self.award_chips() self.logger.info("the round is over")