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

152
ReadMe.md
View File

@ -1,105 +1,87 @@
# 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
(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.)
```
TODO
```
## Leaf Nodes
The easiest node is just a function that will be passed all arguments sent to a
behavior tree it is part of. There is a slightly more complex leaf node for
maintaining an internal state, and optional `start`/`finish` functions only
called at the beginning and end of processing.
```
bt = require "behave"
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
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
}
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
## The Decorator Node
`Node` accepts a table of values to be set on it. It expects a `run` function,
and optionally a `start` and a `finish` function, which will only be run at the
beginning and end of a node being run (whereas `run` can be called multiple
times).
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.
To run a behavior tree, call `update` on itself, with optional arguments (which
will be passed to their children as they are called).
```
InvertSomeNode = {
decorator: (result) ->
return not result
SomeNode
}
```
## Composite Nodes
Pass a table of nodes to these to set their contents. All composite nodes repeat
after the entire tree has been processed.
Selector skips any failures and returns on the first node that returns a truthy
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.
- Selector: Runs children until a success or failure of all.
- Random: Runs a random child and returns its result.
- RandomSequence: Randomizes the order of its children, then acts like a
Sequence.
- RandomSelector: Randomizes the order of its children, then acts like a
Selector.
```
Behaviors = {
type: behave.Random
WalkRandomly, LookAtPhone, LeaveArea
}
```
## Decorator Nodes
## Custom Nodes
Pass a single node to these, except for `Repeat`, which needs a number followed
by a node. All decorator nodes (except `RunOnce`) repeat after the entire tree
has been processed.
You can create custom nodes and easily use them. Rather than explaining how to,
here's an example:
- Decorator: Superclass. Just returns the result of its child node.
- Repeat: Repeats a node a specified number of times, fails if the node fails.
- Succeed: Runs a node and returns success.
- Fail: Runs a node and returns failure.
- Invert: Runs a node, reporting a success on fail, and failure on success.
- RunOnce: Runs a node, reporting its results, and fail after that.
```
-- defining the node type:
Invert = (tab) -> -- your fn will be passed a table describing the node
node = behave.make tab[1] -- call make on any sub-nodes that you will be using, save the result
return (...) -> -- use variable arguments, so sub-nodes can
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
Within the module, `success`, `running`, and `fail` are defined. They are also
present on all nodes (so you can use `self.success` and such).
-- using the node type:
SomeInvertedNode = {
type: Invert -- this is how the library knows what function to call
SomeNode -- this is the node that will be inverted
}
```

View File

@ -1,845 +1,129 @@
local success = setmetatable({ }, {
__tostring = function()
return "success"
end
})
local make, Node, Decorate
local running = setmetatable({ }, {
__tostring = function()
return "running"
end
})
local fail = setmetatable({ }, {
__tostring = function()
return "fail"
local get_nodes
get_nodes = function(tab)
local nodes = { }
for _index_0 = 1, #tab do
local node = tab[_index_0]
table.insert(nodes, make(node))
end
})
local Node
do
local _class_0
local _base_0 = {
addObject = function(self, obj)
obj[self.u] = {
started = false
}
end,
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
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
return result
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
return nodes
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)
make = function(tab)
if "function" == type(tab) then
return tab
elseif "function" == type(tab.type) then
return tab.type(tab)
elseif tab.decorate then
return Decorate(tab)
else
return Node(tab)
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
Node = function(tab)
local state, started = { }, false
return function(...)
local result
if not (started) then
if tab.start then
result = tab.start(state, ...)
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, ...)
if not (result == false) then
started = true
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
if not (result == false) then
result = tab.run(state, ...)
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
if result ~= running then
started = false
if result and tab.finish then
result = tab.finish(state, ...)
end
end
return result
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
local Decorator
do
local _class_0
local _parent_0 = Node
local _base_0 = {
addObject = function(self, obj)
return self.node:addObject(obj)
end,
update = function(self, obj, ...)
return self.node:update(obj, ...)
Decorator = function(tab)
local node = make(tab[1])
return function(...)
local result = node(object, ...)
if not (result == running) then
result = tab.decorate(result, ...)
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)
return result
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
obj[self.u].running = false
end
local Selector
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
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
return result
result = nodes[i](...)
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
if node == nil then
node = Node()
end
self.cycles, self.node = cycles, node
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
else
return val
end
end,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
if result ~= running then
i = 1
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
return result
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
local Sequence
Sequence = function(tab)
local nodes = get_nodes(tab)
local length = #nodes
local i = 1
return function(...)
local result = nodes[i](...)
while result and result ~= running do
i = i + 1
if i > length then
i = 1
return result
else
return fail
end
result = nodes[i](...)
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 = "RunOnce",
__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
if not (result) then
i = 1
end
})
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
return result
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
local Random
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
})
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
if parent.__inherited then
parent:__inherited(newClass)
local result = nodes[r](...)
if not (result == running) then
r = nil
end
return result
end
return newClass, base
end
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,
return {
success = true,
running = running,
fail = fail,
Class = Class
}, {
__call = function(bt, ...)
return bt.Sequence(...)
end
})
failure = false,
Node = Node,
Decorator = Decorator,
Selector = Selector,
Sequence = Sequence,
Random = Random
}

View File

@ -1,316 +1,100 @@
success = setmetatable({}, {__tostring: -> return "success"})
running = setmetatable({}, {__tostring: -> return "running"})
fail = setmetatable({}, {__tostring: -> return "fail"})
local make, Node, Decorate
class Node
new: (fns={}) =>
for key, value in pairs fns
@[key] = value
running = setmetatable {}, __tostring: -> return "running"
@success = success
@running = running
@fail = fail
get_nodes = (tab) ->
nodes = {}
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) =>
obj[@u] = {
started: false
}
run: =>
return success
update: (obj, ...) =>
result = success
if not obj[@u].started and @start
result = @\start obj, ...
obj[@u].started = true
if result == success
result = @\run obj, ...
if result == success
result = @\finish(obj, ...) if @finish
obj[@u].started = false
-- complex leaf node
-- state is preserved through calls, optional start/finish functions
Node = (tab) ->
state, started = {}, false
return (...) ->
local result
unless started
result = tab.start state, ... if tab.start
started = true unless result == false
result = tab.run state, ... unless result == false
if result != running
started = false
result = tab.finish state, ... if result and tab.finish
return result
class Composite extends Node
new: (@nodes={}) =>
super!
addObject: (obj) =>
for node in *@nodes
node\addObject obj
-- Runs children in order until one returns fail, or all succeed.
class Sequence extends Composite
addObject: (obj) =>
super!
obj[@u] = {
index: 0
running: 0
}
update: (obj, ...) =>
result = success
if obj[@u].running
result = obj[@u].running\update obj, ...
unless result == running
obj[@u].running = false
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
-- modifies the result of a node before returning it
Decorator = (tab) ->
node = make tab[1]
return (...) ->
result = node object, ...
result = tab.decorate result, ... unless result == running
return result
-- Runs children in order until one succeeds or all fail.
class Selector extends Composite
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
-- returns first success/running, or failure
Selector = (tab) ->
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 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
-- returns first running/failure, or success
Sequence = (tab) ->
nodes = get_nodes tab
length = #nodes
i = 1
return (...) ->
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
-- 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!
-- returns success/running/failure from random child
Random = (tab) ->
nodes = get_nodes tab
length = #nodes
local r
return (...) ->
unless r
r = 1 + math.random length
result = nodes[r](...)
unless result == running
r = nil
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
{
success: true
:running
:fail
failure: false
-- Utility Fns
:Class
}, {
__call: (bt, ...) ->
bt.Sequence ...
:Node
:Decorator
:Selector
:Sequence
:Random
}