# ┌──────────────────────────────────────────────────────────────┐
# │ Based on the course "Elixir for Programmers" by Dave Thomas. │
# └──────────────────────────────────────────────────────────────┘
defmodule Hangman.Engine do
@moduledoc """
Models the _Hangman Game_. Times out after 30 minutes of inactivity.
##### Based on the course [Elixir for Programmers](https://codestool.coding-gnome.com/courses/elixir-for-programmers) by Dave Thomas.
"""
alias __MODULE__.{DynGameSup, GameServer}
alias Hangman.Game
@doc """
Starts a new game server process and supervises it.
## Examples
iex> alias Hangman.Engine
iex> {:ok, game_id} = Engine.new_game("Meg")
iex> {:error, {:already_started, ^game_id}} = Engine.new_game("Meg")
iex> is_pid(game_id)
true
"""
@spec new_game(Game.name()) :: Supervisor.on_start_child()
def new_game(game_name) when is_binary(game_name) do
DynamicSupervisor.start_child(DynGameSup, {GameServer, game_name})
end
@doc """
Stops a game server process normally. It won't be restarted.
## Examples
iex> alias Hangman.Engine
iex> Engine.new_game("Ben")
iex> Engine.end_game("Ben")
:ok
"""
@spec end_game(Game.name()) :: :ok
def end_game(game_name) when is_binary(game_name) do
GameServer.via(game_name) |> GenServer.stop(:shutdown)
end
@doc """
Returns the tally of a game.
## Examples
iex> alias Hangman.Engine
iex> Engine.new_game("Jim")
iex> tally = Engine.tally("Jim")
iex> %{
...> game_state: :initializing,
...> turns_left: 7,
...> letters: letters,
...> guesses: []
...> } = tally
iex> all_underscores? = Enum.all?(letters, & &1 == "_")
iex> is_list(letters) and length(letters) > 0 and all_underscores?
true
"""
@spec tally(Game.name()) :: Game.tally()
def tally(game_name) when is_binary(game_name) do
GameServer.via(game_name) |> GenServer.call(:tally)
end
@doc """
Makes a move by guessing a letter.
## Examples
iex> alias Hangman.Engine
iex> Engine.new_game("Ed")
iex> Engine.make_move("Ed", "a").game_state in [:good_guess, :bad_guess]
true
"""
@spec make_move(Game.name(), Game.letter()) :: Game.tally()
def make_move(game_name, guess)
when is_binary(game_name) and is_binary(guess) do
GameServer.via(game_name) |> GenServer.call({:make_move, guess})
end
end