Skip to content

Commit

Permalink
Merge #222
Browse files Browse the repository at this point in the history
222: Improve transaction logic, fix SimpleRatio underflow r=charleskawczynski a=charleskawczynski

This PR improves the transaction logic and fixes SimpleRatio underflow. Closes #220, #200 and #223.

Co-authored-by: Charles Kawczynski <[email protected]>
  • Loading branch information
bors[bot] and charleskawczynski authored Aug 27, 2023
2 parents bb388b9 + e980218 commit d41353e
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TexasHoldem"
uuid = "6cef90fc-eb55-4a2a-97d0-7ecce2b738fe"
authors = ["Charles Kawczynski <[email protected]>"]
version = "0.3.2"
version = "0.4.0"

[deps]
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Expand Down
5 changes: 5 additions & 0 deletions src/chips.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export SimpleRatio
struct SimpleRatio
num::Int
den::Int
function SimpleRatio(num, den)
# https://github.com/charleskawczynski/TexasHoldem.jl/issues/223
_den = num == 0 ? 1 : den
return new(num, _den)
end
end

Base.:(+)(x::Int, y::SimpleRatio) = SimpleRatio(x*y.den + y.num, y.den)
Expand Down
16 changes: 9 additions & 7 deletions src/game.jl
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ function _deal_and_play!(game::Game, sf::StartFrom)
@cdebug logger "initial_∑brs = $(initial_∑brs)"
@cdebug logger "sum(bank_roll.(players)) = $(sum(bank_roll.(players)))"
@cdebug logger "initial_brs = $(initial_brs)"
@cdebug logger "bank_roll.(players) = $(bank_roll.(players))"
@cdebug logger "bank_roll_chips.(players) = $(bank_roll_chips.(players))"

if sf.game_point isa StartOfGame
if !(logger isa ByPassLogger)
Expand All @@ -346,12 +346,14 @@ function _deal_and_play!(game::Game, sf::StartFrom)
prof = bank_roll_chips(player) - initial_br
br = map(x->bank_roll_chips(x), players)
# TODO: this is broken due to https://github.com/charleskawczynski/TexasHoldem.jl/issues/200
# @assert prof ≤ mpp string("Over-winning occurred:\n",
# " Player $(name(player))\n",
# " Initial BRs $(initial_brs)\n",
# " BRs $br\n",
# " profit $prof\n",
# " max possible profit $mpp")
@assert prof mpp string("Over-winning occurred:\n",
" Player $(name(player))\n",
" Initial BRs $(initial_brs)\n",
" BRs $br\n",
" profit $prof\n",
" profit.n $(prof.n)\n",
" cond $(prof.n mpp.n)\n",
" max possible profit $mpp")
end

@cinfo logger "Final bank roll summary: $(bank_roll_chips.(players))"
Expand Down
45 changes: 26 additions & 19 deletions src/transactions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ of the (sorted) players at the start of the game.
function contribute!(table, player, amt, call=false)
tm = table.transactions
logger = table.logger
players = players_at_table(table)
@cdebug logger "$(name(player))'s bank roll (pre-contribute) = $(bank_roll(player))"
if !(0 amt bank_roll(player))
msg1 = "$(name(player)) has insufficient bank"
Expand All @@ -175,16 +176,19 @@ function contribute!(table, player, amt, call=false)

@inbounds for i in 1:length(tm.side_pots)
@assert 0 amt_remaining
cap_i = cap(tm.side_pots[i])
cond = amt_remaining < cap_i
amt_contrib = cond ? amt_remaining : cap_i
contributing = !side_pot_full(tm, i) && !(cap_i == 0)
# This is a bit noisy:
# @cdebug logger "$(name(player)) potentially contributing $amt_contrib to side-pot $(i) ($cond). cap_i=$cap_i, amt_remaining=$amt_remaining"
@cdebug logger "contributing, amt_contrib = $contributing, $amt_contrib"
contributing || continue
@assert !(amt_contrib == 0)
tm.side_pots[i].amts[seat_number(player)] += amt_contrib
sp = tm.side_pots[i]
sn = seat_number(player)
if is_player_contribution_to_side_pot_full(sp, sn)
continue
end
@assert 0 < amt_remaining "amt_remaining: $amt_remaining"
amt_contrib = if contribution_fits_in_sidepot(sp, sn, amt_remaining)
amt_remaining
else
contribution_that_fits_in_sidepot(sp, sn)
end
@assert 0 < amt_contrib
sp.amts[sn] += amt_contrib
player.bank_roll -= amt_contrib
amt_remaining -= amt_contrib
amt_remaining == 0 && break
Expand All @@ -196,24 +200,27 @@ function contribute!(table, player, amt, call=false)
player.action_required = false
end

if is_side_pot_full(tm, table, player, call)
if is_side_pot_full(tm, players)
increment_pot_id!(tm)
end
@cdebug logger "$(name(player))'s bank roll (post-contribute) = $(bank_roll(player))"
@cdebug logger "all_in($(name(player))) = $(all_in(player))"
@cdebug logger "post-contribute side-pots: $(tm.side_pots)"
end

function is_side_pot_full(tm::TransactionManager, table, player, call)
players = players_at_table(table)
# To switch from pot_id = 1 to pot_id = 2, then exactly 1 player should be all-in:
# To switch from pot_id = 2 to pot_id = 3, then exactly 2 players should be all-in:
# ...
return @inbounds count(x->all_in(x), players) == tm.pot_id[1] && last_action_of_round(table, player, call)
increment_pot_id!(tm::TransactionManager) = (tm.pot_id[1]+=1)
function is_side_pot_full(tm, players, ith_sidepot = tm.pot_id[1])
sp = tm.side_pots[ith_sidepot]
all(x->is_player_contribution_to_side_pot_full(sp, seat_number(x)), players)
end
is_player_contribution_to_side_pot_full(side_pot, sn::Int) =
side_pot.amts[sn] == cap(side_pot)

increment_pot_id!(tm::TransactionManager) = (tm.pot_id[1]+=1)
side_pot_full(tm::TransactionManager, i) = i < tm.pot_id[1]
contribution_fits_in_sidepot(side_pot, sn::Int, contribution) =
side_pot.amts[sn] + contribution cap(side_pot)

contribution_that_fits_in_sidepot(side_pot, sn::Int) =
cap(side_pot) - side_pot.amts[sn]

Base.@propagate_inbounds function sidepot_winnings(tm::TransactionManager, id::Int)
mapreduce(i->sum(tm.side_pots[i].amts), +, 1:id; init=0)
Expand Down
1 change: 1 addition & 0 deletions test/chips.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ end
@test a + b == Chips(4, SimpleRatio(1, 2))
@test Chips(12, SimpleRatio(0, 1)) == Chips(12, SimpleRatio(0, 2))
@test Chips(12, SimpleRatio(1, 3)) Chips(12, SimpleRatio(1, 2))
@test Chips(94, SimpleRatio(-518400, 1036800)) Chips(175, SimpleRatio(0, 4031078400000))
end
3 changes: 3 additions & 0 deletions test/fuzz_play.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#=
using Revise; include("test/fuzz_utils.jl")
@eval Main using TexasHoldem # don't qualify types in log
@eval Main using TexasHoldem:SidePot # don't qualify types in log
to debug cases, use (for example):
fuzz_debug(;fun=tournament!,n_players=10,bank_roll=30,n_games=3788)
Expand All @@ -12,6 +14,7 @@ players = (
Player(Bot5050(), 3; bank_roll=4),
)
fuzz_given_players_debug(;fun=play!, players, n_games=138)
fuzz_debug(; fun = tournament!, n_players = 10, bank_roll = 30, n_games = 1310)
fuzz_debug(; fun = tournament!, n_players = 2, bank_roll = 6, n_games = 1)
fuzz_debug(; fun = tournament!, n_players = 3, bank_roll = 6, n_games = 38)
fuzz_debug(; fun = play!, n_players = 3, bank_roll = 200, n_games = 2373)
Expand Down
9 changes: 6 additions & 3 deletions test/transactions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,12 @@ end
@test TH.amounts.(tm.side_pots) == [[4, 1, 2], [1, 0, 0], [0, 0, 0]]

call!(table, players[2]) # call
@test_broken TH.amounts.(tm.side_pots) == [10, 2, 0]
@test TH.amounts.(tm.side_pots) == [[4, 4, 2], [1, 1, 0], [0, 0, 0]]

call!(table, players[3]) # call
@test_broken TH.amounts.(tm.side_pots) == [12, 2, 0]
@test_broken TH.side_pot_full(tm, 1) == true
@test TH.amounts.(tm.side_pots) == [[4, 4, 4], [1, 1, 0], [0, 0, 0]]
@test TH.is_side_pot_full(tm, players, 1)
# TODO: all but one player is all-in, is it okay that this
# side-pot is considered not full?
@test !TH.is_side_pot_full(tm, players, 2)
end

2 comments on commit d41353e

@charleskawczynski
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/90357

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.0 -m "<description of version>" d41353e44329c4ee33a7780493dd27b3ef28ff1a
git push origin v0.4.0

Please sign in to comment.