# ┌──────────────────────────────────────────────────────────────┐ # │ 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