Current section
Files
Jump to
Current section
Files
lib/deck.ex
defmodule Deck do
@moduledoc """
Get, shuffle and deal a deck of playing cards of any size (from 4 to 52 cards).
"""
import Deck.Guards, only: [is_valid_number_of_cards: 1]
# T = 10, J = Jack, Q = Queen, K = King, A = Ace
@ranks ~w[2 3 4 5 6 7 8 9 T J Q K A]
# Clubs, diamonds, hearts, spades
@suits ~w[c d h s]
# A full deck of cards ordered by rank
@deck for rank <- @ranks, suit <- @suits, do: "#{rank}#{suit}"
@doc """
Returns a full deck of cards (52 cards) ordered by rank.
## Examples
iex> Deck.new()
["2c", "2d", "2h", "2s", "3c", "3d", "3h", "3s", "4c", "4d", "4h", "4s",
"5c", "5d", "5h", "5s", "6c", "6d", "6h", "6s", "7c", "7d", "7h", "7s",
"8c", "8d", "8h", "8s", "9c", "9d", "9h", "9s", "Tc", "Td", "Th", "Ts",
"Jc", "Jd", "Jh", "Js", "Qc", "Qd", "Qh", "Qs", "Kc", "Kd", "Kh", "Ks",
"Ac", "Ad", "Ah", "As"]
"""
def new do
@deck
end
@doc """
Returns a deck containing high `n_cards` ordered by rank.
`n_cards` must be less than or equal 52 and must be dividable by 4.
## Examples
iex> Deck.new(12)
["Qc", "Qd", "Qh", "Qs", "Kc", "Kd", "Kh", "Ks", "Ac", "Ad", "Ah", "As"]
"""
def new(n_cards) when is_valid_number_of_cards(n_cards) do
new() |> Enum.take(-n_cards)
end
@doc """
Returns a deck containing low `n_cards` ordered by rank.
`n_cards` must be less than or equal 52 and must be dividable by 4.
## Examples
iex> Deck.new_low(12)
["2c", "2d", "2h", "2s", "3c", "3d", "3h", "3s", "4c", "4d", "4h", "4s"]
"""
def new_low(n_cards) when is_valid_number_of_cards(n_cards) do
new() |> Enum.take(n_cards)
end
@doc """
Returns a full shuffled deck of cards (52 cards).
## Examples
iex> Deck.shuffled() != Deck.new()
true
"""
def shuffled do
new() |> shuffle
end
@doc """
Returns a shuffled deck containing high `n_cards`.
`n_cards` must be less than or equal 52 and must be dividable by 4.
## Examples
iex> Deck.shuffled(36) != Deck.new(36)
true
"""
def shuffled(n_cards) when is_valid_number_of_cards(n_cards) do
new(n_cards) |> shuffle
end
@doc """
Returns a shuffled deck containing low `n_cards`.
`n_cards` must be less than or equal 52 and must be dividable by 4.
## Examples
iex> Deck.shuffled_low(36) != Deck.new_low(36)
true
"""
def shuffled_low(n_cards) when is_valid_number_of_cards(n_cards) do
new_low(n_cards) |> shuffle
end
@doc """
Shuffles the `deck` using the modern version of the Fisher–Yates shuffle algorithm.
## Examples
iex> deck = Deck.new()
...> shuffled_deck = Deck.shuffle(deck)
...> shuffled_deck != deck
true
iex> Deck.shuffle(shuffled_deck) != Deck.shuffle(shuffled_deck)
true
"""
def shuffle(deck) when is_list(deck) do
do_shuffle(deck, length(deck), [])
end
defp do_shuffle([last_card | _empty_deck], 1, shuffled_deck), do: [last_card | shuffled_deck]
defp do_shuffle(deck, cards_left, shuffled_deck) do
random_card_index = :rand.uniform(cards_left) - 1
random_card = Enum.at(deck, random_card_index)
{current_last_card, deck_leftovers} = List.pop_at(deck, -1)
deck_leftovers
|> List.replace_at(random_card_index, current_last_card)
|> do_shuffle(cards_left - 1, [random_card | shuffled_deck])
end
@doc """
Returns `n_cards` from the `deck` and the rest of the `deck`.
## Example
iex> Deck.deal(Deck.new(8), 2)
{["Kc", "Kd"], ["Kh", "Ks", "Ac", "Ad", "Ah", "As"]}
"""
def deal(deck, n_cards) when is_list(deck) and is_integer(n_cards) and n_cards > 0 do
deck |> Enum.split(n_cards)
end
@doc """
"Burns" `n_cards` in the `deck` and returns the rest of the `deck`.
## Example
iex> Deck.burn(Deck.new(8), 6)
["Ah", "As"]
"""
def burn(deck, n_cards) when is_list(deck) and is_integer(n_cards) and n_cards > 0 do
deck |> Enum.drop(n_cards)
end
@doc """
Returns number of cards left in the `deck`.
## Example
iex> Deck.size(Deck.new(12))
12
"""
def size(deck) when is_list(deck) do
deck |> Enum.count()
end
end