From 777e6563955c3f04a8cbcb1f7ee93d22976d8e47 Mon Sep 17 00:00:00 2001 From: Charles Kawczynski Date: Fri, 11 Jun 2021 17:48:33 -0700 Subject: [PATCH] Ensure raises are called, fix edge cases with player options --- src/game.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++-------- src/table.jl | 27 +++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/game.jl b/src/game.jl index 5fb2e2d6..7bacd448 100644 --- a/src/game.jl +++ b/src/game.jl @@ -90,10 +90,7 @@ function end_of_actions(table::Table, player) @debug " all_playing_checked(table) = $(all_playing_checked(table))" @debug " all_all_in_or_checked(table) = $(all_all_in_or_checked(table))" @debug " all_all_in_except_bank_roll_leader(table) = $(all_all_in_except_bank_roll_leader(table))" - # all_all_in_except_bank_roll_leader does not supersede - # all_playing_all_in, since it always returns false if - # there are multiple players (still playing) with the - # same (max) bank roll: + @debug " all_oppononents_all_in(table, player) = $(all_oppononents_all_in(table, player))" case_1 = last_to_raise(player) case_2 = all_playing_checked(table) @@ -101,9 +98,48 @@ function end_of_actions(table::Table, player) case_4 = all_all_in_except_bank_roll_leader(table) case_5 = all_all_in_or_checked(table) case_6 = !any(action_required.(players)) - @debug " cases = $((case_1, case_2, case_3, case_4, case_5, case_6))" + case_7 = all_oppononents_all_in(table, player) && paid_current_raise_amount(table, player) + @debug " cases = $((case_1, case_2, case_3, case_4, case_5, case_6, case_7))" - return any((case_1, case_2, case_3, case_4, case_5, case_6)) + return any((case_1, case_2, case_3, case_4, case_5, case_6, case_7)) +end + +function last_player_to_raise(table::Table) + for player in players_at_table(table) + last_to_raise(player) && return player + end + return nothing +end + +function all_raises_were_called(table::Table) + lptr = last_player_to_raise(table) + lptr===nothing && return true + + players = players_at_table(table) + ltr = last_to_raise.(players) + @assert count(ltr) == 1 + lptr = last_player_to_raise(table) + players_who_called = [] + @debug "Checking if all raises were called" + @debug " table.winners.declared = $(table.winners.declared)" + for (i, opponent) in enumerate(circle(table, lptr)) + i>length(players) && break + seat_number(opponent) == seat_number(lptr) && continue + not_playing(opponent) && continue + ah = action_history(opponent) + @assert !isempty(ah) + la = last(ah) + push!(players_who_called, (seat_number(opponent), la isa Call || (la isa Raise && all_in(opponent)))) + if !(pot_investment(opponent) ≈ pot_investment(lptr)) + @debug " all_in(lptr) = $(all_in(lptr))" + @debug " pot_investment(lptr) = $(pot_investment(lptr))" + @debug " pot_investment(opponent) = $(pot_investment(opponent))" + if !all_in(opponent) + error("Pot investments of all players must be equal for all non-in players still playing.") + end + end + end + return all(map(pwc->last(pwc), players_who_called)) end function act_generic!(game::Game, state::AbstractGameState) @@ -114,6 +150,7 @@ function act_generic!(game::Game, state::AbstractGameState) reset_round_bank_rolls!(game, state) any_actions_required(game) || return + play_out_game(table) && return for (i, player) in enumerate(circle(table, FirstToAct())) @debug "Checking to see if it's $(name(player))'s turn to act" @debug " not_playing(player) = $(not_playing(player))" @@ -128,6 +165,7 @@ function act_generic!(game::Game, state::AbstractGameState) player_option!(game, player) table.winners.declared && break end + @assert all_raises_were_called(table) end function act!(game::Game, state::AbstractGameState) @@ -146,6 +184,7 @@ function play!(game::Game) @info "------ Playing game!" table = game.table + set_active_status!(table) players = players_at_table(table) initial_brs = deepcopy(bank_roll.(players)) @@ -215,7 +254,6 @@ end function reset_game!(game::Game) table = game.table players = players_at_table(table) - set_active_status!(game.table) game.table = Table(; deck=ordered_deck(), @@ -236,7 +274,7 @@ function reset_game!(game::Game) player.round_contribution = 0 player.active = true end - set_active_status!(game.table) + set_active_status!(table) end """ diff --git a/src/table.jl b/src/table.jl index 6be7d515..1f905997 100644 --- a/src/table.jl +++ b/src/table.jl @@ -200,6 +200,9 @@ function bank_roll_leader(table::Table) return br_leader, multiple_leaders end +paid_current_raise_amount(table::Table, player::Player) = + round_contribution(player) ≈ current_raise_amt(table) + # Can be true in exactly 2 cases: # 1) Everyone (still playing) is all-in. # 2) Everyone (still playing), except `player`, is all-in. @@ -223,6 +226,7 @@ end # everyone else does not, so nobody can respond to their raise # (if they chose to do so). Therefore, we must "play out" the # entire game with no further actions. +# Note that this method is intended to be used _during_ a round. function all_all_in_except_bank_roll_leader(table::Table) br_leader, multiple_leaders = bank_roll_leader(table) players = players_at_table(table) @@ -231,7 +235,28 @@ function all_all_in_except_bank_roll_leader(table::Table) @assert !multiple_leaders # We have a single bank roll leader return all(map(players) do player - not_playing(player) || all_in(player) || seat_number(player) == seat_number(br_leader) + cond1 = not_playing(player) + cond2 = all_in(player) + cond3 = seat_number(player) == seat_number(br_leader) && !action_required(br_leader) + any((cond1, cond2, cond3)) + end) +end + +# Note that this method is only valid before or after a round has ended. +function play_out_game(table::Table) + br_leader, multiple_leaders = bank_roll_leader(table) + players = players_at_table(table) + if multiple_leaders + return all(map(player -> not_playing(player) || all_in(player), players)) + end + + @assert !multiple_leaders # We have a single bank roll leader + + return all(map(players) do player + cond1 = not_playing(player) + cond2 = all_in(player) + cond3 = seat_number(player) == seat_number(br_leader) + any((cond1, cond2, cond3)) end) end