defmodule CardShark.Compare do @moduledoc "Module for comparing various cards and determining a 'winner'" alias CardShark.Card @doc """ Return the high card from the given set, along with it's index. Accepts an optional trump suit atom The list of cards given should be in the order played, and it is assumed that the first card played determines the suit other cards need to be to be the high card (or be a trump card) ## Examples iex> cards = [%Card{face: "3", suit: :spades, value: 3}] iex> Compare.high_card(cards) {%Card{face: "3", suit: :spades, value: 3}, 0} iex> cards = [ ...> %Card{face: "3", suit: :spades, value: 3}, ...> %Card{face: "6", suit: :diamonds, value: 6} ...> ] iex> Compare.high_card(cards) {%Card{face: "3", suit: :spades, value: 3}, 0} iex> cards = [ ...> %Card{face: "3", suit: :clubs, value: 3}, ...> %Card{face: "8", suit: :clubs, value: 8} ...> ] iex> Compare.high_card(cards) {%Card{face: "8", suit: :clubs, value: 8}, 1} iex> cards = [ ...> %Card{face: "3", suit: :clubs, value: 3}, ...> %Card{face: "ace", suit: :spades, value: 14}, ...> %Card{face: "king", suit: :clubs, value: 13} ...> ] iex> Compare.high_card(cards) {%Card{face: "king", suit: :clubs, value: 13}, 2} iex> cards = [ ...> %Card{face: "3", suit: :clubs, value: 3}, ...> %Card{face: "2", suit: :spades, value: 2}, ...> %Card{face: "king", suit: :clubs, value: 13} ...> ] iex> Compare.high_card(cards, :spades) {%Card{face: "2", suit: :spades, value: 2}, 1} iex> cards = [ ...> %Card{face: "2", suit: :spades, value: 2}, ...> %Card{face: "3", suit: :clubs, value: 3}, ...> %Card{face: "king", suit: :clubs, value: 13} ...> ] iex> Compare.high_card(cards, :spades) {%Card{face: "2", suit: :spades, value: 2}, 0} iex> cards = [ ...> %Card{face: "2", suit: :spades, value: 2}, ...> %Card{face: "3", suit: :clubs, value: 3}, ...> %Card{face: "king", suit: :spades, value: 13} ...> ] iex> Compare.high_card(cards, :diamonds) {%Card{face: "king", suit: :spades, value: 13}, 2} """ @spec high_card([%Card{}], atom | nil) :: {} def high_card(cards, trump \\ nil) when is_list(cards) and is_atom(trump) do [first | rest] = Enum.with_index(cards) compare(first, rest, trump) end @spec compare({%Card{}, integer}, [%Card{}], atom) :: {%Card{}, integer} defp compare({_card, _index} = high, [], _trump), do: high # when a trump card is encountered and it isn't the suit of the high card defp compare({%Card{suit: h_suit}, _}, [{%Card{suit: trump}, _} = card | rest], trump) when h_suit != trump do compare(card, rest, trump) end # when the card suits match then values are compared defp compare( {%Card{suit: suit, value: h_value}, _}, [{%Card{suit: suit, value: value}, _} = card | rest], trump ) when value > h_value do compare(card, rest, trump) end defp compare({%Card{}, _} = high, [{%Card{}, _} | rest], trump) do compare(high, rest, trump) end end