Add doctests for Card and Deck modules.
This commit is contained in:
parent
21a0673ba2
commit
4210670e9e
4 changed files with 174 additions and 4 deletions
|
|
@ -1,4 +1,8 @@
|
|||
defmodule PokerEx.Card do
|
||||
@doc ~S"""
|
||||
Holds functions that operate and symbolise a playing card.
|
||||
"""
|
||||
|
||||
alias __MODULE__
|
||||
|
||||
@valid_suits [:spades, :hearts, :diamonds, :clubs]
|
||||
|
|
@ -15,21 +19,85 @@ defmodule PokerEx.Card do
|
|||
when (is_integer(rank) and 2 <= rank and rank <= 10) or
|
||||
rank in [:jack, :queen, :king, :ace]
|
||||
|
||||
@doc ~S"""
|
||||
Checks if a given card is valid. To be valid, it should be the card
|
||||
string with a valid suit and rank, which are specified as module types.
|
||||
|
||||
Valid suits: :hearts, :diamonds, :clubs, :spades
|
||||
Valid ranks: Numbers 2-10, :jack, :queen, :king, :ace
|
||||
|
||||
## Examples
|
||||
|
||||
iex> PokerEx.Card.valid? %PokerEx.Card{suit: :spades, rank: :ace}
|
||||
true
|
||||
|
||||
iex> PokerEx.Card.valid? %PokerEx.Card{suit: :non_existent, rank: 2}
|
||||
false
|
||||
|
||||
iex> PokerEx.Card.valid? %PokerEx.Card{suit: :diamonds, rank: 11}
|
||||
false
|
||||
"""
|
||||
@spec valid?(Card.t()) :: boolean
|
||||
def valid?(%Card{suit: suit, rank: rank}) when is_valid_rank(rank) and is_valid_suit(suit),
|
||||
do: true
|
||||
|
||||
def valid?(_card), do: false
|
||||
|
||||
@doc ~S"""
|
||||
Returns all valid suits as array.
|
||||
"""
|
||||
@spec all_suits() :: [suit()]
|
||||
def all_suits(), do: @valid_suits
|
||||
|
||||
@doc ~S"""
|
||||
Returns all cards with a given suit. This means all the numbers
|
||||
2 through 10, the jack, queen, king, and ace.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> PokerEx.Card.all_cards_for_suit :diamonds
|
||||
[
|
||||
%PokerEx.Card{suit: :diamonds, rank: 2},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 3},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 4},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 5},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 6},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 7},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 8},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 9},
|
||||
%PokerEx.Card{suit: :diamonds, rank: 10},
|
||||
%PokerEx.Card{suit: :diamonds, rank: :jack},
|
||||
%PokerEx.Card{suit: :diamonds, rank: :queen},
|
||||
%PokerEx.Card{suit: :diamonds, rank: :king},
|
||||
%PokerEx.Card{suit: :diamonds, rank: :ace}
|
||||
]
|
||||
"""
|
||||
@spec all_cards_for_suit(suit()) :: [Card.t()]
|
||||
def all_cards_for_suit(suit) when is_valid_suit(suit) do
|
||||
[2, 3, 4, 5, 6, 7, 8, 9, 10, :jack, :queen, :king, :ace]
|
||||
|> Enum.map(fn rank -> %Card{suit: suit, rank: rank} end)
|
||||
end
|
||||
|
||||
@doc ~S"""
|
||||
Makes a card struct from the given integers. Mostly used when parsing
|
||||
the unicode glyph into a card.
|
||||
|
||||
The jack, queen, king, and ace are 11, 13, 14, 1.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> PokerEx.Card.from_integers(0, 5)
|
||||
{:ok, %PokerEx.Card{suit: :spades, rank: 5}}
|
||||
|
||||
iex> PokerEx.Card.from_integers(5, 5)
|
||||
{:error, "Invalid integer suit: 5"}
|
||||
|
||||
iex> PokerEx.Card.from_integers(0, 1)
|
||||
{:ok, %PokerEx.Card{suit: :spades, rank: :ace}}
|
||||
|
||||
iex> PokerEx.Card.from_integers(0, 15)
|
||||
{:error, "Invalid integer rank: 15"}
|
||||
"""
|
||||
@spec from_integers(integer, integer) :: {:ok, Card.t()} | {:error, String.t()}
|
||||
def from_integers(suit, _rank)
|
||||
when suit < 0
|
||||
|
|
@ -38,7 +106,7 @@ defmodule PokerEx.Card do
|
|||
end
|
||||
|
||||
def from_integers(_suit, rank)
|
||||
when rank < 2
|
||||
when rank < 1
|
||||
when rank > 0xE do
|
||||
{:error, "Invalid integer rank: #{rank}"}
|
||||
end
|
||||
|
|
@ -46,18 +114,36 @@ defmodule PokerEx.Card do
|
|||
def from_integers(suit, rank) do
|
||||
suit = Enum.at(@valid_suits, suit)
|
||||
|
||||
case rank do
|
||||
{:ok, case rank do
|
||||
n when n >= 2 and n <= 10 -> %Card{suit: suit, rank: n}
|
||||
0x1 -> %Card{suit: suit, rank: :ace}
|
||||
0xB -> %Card{suit: suit, rank: :jack}
|
||||
0xD -> %Card{suit: suit, rank: :queen}
|
||||
0xE -> %Card{suit: suit, rank: :king}
|
||||
end
|
||||
end}
|
||||
end
|
||||
|
||||
@doc ~S"""
|
||||
Converts a unicode playing card glyph into a playing card struct.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> PokerEx.Card.from_string "🂸"
|
||||
{:ok, %PokerEx.Card{suit: :hearts, rank: 8}}
|
||||
|
||||
iex> PokerEx.Card.from_string "🃍"
|
||||
{:ok, %PokerEx.Card{suit: :diamonds, rank: :queen}}
|
||||
|
||||
iex> PokerEx.Card.from_string "Hello world"
|
||||
{:error, "Not a valid card glyph!"}
|
||||
|
||||
iex> PokerEx.Card.from_string "💩"
|
||||
{:error, "Not a valid card glyph!"}
|
||||
"""
|
||||
@spec from_string(String.t()) :: {:ok, Card.t()} | {:error, String.t()}
|
||||
def from_string(card_rep) do
|
||||
case card_rep do
|
||||
<<codepoint::utf8>> ->
|
||||
<<codepoint::utf8>> when codepoint > 0x1F0A0 and codepoint < 0x1F0DF ->
|
||||
offset = codepoint - ?🂠
|
||||
suit = div(offset, 0x10)
|
||||
rank = rem(offset, 0x10)
|
||||
|
|
@ -69,6 +155,43 @@ defmodule PokerEx.Card do
|
|||
end
|
||||
end
|
||||
|
||||
@doc ~S"""
|
||||
Provides a helper function to easily generate a card.
|
||||
Takes the short-hand notation for a suit, and then a rank.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> import PokerEx.Card
|
||||
iex> ~p"CA"
|
||||
%PokerEx.Card{suit: :clubs, rank: :ace}
|
||||
iex> ~p"D10"
|
||||
%PokerEx.Card{suit: :diamonds, rank: 10}
|
||||
"""
|
||||
@spec sigil_p(atom() | String.t(), [char()]) :: Card.t()
|
||||
def sigil_p(<<suit::utf8, rank::binary>>, []) do
|
||||
suit = case suit do
|
||||
?S -> :spades
|
||||
?H -> :hearts
|
||||
?D -> :diamonds
|
||||
?C -> :clubs
|
||||
end
|
||||
|
||||
rank = case rank do
|
||||
"A" -> :ace
|
||||
"K" -> :king
|
||||
"Q" -> :queen
|
||||
"J" -> :jack
|
||||
n -> String.to_integer(n)
|
||||
end
|
||||
|
||||
card = %Card{suit: suit, rank: rank}
|
||||
if not valid? card do
|
||||
raise "Invalid card!"
|
||||
end
|
||||
|
||||
card
|
||||
end
|
||||
|
||||
defimpl Inspect, for: Card do
|
||||
@suit_letters %{spades: "S", hearts: "H", diamonds: "D", clubs: "C"}
|
||||
@rank_letters %{ace: "A", jack: "J", queen: "Q", king: "K"}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,51 @@ defmodule PokerEx.Deck do
|
|||
|
||||
@type deck() :: [Card.t()]
|
||||
|
||||
@doc ~S"""
|
||||
Creates a new empty deck. This deck is sorted and contains all cards
|
||||
2-10, jack, queen, king, and ace for ranks spades, hearts, diamonds, and clubs.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> deck = PokerEx.Deck.new()
|
||||
iex> PokerEx.Deck.valid? deck
|
||||
true
|
||||
iex> length deck
|
||||
52
|
||||
iex> Enum.take(deck, 3)
|
||||
[
|
||||
%PokerEx.Card{suit: :spades, rank: 2},
|
||||
%PokerEx.Card{suit: :spades, rank: 3},
|
||||
%PokerEx.Card{suit: :spades, rank: 4}
|
||||
]
|
||||
"""
|
||||
@spec new() :: deck()
|
||||
def new(), do: Card.all_suits() |> Enum.flat_map(&Card.all_cards_for_suit/1)
|
||||
|
||||
@spec new_shuffled() :: deck()
|
||||
def new_shuffled(), do: new() |> Enum.shuffle()
|
||||
|
||||
@doc ~S"""
|
||||
Returns true if a deck is valid. A deck is valid if, and only if,
|
||||
it contains valid cards. There is no limitation on the size.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> PokerEx.Deck.valid? PokerEx.Deck.new()
|
||||
true
|
||||
|
||||
iex> PokerEx.Deck.valid? PokerEx.Deck.new_shuffled()
|
||||
true
|
||||
|
||||
iex> PokerEx.Deck.valid? ["Haha!" | PokerEx.Deck.new()]
|
||||
false
|
||||
|
||||
iex> PokerEx.Deck.valid? [%PokerEx.Card{suit: :diamonds, rank: 5}]
|
||||
true
|
||||
|
||||
iex> PokerEx.Deck.valid? []
|
||||
true
|
||||
"""
|
||||
@spec valid?(deck()) :: boolean
|
||||
def valid?(deck), do: deck |> Enum.all?(&Card.valid?/1)
|
||||
end
|
||||
|
|
|
|||
4
poker_ex/test/poker_ex/card_test.exs
Normal file
4
poker_ex/test/poker_ex/card_test.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
defmodule PokerEx.CardTest do
|
||||
use ExUnit.Case
|
||||
doctest PokerEx.Card
|
||||
end
|
||||
4
poker_ex/test/poker_ex/deck_test.exs
Normal file
4
poker_ex/test/poker_ex/deck_test.exs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
defmodule PokerEx.DeckTest do
|
||||
use ExUnit.Case
|
||||
doctest PokerEx.Deck
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue