Packages
islands_engine
0.1.4
0.2.65
0.2.64
0.2.63
0.2.62
0.2.61
0.2.60
0.2.59
0.2.58
0.2.56
0.2.55
0.2.54
0.2.53
0.2.52
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.43
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.34
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.27
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.13
0.2.12
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.81
0.1.80
0.1.79
0.1.78
0.1.77
0.1.76
0.1.75
0.1.73
0.1.72
0.1.71
0.1.70
0.1.69
0.1.68
0.1.67
0.1.66
0.1.65
0.1.64
0.1.63
0.1.62
0.1.61
0.1.60
0.1.59
0.1.58
0.1.57
0.1.56
0.1.55
0.1.54
0.1.53
0.1.52
0.1.51
0.1.50
0.1.49
0.1.48
0.1.47
0.1.46
0.1.45
0.1.44
0.1.43
0.1.42
0.1.41
0.1.40
0.1.39
0.1.38
0.1.37
0.1.36
0.1.35
0.1.34
0.1.33
0.1.32
0.1.31
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.0
Models the Game of Islands.
Current section
Files
Jump to
Current section
Files
lib/islands/engine.ex
# ┌────────────────────────────────────────────────────────────────────┐
# │ Based on the book "Functional Web Development" by Lance Halvorsen. │
# └────────────────────────────────────────────────────────────────────┘
defmodule Islands.Engine do
use PersistConfig
@book_ref Application.get_env(@app, :book_ref)
@moduledoc """
Models an Islands game.
##### #{@book_ref}
"""
alias __MODULE__.{Coord, Game, Island, Server, Sup, Tally}
@board_range Application.get_env(@app, :board_range)
@island_types Application.get_env(@app, :island_types)
@player_ids Application.get_env(@app, :player_ids)
@doc """
Starts a new game.
## Examples
iex> alias Islands.Engine
iex> me = self()
iex> {:ok, game_id} = Engine.new_game("Meg", me)
iex> {:error, {:already_started, ^game_id}} = Engine.new_game("Meg", me)
iex> is_pid(game_id)
true
"""
@spec new_game(String.t(), pid) :: Supervisor.on_start_child()
def new_game(player1_name, player1_pid)
when is_binary(player1_name) and is_pid(player1_pid) do
DynamicSupervisor.start_child(Sup, {Server, {player1_name, player1_pid}})
end
@doc """
Ends a game.
## Examples
iex> alias Islands.Engine
iex> me = self()
iex> Engine.new_game("Ben", me)
iex> Engine.end_game("Ben")
:ok
"""
@spec end_game(String.t()) :: :ok
def end_game(player1_name) when is_binary(player1_name) do
player1_name |> Server.via() |> GenServer.stop(:shutdown)
end
@doc """
Stops a game.
# ## Examples
# iex> alias Islands.Engine
# iex> me = self()
# iex> Engine.new_game("Ben", me)
# iex> Engine.end_game("Ben")
# :ok
"""
@spec stop_game(String.t(), Game.player_id()) :: Tally.t()
def stop_game(player1_name, player_id)
when is_binary(player1_name) and player_id in @player_ids do
player1_name |> Server.via() |> GenServer.call({:stop, player_id})
end
@doc """
Adds the second player of a game.
## Examples
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> him = self()
iex> her = self()
iex> {:ok, game_id} = Engine.new_game("Romeo", him)
iex> tally = Engine.add_player("Romeo", "Juliet", her)
iex> %Tally{
...> game_state: :players_set,
...> player1_state: :islands_not_set,
...> player2_state: :islands_not_set,
...> request: {:add_player, "Juliet", player2_pid},
...> response: {:ok, :player2_added},
...> board: board,
...> guesses: guesses
...> } = tally
iex> board == Grid.new() and guesses == Grid.new() and
...> player2_pid == her and is_pid(game_id)
true
"""
@spec add_player(String.t(), String.t(), pid) :: Tally.t()
def add_player(player1_name, player2_name, player2_pid)
when is_binary(player1_name) and is_binary(player2_name) and
is_pid(player2_pid) do
player1_name
|> Server.via()
|> GenServer.call({:add_player, player2_name, player2_pid})
end
@doc """
Positions an island on a player's board.
# Examples
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> her = self()
iex> him = self()
iex> {:ok, game_id} = Engine.new_game("Bonnie", her)
iex> Engine.add_player("Bonnie", "Clyde", him)
iex> tally = Engine.position_island("Bonnie", :player2, :atoll, 1, 1)
iex> %Tally{
...> game_state: :players_set,
...> player1_state: :islands_not_set,
...> player2_state: :islands_not_set,
...> request: {:position_island, :player2, :atoll, 1, 1},
...> response: {:ok, :island_positioned},
...> board: board,
...> guesses: guesses
...> } = tally
iex> is_map(board) and guesses == Grid.new() and is_pid(game_id)
true
"""
@spec position_island(
String.t(),
Game.player_id(),
Island.type(),
Coord.row(),
Coord.col()
) :: Tally.t()
def position_island(player1_name, player_id, island_type, row, col)
when is_binary(player1_name) and player_id in @player_ids and
island_type in @island_types and row in @board_range and
col in @board_range do
player1_name
|> Server.via()
|> GenServer.call({:position_island, player_id, island_type, row, col})
end
@doc """
Declares all islands set for a player.
## Examples
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> him = self()
iex> her = self()
iex> {:ok, game_id} = Engine.new_game("Adam", him)
iex> Engine.add_player("Adam", "Eve", her)
iex> Engine.position_island("Adam", :player2, :atoll, 1, 1)
iex> tally = Engine.set_islands("Adam", :player2)
iex> %Tally{
...> game_state: :players_set,
...> player1_state: :islands_not_set,
...> player2_state: :islands_not_set,
...> request: {:set_islands, :player2},
...> response: {:error, :not_all_islands_positioned},
...> board: board,
...> guesses: guesses
...> } = tally
iex> is_map(board) and guesses == Grid.new() and is_pid(game_id)
true
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> her = self()
iex> him = self()
iex> {:ok, game_id} = Engine.new_game("Mary", her)
iex> Engine.add_player("Mary", "Joseph", him)
iex> Engine.position_island("Mary", :player2, :atoll, 1, 1)
iex> Engine.position_island("Mary", :player2, :l_shape, 3, 7)
iex> Engine.position_island("Mary", :player2, :s_shape, 6, 2)
iex> Engine.position_island("Mary", :player2, :square, 9, 5)
iex> Engine.position_island("Mary", :player2, :dot, 9, 9)
iex> tally = Engine.set_islands("Mary", :player2)
iex> %Tally{
...> game_state: :players_set,
...> player1_state: :islands_not_set,
...> player2_state: :islands_set,
...> request: {:set_islands, :player2},
...> response: {:ok, :islands_set},
...> board: board,
...> guesses: guesses
...> } = tally
iex> is_map(board) and guesses == Grid.new() and is_pid(game_id)
true
"""
@spec set_islands(String.t(), Game.player_id()) :: Tally.t()
def set_islands(player1_name, player_id)
when is_binary(player1_name) and player_id in @player_ids do
player1_name |> Server.via() |> GenServer.call({:set_islands, player_id})
end
@doc """
Allows a player to guess a coordinate.
# ## Examples
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> him = self()
iex> her = self()
iex> {:ok, game_id} = Engine.new_game("Caesar", him)
iex> Engine.add_player("Caesar", "Cleopatra", her)
iex> Engine.position_island("Caesar", :player2, :atoll, 1, 1)
iex> Engine.position_island("Caesar", :player2, :l_shape, 3, 7)
iex> Engine.position_island("Caesar", :player2, :s_shape, 6, 2)
iex> Engine.position_island("Caesar", :player2, :square, 9, 5)
iex> Engine.position_island("Caesar", :player2, :dot, 9, 9)
iex> Engine.set_islands("Caesar", :player2)
iex> tally = Engine.guess_coord("Caesar", :player1, 9, 9)
iex> %Tally{
...> game_state: :players_set,
...> player1_state: :islands_not_set,
...> player2_state: :islands_set,
...> request: {:guess_coord, :player1, 9, 9},
...> response: {:error, :islands_not_set},
...> board: board,
...> guesses: guesses
...> } = tally
iex> board == Grid.new() and guesses == Grid.new() and is_pid(game_id)
true
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> him = self()
iex> her = self()
iex> {:ok, game_id} = Engine.new_game("Tristan", him)
iex> Engine.add_player("Tristan", "Iseult", her)
iex> Engine.position_island("Tristan", :player2, :atoll, 1, 1)
iex> Engine.position_island("Tristan", :player2, :l_shape, 3, 7)
iex> Engine.position_island("Tristan", :player2, :s_shape, 6, 2)
iex> Engine.position_island("Tristan", :player2, :square, 9, 5)
iex> Engine.position_island("Tristan", :player2, :dot, 9, 9)
iex> Engine.set_islands("Tristan", :player2)
iex> Engine.position_island("Tristan", :player1, :atoll, 1, 1)
iex> Engine.position_island("Tristan", :player1, :l_shape, 3, 7)
iex> Engine.position_island("Tristan", :player1, :s_shape, 6, 2)
iex> Engine.position_island("Tristan", :player1, :square, 9, 5)
iex> Engine.position_island("Tristan", :player1, :dot, 9, 9)
iex> Engine.set_islands("Tristan", :player1)
iex> tally = Engine.guess_coord("Tristan", :player1, 9, 9)
iex> %Tally{
...> game_state: :player2_turn,
...> player1_state: :islands_set,
...> player2_state: :islands_set,
...> request: {:guess_coord, :player1, 9, 9},
...> response: {:hit, :dot, :no_win},
...> board: board,
...> guesses: guesses
...> } = tally
iex> is_map(board) and is_map(guesses) and is_pid(game_id)
true
"""
@spec guess_coord(String.t(), Game.player_id(), Coord.row(), Coord.col()) ::
Tally.t()
def guess_coord(player1_name, player_id, row, col)
when is_binary(player1_name) and player_id in @player_ids and
row in @board_range and col in @board_range do
player1_name
|> Server.via()
|> GenServer.call({:guess_coord, player_id, row, col})
end
@doc """
Returns the tally of a game for a given player.
# Examples
iex> alias Islands.Engine
iex> alias Islands.Engine.{Grid, Tally}
iex> him = self()
iex> {:ok, game_id} = Engine.new_game("Jim", him)
iex> tally = Engine.tally("Jim", :player1)
iex> %Tally{
...> game_state: :initialized,
...> player1_state: :islands_not_set,
...> player2_state: :islands_not_set,
...> request: {},
...> response: {},
...> board: board,
...> guesses: guesses
...> } = tally
iex> board == Grid.new() and guesses == Grid.new() and is_pid(game_id)
true
"""
@spec tally(String.t(), Game.player_id()) :: Tally.t()
def tally(player1_name, player_id)
when is_binary(player1_name) and player_id in @player_ids do
player1_name |> Server.via() |> GenServer.call({:tally, player_id})
end
end