Packages
islands_game
0.1.21
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.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.4
0.1.3
0.1.2
0.1.1
0.1.0
A game struct and functions for the Game of Islands.
Current section
Files
Jump to
Current section
Files
lib/islands/game.ex
# ┌────────────────────────────────────────────────────────────────────┐
# │ Based on the book "Functional Web Development" by Lance Halvorsen. │
# └────────────────────────────────────────────────────────────────────┘
defmodule Islands.Game do
@moduledoc """
Models a `game` in the _Game of Islands_.
##### Based on the book [Functional Web Development](https://pragprog.com/book/lhelph/functional-web-development-with-elixir-otp-and-phoenix) by Lance Halvorsen.
"""
@behaviour Access
use PersistConfig
alias __MODULE__
alias Islands.{
Board,
Coord,
Guesses,
Player,
PlayerID,
Request,
Response,
State
}
@adjectives get_env(:haiku_adjectives)
@genders [:f, :m]
@hit_or_miss [:hit, :miss]
@nouns get_env(:haiku_nouns)
@player_ids [:player1, :player2]
@derive [Poison.Encoder]
@derive Jason.Encoder
@enforce_keys [:name, :player1, :player2]
defstruct name: nil,
player1: nil,
player2: nil,
request: {},
response: {},
state: State.new()
@type name :: String.t()
@type t :: %Game{
name: name,
player1: Player.t(),
player2: Player.t(),
request: Request.t(),
response: Response.t(),
state: State.t()
}
# Access behaviour
defdelegate fetch(game, key), to: Map
defdelegate get_and_update(game, key, fun), to: Map
defdelegate pop(game, key), to: Map
@spec new(name, String.t(), Player.gender(), pid) :: t | {:error, atom}
def new(name, player1_name, gender, pid)
when is_binary(name) and is_binary(player1_name) and is_pid(pid) and
gender in @genders do
%Game{
name: name,
player1: Player.new(player1_name, gender, pid),
player2: Player.new("?", :f, nil)
}
end
def new(_name, _player1_name, _gender, _pid), do: {:error, :invalid_game_args}
@spec update_board(t, PlayerID.t(), Board.t()) :: t
def update_board(%Game{} = game, player_id, %Board{} = board)
when player_id in @player_ids,
do: put_in(game[player_id].board, board)
@spec update_guesses(t, PlayerID.t(), Guesses.type(), Coord.t()) :: t
def update_guesses(%Game{} = game, player_id, hit_or_miss, %Coord{} = guess)
when player_id in @player_ids and hit_or_miss in @hit_or_miss do
update_in(game[player_id].guesses, &Guesses.add(&1, hit_or_miss, guess))
end
@spec update_player(t, PlayerID.t(), Player.name(), Player.gender(), pid) :: t
def update_player(%Game{} = game, player_id, name, gender, pid)
when player_id in @player_ids and is_binary(name) and is_pid(pid) and
gender in @genders do
player = %Player{game[player_id] | name: name, gender: gender, pid: pid}
put_in(game[player_id], player)
end
@spec notify_player(t, PlayerID.t()) :: t
def notify_player(%Game{} = game, player_id) when player_id in @player_ids do
send(game[player_id].pid, game.state.game_state)
game
end
@spec player_board(t, PlayerID.t()) :: Board.t()
def player_board(%Game{} = game, player_id) when player_id in @player_ids,
do: game[player_id].board
@spec opponent_id(PlayerID.t()) :: PlayerID.t()
def opponent_id(:player1), do: :player2
def opponent_id(:player2), do: :player1
@spec update_state(t, State.t()) :: t
def update_state(%Game{} = game, %State{} = state),
do: put_in(game.state, state)
@spec update_request(t, Request.t()) :: t
def update_request(%Game{} = game, request) when is_tuple(request),
do: put_in(game.request, request)
@spec update_response(t, Response.t()) :: t
def update_response(%Game{} = game, response) when is_tuple(response),
do: put_in(game.response, response)
@doc """
Generates a random name.
"""
@spec random_name :: name
def random_name do
length = Enum.random(4..10)
:crypto.strong_rand_bytes(length)
|> Base.url_encode64()
# Starting at 0 with length "length"...
|> binary_part(0, length)
end
@doc """
Generates a unique, URL-friendly name such as "bold-frog-8249".
"""
@spec haiku_name :: String.t()
def haiku_name do
[Enum.random(@adjectives), Enum.random(@nouns), :rand.uniform(9999)]
|> Enum.join("-")
end
defimpl Poison.Encoder, for: Tuple do
def encode(data, options) when is_tuple(data) do
Tuple.to_list(data) |> Poison.Encoder.List.encode(options)
end
end
defimpl Jason.Encoder, for: Tuple do
def encode(data, opts) when is_tuple(data) do
Tuple.to_list(data) |> Jason.Encode.list(opts)
end
end
end