Skip to content

Commit

Permalink
Restructure, fix parts of deal logic
Browse files Browse the repository at this point in the history
  • Loading branch information
charleskawczynski committed May 2, 2021
1 parent 1b7bbc3 commit 416ff6e
Show file tree
Hide file tree
Showing 14 changed files with 665 additions and 317 deletions.
1 change: 1 addition & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

```@docs
NoLimitHoldem.move_button!
NoLimitHoldem.play
```
9 changes: 9 additions & 0 deletions src/NoLimitHoldem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ using PokerHandEvaluator.HandTypes
using UnPack
using Printf

export PreFlop, Flop, Turn, River

abstract type AbstractGameState end
struct PreFlop <: AbstractGameState end
struct Flop <: AbstractGameState end
struct Turn <: AbstractGameState end
struct River <: AbstractGameState end

include("player_types.jl")
include("transactions.jl")
include("table.jl")
include("game.jl")
include("player_actions.jl")
include("player_options.jl")
Expand Down
12 changes: 4 additions & 8 deletions src/config_game.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,11 @@ end
function configure_basic_heads_up_game()
bank_roll = 100
blinds = Blinds(1, 2)
deck = ordered_deck()
shuffle!(deck)
players = (
Player(Human(), 1, pop!(deck, 2); bank_roll=bank_roll),
Player(BotRandom(), 2, pop!(deck, 2); bank_roll=bank_roll)
Player(Human(), 1; bank_roll=bank_roll),
Player(BotRandom(), 2; bank_roll=bank_roll)
)
return Game(;
players=players,
return Game(players;
deck=deck,
blinds=blinds,
)
Expand Down Expand Up @@ -100,8 +97,7 @@ function configure_custom_game()
end
end

return Game(;
players=players,
return Game(players;
deck=deck,
blinds=blinds,
)
Expand Down
210 changes: 76 additions & 134 deletions src/game.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,6 @@
#####

export Game, play
export Deal, PayBlinds, Flop, Turn, River
export move_button!

abstract type AbstractGameState end
struct Deal <: AbstractGameState end
struct PayBlinds <: AbstractGameState end
struct Flop <: AbstractGameState end
struct Turn <: AbstractGameState end
struct River <: AbstractGameState end

mutable struct Table{C, P}
cards::C
pot::P
state::AbstractGameState
button_id::Int
end

function Base.show(io::IO, table::Table, include_type = true)
include_type && println(io, typeof(table))
println(io, "Button = $(table.button_id)")
println(io, "Pot = $(table.pot)")
println(io, "All cards = $(table.cards)")
println(io, "Observed cards = $(observed_cards(table))")
end

Table(cards::Tuple) = Table(cards, 0, Deal(), 1)
Table!(deck::Deck) =
Table(Iterators.flatten(ntuple(i->pop!(deck, 1), 5)) |> collect |> Tuple)

observed_cards(table::Table) = observed_cards(table, table.state)
observed_cards(table::Table, ::Deal) = ()
observed_cards(table::Table, ::PayBlinds) = ()
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

struct Blinds{S,B}
small::S
big::B
end

Base.@kwdef mutable struct Winners
declared::Bool = false
Expand All @@ -58,53 +18,65 @@ end
mutable struct Game
state::AbstractGameState
table::Table
players::Tuple
blinds::Blinds
winners::Winners
current_raise_amt::Float64
end

function Base.show(io::IO, blinds::Blinds, include_type = true)
include_type && println(io, typeof(blinds))
println(io, "Blinds = (small=$(blinds.small),big=$(blinds.big))")
end

function Base.show(io::IO, player::Player, include_type = true)
include_type && println(io, typeof(player))
println(io, "Player[$(player.id)] = $(player.cards)")
end

function Base.show(io::IO, game::Game)
println(io, "\n----------------------- Poker game")
show(io, game.blinds, false)
show(io, game.table, false)
for player in game.players
for player in players_at_table(game)
show(io, player, false)
end
show(io, game.winners, false)
println(io, "-----------------------")
end

function Game(;
deck,
players,
function Game(players;
deck = ordered_deck(),
table_in = nothing,
button_id::Int = button_id(),
blinds::Blinds = Blinds(1,2),
winners::Winners = Winners()
)
table = Table!(deck)

n_player_cards = sum(map(x->cards(x)==nothing ? 0 : length(cards(x)), players))

if length(deck) 52
# if the deck isn't full, then players should have been dealt cards.
@assert n_player_cards > 0

# 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 cards(table) isa Tuple{Card,Card,Card,Card,Card}
@assert legnth(cards(table)) == 5
@assert length(deck) + n_player_cards + length(cards(table)) == 52
else # nobody has been dealt yet
table = Table(;
deck=deck,
players=players,
button_id=button_id,
blinds=blinds
)

@assert length(deck) == 52
@assert n_player_cards == 0
@assert cards(table) == nothing
end

state = table.state
targs = (table, players, blinds, winners)
current_raise_amt = 0
args = (state, table, players, blinds, winners, current_raise_amt)
args = (state, table, winners, current_raise_amt)
return Game(args...)
end

folded(player::Player) = player.folded
action_history(player::Player) = player.action_history
n_players(game::Game) = length(game.players)
n_players(game::Game) = length(players_at_table(game))
players_at_table(game::Game) = players_at_table(game.table)

function declare_winners!(game::Game)
fhe = map(game.players) do player
fhe = map(players_at_table(game)) do player
FullHandEval((player.cards..., observed_cards(game.table)...))
end

Expand All @@ -114,67 +86,35 @@ function declare_winners!(game::Game)
@show best_cards.(fhe)

min_hr = min(hr...)
game.winners.players = filter(game.players) do player
game.winners.players = filter(players_at_table(game)) do player
hr[player.id] == min_hr
end
game.winners.declared = true
game.current_raise_amt = 0
end

function check_for_winner!(game)
game.winners.declared = count(folded.(game.players)) == n_players(game)-1
game.winners.declared = count(folded.(players_at_table(game))) == n_players(game)-1
if game.winners.declared
for player in game.players
for player in players_at_table(game)
folded(player) && continue
game.winners.players = player
end
end
end

move_button!(game::Game) = move_button!(game.table, game.players)

"""
move_button!(table::Table)
Move the button to the next player on
the table.
"""
function move_button!(table::Table, players)
table.button_id = mod(table.button_id, length(players))+1
end

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)

"""
action_id(n_players, button_id, state)
The player ID whose action it is, given
- `state` the global iteration state (starting from 1)
- `n_players` the total number of players
- `button_id` the dealer ID (from `1:n_players`)
"""
action_id(n_players, button_id, state) =
mod(state + button_id+1, n_players)+1

action_id(game::Game, state) =
action_id(length(game.players), game.table.button_id, state)

acting_player(game::Game, state) =
game.players[action_id(game, state)]

any_actions_required(game::Game) =
any(map(player -> player.action_required, game.players))

player_button_star(table::Table, player::Player) =
table.button_id == player.id ? "*" : ""

small_blind(game::Game) = game.players[action_id(game, -1)]
big_blind(game::Game) = game.players[action_id(game, 0)]
blinds(game::Game) = game.blinds

function reset_actions_required!(game::Game)
for player in game.players
function finalize_round!(game::Game)
players = players_at_table(game)
for player in players
player.checked = false
folded(player) && continue
player.action_required = true
player.last_to_raise = false
end
game.current_raise_amt = 0
end
Expand All @@ -189,47 +129,49 @@ function act_generic!(game::Game, state::AbstractGameState)
set_state!(game, state)
print_state(game)

# TODO: incorporate winners.declared into logic

any_actions_required(game) || return
i = 1
while any_actions_required(game)
player = acting_player(game, i)
folded(player) || player_option!(game, player)
for player in circle(game.table, SmallBlind())
last_to_raise(player) && break
all_checked_or_folded(game.table) && break
folded(player) && continue
player_option!(game, player)
game.winners.declared && break
i+=1
end
end

function act!(game::Game, state::AbstractGameState)
act_generic!(game, state)
reset_actions_required!(game)
finalize_round!(game)
end

function act!(game::Game, state::River)
act_generic!(game, state)
declare_winners!(game)
end

function act!(game::Game, state::PayBlinds)
set_state!(game, state)
# Always call blinds:
call!(game, small_blind(game), blinds(game).small)
call!(game, big_blind(game), blinds(game).big)
print_state(game)
reset_actions_required!(game)
end
"""
play(::Game)
Play a game. Note that this method
expects no cards (players and table)
to be dealt.
"""
function play(game::Game; debug = false)
table = game.table
players = players_at_table(table)

table.transactions = TransactionManager(players)

@assert all(cards.(players) .== nothing)
@assert cards(table) == nothing
deal!(table, blinds(table))
@assert cards(table) nothing

act!(game, PreFlop()) # Pre-flop bet/check/raise

function play(game::Game)
# TODO: deal cards here instead of cards dealt into Game before play
# Also: Players who cannot afford the blinds are all-in, which is likely
# not caught here.
# @assert all(cards.(game.players) .== nothing)
act!(game, Deal()) # Deal player cards, then bet/check/raise based on player cards
act!(game, PayBlinds()) #
game.winners.declared || act!(game, Flop()) # Deal flop , then bet/check/raise based on flop
game.winners.declared || act!(game, Turn()) # Deal turn , then bet/check/raise based on turn
game.winners.declared || act!(game, River()) # Deal river , then bet/check/raise based on river
game.winners.declared || act!(game, Flop()) # Deal flop , then bet/check/raise
game.winners.declared || act!(game, Turn()) # Deal turn , then bet/check/raise
game.winners.declared || act!(game, River()) # Deal river, then bet/check/raise
return game.winners
end

Loading

0 comments on commit 416ff6e

Please sign in to comment.