diff --git a/poker_ex/lib/poker_ex/combination.ex b/poker_ex/lib/poker_ex/combination.ex new file mode 100644 index 0000000..59e084b --- /dev/null +++ b/poker_ex/lib/poker_ex/combination.ex @@ -0,0 +1,86 @@ +defmodule PokerEx.Combination do + alias __MODULE__ + alias PokerEx.Card, as: Card + + @type score() :: + :high_card + | :pair + | :two_pair + | :three_of_a_kind + | :straight + | :flush + | :full_house + | :four_of_a_kind + | :straight_flush + | :royal_flush + @type t() :: %Combination{score: score(), cards: [Card.t()]} + defstruct score: :invalid, cards: [] + + def straight?(sorted_hand) do + # Grab the ranks of the card, have a sliding window and check if the + # difference between all cards is 1. + + sorted_hand + |> Enum.map(& &1.rank) + |> Enum.map( + &case &1 do + :jack -> 11 + :queen -> 12 + :king -> 13 + :ace -> 14 + n -> n + end + ) + |> Enum.chunk_every(2, 1, :discard) + |> Enum.map( + &case &1 do + [14, 2] -> 1 # Special case of A followed by a 2 + [a, b] -> b - a + end + ) + |> Enum.all?(& &1 == 1) + end + + def flush?(hand) do + hand + |> Enum.map(& &1.suit) + |> Enum.frequencies() + |> Map.values() + |> Enum.any?(& &1 >= 5) + end + + def evaluate_hand(hand) do + end + + @doc ~S""" + Finds the highest combination that can be made using the hand and the cards + that are present. + + ## Examples + + iex> PokerEx.Combination.find [%PokerEx.Card{suit: :diamonds, rank: 5}] + {:ok, %PokerEx.Combination{score: :high_card, cards: [%PokerEx.Card{suit: :diamonds, rank: 5}]}} + + iex> PokerEx.Combination.find [%PokerEx.Card{suit: :diamonds, rank: 5}, %PokerEx.Card{suit: :hearts, rank: :ace}] + {:ok, %PokerEx.Combination{score: :high_card, cards: [%PokerEx.Card{suit: :hearts, rank: :ace}]}} + + iex> comb = PokerEx.Combination.find [ + ...> %PokerEx.Card{suit: :spades, rank: :king}, + ...> %PokerEx.Card{suit: :spades, rank: :king}, + ...> %PokerEx.Card{suit: :spades, rank: :king}, + ...> %PokerEx.Card{suit: :spades, rank: :queen}, + ...> %PokerEx.Card{suit: :spades, rank: :queen} + ...> ] + iex> comb.score == :full_house + true + + iex> PokerEx.Combination.find [] + {:error, :invalid_list} + + iex> PokerEx.Combination.find ["Hello"] + {:error, :invalid_list} + """ + @spec find([Card.t()]) :: {:ok, Combination.t()} | {:error, atom} + def find([%Card{suit: s, rank: :ace}]) do + end +end diff --git a/poker_ex/test/poker_ex/combination_test.exs b/poker_ex/test/poker_ex/combination_test.exs new file mode 100644 index 0000000..a8f84d5 --- /dev/null +++ b/poker_ex/test/poker_ex/combination_test.exs @@ -0,0 +1,24 @@ +defmodule PokerEx.CombinationTest do + alias PokerEx.Combination + import PokerEx.Card + use ExUnit.Case + + test "test straight" do + hand = [~p"C2", ~p"C3", ~p"C4", ~p"C5", ~p"C6"] + assert Combination.straight?(hand) == true + + hand = [~p"C2", ~p"D3", ~p"S4", ~p"S5", ~p"C7"] + assert Combination.straight?(hand) == false + + hand = [~p"D10", ~p"SJ", ~p"HQ", ~p"CK", ~p"DA"] + assert Combination.straight?(hand) == true + + hand = [~p"SA", ~p"D2", ~p"H3", ~p"C4", ~p"S5"] + assert Combination.straight?(hand) == true + end + + test "test flush" do + assert Combination.flush?([~p"CA", ~p"C10", ~p"C2", ~p"CK", ~p"C5"]) == true + assert Combination.flush?([~p"CA", ~p"C10", ~p"C2", ~p"CK", ~p"D5"]) == false + end +end