# ┌──────────────────────────────────────────────────────────────┐
# │ Based on the course "Elixir for Programmers" by Dave Thomas. │
# └──────────────────────────────────────────────────────────────┘
defmodule Hangman.Dictionary do
@moduledoc """
Dictionary for the _Hangman Game_. Returns a random word.
##### Based on the course [Elixir for Programmers](https://codestool.coding-gnome.com/courses/elixir-for-programmers) by Dave Thomas.
"""
alias __MODULE__.WordsAgent
@typedoc "A word with letters from a to z"
@type word :: String.t()
@doc """
Returns a random word from the dictionary agent.
## Examples
iex> alias Hangman.Dictionary
iex> for _ <- 0..99, uniq: true do
iex> Dictionary.random_word() =~ ~r/^[a-z]+$/
iex> end
[true]
iex> alias Hangman.Dictionary.WordsAgent
iex> words = Agent.get(WordsAgent, & &1)
iex> Enum.all?(words, & &1 =~ ~r/^[a-z]+$/)
true
"""
@spec random_word :: word
def random_word, do: Agent.get(WordsAgent, &Enum.random/1)
@doc """
Returns the number of words in the dictionary agent.
## Examples
iex> alias Hangman.Dictionary
iex> Dictionary.word_count
57708
"""
@spec word_count :: pos_integer
def word_count, do: Agent.get(WordsAgent, &length/1)
@doc """
Returns the shortest words in the dictionary agent (shorter than `ceil`).
In iex, you may want to run:
`IEx.configure(inspect: [limit: :infinity])`
## Examples
iex> alias Hangman.Dictionary
iex> Dictionary.shortest_words() |> Enum.take(49) |> Enum.take(-10)
["act", "add", "ado", "ads", "adz", "aft", "age", "ago", "aid", "ail"]
iex> alias Hangman.Dictionary
iex> Dictionary.shortest_words(2)
["a", "i"]
"""
@spec shortest_words(pos_integer) :: [word]
def shortest_words(ceil \\ 5) do
Agent.get(WordsAgent, fn words ->
words
|> Stream.filter(&(byte_size(&1) < ceil))
|> Enum.sort_by(&{byte_size(&1), &1})
end)
end
@doc """
Returns the longest words in the dictionary agent (longer than `floor`).
Note that word lengths will be enclosed in parentheses after each word.
In iex, you may want to run:
`IEx.configure(inspect: [limit: :infinity])`
## Examples
iex> alias Hangman.Dictionary
iex> Dictionary.longest_words() |> Enum.take(9)
[
"telecommunications (18)",
"characterization (16)",
"responsibilities (16)",
"sublimedirectory (16)",
"characteristics (15)",
"confidentiality (15)",
"congratulations (15)",
"instrumentation (15)",
"internationally (15)"
]
iex> alias Hangman.Dictionary
iex> Dictionary.longest_words(14)
[
"telecommunications (18)",
"characterization (16)",
"responsibilities (16)",
"sublimedirectory (16)",
"characteristics (15)",
"confidentiality (15)",
"congratulations (15)",
"instrumentation (15)",
"internationally (15)",
"pharmaceuticals (15)",
"recommendations (15)",
"representations (15)",
"representatives (15)",
"troubleshooting (15)"
]
"""
@spec longest_words(non_neg_integer) :: [String.t()]
def longest_words(floor \\ 13) do
Agent.get(WordsAgent, fn words ->
words
|> Stream.filter(&(byte_size(&1) > floor))
|> Stream.map(&"#{&1} (#{byte_size(&1)})")
|> Enum.sort_by(&{-byte_size(&1), &1})
end)
end
@doc """
Returns all the words in the dictionary agent of length `word_length`.
In iex, you may want to run:
`IEx.configure(inspect: [limit: :infinity])`
## Examples
iex> alias Hangman.Dictionary
iex> Dictionary.words_of_length(16)
["characterization", "responsibilities", "sublimedirectory"]
iex> alias Hangman.Dictionary
iex> Dictionary.words_of_length(3) |> Enum.take(10)
["ace", "act", "add", "ado", "ads", "adz", "aft", "age", "ago", "aid"]
"""
@spec words_of_length(pos_integer) :: [word]
def words_of_length(word_length) do
Agent.get(WordsAgent, fn words ->
words
|> Stream.filter(&(byte_size(&1) == word_length))
|> Enum.sort()
end)
end
@doc """
Returns all the repeated words in the dictionary agent.
Note that repetition counts will be enclosed in parentheses after each word.
## Examples
iex> alias Hangman.Dictionary
iex> Dictionary.repeated_words()
["echo (3)", "hello (2)"]
"""
@spec repeated_words :: [String.t()]
def repeated_words do
Agent.get(WordsAgent, fn words ->
words
|> Enum.frequencies()
|> Stream.filter(fn {_word, count} -> count > 1 end)
|> Enum.sort_by(fn {word, count} -> {-count, word} end)
|> Enum.map(fn {word, count} -> "#{word} (#{count})" end)
end)
end
end