replaced w better implementation

This commit is contained in:
Paul Liverman III 2018-03-16 00:07:40 -07:00
parent b34b6c8bc5
commit 6455230334
3 changed files with 244 additions and 1194 deletions

156
ReadMe.md
View File

@ -1,105 +1,87 @@
# Behave # Behave
A simple implementation of behavior trees in MoonScript / Lua. A simple implementation of behavior trees in MoonScript / Lua. Define behaviors
as functions or small tables, then call `behave.make` on them to get a function
you can call repeatedly to execute behaviors.
A node should return a truthy or falsy value to indicate success or failure, or
`behave.running` to indicate that the node needs to be called again to continue
running.
Examples are in MoonScript, but shouldn't be too difficult to understand even if
you are unfamiliar with its syntax.
## Example ## Example
(Note: The top example and last example work. The others are from experimenting
with ideas about how to make it easier to write code interfacing with this
library.)
``` ```
bt = require "behave" TODO
tree = bt.Sequence({
bt.Node({
run = function(self, obj, ...)
-- do stuff
end,
finish = function(self, obj, ...)
-- finish up
end
}),
bt.Random({
bt.Node({
-- add some functions
}),
bt.Node({
-- even more functionality!
})
})
})
tree:addObject(obj)
tree:update(obj)
node1 = {
-- pretend this has run/finish functions or whatever
}
node2 = {} -- same
bt.Make ({
"Sequence",
node1, node2, {
"Random",
node3, node4 -- these are in 'Random'
}
})
tree = bt.Factory({
"Sequence",
{
"Node",
{
run = function(self) end,
finish = function(self) end
}
}
})
node1 = bt.Node()
function node1:run()
-- do stuff!
end
``` ```
## The Leaf Node ## Leaf Nodes
`Node` accepts a table of values to be set on it. It expects a `run` function, The easiest node is just a function that will be passed all arguments sent to a
and optionally a `start` and a `finish` function, which will only be run at the behavior tree it is part of. There is a slightly more complex leaf node for
beginning and end of a node being run (whereas `run` can be called multiple maintaining an internal state, and optional `start`/`finish` functions only
times). called at the beginning and end of processing.
To run a behavior tree, call `update` on itself, with optional arguments (which ```
will be passed to their children as they are called). WalkPath = {
start: (state, entity) ->
state.path = findPath(entity, entity.target)
return state.path
run: (state, entity) ->
-- this will not run if start fails
state.path.step!
finish: (state, entity) ->
-- this will run only if run returns a truthy value besides behave.running
state.path = nil
}
```
## The Decorator Node
A very basic extension to the result of a leaf node, allowing you to alter the
result it returns. Note: You cannot alter a `behave.running` return.
```
InvertSomeNode = {
decorator: (result) ->
return not result
SomeNode
}
```
## Composite Nodes ## Composite Nodes
Pass a table of nodes to these to set their contents. All composite nodes repeat Selector skips any failures and returns on the first node that returns a truthy
after the entire tree has been processed. value (or returns `false` if nothing succeeds). Sequence continues until there
is a failure or returns success. Random executes a random node underneath it.
- Composite: Super class of the other composite nodes. ```
- Sequence: Runs children until a failure or success of all. Behaviors = {
- Selector: Runs children until a success or failure of all. type: behave.Random
- Random: Runs a random child and returns its result. WalkRandomly, LookAtPhone, LeaveArea
- RandomSequence: Randomizes the order of its children, then acts like a }
Sequence. ```
- RandomSelector: Randomizes the order of its children, then acts like a
Selector.
## Decorator Nodes ## Custom Nodes
Pass a single node to these, except for `Repeat`, which needs a number followed You can create custom nodes and easily use them. Rather than explaining how to,
by a node. All decorator nodes (except `RunOnce`) repeat after the entire tree here's an example:
has been processed.
- Decorator: Superclass. Just returns the result of its child node. ```
- Repeat: Repeats a node a specified number of times, fails if the node fails. -- defining the node type:
- Succeed: Runs a node and returns success. Invert = (tab) -> -- your fn will be passed a table describing the node
- Fail: Runs a node and returns failure. node = behave.make tab[1] -- call make on any sub-nodes that you will be using, save the result
- Invert: Runs a node, reporting a success on fail, and failure on success. return (...) -> -- use variable arguments, so sub-nodes can
- RunOnce: Runs a node, reporting its results, and fail after that. result = node(...) -- get what they need
unless result == behave.running -- running nodes should not be interrupted
result = not result -- and finally we invert the result before
return result -- returning it
## Return Values -- using the node type:
SomeInvertedNode = {
Within the module, `success`, `running`, and `fail` are defined. They are also type: Invert -- this is how the library knows what function to call
present on all nodes (so you can use `self.success` and such). SomeNode -- this is the node that will be inverted
}
```

View File

@ -1,845 +1,129 @@
local success = setmetatable({ }, { local make, Node, Decorate
__tostring = function()
return "success"
end
})
local running = setmetatable({ }, { local running = setmetatable({ }, {
__tostring = function() __tostring = function()
return "running" return "running"
end end
}) })
local fail = setmetatable({ }, { local get_nodes
__tostring = function() get_nodes = function(tab)
return "fail" local nodes = { }
for _index_0 = 1, #tab do
local node = tab[_index_0]
table.insert(nodes, make(node))
end end
}) return nodes
local Node end
do make = function(tab)
local _class_0 if "function" == type(tab) then
local _base_0 = { return tab
addObject = function(self, obj) elseif "function" == type(tab.type) then
obj[self.u] = { return tab.type(tab)
elseif tab.decorate then
return Decorate(tab)
else
return Node(tab)
end
end
Node = function(tab)
local state, started = { }, false
return function(...)
local result
if not (started) then
if tab.start then
result = tab.start(state, ...)
end
if not (result == false) then
started = true
end
end
if not (result == false) then
result = tab.run(state, ...)
end
if result ~= running then
started = false started = false
} if result and tab.finish then
end, result = tab.finish(state, ...)
run = function(self)
return success
end,
update = function(self, obj, ...)
local result = success
if not obj[self.u].started and self.start then
result = self:start(obj, ...)
obj[self.u].started = true
end end
if result == success then
result = self:run(obj, ...)
end
if result == success then
if self.finish then
result = self:finish(obj, ...)
end
obj[self.u].started = false
end end
return result return result
end end
}
_base_0.__index = _base_0
_class_0 = setmetatable({
__init = function(self, fns)
if fns == nil then
fns = { }
end
for key, value in pairs(fns) do
self[key] = value
end
self.success = success
self.running = running
self.fail = fail
self.u = { }
end,
__base = _base_0,
__name = "Node"
}, {
__index = _base_0,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
Node = _class_0
end
local Composite
do
local _class_0
local _parent_0 = Node
local _base_0 = {
addObject = function(self, obj)
local _list_0 = self.nodes
for _index_0 = 1, #_list_0 do
local node = _list_0[_index_0]
node:addObject(obj)
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, nodes)
if nodes == nil then
nodes = { }
end
self.nodes = nodes
return _class_0.__parent.__init(self)
end,
__base = _base_0,
__name = "Composite",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Composite = _class_0
end
local Sequence
do
local _class_0
local _parent_0 = Composite
local _base_0 = {
addObject = function(self, obj)
_class_0.__parent.__base.addObject(self)
obj[self.u] = {
index = 0,
running = 0
}
end,
update = function(self, obj, ...)
local result = success
if obj[self.u].running then
result = obj[self.u].running:update(obj, ...)
if not (result == running) then
obj[self.u].running = false
end
end
while result == success and obj[self.u].index < #self.nodes do
obj[self.u].index = obj[self.u].index + 1
result = self.nodes[obj[self.u].index]:update(obj, ...)
end
if result == running then
obj[self.u].running = self.nodes[obj[self.u].index]
else
obj[self.u].index = 0
end
return result
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Sequence",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Sequence = _class_0
end
local Selector
do
local _class_0
local _parent_0 = Composite
local _base_0 = {
addObject = function(self, obj)
_class_0.__parent.__base.addObject(self)
obj[self.u] = {
index = 0,
running = 0
}
end,
update = function(self, obj, ...)
local result = fail
if obj[self.u].running then
result = obj[self.u].running:update(obj, ...)
if not (result == running) then
obj[self.u].running = false
end
end
while result == fail and obj[self.u].index < #self.nodes do
obj[self.u].index = obj[self.u].index + 1
result = self.nodes[obj[self.u].index]:update(obj, ...)
end
if result == running then
obj[self.u].running = self.nodes[obj[self.u].index]
else
obj[self.u].index = 0
end
return result
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Selector",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Selector = _class_0
end
local Random
do
local _class_0
local _parent_0 = Composite
local _base_0 = {
update = function(self, obj, ...)
local index = math.floor(math.random() * #self.nodes + 1)
return self.nodes[index]:update(obj, ...)
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Random",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Random = _class_0
end
local Randomizer
do
local _class_0
local _parent_0 = Composite
local _base_0 = {
addObject = function(self, obj)
_class_0.__parent.__base.addObject(self)
obj[self.u] = {
running = false
}
return self:shuffle(obj)
end,
shuffle = function(self, obj)
obj[self.u].shuffledNodes = { }
for i = 1, #self.nodes do
local r = math.random(i)
if not (r == i) then
obj[self.u].shuffledNodes[i] = obj[self.u].shuffledNodes[r]
end
obj[self.u].shuffledNodes[r] = self.nodes[i]
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Randomizer",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Randomizer = _class_0
end
local RandomSequence
do
local _class_0
local _parent_0 = Randomizer
local _base_0 = {
update = function(self, obj, ...)
local result = success
if obj[self.u].running then
result = obj[self.u].running:update(obj, ...)
if not (result == running) then
obj[self.u].running = false
end
end
local tmp
while result == success and #obj[self.u].shuffledNodes > 0 do
result = obj[self.u].shuffledNodes[1]:update(obj, ...)
tmp = table.remove(obj[self.u].shuffledNodes, 1)
end
if result == running then
obj[self.u].running = tmp
else
self:shuffle(obj)
end
return result
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "RandomSequence",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
RandomSequence = _class_0
end
local RandomSelector
do
local _class_0
local _parent_0 = Randomizer
local _base_0 = {
update = function(self, obj, ...)
local result = fail
if obj[self.u].running then
result = obj[self.u].running:update(obj, ...)
if not (result == running) then
obj[self.u].running = false
end
end
local tmp
while result == fail and #obj[self.u].shuffledNodes > 0 do
result = obj[self.u].shuffledNodes[1]:update(obj, ...)
tmp = table.remove(obj[self.u].shuffledNodes, 1)
end
if result == running then
obj[self.u].running = tmp
else
self:shuffle()
end
return result
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "RandomSelector",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
RandomSelector = _class_0
end end
local Decorator local Decorator
do Decorator = function(tab)
local _class_0 local node = make(tab[1])
local _parent_0 = Node return function(...)
local _base_0 = { local result = node(object, ...)
addObject = function(self, obj)
return self.node:addObject(obj)
end,
update = function(self, obj, ...)
return self.node:update(obj, ...)
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, node)
if node == nil then
node = Node()
end
self.node = node
return _class_0.__parent.__init(self)
end,
__base = _base_0,
__name = "Decorator",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Decorator = _class_0
end
local Repeat
do
local _class_0
local _parent_0 = Decorator
local _base_0 = {
addObject = function(self, obj)
_class_0.__parent.__base.addObject(self)
obj[self.u] = {
counter = 1,
running = false
}
end,
update = function(self, obj, ...)
local result = success
if obj[self.u].running then
result = self.node:update(obj, ...)
if not (result == running) then if not (result == running) then
obj[self.u].running = false result = tab.decorate(result, ...)
end
end
while result == success and obj[self.u].counter < self.cycles do
obj[self.u].counter = obj[self.u].counter + 1
result = self.node:update(obj, ...)
end
if result == running then
obj[self.u].running = true
else
obj[self.u].counter = 1
end end
return result return result
end end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, cycles, node)
if cycles == nil then
cycles = 2
end end
if node == nil then local Selector
node = Node() Selector = function(tab)
local nodes = get_nodes(tab)
local length = #nodes
local i = 1
return function(...)
local result = nodes[i](...)
while not result do
i = i + 1
if i > length then
i = 1
return false
end end
self.cycles, self.node = cycles, node result = nodes[i](...)
return _class_0.__parent.__init(self, self.node)
end,
__base = _base_0,
__name = "Repeat",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end end
else if result ~= running then
return val i = 1
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Repeat = _class_0
end
local Succeed
do
local _class_0
local _parent_0 = Decorator
local _base_0 = {
update = function(self, obj, ...)
if running == self.node:update(obj, ...) then
return running
else
return success
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Succeed",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Succeed = _class_0
end
local Fail
do
local _class_0
local _parent_0 = Decorator
local _base_0 = {
update = function(self, obj, ...)
if running == self.node:update(obj, ...) then
return running
else
return fail
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Fail",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Fail = _class_0
end
local Invert
do
local _class_0
local _parent_0 = Decorator
local _base_0 = {
update = function(self, obj, ...)
local result = self.node:update(obj, ...)
if result == running then
return running
elseif result == success then
return fail
else
return success
end
end
}
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
__init = function(self, ...)
return _class_0.__parent.__init(self, ...)
end,
__base = _base_0,
__name = "Invert",
__parent = _parent_0
}, {
__index = function(cls, name)
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
Invert = _class_0
end
local RunOnce
do
local _class_0
local _parent_0 = Decorator
local _base_0 = {
addObject = function(self, obj)
obj[self.u] = {
ran = false
}
end,
update = function(self, obj, ...)
if not (obj[self.u].ran) then
local result = self.node:update(obj, ...)
if not (result == running) then
obj[self.u].ran = true
end end
return result return result
else
return fail
end end
end end
} local Sequence
_base_0.__index = _base_0 Sequence = function(tab)
setmetatable(_base_0, _parent_0.__base) local nodes = get_nodes(tab)
_class_0 = setmetatable({ local length = #nodes
__init = function(self, ...) local i = 1
return _class_0.__parent.__init(self, ...) return function(...)
end, local result = nodes[i](...)
__base = _base_0, while result and result ~= running do
__name = "RunOnce", i = i + 1
__parent = _parent_0 if i > length then
}, { i = 1
__index = function(cls, name) return result
local val = rawget(_base_0, name)
if val == nil then
local parent = rawget(cls, "__parent")
if parent then
return parent[name]
end end
else result = nodes[i](...)
return val
end end
end, if not (result) then
__call = function(cls, ...) i = 1
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end end
}) return result
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
RunOnce = _class_0
end
local Class
Class = function(name, parent)
local newClass, base
base = {
__index = base,
__class = newClass
}
newClass = setmetatable({
__init = function() end,
__base = base,
__name = name
}, {
__call = function(cls, ...)
local self = setmetatable({ }, base)
cls.__init(self, ...)
return self
end
})
if parent then
setmetatable(base, {
__parent = parent.__base
})
newClass.__parent = parent
newClass.__index = function(cls, name)
local val = rawget(base, name)
if val == nil then
return parent[name]
else
return val
end end
end end
if parent.__inherited then local Random
parent:__inherited(newClass) Random = function(tab)
local nodes = get_nodes(tab)
local length = #nodes
local r
return function(...)
if not (r) then
r = 1 + math.random(length)
end
local result = nodes[r](...)
if not (result == running) then
r = nil
end
return result
end end
end end
return newClass, base return {
end success = true,
return setmetatable({
Node = Node,
Sequence = Sequence,
Selector = Selector,
Random = Random,
RandomSequence = RandomSequence,
RandomSelector = RandomSelector,
Decorator = Decorator,
Repeat = Repeat,
Succeed = Succeed,
Fail = Fail,
Invert = Invert,
RunOnce = RunOnce,
success = success,
running = running, running = running,
fail = fail, failure = false,
Class = Class Node = Node,
}, { Decorator = Decorator,
__call = function(bt, ...) Selector = Selector,
return bt.Sequence(...) Sequence = Sequence,
end Random = Random
}) }

View File

@ -1,316 +1,100 @@
success = setmetatable({}, {__tostring: -> return "success"}) local make, Node, Decorate
running = setmetatable({}, {__tostring: -> return "running"})
fail = setmetatable({}, {__tostring: -> return "fail"})
class Node running = setmetatable {}, __tostring: -> return "running"
new: (fns={}) =>
for key, value in pairs fns
@[key] = value
@success = success get_nodes = (tab) ->
@running = running nodes = {}
@fail = fail for node in *tab
table.insert nodes, make node
return nodes
@u = {} -- produces a callable function from a defined node or full behavior tree
make = (tab) ->
if "function" == type tab
return tab
elseif "function" == type tab.type
return tab.type tab
elseif tab.decorate
return Decorate tab
else
return Node tab
addObject: (obj) => -- complex leaf node
obj[@u] = { -- state is preserved through calls, optional start/finish functions
started: false Node = (tab) ->
} state, started = {}, false
return (...) ->
run: => local result
return success unless started
result = tab.start state, ... if tab.start
update: (obj, ...) => started = true unless result == false
result = success result = tab.run state, ... unless result == false
if not obj[@u].started and @start if result != running
result = @\start obj, ... started = false
obj[@u].started = true result = tab.finish state, ... if result and tab.finish
if result == success
result = @\run obj, ...
if result == success
result = @\finish(obj, ...) if @finish
obj[@u].started = false
return result return result
class Composite extends Node -- modifies the result of a node before returning it
new: (@nodes={}) => Decorator = (tab) ->
super! node = make tab[1]
return (...) ->
result = node object, ...
result = tab.decorate result, ... unless result == running
return result
addObject: (obj) => -- returns first success/running, or failure
for node in *@nodes Selector = (tab) ->
node\addObject obj nodes = get_nodes tab
length = #nodes
i = 1
return (...) ->
result = nodes[i](...)
while not result
i += 1
if i > length
i = 1
return false
result = nodes[i](...)
i = 1 if result != running
return result
-- Runs children in order until one returns fail, or all succeed. -- returns first running/failure, or success
class Sequence extends Composite Sequence = (tab) ->
addObject: (obj) => nodes = get_nodes tab
super! length = #nodes
obj[@u] = { i = 1
index: 0 return (...) ->
running: 0 result = nodes[i](...)
} while result and result != running
i += 1
if i > length
i = 1
return result
result = nodes[i](...)
i = 1 unless result
return result
update: (obj, ...) => -- returns success/running/failure from random child
result = success Random = (tab) ->
nodes = get_nodes tab
if obj[@u].running length = #nodes
result = obj[@u].running\update obj, ... local r
return (...) ->
unless r
r = 1 + math.random length
result = nodes[r](...)
unless result == running unless result == running
obj[@u].running = false r = nil
while result == success and obj[@u].index < #@nodes
obj[@u].index += 1
result = @nodes[obj[@u].index]\update obj, ...
if result == running
obj[@u].running = @nodes[obj[@u].index]
else
obj[@u].index = 0
return result return result
-- Runs children in order until one succeeds or all fail. {
class Selector extends Composite success: true
addObject: (obj) =>
super!
obj[@u] = {
index: 0
running: 0
}
update: (obj, ...) =>
result = fail
if obj[@u].running
result = obj[@u].running\update obj, ...
unless result == running
obj[@u].running = false
while result == fail and obj[@u].index < #@nodes
obj[@u].index += 1
result = @nodes[obj[@u].index]\update obj, ...
if result == running
obj[@u].running = @nodes[obj[@u].index]
else
obj[@u].index = 0
return result
-- Runs a random child.
class Random extends Composite
update: (obj, ...) =>
index = math.floor math.random! * #@nodes + 1
return @nodes[index]\update obj, ...
class Randomizer extends Composite
addObject: (obj) =>
super!
obj[@u] = {
running: false
}
@shuffle obj
shuffle: (obj) =>
obj[@u].shuffledNodes = {}
for i = 1, #@nodes
r = math.random i
unless r == i
obj[@u].shuffledNodes[i] = obj[@u].shuffledNodes[r]
obj[@u].shuffledNodes[r] = @nodes[i]
-- Randomizes order of nodes in between complete runs of them as a Sequence.
class RandomSequence extends Randomizer
update: (obj, ...) =>
result = success
if obj[@u].running
result = obj[@u].running\update obj, ...
unless result == running
obj[@u].running = false
local tmp
while result == success and #obj[@u].shuffledNodes > 0
result = obj[@u].shuffledNodes[1]\update obj, ...
tmp = table.remove obj[@u].shuffledNodes, 1
if result == running
obj[@u].running = tmp
else
@shuffle obj
return result
-- Randomizes order of nodes in between complete runs of them as a Selector.
class RandomSelector extends Randomizer
update: (obj, ...) =>
result = fail
if obj[@u].running
result = obj[@u].running\update obj, ...
unless result == running
obj[@u].running = false
local tmp
while result == fail and #obj[@u].shuffledNodes > 0
result = obj[@u].shuffledNodes[1]\update obj, ...
tmp = table.remove obj[@u].shuffledNodes, 1
if result == running
obj[@u].running = tmp
else
@shuffle!
return result
class Decorator extends Node
new: (@node=Node!) =>
super!
addObject: (obj) =>
@node\addObject obj
update: (obj, ...) =>
return @node\update obj, ...
-- Repeats a node a specified number of times, unless it fails.
class Repeat extends Decorator
new: (@cycles=2, @node=Node!) =>
super @node
addObject: (obj) =>
super!
obj[@u] = {
counter: 1
running: false
}
update: (obj, ...) =>
result = success
if obj[@u].running
result = @node\update obj, ...
unless result == running
obj[@u].running = false
while result == success and obj[@u].counter < @cycles
obj[@u].counter += 1
result = @node\update obj, ...
if result == running
obj[@u].running = true
else
obj[@u].counter = 1
return result
-- Returns success whether or not the node succeeds.
class Succeed extends Decorator
update: (obj, ...) =>
if running == @node\update obj, ...
return running
else
return success
-- Returns fail whether or not the node fails.
class Fail extends Decorator
update: (obj, ...) =>
if running == @node\update obj, ...
return running
else
return fail
-- Returns success when the node fails, and failure on success.
class Invert extends Decorator
update: (obj, ...) =>
result = @node\update obj, ...
if result == running
return running
elseif result == success
return fail
else
return success
-- Only runs children once, and returns fail from then on.
class RunOnce extends Decorator
addObject: (obj) =>
obj[@u] = {
ran: false
}
update: (obj, ...) =>
unless obj[@u].ran
result = @node\update obj, ...
unless result == running
obj[@u].ran = true
return result
else
return fail
-- A MoonScript-compatible class implementation for creating your own classes.
Class = (name, parent) ->
local newClass, base
base = {
__index: base
__class: newClass
}
newClass = setmetatable {
__init: ->
__base: base
__name: name
}, {
__call: (cls, ...) ->
@ = setmetatable({}, base)
cls.__init(@, ...)
return @
}
if parent
setmetatable base, {
__parent: parent.__base
}
newClass.__parent = parent
newClass.__index = (cls, name) ->
val = rawget(base, name)
if val == nil
return parent[name]
else
return val
if parent.__inherited
parent\__inherited newClass
return newClass, base
setmetatable {
-- Leaf Node
:Node
-- Composite Nodes
:Composite
:Sequence
:Selector
:Random
:RandomSequence
:RandomSelector
-- Decorator Nodes
:Decorator
:Repeat
:Succeed
:Fail
:Invert
:RunOnce
-- Return Values
:success
:running :running
:fail failure: false
-- Utility Fns :Node
:Class :Decorator
}, { :Selector
__call: (bt, ...) -> :Sequence
bt.Sequence ... :Random
} }