Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion lib/behavior_tree/node.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ defmodule BehaviorTree.Node do
| :repeat_until_fail
| :repeat_n
| :random
| :random_weighted,
| :random_weighted
| :always_succeed
| :always_fail
| :negate,
children: nonempty_list(any()),
repeat_count: pos_integer(),
weights: list(pos_integer())
Expand Down Expand Up @@ -157,6 +160,12 @@ defmodule BehaviorTree.Node do

def on_succeed(%BehaviorTree.Node{type: :random_weighted}, _zipper), do: :succeed

def on_succeed(%BehaviorTree.Node{type: :always_succeed}, _zipper), do: :succeed

def on_succeed(%BehaviorTree.Node{type: :always_fail}, _zipper), do: :fail

def on_succeed(%BehaviorTree.Node{type: :negate}, _zipper), do: :fail

def on_fail(%BehaviorTree.Node{type: :sequence}, _zipper), do: :fail

def on_fail(%BehaviorTree.Node{type: :select}, zipper) do
Expand Down Expand Up @@ -187,6 +196,12 @@ defmodule BehaviorTree.Node do
def on_fail(%BehaviorTree.Node{type: :random}, _zipper), do: :fail

def on_fail(%BehaviorTree.Node{type: :random_weighted}, _zipper), do: :fail

def on_fail(%BehaviorTree.Node{type: :always_succeed}, _zipper), do: :succeed

def on_fail(%BehaviorTree.Node{type: :always_fail}, _zipper), do: :fail

def on_fail(%BehaviorTree.Node{type: :negate}, _zipper), do: :succeed
end

@doc """
Expand Down Expand Up @@ -394,4 +409,75 @@ defmodule BehaviorTree.Node do
weights: Enum.map(children, &elem(&1, 1))
}
end

@doc """
Create an "always_succeed" style "decorator" node.

This node takes a single child, and will always succeed, regardless of the outcome of the child.

This may be useful when used in combination with the "random" nodes.

## Example

iex> tree = Node.sequence([
...> Node.always_succeed(:a),
...> :b
...> ])
iex> tree |> BehaviorTree.start |> BehaviorTree.value
:a
iex> tree |> BehaviorTree.start |> BehaviorTree.fail |> BehaviorTree.value
:b
"""
@spec always_succeed(any()) :: __MODULE__.t()
def always_succeed(child) do
%__MODULE__{type: :always_succeed, children: [child]}
end

@doc """
Create an "always_fail" style "decorator" node.

This node takes a single child, and will always fail, regardless of the outcome of the child.

This may be useful when used in combination with the "random" nodes.

## Example

iex> tree = Node.sequence([
...> Node.always_fail(:a),
...> :b
...> ])
iex> tree |> BehaviorTree.start |> BehaviorTree.value
:a
iex> tree |> BehaviorTree.start |> BehaviorTree.succeed |> BehaviorTree.value
:a
"""
@spec always_fail(any()) :: __MODULE__.t()
def always_fail(child) do
%__MODULE__{type: :always_fail, children: [child]}
end

@doc """
Create a "negate" style "decorator" node.

This node takes a single child. If the child fails, this node succeeds. If the child succeeds, this node fails.

This may be useful to simplify handlers code.

## Example

iex> tree = Node.sequence([
...> Node.negate(:a),
...> :b
...> ])
iex> tree |> BehaviorTree.start |> BehaviorTree.value
:a
iex> tree |> BehaviorTree.start |> BehaviorTree.succeed |> BehaviorTree.value
:a
iex> tree |> BehaviorTree.start |> BehaviorTree.fail |> BehaviorTree.value
:b
"""
@spec negate(any()) :: __MODULE__.t()
def negate(child) do
%__MODULE__{type: :negate, children: [child]}
end
end