Current section
Files
Jump to
Current section
Files
lib/sternhalma.ex
defmodule Sternhalma do
@moduledoc """
"""
alias Sternhalma.{Board, Cell, Pathfinding, Hex}
@doc """
Return {x, y} pixel coordinates for a given Hex coordinate.
## Examples
iex> to_pixel(Sternhalma.Hex.new({1, -4, 3}))
{8.267949192431123, 4.0}
"""
@spec to_pixel(Hex.t()) :: {number(), number()}
defdelegate to_pixel(position), to: Hex
@doc """
Return Hex coordinate for a given pixel coordinate {x, y}.
## Examples
iex> from_pixel({8.267949192431123, 4.0})
%Sternhalma.Hex{x: 1, y: 3, z: -4}
"""
@spec from_pixel({number(), number()}) :: Hex.t()
defdelegate from_pixel(position), to: Hex
@doc """
Move a marble from one cell on the board to another.
The function does not take into account if there is a
valid path between the two cells.
"""
@spec move_marble(Board.t(), String.t(), Cell.t(), Cell.t()) :: Board.t()
def move_marble(board, marble, from, to) do
Enum.map(board, fn cell ->
cond do
cell.position == from.position ->
Cell.set_marble(cell, nil)
cell.position == to.position ->
Cell.set_marble(cell, marble)
true ->
cell
end
end)
end
@doc """
Return a list of board cells from one position to another.
Returns an empty list if there is no path possible.
"""
@spec find_path(Board.t(), Cell.t(), Cell.t()) :: list(Cell.t())
def find_path(board, from, to) do
Pathfinding.path(board, from, to)
end
@doc """
Generate an empty board.
"""
@spec empty_board() :: Board.t()
defdelegate empty_board(), to: Board, as: :empty
@doc """
Return a cell from the game board based on pixel coordinates, x and y.
Return nil if the cell does not exist.
## Examples
iex> get_board_cell(empty_board(), {17.794, 14.5})
{:ok, %Sternhalma.Cell{marble: nil, position: %Sternhalma.Hex{x: 3, y: -6, z: 3}}}
iex> get_board_cell(empty_board(), {172.794, -104.5})
{:error, nil}
"""
@spec get_board_cell(Board.t(), {number(), number()}) :: {:ok | :error, Cell.t() | nil}
defdelegate get_board_cell(board, position), to: Board
@doc """
Add new marbles to the board.
The location of the marbles being added is determined based
on the number of unique marbles that are already on the board.
"""
@spec setup_marbles(Board.t(), String.t()) :: {:ok, Board.t()} | {:error, :board_full}
def setup_marbles(board, marble) do
unique_existing_marble_count = Board.count_marbles(board)
with {:ok, triangle_location} <- Board.position_opponent(unique_existing_marble_count) do
{:ok,
Board.setup_triangle(
board,
triangle_location,
marble
)}
else
{:error, _} ->
{:error, :board_full}
end
end
@doc """
Return the list of unique marbles found on a game board.
"""
@spec unique_marbles(Board.t()) :: list(String.t())
defdelegate unique_marbles(board), to: Board
@doc """
Indicates if all the marbles of the given type are located
in their winning locations.
"""
@spec won_game?(Board.t(), String.t()) :: boolean()
def won_game?(board, marble) do
Board.find_winners(board)
|> Enum.any?(&(&1.marble == marble))
end
@doc """
Returns the winner, if there is one.
To win, all 10 marbles must be in their
target positions.
"""
@spec winner(Board.t()) :: String.t() | nil
def winner(board) do
with [[winner | _] | _] <- Board.find_winners(board) do
winner.marble
else
_ ->
nil
end
end
end