diff --git a/src/game.jl b/src/game.jl index b5bdc32d..490b8854 100644 --- a/src/game.jl +++ b/src/game.jl @@ -72,6 +72,7 @@ function set_preflop_blind_raise!(table::Table, player::Player, ::PreFlop, i::In if is_first_to_act(table, player) # everyone must call big blind to see flop: table.current_raise_amt = blinds(table).big + table.initial_round_raise_amt = blinds(table).big end end end diff --git a/src/player_actions.jl b/src/player_actions.jl index 7590bd99..fbfa8f4e 100644 --- a/src/player_actions.jl +++ b/src/player_actions.jl @@ -136,13 +136,15 @@ for a more verbose but simpler implementation. """ function valid_raise_bounds(table::Table, player::Player) cra = current_raise_amt(table) + irra = initial_round_raise_amt(table) + mra = minimum_raise_amt(table) rbr = round_bank_roll(player) max_orbr = max_opponent_round_bank_roll(table, player) @debug "determining valid_raise_bounds" @debug " rbr = $rbr, max_orbr = $max_orbr" @debug " cra ≈ 0 = $(cra ≈ 0)" @debug " max_orbr > rbr = $(max_orbr > rbr)" - lim = cra ≈ 0 ? blinds(table).big : 2*cra + lim = cra ≈ 0 ? mra : (cra+irra) vrb = custom_clamp(min(max_orbr, rbr), lim) @assert vrb[2] ≥ vrb[1] "Min valid raise bound must be ≤ max valid raise bound." return vrb @@ -238,10 +240,14 @@ function raise_to_valid_raise_amount!(table::Table, player::Player, amt::Real) table.current_raise_amt = amt push!(player.action_history, Raise(amt)) + + players = players_at_table(table) + if all(player -> !player.last_to_raise, players) + table.initial_round_raise_amt = amt + end player.action_required = false player.last_to_raise = true player.checked = false - players = players_at_table(table) for opponent in players seat_number(opponent) == seat_number(player) && continue not_playing(opponent) && continue diff --git a/src/table.jl b/src/table.jl index b2483ebd..1941799b 100644 --- a/src/table.jl +++ b/src/table.jl @@ -59,6 +59,7 @@ mutable struct Table state::AbstractGameState buttons::Buttons current_raise_amt::Float64 + initial_round_raise_amt::Float64 transactions::TransactionManager winners::Winners play_out_game::Bool @@ -103,6 +104,7 @@ function Table(; state = PreFlop(), dealer_id = default_dealer_id(), current_raise_amt = Float64(0), + initial_round_raise_amt = blinds.small, transactions = nothing, winners = Winners(), play_out_game = false, @@ -121,6 +123,7 @@ function Table(; state, buttons, current_raise_amt, + initial_round_raise_amt, transactions, winners, play_out_game, @@ -182,6 +185,8 @@ observed_cards(table::Table, ::Flop) = table.cards[1:3] observed_cards(table::Table, ::Turn) = table.cards[1:4] observed_cards(table::Table, ::River) = table.cards current_raise_amt(table::Table) = table.current_raise_amt +initial_round_raise_amt(table::Table) = table.initial_round_raise_amt +minimum_raise_amt(table::Table) = blinds(table).small state(table::Table) = table.state @@ -328,6 +333,7 @@ function reset_round!(table::Table) player.last_to_raise = false player.round_contribution = 0 end + table.initial_round_raise_amt = blinds(table).small table.current_raise_amt = 0 end diff --git a/test/call_raise_validation.jl b/test/call_raise_validation.jl index e1162709..91e8622f 100644 --- a/test/call_raise_validation.jl +++ b/test/call_raise_validation.jl @@ -6,35 +6,36 @@ TH = TexasHoldem function valid_raise_bounds_simple(table::Table, player::Player) cra = TH.current_raise_amt(table) + irra = TH.initial_round_raise_amt(table) rbr = TH.round_bank_roll(player) max_orbr = TH.max_opponent_round_bank_roll(table, player) if cra ≈ 0 # initial raise - bb = TH.blinds(table).big + mr = TH.minimum_raise_amt(table) if max_orbr > rbr # at least one opponent can cover `player`'s all-in - if bb > rbr + if mr > rbr vrb, case = (rbr, rbr), 1 else - vrb, case = (bb, rbr), 2 + vrb, case = (mr, rbr), 2 end else # raise limited by opponent's bank roll - if bb > max_orbr + if mr > max_orbr vrb, case = (max_orbr, max_orbr), 3 else - vrb, case = (bb, max_orbr), 4 + vrb, case = (mr, max_orbr), 4 end end else # re-raise if max_orbr > rbr # at least one opponent can cover `player`'s all-in - if 2*cra > rbr + if (cra+irra) > rbr vrb, case = (rbr, rbr), 5 else - vrb, case = (2*cra, rbr), 6 + vrb, case = ((cra+irra), rbr), 6 end else # raise limited by opponent's bank roll - if 2*cra > max_orbr + if (cra+irra) > max_orbr vrb, case = (max_orbr, max_orbr), 7 else - vrb, case = (2*cra, max_orbr), 8 + vrb, case = ((cra+irra), max_orbr), 8 end end end @@ -44,7 +45,7 @@ end @testset "valid_raise_bounds" begin # Case 1 - table = Game((Player(Bot5050(), 1; bank_roll=1), Player(Bot5050(), 2))).table + table = Game((Player(Bot5050(), 1; bank_roll=0.5), Player(Bot5050(), 2))).table players = TH.players_at_table(table) table.current_raise_amt = 0 @test valid_raise_bounds_simple(table, players[1]) == (TH.valid_raise_bounds(table, players[1]), 1) @@ -95,17 +96,20 @@ end @testset "is_valid_raise_amount" begin players = (Player(Human(), 1), Player(Bot5050(), 2)) table = Game(players).table - @test TH.is_valid_raise_amount(table, players[1], 0) == (false, "Cannot raise 0. Raise must be between [\$2, \$200.0]") - @test TH.is_valid_raise_amount(table, players[1], TH.bank_roll(players[1])+1) == (false, "Insufficient funds (\$200.0) to raise \$201.0. Raise must be between [\$2, \$200.0]") - @test TH.is_valid_raise_amount(table, players[1], -1) == (false, "Raise must be between [\$2, \$200.0]") + mra = TH.minimum_raise_amt(table) + @assert mra == TH.blinds(table).small + @test TH.is_valid_raise_amount(table, players[1], 0) == (false, "Cannot raise 0. Raise must be between [\$$mra, \$200.0]") + @test TH.is_valid_raise_amount(table, players[1], TH.bank_roll(players[1])+1) == (false, "Insufficient funds (\$200.0) to raise \$201.0. Raise must be between [\$$mra, \$200.0]") + @test TH.is_valid_raise_amount(table, players[1], -1) == (false, "Raise must be between [\$$mra, \$200.0]") @test TH.is_valid_raise_amount(table, players[1], TH.bank_roll(players[1])-1) == (true, "") - @test TH.is_valid_raise_amount(table, players[1], TH.blinds(table).small) == (false, "Raise must be between [\$2, \$200.0]") + @test TH.is_valid_raise_amount(table, players[1], TH.blinds(table).small) == (true, "") @test TH.is_valid_raise_amount(table, players[1], TH.blinds(table).big) == (true, "") @test TH.is_valid_raise_amount(table, players[1], TH.bank_roll(players[1])) == (true, "") players = (Player(Human(), 1), Player(Bot5050(), 2)) table = Game(players).table + table.initial_round_raise_amt = 20.0 table.current_raise_amt = 20.0 players[1].round_contribution = 200 players[1].round_bank_roll = 500 # oops @@ -113,15 +117,31 @@ end players = (Player(Human(), 1), Player(Bot5050(), 2)) table = Game(players).table + table.initial_round_raise_amt = 10.0 table.current_raise_amt = 10.0 players[1].round_bank_roll = 20 @test TH.is_valid_raise_amount(table, players[1], 10) == (false, "Only allowable raise is \$20.0 (all-in)") players = (Player(Human(), 1), Player(Bot5050(), 2)) table = Game(players).table + table.initial_round_raise_amt = 10.0 table.current_raise_amt = 10.0 players[1].round_bank_roll = 20 @test TH.is_valid_raise_amount(table, players[1], 20) == (true, "") + + players = (Player(Human(), 1), Player(Bot5050(), 2)) + table = Game(players).table + table.initial_round_raise_amt = 5.0 + table.current_raise_amt = 20.0 + players[1].round_bank_roll = 30 + @test TH.is_valid_raise_amount(table, players[1], 25) == (true, "") + + players = (Player(Human(), 1), Player(Bot5050(), 2)) + table = Game(players).table + table.initial_round_raise_amt = 5.0 + table.current_raise_amt = 20.0 + players[1].round_bank_roll = 30 + @test TH.is_valid_raise_amount(table, players[1], 22) == (false, "Raise must be between [\$25.0, \$30.0]") end @testset "call_amount" begin diff --git a/test/play.jl b/test/play.jl index 9cd229ff..0530c60a 100644 --- a/test/play.jl +++ b/test/play.jl @@ -21,7 +21,7 @@ end @testset "Game: Play (BotBetSB) - breaks lowest allowable bet" begin game = Game((Player(BotBetSB(),1), Player(BotCheckCall(),2))) - # Cannot raise small blind on pre-flop, must raise at least big-blind + # Cannot raise small blind on pre-flop! The big blind is the raise min raise, so we must raise at least big-blind @test_throws AssertionError("Raise must be between [\$4.0, \$200.0]") play!(game) end