defmodule Hangman.Text.Client.Player do @moduledoc """ Plays a game until it is won, lost or stopped. """ alias Hangman.{Game, Engine} alias Hangman.Text.Client.{Mover, Prompter, State, Summary} @alphabet Enum.map(?a..?z, &<<&1>>) @bad :light_red @emit? true @good :light_green @lost :light_magenta @rep :light_yellow @won :light_cyan @doc """ Plays a game until it is won, lost or stopped. """ # won, lost, good guess, bad guess, already used, initializing... @spec play(State.t()) :: no_return def play(%State{tally: %{game_state: :won}} = state), do: end_game(state, [@won, "Bravo, you WON!"]) def play(%State{tally: %{game_state: :lost}} = state), do: end_game(state, [@lost, "Sorry, you lost."]) def play(%State{tally: %{game_state: :good_guess}} = state), do: continue(state, [@good, "Good guess!"]) def play(%State{tally: %{game_state: :bad_guess}} = state), do: continue(state, [@bad, "Bad guess..."]) def play(%State{tally: %{game_state: :already_used}} = state), do: continue(state, [@rep, "Already used..."]) # initializing def play(%State{} = state), do: continue(state) @doc """ Displays a `message` and ends the game normally. """ @spec end_game(State.t(), IO.ANSI.ansidata()) :: no_return def end_game(%State{tally: tally, game_name: game_name}, message) do message |> IO.ANSI.format(@emit?) |> IO.puts() tally = win_lose(tally, game_name) IO.puts("\nThe word was: #{Enum.join(tally.letters, " ")}") Engine.end_game(game_name) self() |> Process.exit(:normal) end ## Private functions @spec continue(State.t(), IO.ANSI.ansidata()) :: no_return defp continue(state, message) do message |> IO.ANSI.format(@emit?) |> IO.puts() continue(state) end @spec continue(State.t()) :: no_return defp continue(state) do state |> Summary.display() |> Prompter.accept_move() |> Mover.make_move() |> play() end @spec win_lose(Game.tally(), Game.name()) :: Game.tally() defp win_lose(%{game_state: game_state} = tally, _game_name) when game_state in [:won, :lost], do: tally defp win_lose(%{guesses: guesses}, game_name) do [guess | _] = @alphabet -- guesses Engine.make_move(game_name, guess) |> win_lose(game_name) end end