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 # 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 TODO
library.) ```
## 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" WalkPath = {
start: (state, entity) ->
tree = bt.Sequence({ state.path = findPath(entity, entity.target)
bt.Node({ return state.path
run = function(self, obj, ...) run: (state, entity) ->
-- do stuff -- this will not run if start fails
end, state.path.step!
finish = function(self, obj, ...) finish: (state, entity) ->
-- finish up -- this will run only if run returns a truthy value besides behave.running
end state.path = nil
}),
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 ## The Decorator Node
`Node` accepts a table of values to be set on it. It expects a `run` function, A very basic extension to the result of a leaf node, allowing you to alter the
and optionally a `start` and a `finish` function, which will only be run at the result it returns. Note: You cannot alter a `behave.running` return.
beginning and end of a node being run (whereas `run` can be called multiple
times).
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 ## 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
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
end end
local Composite make = function(tab)
do if "function" == type(tab) then
local _class_0 return tab
local _parent_0 = Node elseif "function" == type(tab.type) then
local _base_0 = { return tab.type(tab)
addObject = function(self, obj) elseif tab.decorate then
local _list_0 = self.nodes return Decorate(tab)
for _index_0 = 1, #_list_0 do else
local node = _list_0[_index_0] return Node(tab)
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 end
Composite = _class_0
end end
local Sequence Node = function(tab)
do local state, started = { }, false
local _class_0 return function(...)
local _parent_0 = Composite local result
local _base_0 = { if not (started) then
addObject = function(self, obj) if tab.start then
_class_0.__parent.__base.addObject(self) result = tab.start(state, ...)
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 end
while result == success and obj[self.u].index < #self.nodes do if not (result == false) then
obj[self.u].index = obj[self.u].index + 1 started = true
result = self.nodes[obj[self.u].index]:update(obj, ...)
end 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 end
} if not (result == false) then
_base_0.__index = _base_0 result = tab.run(state, ...)
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 end
}) if result ~= running then
_base_0.__class = _class_0 started = false
if _parent_0.__inherited then if result and tab.finish then
_parent_0.__inherited(_parent_0, _class_0) result = tab.finish(state, ...)
end
end
return result
end 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) if not (result == running) then
return self.node:addObject(obj) result = tab.decorate(result, ...)
end,
update = function(self, obj, ...)
return self.node:update(obj, ...)
end end
} return result
_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 end
Decorator = _class_0
end end
local Repeat local Selector
do Selector = function(tab)
local _class_0 local nodes = get_nodes(tab)
local _parent_0 = Decorator local length = #nodes
local _base_0 = { local i = 1
addObject = function(self, obj) return function(...)
_class_0.__parent.__base.addObject(self) local result = nodes[i](...)
obj[self.u] = { while not result do
counter = 1, i = i + 1
running = false if i > length then
} i = 1
end, return false
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
end end
while result == success and obj[self.u].counter < self.cycles do result = nodes[i](...)
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
end end
} if result ~= running then
_base_0.__index = _base_0 i = 1
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
end end
}) return result
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end end
Repeat = _class_0
end end
local Succeed local Sequence
do Sequence = function(tab)
local _class_0 local nodes = get_nodes(tab)
local _parent_0 = Decorator local length = #nodes
local _base_0 = { local i = 1
update = function(self, obj, ...) return function(...)
if running == self.node:update(obj, ...) then local result = nodes[i](...)
return running while result and result ~= running do
else i = i + 1
return success if i > length then
end i = 1
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
return result return result
else
return fail
end end
result = nodes[i](...)
end end
} if not (result) then
_base_0.__index = _base_0 i = 1
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
end end
}) return result
_base_0.__class = _class_0
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end end
RunOnce = _class_0
end end
local Class local Random
Class = function(name, parent) Random = function(tab)
local newClass, base local nodes = get_nodes(tab)
base = { local length = #nodes
__index = base, local r
__class = newClass return function(...)
} if not (r) then
newClass = setmetatable({ r = 1 + math.random(length)
__init = function() end,
__base = base,
__name = name
}, {
__call = function(cls, ...)
local self = setmetatable({ }, base)
cls.__init(self, ...)
return self
end end
}) local result = nodes[r](...)
if parent then if not (result == running) then
setmetatable(base, { r = nil
__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)
end end
return result
end end
return newClass, base
end end
return setmetatable({ return {
Node = Node, success = true,
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 (...) ->
addObject: (obj) => result = node object, ...
for node in *@nodes result = tab.decorate result, ... unless result == running
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
return result return result
-- Runs children in order until one succeeds or all fail. -- returns first success/running, or failure
class Selector extends Composite Selector = (tab) ->
addObject: (obj) => nodes = get_nodes tab
super! length = #nodes
obj[@u] = { i = 1
index: 0 return (...) ->
running: 0 result = nodes[i](...)
} while not result
i += 1
update: (obj, ...) => if i > length
result = fail i = 1
if obj[@u].running return false
result = obj[@u].running\update obj, ... result = nodes[i](...)
unless result == running i = 1 if 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 return result
-- Runs a random child. -- returns first running/failure, or success
class Random extends Composite Sequence = (tab) ->
update: (obj, ...) => nodes = get_nodes tab
index = math.floor math.random! * #@nodes + 1 length = #nodes
return @nodes[index]\update obj, ... i = 1
return (...) ->
class Randomizer extends Composite result = nodes[i](...)
addObject: (obj) => while result and result != running
super! i += 1
obj[@u] = { if i > length
running: false i = 1
} return result
@shuffle obj result = nodes[i](...)
i = 1 unless result
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 return result
-- Randomizes order of nodes in between complete runs of them as a Selector. -- returns success/running/failure from random child
class RandomSelector extends Randomizer Random = (tab) ->
update: (obj, ...) => nodes = get_nodes tab
result = fail length = #nodes
local r
if obj[@u].running return (...) ->
result = obj[@u].running\update obj, ... unless r
unless result == running r = 1 + math.random length
obj[@u].running = false result = nodes[r](...)
unless result == running
local tmp r = nil
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 return result
class Decorator extends Node {
new: (@node=Node!) => success: true
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
} }