diff --git a/src/game.jl b/src/game.jl index 205fa6b9..05ea1da5 100644 --- a/src/game.jl +++ b/src/game.jl @@ -19,7 +19,7 @@ end function Game(players::Tuple; deck = ordered_deck(), - table_in = nothing, + table = nothing, button_id::Int = button_id(), blinds::Blinds = Blinds(1,2), ) @@ -32,10 +32,9 @@ function Game(players::Tuple; # If the user is specifying the player cards, then the # user should probably also handle the table cards too. - @assert table_in ≠ nothing - table = table_in + @assert table ≠ nothing @assert cards(table) isa Tuple{Card,Card,Card,Card,Card} - @assert legnth(cards(table)) == 5 + @assert length(cards(table)) == 5 @assert length(deck) + n_player_cards + length(cards(table)) == 52 else # nobody has been dealt yet table = Table(; @@ -54,8 +53,6 @@ function Game(players::Tuple; end players_at_table(game::Game) = players_at_table(game.table) -small_blind(game::Game) = small_blind(players_at_table(game), game.table) -big_blind(game::Game) = big_blind(players_at_table(game), game.table) blinds(game::Game) = blinds(game.table) any_actions_required(game::Game) = any_actions_required(game.table) diff --git a/src/player_options.jl b/src/player_options.jl index a091e982..3d352b3b 100644 --- a/src/player_options.jl +++ b/src/player_options.jl @@ -63,6 +63,28 @@ end ##### AbstractAI ##### +##### BotSitOut + +player_option(player::Player{BotSitOut}, ::PayBlindSitOut) = SitOut() # no other options needed + +##### BotCheckFold + +player_option(player::Player{BotCheckFold}, ::PayBlindSitOut) = PayBlind() +player_option!(game::Game, player::Player{BotCheckFold}, ::CheckRaiseFold) = check!(game, player) +player_option!(game::Game, player::Player{BotCheckFold}, ::CallRaiseFold) = fold!(game, player) + +##### BotCheckCall + +player_option(player::Player{BotCheckCall}, ::PayBlindSitOut) = PayBlind() +player_option!(game::Game, player::Player{BotCheckCall}, ::CheckRaiseFold) = check!(game, player) +function player_option!(game::Game, player::Player{BotCheckCall}, ::CallRaiseFold) + if game.table.current_raise_amt ≤ bank_roll(player) + call!(game, player, game.table.current_raise_amt) + else + call!(game, player, bank_roll(player)) + end +end + ##### BotRandom player_option(player::Player{BotRandom}, ::PayBlindSitOut) = PayBlind() @@ -77,16 +99,15 @@ function player_option!(game::Game, player::Player{BotRandom}, ::CheckRaiseFold) end function player_option!(game::Game, player::Player{BotRandom}, ::CallRaiseFold) if rand() < 0.5 - if rand() < 0.5 # TODO: broken: if false: no action is taken. Need raise_to! add validate_raise to properly fix - if game.table.current_raise_amt ≤ bank_roll(player) - call!(game, player, game.table.current_raise_amt) - else - call!(game, player, bank_roll(player)) - end - if rand() < 0.5 - amt = Int(round(rand()*bank_roll(player), digits=0)) - raise_to!(game, player, min(amt, blinds(game).small)) - end + # Call + if game.table.current_raise_amt ≤ bank_roll(player) + call!(game, player, game.table.current_raise_amt) + else + call!(game, player, bank_roll(player)) + end + if rand() < 0.5 + amt = Int(round(rand()*bank_roll(player), digits=0)) + raise_to!(game, player, min(amt, blinds(game).small)) end else fold!(game, player) diff --git a/src/player_types.jl b/src/player_types.jl index 789e5bed..270b3188 100644 --- a/src/player_types.jl +++ b/src/player_types.jl @@ -2,8 +2,9 @@ ##### Player types ##### +export bank_roll export Human -export BotRandom, bank_roll +export BotRandom, BotSitOut, BotCheckFold, BotCheckCall abstract type AbstractLifeForm end @@ -11,6 +12,9 @@ struct Human <: AbstractLifeForm end abstract type AbstractAI <: AbstractLifeForm end struct BotRandom <: AbstractAI end +struct BotSitOut <: AbstractAI end +struct BotCheckFold <: AbstractAI end +struct BotCheckCall <: AbstractAI end ai_to_use() = BotRandom() diff --git a/src/table.jl b/src/table.jl index c07421b1..82f39366 100644 --- a/src/table.jl +++ b/src/table.jl @@ -137,15 +137,11 @@ circle_table(n_players, button_id, state) = circle_table(table::Table, state) = circle_table(length(table.players), table.button_id, state) -button(players::Tuple, table::Table) = players[circle_table(table, 1)] -small_blind(players::Tuple, table::Table) = players[circle_table(table, 2)] -big_blind(players::Tuple, table::Table) = players[circle_table(table, 3)] +small_blind(table::Table) = players_at_table(table)[circle_table(table, 2)] +big_blind(table::Table) = players_at_table(table)[circle_table(table, 3)] any_actions_required(table::Table) = any(action_required.(players_at_table(table))) -player_button_star(table::Table, player::Player) = - table.button_id == player.id ? "*" : "" - abstract type TablePosition end struct Button <: TablePosition end struct SmallBlind <: TablePosition end @@ -195,22 +191,22 @@ function deal!(table::Table, blinds::Blinds) @info "$(name(player)) sat out a hand." check_for_winner!(table) elseif po isa PayBlind - if player.id == small_blind(players, table).id && bank_roll(player) ≤ blinds.small + if player.id == small_blind(table).id && bank_roll(player) ≤ blinds.small player.cards = pop!(table.deck, 2) player.all_in = true @info "$(name(player)) paid the small blind (all-in) and dealt cards: $(player.cards)" contribute!(table, player, bank_roll(player)) - elseif player.id == big_blind(players, table).id && bank_roll(player) ≤ blinds.big + elseif player.id == big_blind(table).id && bank_roll(player) ≤ blinds.big player.cards = pop!(table.deck, 2) player.all_in = true @info "$(name(player)) paid the big blind (all-in) and dealt cards: $(player.cards)" contribute!(table, player, bank_roll(player)) else player.cards = pop!(table.deck, 2) - if player.id == small_blind(players, table).id + if player.id == small_blind(table).id @info "$(name(player)) paid the small blind and dealt cards: $(player.cards)" contribute!(table, player, blinds.small) - elseif player.id == big_blind(players, table).id + elseif player.id == big_blind(table).id @info "$(name(player)) paid the big blind and dealt cards: $(player.cards)" contribute!(table, player, blinds.big) else diff --git a/src/transactions.jl b/src/transactions.jl index 4949ad23..1bdcc287 100644 --- a/src/transactions.jl +++ b/src/transactions.jl @@ -115,7 +115,7 @@ function contribute!(table, player, amt, call=false) for i in 1:length(tm.side_pots) @assert 0 ≤ amt_remaining side_pot_full(tm, i) && continue - cap_i = tm.side_pots[i].cap + cap_i = cap(tm.side_pots[i]) @assert 0 ≤ amt_remaining cond = amt_remaining < cap_i amt_contrib = cond ? amt_remaining : cap_i @@ -179,7 +179,9 @@ function distribute_winnings!(players, tm::TransactionManager, table_cards) end all_valid_min_hrs = min(map(x->hand_rank(x.fhe), filter(x->x.eligible, hand_evals_sorted))...) - winner_ids = findall(x->hand_rank(x.fhe)==all_valid_min_hrs && x.eligible, hand_evals_sorted) + winner_ids = findall(hand_evals_sorted) do x + x.eligible ? hand_rank(x.fhe)==all_valid_min_hrs : false + end n_winners = length(winner_ids) @debug "winner_ids = $(winner_ids)" for winner_id in winner_ids diff --git a/test/game.jl b/test/game.jl index 4befc467..60048d48 100644 --- a/test/game.jl +++ b/test/game.jl @@ -14,6 +14,16 @@ NLH = NoLimitHoldem sprint(show, game) end +@testset "Game: pre-dealt deck" begin + deck = ordered_deck() + shuffle!(deck) + players = ntuple(3) do i + NLH.Player(BotRandom(), i, pop!(deck, 2)) + end + table = Table(;players=players,deck=deck,cards=pop!(deck, 5)) + game = Game(players;deck=deck,table=table) +end + @testset "Game: contrived game" begin players = ntuple(3) do i NLH.Player(BotRandom(), i) diff --git a/test/play.jl b/test/play.jl index ef556408..43077f31 100644 --- a/test/play.jl +++ b/test/play.jl @@ -11,3 +11,23 @@ NLH = NoLimitHoldem play(game) end + +@testset "Game: Play (BotSitOut)" begin + players = ( + NLH.Player(BotSitOut(), 1), + NLH.Player(BotRandom(), 2), + NLH.Player(BotRandom(), 3), + ) + game = Game(players) + play(game) +end + +@testset "Game: Play (BotCheckFold)" begin + players = ( + NLH.Player(BotCheckFold(), 1), + NLH.Player(BotCheckFold(), 2), + NLH.Player(BotCheckFold(), 3), + ) + game = Game(players) + play(game) +end