Snek v0.4.0 Snek.Board.Snake View Source

Represents a snake on a board.

You may also refer to it as a "snake on a plane", as the joke goes in the Battlesnake community. 😎

Link to this section Summary

Types

A unique ID to differentiate between snakes on a board

A valid direction for a snake to move according to the game rules.

Whether a snake is currently alive, or has been eliminated.

t()

A snake on a board.

Functions

Returns true if and only if the snake is alive (not eliminated).

Returns true if and only if the snake is eliminated.

Feed a snake and grow its tail.

Grow a snake's tail.

Returns the head of a snake.

Decrements the snake's health by 1 point.

Moves the snake one space in a given direction.

Returns the point that is one step toward a given direction from this snake's perspective.

Link to this section Types

Specs

id() :: any()

A unique ID to differentiate between snakes on a board

Link to this type

snake_move()

View Source (since 0.1.0)

Specs

snake_move() ::
  :north | :south | :east | :west | :forward | :backward | :left | :right

A valid direction for a snake to move according to the game rules.

Specs

state() ::
  :alive
  | {:eliminated, :starvation}
  | {:eliminated, :out_of_bounds}
  | {:eliminated, :self_collision}
  | {:eliminated, :collision, id()}
  | {:eliminated, :head_to_head, id()}

Whether a snake is currently alive, or has been eliminated.

If eliminated, the reason is encoded. If the elimination was caused by an opponent, the opponent's snake ID is also specified.

Specs

t() :: %Snek.Board.Snake{
  body: [Snek.Board.Point.t()],
  health: non_neg_integer(),
  id: any(),
  state: state()
}

A snake on a board.

Link to this section Functions

Link to this function

alive?(snake)

View Source (since 0.1.0)

Specs

alive?(t()) :: boolean()

Returns true if and only if the snake is alive (not eliminated).

This does not check whether the snake's elimination status should be changed, it is just a helper to check current state.

Examples

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> Snake.alive?(%Snake{id: "mysnek", state: :alive, health: 98, body: body})
true

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> Snake.alive?(%Snake{id: "mysnek", state: {:eliminated, :starvation}, health: 0, body: body})
false
Link to this function

eliminated?(snake)

View Source (since 0.1.0)

Specs

eliminated?(t()) :: boolean()

Returns true if and only if the snake is eliminated.

This does not check whether the snake's elimination status should be changed, it is just a helper to check current state.

Examples

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> Snake.eliminated?(%Snake{id: "mysnek", state: :alive, health: 98, body: body})
false

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> Snake.eliminated?(%Snake{id: "mysnek", state: {:eliminated, :starvation}, health: 0, body: body})
true
Link to this function

feed(snake, new_health)

View Source (since 0.1.0)

Specs

feed(t(), non_neg_integer()) :: t()

Feed a snake and grow its tail.

A snake is fed by restoring its health to a given value, and adding a part to its tail. The new tail part is added in the same position as the current tail (the last body part). Tail body parts may overlap until the snake moves.

Returns the modified snake.

Examples

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 98, body: body}
iex> Snake.feed(snake, 100)
%Snake{
  id: "mysnek",
  state: :alive,
  health: 100,
  body: [
    Snek.Board.Point.new(1, 0),
    Snek.Board.Point.new(1, 1),
    Snek.Board.Point.new(1, 2),
    Snek.Board.Point.new(1, 2)
  ]
}
Link to this function

grow(snake)

View Source (since 0.1.0)

Specs

grow(t()) :: t()

Grow a snake's tail.

Adds a part to the snake's tail. The new tail part is added in the same position as the current tail (the last body part). Tail body parts may overlap until the snake moves.

This is equivelent to the tail growth in feed/2 but without affecting the snake's health.

Returns the modified snake.

Examples

iex> body = [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 2)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 98, body: body}
iex> Snake.grow(snake)
%Snake{
  id: "mysnek",
  state: :alive,
  health: 98,
  body: [
    Snek.Board.Point.new(1, 0),
    Snek.Board.Point.new(1, 1),
    Snek.Board.Point.new(1, 2),
    Snek.Board.Point.new(1, 2)
  ]
}

iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: []}
iex> snake == Snake.grow(snake)
true
Link to this function

head(snake)

View Source (since 0.1.0)

Specs

head(t()) :: Snek.Board.Point.t() | nil

Returns the head of a snake.

If the snake has at least one body part, the first body part (the head) is returned. Otherwise, nil is returned.

Examples

iex> body = [Snek.Board.Point.new(1, 2), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: body}
iex> Snake.head(snake)
%Snek.Board.Point{x: 1, y: 2}

iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: []}
iex> Snake.head(snake)
nil
Link to this function

hurt(snake)

View Source (since 0.1.0)

Specs

hurt(t()) :: t()

Decrements the snake's health by 1 point.

Returns the modified snake.

Examples

iex> body = List.duplicate(Snek.Board.Point.new(1, 1), 3)
iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: body}
iex> Snake.hurt(snake).health
99
Link to this function

move(snake, direction)

View Source (since 0.1.0)

Specs

move(t(), snake_move() | nil) :: t()

Moves the snake one space in a given direction.

Moving consists of adding a body part to the head of the snake in the given direction, and also removing the tail body part. The snake's body length remains unchanged.

If the snake is already eliminated or the snake does not have any body parts, no move will be applied and the snake will remain unchanged.

If the direction given is :left, :right, :forward or :backward, then the snake is moved in that direction relative to the snake's last moved direction.

If the direction given is nil, or not a valid direction in which to move, the snake will be moved in the :forward direction. If the snake does not have both head and neck body parts, or the head and neck are at teh same position, the snake will default to moving :north instead, as in that case the last moved direction cannot be extrapolated.

If the snake is already eliminated it will not be moved.

Returns the modified snake.

Examples

iex> body = [Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: body}
iex> Snake.move(snake, :north)
%Snake{
  id: "mysnek",
  state: :alive,
  health: 100,
  body: [Snek.Board.Point.new(1, 0), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
}

iex> body = [Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "mysnek", state: {:eliminated, :starvation}, health: 0, body: body}
iex> snake == Snake.move(snake, :north)
true

iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: []}
iex> snake == Snake.move(snake, :north)
true

iex> body = [Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: body}
iex> snake |> Snake.move(:east) |> Snake.move(nil)
%Snake{
  id: "mysnek",
  state: :alive,
  health: 100,
  body: [Snek.Board.Point.new(3, 1), Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1)]
}

iex> body = [Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "mysnek", state: :alive, health: 100, body: body}
iex> snake |> Snake.move(:east) |> Snake.move(:left) |> Snake.move(:right)
%Snake{
  id: "mysnek",
  state: :alive,
  health: 100,
  body: [Snek.Board.Point.new(3, 0), Snek.Board.Point.new(2, 0), Snek.Board.Point.new(2, 1)]
}
Link to this function

step(snake, direction)

View Source (since 0.2.0)

Specs

Returns the point that is one step toward a given direction from this snake's perspective.

If the snake has no body parts, nil is returned instead of a point.

If the direction given is :left, :right, :forward, or :backward then the point returned will be in that direction relative to the snake's last moved direction. If the snake does not have both head and neck body parts, or the head and neck are at the same position, then nil will be returned instead of a point, as in that case the last moved direction cannot be extrapolated.

Examples

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :north)
%Snek.Board.Point{x: 2, y: 0}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :left)
%Snek.Board.Point{x: 2, y: 0}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :east)
%Snek.Board.Point{x: 3, y: 1}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :forward)
%Snek.Board.Point{x: 3, y: 1}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :right)
%Snek.Board.Point{x: 2, y: 2}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :south)
%Snek.Board.Point{x: 2, y: 2}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :backward)
%Snek.Board.Point{x: 1, y: 1}

iex> body = [Snek.Board.Point.new(2, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 99, body: body}
iex> Snake.step(snake, :west)
%Snek.Board.Point{x: 1, y: 1}

iex> snake = %Snake{id: "snek0", state: :alive, health: 0, body: []}
iex> Snake.step(snake, :south)
nil

iex> body = [Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1), Snek.Board.Point.new(1, 1)]
iex> snake = %Snake{id: "snek0", state: :alive, health: 100, body: body}
iex> Snake.step(snake, :forward)
nil