I am writing a poker application and trying to figure out how to deal with split pots and side pots
There are four rounds of betting and each round of betting can have multiple orbits
Can only bet the chips in front of you
So if a player is all in a side pot is created
Hands can tie – in which case the pot is split
If it does not spit evenly the ‘last aggressor (raise)’ gets the odd chip(s)
If you are raised you must call or fold
If you fold you lose your bets even if the hand wins
You cannot just cap you betting and create a side pot
You only create a side pot if you are all in
PlayerA all in 40 Win
PlayerB 80 second
PlayerC 80 last
Player A would take 120 (win 80 as 40 was his money in)
Player B would take 80
But you don’t know the win order until the end
And when PlayerA went in you don’t know how many people are going to call
What could be good algorithm for this?
This code is C# but really just looking for an approach.
My initial thought is to just record all the betting for each player
street, orbit, bet
Where that gets messy is that in a single orbit you could have multiple players all in for different amounts (bet)
And a different amount is not just all in
Player could be raised and then fold
In that case you know they lose but you still don’t know who they lose to
There are rarely more than 3 orbits in a round
You could assume there would never be more than 10 as with min raise players would run out of chip
12
Here is a working code to create and distribute winnings from side-pots.
class Player:
def __init__(self, l, i, hs):
self.live = l #False
self.invested = i #0
self.hand_strength = hs #100
self.result = 0
def distribute(pot, players):
for p in players:
p.result= -p.invested # invested money is lost originally
# while there are still players with money
# we build a side-pot matching the lowest stack and distribute money to winners
while len(players)>1 :
min_stack = min([p.invested for p in players])
pot += min_stack * len(players)
for p in players:
p.invested -= min_stack
max_hand = max([p.hand_strength for p in players if p.live])
winners = [p for p in players if p.hand_strength == max_hand if p.live]
for p in winners:
p.result += pot / len(winners)
players = [p for p in players if p.invested > 0]
pot = 0
if len(players) == 1:
p = players[0]
# return uncalled bet
p.result += p.invested
This example shows a case where there are 5 players and the starting pot is 100.
The main pot is split between player who are allin for 20 and 80. One player has folded after investing 50 and his best hand is wasted. One player has overbet all others and his bet is returned to him while he loses only the amount matched by the other players (80)
players = [Player(True, 20, 100), Player(False, 50, 200), Player(True, 80, 90), Player(True, 80, 100), Player(True, 1000, 0)]
distribute(100, players)
for p in players:
print(p.result)
#80.0
#-50
#-80
#230.0
#-80
0
In my code, after every player has spoken, for every stake in the set of stakes of the players who are still in play, i create a pot. Then i order the pots from the smallest (that will be the main pot) to the highest (side pots).
The side pots are represented as a list, in this format:
[ needed stake, pot amount].
For the main pot, the “pot amount” will be equal to:
“needed stake” * number of players (including the ones who folded) whit a stake equal or higher than “needed stake”.
For the remaining pots, the “pot amount” will be equal to:
(“needed stake” – needed stake of the pot preceding this one in the list) * number of players whit a stake equal or higher than “needed stake”
The basic algorithm is the dead chips (folds) + the minimum committed chips are distributed among the winners of the hand (main pot). If after this, there are still chips left in the pot, the process is repeated (for every side pot).
So let’s say at the end of a hand there were three players all-in and $300 in dead chips:
P1 shoved $50, P2 shoved $100 and P3 shoved $150.
Scenario 1
If the hands were equal (3 way pot), everyone would get $150 in the first iteration.
In the next, P2 & P3 would collect an additional $50. In the final iteration, P3 would collect an additional $50.
So the winnings would be:
P1 $150
P2 $200
P3 $250
Scenario 2
Let us assume here that P1 wins.
P1 collects $450 (his $50 and $50 each from P2 & P3 + $300 dead chips).
In the next iteration, let’s say neither hand wins outright (board flush). P2 would collect side pot 1 ($50) and P3 would collect side pot 2 ($100).
10
Only track total bet by player and if they are folded or not
1) determine minimum bet from all player in the pot
and player not folded
2) remove that amount from the every player (including folded) bet
and sum it into a side-pot
3) based on hand strength divvy up that side pot to player stacks
if a split (tie) and there are odd chips then assign my hand position at the table
4) reset side-pot to zero (plus left over)
5) go to 1) if any player any player has any bet left
(a hand cannot end with every hand folded)
7
First determine if the round of betting has finished. i.e. all players have acted, AND EITHER all remaining players have met the price (the biggest bet) OR they have gone all-in. Only if the round of betting has finished do you start adding chips to pots.
Algorithm:
Each player has a stack, and a hasBet amount, which is the amount of money they’re betting before it gets added to a pot.
-
Take the lowest hasBet
-
For every player left in the hand, add that amount to the pot, and subtract it from the player’s hasBet
-
Are there at least 2 players left with hasBet > 0?
YES: start a new pot, go to (1)
NO: go to (4)
- Is this an all-in/cards-up situation? (i.e. all players are all-in, or all players are all in except one, so there are no more betting rounds).
YES: return any remaining hasBet chips to that player, deal all the remaining cards, and determine winners (multiple pots can mean multiple winners).
NO: deal the next card and move to the next round.
Example (python)
if all_in_situation:
add_chips_to_pot()
cards_up_scenario() # no more betting, deal all remaining cards
elif all_players_have_acted and all_players_have_met_price_or_shoved:
add_chips_to_pot()
move_to_next_stage() # stage being flop, turn, etc
else:
increment_action() # action moves on to the next player
def add_chips_to_pot(self):
lowest_bet = work_out_lowest_bet() # omitted for brevity
current_pot_index = len(self.pots) - 1 # i.e. if there are 2 pots, the one we want is at index 1
for player in self.players:
if player.has_bet == 0:
continue
self.pots[current_pot_index].amount = self.pots[current_pot_index].amount + lowest_bet
self.pots[current_pot_index].players.append(player)
player.has_bet = player.has_bet - lowest_bet
players_with_hasbet_remaining = work_this_out() # omitted for brevity
if players_with_hasbet_remaining > 1:
self.pots.append(Pot())
self.add_chips_to_pot()
else:
return
..where self.pots
is a list of Pot
s, and a Pot
has 2 properties: the pot amount and a list of players that are in that Pot, which we need when determining the winner of each pot. You can have whatever data structures you like to achieve the same thing.
Split pots
You’ll need a way of determining hand strength, which is beyond the scope of this question. To handle split pots we can say that each Pot
has a list of winners.
def determine_winners(self):
for pot in self.pots:
winners = [] # multiple winners if split pot
winning_score = 0
for player in pot.players:
if player.score == winning_score:
winners.append(player)
elif player.score > winning_score:
winners = []
winning_score = player.score
winners.append(player)
number_of_winners = len(pot.winners)
for winner in pot.winners:
winner.stack = winner.stack + (pot.amount / number_of_winners) # division assumes ints, not decimals
# return any spare chips from a split pot to the player in earliest position
spare = pot.amount % number_of_winners
self.players[0].stack = self.players[0].stack + spare
Note here that I’m returning spare chips left over from the split to the player in earliest position, which I believe is the common rule, but you can do whatever you want with them.