Packages
islands_grid
0.1.24
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.3
0.1.2
0.1.1
A grid (map of maps) and functions for the Game of Islands.
Current section
Files
Jump to
Current section
Files
lib/islands/grid.ex
# ┌───────────────────────────────────────────────────────────────────────┐
# │ Inspired by the book "Functional Web Development" by Lance Halvorsen. │
# └───────────────────────────────────────────────────────────────────────┘
defmodule Islands.Grid do
@moduledoc """
A grid (map of maps) and functions for the _Game of Islands_.
##### Inspired by the book [Functional Web Development](https://pragprog.com/book/lhelph/functional-web-development-with-elixir-otp-and-phoenix) by Lance Halvorsen.
"""
import Enum, only: [reduce: 3]
alias __MODULE__.Tile
alias IO.ANSI.Plus, as: ANSI
alias Islands.{Board, Coord, Guesses, Island}
@col_range 1..10
@row_range 1..10
@typedoc "A grid (map of maps) allowing the `grid[row][col]` syntax"
@type t :: %{Coord.row() => %{Coord.col() => atom}}
@typedoc "Creates a tile from a cell value"
@type tile_fun :: (atom -> ANSI.ansidata())
@doc """
Creates an "empty" grid.
## Examples
iex> alias Islands.Grid
iex> grid = Grid.new()
iex> {grid[1][1], grid[10][10]}
{nil, nil}
iex> alias Islands.Grid
iex> grid = Grid.new()
iex> for row <- 1..10 do
iex> for col <- 1..10, uniq: true do
iex> grid[row][col]
iex> end
iex> end
[[nil], [nil], [nil], [nil], [nil], [nil], [nil], [nil], [nil], [nil]]
"""
@spec new :: t
def new do
row_map = for col <- @col_range, into: %{}, do: {col, nil}
for row <- @row_range, into: %{}, do: {row, row_map}
end
@doc """
Converts a board or guesses struct into a grid.
"""
@spec new(Board.t() | Guesses.t()) :: t
def new(board_or_guesses)
def new(%Board{islands: islands, misses: misses}) do
islands
|> Map.values()
|> reduce(new(), fn %Island{type: type, coords: coords, hits: hits}, grid ->
grid
|> update(coords, type)
|> update(hits, :"#{type}_hit")
end)
|> update(misses, :board_miss)
end
def new(%Guesses{hits: hits, misses: misses}),
do: new() |> update(hits, :hit) |> update(misses, :miss)
@doc """
Converts a board or guesses struct into a list of maps.
"""
@spec to_maps(Board.t() | Guesses.t(), tile_fun) :: [map]
def to_maps(board_or_guesses, tile_fun \\ &Tile.new/1)
def to_maps(%Board{} = board, tile_fun) when is_function(tile_fun, 1),
do: new(board) |> do_to_maps(tile_fun)
def to_maps(%Guesses{} = guesses, tile_fun) when is_function(tile_fun, 1),
do: new(guesses) |> do_to_maps(tile_fun)
## Private functions
@spec update(t, Island.coords(), atom) :: t
defp update(grid, coords, value) do
coords
|> MapSet.to_list()
|> reduce(grid, fn %Coord{row: row, col: col}, grid ->
put_in(grid[row][col], value)
end)
end
@spec do_to_maps(t, tile_fun) :: [map]
defp do_to_maps(grid, tile_fun) do
for {row, row_map} <- grid do
row_list = for {col, cell_val} <- row_map, do: {col, tile_fun.(cell_val)}
[{"row", row} | row_list] |> Map.new()
end
end
end