Packages
islands_engine
0.1.7
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/server.ex
defmodule Islands.Engine.Server do
@moduledoc false
use GenServer, restart: :transient
use PersistConfig
alias __MODULE__
alias Islands.Engine.{Board, Coord, Error, Game, Island, State, Tally}
require Logger
@typep from :: GenServer.from()
@ets Application.get_env(@app, :ets_name)
@phrase "saving..."
# @reg Application.get_env(@app, :registry)
@spec start_link({String.t(), pid}) :: GenServer.on_start()
def start_link({player1_name, pid}) do
GenServer.start_link(Server, {player1_name, pid}, name: via(player1_name))
end
# @spec via(String.t()) :: {:via, module, {atom, tuple}}
# def via(player1_name), do: {:via, Registry, {@reg, key(player1_name)}}
@spec via(String.t()) :: {:global, tuple}
def via(player1_name), do: {:global, key(player1_name)}
## Private functions
@spec key(String.t()) :: tuple
defp key(player1_name), do: {Server, player1_name}
@spec save(Game.t()) :: Game.t()
defp save(game) do
game |> text() |> Logger.info()
true = :ets.insert(@ets, {key(game.player1.name), game})
game
end
@spec text(Game.t(), String.t()) :: String.t()
defp text(game, phrase \\ @phrase) do
"""
#{game.player1.name |> key() |> inspect()} #{self() |> inspect()}
#{phrase}
#{inspect(game, pretty: true)}
"""
end
@spec game(String.t(), pid) :: Game.t()
defp game(player1_name, pid) do
case :ets.lookup(@ets, key(player1_name)) do
[] ->
player1_name
|> Game.new()
|> Game.update_player_pid(:player1, pid)
|> save()
[{_key, game}] ->
game
end
end
@spec reply(Game.t(), Game.player_id()) :: {:reply, Tally.t(), Game.t()}
defp reply(game, player_id), do: {:reply, Tally.new(game, player_id), game}
## Callbacks
@spec init({String.t(), pid}) :: {:ok, Game.t()}
def init({player1_name, pid}), do: {:ok, game(player1_name, pid)}
# def handle_call({:add_player, _, _} = request, from, game) do
# AddPlayer.handle_call(request, from, game)
# end
@spec handle_call(term, from, Game.t()) :: {:reply, Tally.t(), Game.t()}
def handle_call({:add_player = action, name, pid} = request, _from, game) do
with {:ok, state} <- State.check(game.state, action) do
game
|> Game.update_player2_name(name)
|> Game.update_player_pid(:player2, pid)
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response({:ok, :player2_added})
|> Game.notify_player(:player1)
|> save()
|> reply(:player2)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :player2_already_added})
|> save()
|> reply(:player2)
end
end
# def handle_call({:position_island, _, _, _, _} = request, from, game) do
# PositionIsland.handle_call(request, from, game)
# end
def handle_call(
{:position_island = action, player_id, island_type, row, col} = request,
_from,
game
) do
with {:ok, state} <- State.check(game.state, {action, player_id}),
{:ok, origin} <- Coord.new(row, col),
{:ok, island} <- Island.new(island_type, origin),
%Board{} = board <- Game.player_board(game, player_id),
%Board{} = board <- Board.position_island(board, island) do
response =
{:ok,
if Board.all_islands_positioned?(board) do
GenServer.cast(self(), {:persist_board, board})
:all_islands_positioned
else
:island_positioned
end}
game
|> Game.update_board(player_id, board)
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response(response)
|> save()
|> reply(player_id)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :islands_already_set})
|> save()
|> reply(player_id)
{:error, reason} ->
game
|> Game.update_request(request)
|> Game.update_response({:error, reason})
|> save()
|> reply(player_id)
non_matched_value ->
Error.log(non_matched_value, request)
game
|> Game.update_request(request)
|> Game.update_response({:error, :unknown})
|> save()
|> reply(player_id)
end
end
def handle_call(
{:position_all_islands = action, player_id} = request,
_from,
game
) do
with {:ok, state} <- State.check(game.state, {action, player_id}),
%Board{} = board <- Board.restore() do
game
|> Game.update_board(player_id, board)
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response({:ok, :all_islands_positioned})
|> save()
|> reply(player_id)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :islands_already_set})
|> save()
|> reply(player_id)
{:error, reason} ->
game
|> Game.update_request(request)
|> Game.update_response({:error, reason})
|> save()
|> reply(player_id)
non_matched_value ->
Error.log(non_matched_value, request)
game
|> Game.update_request(request)
|> Game.update_response({:error, :unknown})
|> save()
|> reply(player_id)
end
end
def handle_call({:stop = action, player_id} = request, _from, game) do
with {:ok, state} <- State.check(game.state, action) do
opponent_id = Game.opponent(player_id)
game
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response({:ok, :stopping})
|> Game.notify_player(opponent_id)
|> save()
|> reply(player_id)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :not_both_players_islands_set})
|> save()
|> reply(player_id)
end
end
def handle_call({:set_islands = action, player_id} = request, _from, game) do
with {:ok, state} <- State.check(game.state, {action, player_id}),
%Board{} = board <- Game.player_board(game, player_id),
true <- Board.all_islands_positioned?(board) do
opponent_id = Game.opponent(player_id)
game
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response({:ok, :islands_set})
|> Game.notify_player(opponent_id)
|> save()
|> reply(player_id)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :both_players_islands_set})
|> save()
|> reply(player_id)
false ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :not_all_islands_positioned})
|> save()
|> reply(player_id)
_other ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :unknown})
|> save()
|> reply(player_id)
end
end
# def handle_call({:guess_coord, _, _, _} = request, from, game) do
# GuessCoord.handle_call(request, from, game)
# end
def handle_call(
{:guess_coord = action, player_id, row, col} = request,
_from,
game
) do
with {:ok, state} <- State.check(game.state, {action, player_id}),
{:ok, guess} <- Coord.new(row, col),
opponent_id = Game.opponent(player_id),
%Board{} = opponent_board <- Game.player_board(game, opponent_id),
{hit_or_miss, forested_island_type, win_status, opponent_board} <-
Board.guess(opponent_board, guess),
{:ok, state} <- State.check(state, {:win_check, win_status}) do
game
|> Game.update_board(opponent_id, opponent_board)
|> Game.update_guesses(player_id, hit_or_miss, guess)
|> Game.update_state(state)
|> Game.update_request(request)
|> Game.update_response({hit_or_miss, forested_island_type, win_status})
|> Game.notify_player(opponent_id)
|> save()
|> reply(player_id)
else
:error ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :islands_not_set})
|> save()
|> reply(player_id)
{:error, reason} ->
game
|> Game.update_request(request)
|> Game.update_response({:error, reason})
|> save()
|> reply(player_id)
_other ->
game
|> Game.update_request(request)
|> Game.update_response({:error, :unknown})
|> save()
|> reply(player_id)
end
end
def handle_call({:tally, player_id}, _from, game) do
reply(game, player_id)
end
@spec handle_cast(term, Game.t()) :: {:noreply, Game.t()}
def handle_cast({:persist_board, board}, game) do
:ok = Board.persist(board)
{:noreply, game}
end
@spec terminate(term, Game.t()) :: true
def terminate(:shutdown, game) do
true = :ets.delete(@ets, key(game.player1.name))
end
end