From 0d2f4b19a04a2b52c88ba1e7123b69a5bf900f11 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 player option case --- src/game.jl | 60 +++++++++++++++++++++++++++++++++++++------ src/player_actions.jl | 8 +++--- src/table.jl | 34 ++++++++++++++++++++++-- test/fuzz_play.jl | 6 +++++ test/runtests.jl | 1 + 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/src/game.jl b/src/game.jl index 5fb2e2d6..3ec8fd8d 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,52 @@ 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 + players_who_called = [] + @debug "Checking if all raises were called" + @debug " table.winners.declared = $(table.winners.declared)" + arwc = false + conds = map(players) do player + cond1 = seat_number(player) == seat_number(lptr) + cond2 = not_playing(player) + cond3 = all_in(player) + cond4 = pot_investment(player) ≈ pot_investment(lptr) + (cond1, cond2, cond3, cond4) + end + @debug begin + conds_debug = map(players) do player + sn = seat_number(player) + cond1 = seat_number(player) == seat_number(lptr) + cond2 = not_playing(player) + cond3 = all_in(player) + cond4 = pot_investment(player) ≈ pot_investment(lptr) + (sn, cond1, cond2, cond3, cond4) + end + for cond in conds_debug + @debug "sn, cond = $(cond[1]), $(cond[2:end])" + end + @debug "snlptr = $(seat_number(lptr))" + end + return all(map(cond->any(cond), conds)) end function act_generic!(game::Game, state::AbstractGameState) @@ -114,6 +154,8 @@ function act_generic!(game::Game, state::AbstractGameState) reset_round_bank_rolls!(game, state) any_actions_required(game) || return + play_out_game(table) && return + set_play_out_game!(table) 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 +170,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 +189,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 +259,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 +279,8 @@ function reset_game!(game::Game) player.round_contribution = 0 player.active = true end - set_active_status!(game.table) + set_active_status!(table) + table.play_out_game = false end """ diff --git a/src/player_actions.jl b/src/player_actions.jl index dd08af8f..4599de27 100644 --- a/src/player_actions.jl +++ b/src/player_actions.jl @@ -246,10 +246,12 @@ function raise_to_valid_raise_amount!(table::Table, player::Player, amt::Real) for opponent in players seat_number(opponent) == seat_number(player) && continue not_playing(opponent) && continue - all_in(opponent) && continue - opponent.action_required = true - opponent.checked = false # to avoid exiting on all_all_in_or_checked(table). TODO: there's got to be a cleaner way + if !all_in(opponent) + opponent.action_required = true + end opponent.last_to_raise = false + # TODO: there's got to be a cleaner way + opponent.checked = false # to avoid exiting on all_all_in_or_checked(table). end if bank_roll(player) ≈ 0 if isempty(pbpai) diff --git a/src/table.jl b/src/table.jl index 6be7d515..0fbb1fca 100644 --- a/src/table.jl +++ b/src/table.jl @@ -62,6 +62,7 @@ mutable struct Table current_raise_amt::Float64 transactions::TransactionManager winners::Winners + play_out_game::Bool end buttons(table::Table) = table.buttons @@ -77,6 +78,7 @@ function Table(; current_raise_amt = Float64(0), transactions = nothing, winners = Winners(), + play_out_game = false, ) if transactions == nothing transactions = TransactionManager(players) @@ -91,7 +93,8 @@ function Table(; buttons, current_raise_amt, transactions, - winners) + winners, + play_out_game) end function Buttons(dealer_id, players::Tuple) @@ -152,6 +155,8 @@ current_raise_amt(table::Table) = table.current_raise_amt state(table::Table) = table.state +play_out_game(table::Table) = table.play_out_game + dealer_id(table::Table) = dealer_id(table.buttons) small_blind_id(table::Table) = small_blind_id(table.buttons) big_blind_id(table::Table) = big_blind_id(table.buttons) @@ -200,6 +205,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 +231,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 +240,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 set_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 + + table.play_out_game = 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 diff --git a/test/fuzz_play.jl b/test/fuzz_play.jl index c10ebe1b..0c8a785b 100644 --- a/test/fuzz_play.jl +++ b/test/fuzz_play.jl @@ -20,6 +20,12 @@ end end end +@testset "Game: tournament! (3 Bot5050's)" begin + for n in 1:n_fuzz_3_players + tournament!(Game(ntuple(i->Player(Bot5050(), i; bank_roll = 6), 3))) + end +end + @testset "Game: tournament! (10 Bot5050's)" begin for n in 1:n_fuzz_10_players tournament!(Game(ntuple(i->Player(Bot5050(), i; bank_roll = 6), 10))) diff --git a/test/runtests.jl b/test/runtests.jl index 6da84882..7db0eb1e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,6 +17,7 @@ submodules = [ local_run = isempty(get(ENV, "CI", "")) n_fuzz = local_run ? 10 : 100 n_fuzz_10_players = local_run ? 1 : 10 +n_fuzz_3_players = local_run ? 10 : 20 tests_to_debug = ["play", "fuzz_play"] # tests_to_debug = submodules