defmodule Tabletop.Board do
@moduledoc """
This module provides the struct for representing the board of a tabletop game
as well as the functions for creating and manipulating it.
## The Tabletop.Board Struct
The public fields are:
* `pieces` - A map comprised of position keys and piece values, empty positions
contain `nil` values
* `attributes` - A map of attributes held by the board
* `turn` - A number that represents the current turn
* `effects` - A list of functions that are applied at the end of each turn
## Effects
At the end of each turn, the board will invoke each of its' effects in turn. Each effect
should accept the board as input and return a board as output. This allows a game to perform
updates before the end of each turn like spawning some zombies or determining if the game
is over.
"""
defstruct [pieces: %{}, attributes: %{}, turn: 1, effects: []]
alias Tabletop.Actions
@doc """
Creates a `%Tabletop.Board` with empty positions forming a square grid. Each position
is a tuple containing `x` and `y` coordinates.
"""
def square(size) do
positions = for x <- 0..size-1, do: List.flatten(for y <- 0..size-1, do: {x, y})
pieces = Enum.reduce List.flatten(positions), %{}, fn pos, map ->
Map.put(map, pos, nil)
end
%Tabletop.Board{pieces: pieces}
end
@doc """
Assigns the provided `attributes` to the `board`. Any existing attributes that share
a key will be overriden.
"""
def assign(board, attributes) do
%Tabletop.Board{board | attributes: Enum.into(attributes, board.attributes)}
end
@doc """
Gets the value of the attribute named `attribute_name`. Raises a KeyError if it doesn't exist.
"""
def get(%Tabletop.Board{attributes: attributes}, attribute_name) do
Map.fetch!(attributes, attribute_name)
end
@doc """
Adds the provided `effects` to the `board`. Each effect should be a function that accepts a board
and then returns a board.
A single `effect` may also be provided.
"""
def add_effects(board, effects) do
%Tabletop.Board{board | effects: board.effects ++ List.wrap(effects)}
end
@doc """
Increments the turn count of the `board` by 1.
"""
def advance_turn(board) do
%Tabletop.Board{board | turn: board.turn + 1}
end
@doc """
Applies the list of `actions` to the `board` in order and then returns the resulting board.
"""
def apply_actions(board, actions) do
Enum.reduce actions, board, fn {action, args}, acc ->
Actions.apply(acc, action, args)
end
end
@doc """
Invokes each of the board's effects in order and then returns the affected board.
"""
def apply_effects(board) do
Enum.reduce board.effects, board, fn effect, acc ->
effect.(acc)
end
end
end