Packages
islands_engine
0.1.10
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/board.ex
defmodule Islands.Engine.Board do
@moduledoc false
use PersistConfig
alias __MODULE__
alias Islands.Engine.{Coord, Island}
@enforce_keys [:islands, :misses]
defstruct [:islands, :misses]
@type islands :: %{Island.type() => Island.t()}
@type response :: hit | miss
@type t :: %Board{islands: islands, misses: Island.coords()}
@typep hit :: hit_forested_no_win | hit_forested_none | hit_forested_win
@typep hit_forested_no_win :: {:hit, Island.type(), :no_win, t}
@typep hit_forested_none :: {:hit, :none, :no_win, t}
@typep hit_forested_win :: {:hit, Island.type(), :win, t}
@typep miss :: {:miss, :none, :no_win, t}
@board_set_path Application.get_env(@app, :board_set_path)
@island_types Application.get_env(@app, :island_types)
@spec new() :: t
def new(), do: %Board{islands: %{}, misses: MapSet.new()}
@spec position_island(t, Island.t()) :: t | {:error, atom}
def position_island(%Board{} = board, %Island{} = island) do
if overlaps_other_island?(board.islands, island),
do: {:error, :overlapping_island},
else: put_in(board.islands[island.type], island)
end
@spec all_islands_positioned?(t) :: boolean
def all_islands_positioned?(%Board{} = board) do
Enum.all?(@island_types, &Map.has_key?(board.islands, &1))
end
@spec guess(t, Coord.t()) :: response
def guess(%Board{} = board, %Coord{} = guess) do
board |> check_islands(guess) |> response(board)
end
@spec persist(t) :: :ok
def persist(%Board{} = board) do
@board_set_path
|> File.write!(
case File.read(@board_set_path) do
{:ok, binary} -> :erlang.binary_to_term(binary)
{:error, _reason} -> MapSet.new()
end
|> MapSet.put(board)
|> :erlang.term_to_binary()
)
end
@spec restore :: t
def restore do
@board_set_path
|> File.read!()
|> :erlang.binary_to_term()
|> MapSet.to_list()
|> Enum.random()
end
## Private functions
@spec overlaps_other_island?(islands, Island.t()) :: boolean
defp overlaps_other_island?(islands, new_island) do
Enum.any?(islands, fn {type, island} ->
type != new_island.type and Island.overlaps?(island, new_island)
end)
end
@spec check_islands(t, Coord.t()) :: {:hit, Island.t()} | {:miss, Coord.t()}
defp check_islands(board, guess) do
Enum.find_value(board.islands, {:miss, guess}, fn {_type, island} ->
case Island.guess(island, guess) do
{:hit, island} -> {:hit, island}
:miss -> false
end
end)
end
@spec response({:hit, Island.t()} | {:miss, Coord.t()}, t) :: response
defp response({:hit, island}, board) do
board = put_in(board.islands[island.type], island)
{:hit, forest_check(board, island), win_check(board), board}
end
defp response({:miss, guess}, board) do
board = update_in(board.misses, &MapSet.put(&1, guess))
{:miss, :none, :no_win, board}
end
@spec forest_check(t, Island.t()) :: Island.type() | :none
defp forest_check(board, %Island{type: type} = _island) do
if Island.forested?(board.islands[type]), do: type, else: :none
end
@spec win_check(t) :: :win | :no_win
defp win_check(board), do: if(all_forested?(board), do: :win, else: :no_win)
@spec all_forested?(t) :: boolean
defp all_forested?(board) do
Enum.all?(board.islands, fn {_type, island} -> Island.forested?(island) end)
end
end