Packages
islands_score
0.1.24
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.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.3
0.1.2
0.1.1
A score struct and functions for the Game of Islands.
Current section
Files
Jump to
Current section
Files
lib/islands/score.ex
# ┌───────────────────────────────────────────────────────────────────────┐
# │ Inspired by the book "Functional Web Development" by Lance Halvorsen. │
# └───────────────────────────────────────────────────────────────────────┘
defmodule Islands.Score do
@moduledoc """
Creates a `score` struct for the _Game of Islands_.
Also formats the `score` of a player.
##### Inspired by the book [Functional Web Development](https://pragprog.com/book/lhelph/functional-web-development-with-elixir-otp-and-phoenix) by Lance Halvorsen.
"""
alias __MODULE__
alias IO.ANSI.Plus, as: ANSI
alias Islands.Client.IslandType
alias Islands.{Board, Game, Island, Player, PlayerID}
@island_type_codes ["a", "d", "l", "s", "q"]
@player_ids [:player1, :player2]
@sp ANSI.cursor_right()
@symbols [f: "♀", m: "♂"]
@derive [Poison.Encoder]
@derive Jason.Encoder
@enforce_keys [:name, :gender, :hits, :misses, :forested_types]
defstruct [:name, :gender, :hits, :misses, :forested_types]
@type t :: %Score{
name: Player.name(),
gender: Player.gender(),
hits: non_neg_integer,
misses: non_neg_integer,
forested_types: [Island.type()]
}
@doc """
Creates a `score` struct from the board of a player.
"""
@spec board_score(Game.t(), PlayerID.t()) :: t
def board_score(%Game{} = game, player_id) when player_id in @player_ids do
player = game[player_id]
board = player.board
new(player, board)
end
@doc """
Creates a `score` struct from the board of a player's opponent.
"""
@spec guesses_score(Game.t(), PlayerID.t()) :: t
def guesses_score(%Game{} = game, player_id) when player_id in @player_ids do
opponent = game[Game.opponent_id(player_id)]
board = opponent.board
new(opponent, board)
end
@doc """
Formats the `score` of a player.
"""
@spec format(t, Keyword.t()) :: :ok
def format(%Score{} = score, options) do
{up, right} = {options[:up], options[:right]}
[
[cursor_up(up), ANSI.cursor_right(right), player(score)],
["\n", ANSI.cursor_right(right), top_score(score)],
["\n", ANSI.cursor_right(right), bottom_score(score)]
]
|> ANSI.puts()
end
## Private functions
@spec new(Player.t(), Board.t() | nil) :: t
defp new(player, nil) do
%Score{
name: player.name,
gender: player.gender,
hits: 0,
misses: 0,
forested_types: []
}
end
defp new(player, board) do
%Score{
name: player.name,
gender: player.gender,
hits: Board.hits(board),
misses: Board.misses(board),
forested_types: Board.forested_types(board)
}
end
@spec cursor_up(non_neg_integer) :: String.t()
defp cursor_up(up) when up > 0, do: ANSI.cursor_up(up)
defp cursor_up(_up), do: ""
@spec player(t) :: ANSI.ansilist()
defp player(%Score{name: name, gender: gender}) do
name = String.slice(name, 0, 21 - 2)
span = div(21 + String.length(name) + 2, 2) - 2
[
[:chartreuse_yellow, String.pad_leading(name, span)],
[:reset, @sp, :spring_green, "#{@symbols[gender]}"]
]
end
@spec top_score(t) :: ANSI.ansilist()
defp top_score(%Score{hits: hits, misses: misses}) do
[
[:chartreuse_yellow, "hits: "],
[:spring_green, String.pad_leading("#{hits}", 2)],
[:chartreuse_yellow, " misses: "],
[:spring_green, String.pad_leading("#{misses}", 2)]
]
end
@spec bottom_score(t) :: ANSI.ansilist()
defp bottom_score(score) do
[
[:reset, :spring_green, :underline, "forested"],
[:reset, @sp, :chartreuse_yellow, "➔", forested_codes(score)]
]
end
@spec forested_codes(t) :: ANSI.ansilist()
defp forested_codes(%Score{forested_types: forested_types}) do
for code <- @island_type_codes do
[attr(IslandType.new(code) in forested_types), code]
end
end
@spec attr(boolean) :: ANSI.ansilist()
defp attr(true = _forested?), do: [:reset, @sp, :spring_green, :underline]
defp attr(false = _forested?), do: [:reset, @sp, :chartreuse_yellow]
end