diff --git a/poker_ex/lib/poker_ex/round.ex b/poker_ex/lib/poker_ex/round.ex new file mode 100644 index 0000000..c9879d4 --- /dev/null +++ b/poker_ex/lib/poker_ex/round.ex @@ -0,0 +1,92 @@ +defmodule PokerEx.Round do + alias __MODULE__ + alias PokerEx.Card + alias PokerEx.Deck + alias PokerEx.Combination + + @type player_state() :: :folded | :all_in | :active + @type player() :: %{name: String.t(), state: player_state(), hand: {Card.t(), Card.t()}} + @type stage() :: :pre_flop | :flop | :turn | :river + @type round_result() :: + {:end, Round.t(), [player()]} | {:next, Round.t()} | {:error, String.t()} + @type player_action() :: {:raise, integer()} | {:call} | {:fold} + + @type t() :: %Round{ + pot: integer(), + stage: stage(), + deck: Deck.deck(), + cards: [Card.t()], + players: [player()], + player_at_turn: integer(), + last_better: integer(), + dealer: integer() + } + @enforce_keys [:players, :dealer] + defstruct pot: 0, + stage: :pre_flop, + deck: Deck.new_shuffled(), + cards: [], + players: [], + player_at_turn: 0, + last_better: 0, + dealer: 0 + + @spec evaluate_player({Card.t(), Card.t()}, [Card.t()]) :: + {Combination.score(), {integer(), integer()}} + defp evaluate_player({card1, card2}, round_cards) do + [card1, card2] = Enum.sort([card1, card2], Card) + + { + ([card1, card2] ++ round_cards) |> Combination.evaluate_hand(), + {Card.rank_to_number(card1.rank), Card.rank_to_number(card2.rank)} + } + end + + @spec take_while_max_by([any()], (Enum.element() -> any())) :: [any()] + defp take_while_max_by(sorted_list, fun) do + case sorted_list do + [] -> + [] + + [head | _] -> + highest = fun.(head) + Enum.take_while(sorted_list, &(fun.(&1) == highest)) + end + end + + @spec winners(Round.t()) :: {[player()], Combination.score()} + defp winners(round) do + winning_players = + round.players + |> Enum.filter(&(&1.state != :folded)) + |> Enum.map(&{&1, evaluate_player(&1.hand, round.cards)}) + |> Enum.sort_by(fn {_player, {score, _hand}} -> score end, {:desc, Combination}) + |> take_while_max_by(fn {_player, {score, _hand}} -> score end) + |> Enum.sort_by(fn {_player, {_score, hand}} -> hand end, :desc) + |> take_while_max_by(fn {_player, {_score, hand}} -> hand end) + + case winning_players do + [] -> + [] + + [winner | tail] -> + {_, {winning_score, _}} = winner + {[winner | tail] |> Enum.map(fn {player, _score} -> player end), winning_score} + end + end + + @spec play(Round.t(), player_action()) :: round_result() + def play(round, {:fold}) do + { + :next, + %{ + round + | players: List.update_at(round.players, round.player_at_turn, &%{&1 | state: :folded}), + player_at_turn: rem(round.player_at_turn + 1, length(round.players)) + } + } + end + + def play(round, current_player_action) do + end +end