mirror of
https://github.com/TangentFoxy/Behave.git
synced 2024-11-15 11:04:21 +00:00
init
This commit is contained in:
commit
c5adc1b351
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.lua
|
100
ReadMe.md
Normal file
100
ReadMe.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# Behave
|
||||||
|
|
||||||
|
A simple implementation of behavior trees in MoonScript / Lua.
|
||||||
|
|
||||||
|
## 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"
|
||||||
|
|
||||||
|
tree = bt.Sequence({
|
||||||
|
bt.Node({
|
||||||
|
run = function(self)
|
||||||
|
-- do stuff
|
||||||
|
end,
|
||||||
|
finish = function(self)
|
||||||
|
-- finish up
|
||||||
|
end
|
||||||
|
}),
|
||||||
|
bt.Random({
|
||||||
|
bt.Node({
|
||||||
|
-- add some functions
|
||||||
|
}),
|
||||||
|
bt.Node({
|
||||||
|
-- even more functionality!
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
`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).
|
||||||
|
|
||||||
|
To run a behavior tree, call `update` on itself, with optional arguments (which
|
||||||
|
will be passed to their children as they are called).
|
||||||
|
|
||||||
|
## Composite Nodes
|
||||||
|
|
||||||
|
Pass a table of nodes to these to set their contents. All composite nodes repeat
|
||||||
|
after the entire tree has been processed.
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## Decorator Nodes
|
||||||
|
|
||||||
|
Pass a single node to these, except for `Repeat`, which needs a number & a node.
|
||||||
|
All decorator nodes (except `RunOnce`) repeat after the entire tree has been
|
||||||
|
processed.
|
||||||
|
|
||||||
|
- Repeat: Repeats a node a specified number of times, aborts 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.
|
||||||
|
|
||||||
|
## 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).
|
287
behave.moon
Normal file
287
behave.moon
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
success = setmetatable({}, {__tostring: -> return "success"})
|
||||||
|
running = setmetatable({}, {__tostring: -> return "running"})
|
||||||
|
fail = setmetatable({}, {__tostring: -> return "fail"})
|
||||||
|
|
||||||
|
class Node
|
||||||
|
new: (fns={}) =>
|
||||||
|
for key, value in pairs fns
|
||||||
|
@[key] = value
|
||||||
|
|
||||||
|
@success = success
|
||||||
|
@running = running
|
||||||
|
@fail = fail
|
||||||
|
|
||||||
|
@started = false
|
||||||
|
|
||||||
|
run: =>
|
||||||
|
return success
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = success
|
||||||
|
if not @started and @start
|
||||||
|
result = @\start ...
|
||||||
|
@started = true
|
||||||
|
if result == success
|
||||||
|
result = @\run ...
|
||||||
|
if result == success
|
||||||
|
result = @\finish(...) if @finish
|
||||||
|
@started = false
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Runs children in order until one returns fail, or all succeed.
|
||||||
|
class Sequence extends Node
|
||||||
|
new: (@nodes={}) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@index = 0
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = success
|
||||||
|
|
||||||
|
if @_running
|
||||||
|
result = @_running\update ...
|
||||||
|
unless result == running
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
while result == success and @index < #@nodes
|
||||||
|
@index += 1
|
||||||
|
result = @nodes[@index]\update ...
|
||||||
|
|
||||||
|
if result == running
|
||||||
|
@_running = @nodes[@index]
|
||||||
|
else
|
||||||
|
@index = 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Runs children in order until one succeeds or all fail.
|
||||||
|
class Selector extends Node
|
||||||
|
new: (@nodes={}) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@index = 0
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = fail
|
||||||
|
if @_running
|
||||||
|
result = @_running\update ...
|
||||||
|
unless result == running
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
while result == fail and @index < #@nodes
|
||||||
|
@index += 1
|
||||||
|
result = @nodes[@index]\update ...
|
||||||
|
|
||||||
|
if result == running
|
||||||
|
@_running = @nodes[@index]
|
||||||
|
else
|
||||||
|
@index = 0
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Runs a random child.
|
||||||
|
class Random extends Node
|
||||||
|
new: (@nodes={}) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
index = math.floor math.random! * #@nodes + 1
|
||||||
|
return @nodes[index]\update ...
|
||||||
|
|
||||||
|
-- Randomizes order of nodes in between complete runs of them as a Sequence.
|
||||||
|
class RandomSequence extends Node
|
||||||
|
new: (@nodes={}) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@_running = false
|
||||||
|
@shuffle!
|
||||||
|
|
||||||
|
shuffle: =>
|
||||||
|
@_shuffled = {}
|
||||||
|
for i = 1, #@nodes
|
||||||
|
r = math.random i
|
||||||
|
unless r == i
|
||||||
|
@_shuffled[i] = @_shuffled[r]
|
||||||
|
@_shuffled[r] = @nodes[i]
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = success
|
||||||
|
|
||||||
|
if @_running
|
||||||
|
result = @_running\update ...
|
||||||
|
unless result == running
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
local tmp
|
||||||
|
while result == success and #@_shuffled > 0
|
||||||
|
result = @_shuffled[1]\update ...
|
||||||
|
tmp = table.remove @_shuffled, 1
|
||||||
|
|
||||||
|
if result == running
|
||||||
|
@_running = tmp
|
||||||
|
else
|
||||||
|
@shuffle!
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Randomizes order of nodes in between complete runs of them as a Selector.
|
||||||
|
class RandomSelector extends Node
|
||||||
|
new: (@nodes={}) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@_running = false
|
||||||
|
@shuffle!
|
||||||
|
|
||||||
|
shuffle: =>
|
||||||
|
@_shuffled = {}
|
||||||
|
for i = 1, #@nodes
|
||||||
|
r = math.random i
|
||||||
|
unless r == i
|
||||||
|
@_shuffled[i] = @_shuffled[r]
|
||||||
|
@_shuffled[r] = @nodes[i]
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = fail
|
||||||
|
|
||||||
|
if @_running
|
||||||
|
result = @_running\update ...
|
||||||
|
unless result == running
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
local tmp
|
||||||
|
while result == fail and #@_shuffled > 0
|
||||||
|
result = @_shuffled[1]\update ...
|
||||||
|
tmp = table.remove @_shuffled, 1
|
||||||
|
|
||||||
|
if result == running
|
||||||
|
@_running = tmp
|
||||||
|
else
|
||||||
|
@shuffle!
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Repeats a node a specified number of times, unless it fails.
|
||||||
|
class Repeat extends Node
|
||||||
|
new: (@cycles=2, @node=Node!) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@counter = 1
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = success
|
||||||
|
|
||||||
|
if @_running
|
||||||
|
result = @node\update ...
|
||||||
|
unless result == running
|
||||||
|
@_running = false
|
||||||
|
|
||||||
|
while result == success and @counter < @cycles
|
||||||
|
@counter += 1
|
||||||
|
result = @node\update ...
|
||||||
|
|
||||||
|
if result == running
|
||||||
|
@_running = true
|
||||||
|
else
|
||||||
|
@counter = 1
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
-- Returns success whether or not the node succeeds.
|
||||||
|
class Succeed extends Node
|
||||||
|
new: (@node=Node!) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
if running == @node\update ...
|
||||||
|
return running
|
||||||
|
else
|
||||||
|
return success
|
||||||
|
|
||||||
|
-- Returns fail whether or not the node fails.
|
||||||
|
class Fail extends Node
|
||||||
|
new: (@node=Node!) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
if running == @node\update ...
|
||||||
|
return running
|
||||||
|
else
|
||||||
|
return fail
|
||||||
|
|
||||||
|
-- Returns success when the node fails, and failure on success.
|
||||||
|
class Invert extends Node
|
||||||
|
new: (@node=Node!) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
result = @node\update ...
|
||||||
|
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 Node
|
||||||
|
new: (@node=Node!) =>
|
||||||
|
super!
|
||||||
|
|
||||||
|
@ran = false
|
||||||
|
|
||||||
|
update: (...) =>
|
||||||
|
unless @ran
|
||||||
|
result = @node\update ...
|
||||||
|
unless result == running
|
||||||
|
@run = true
|
||||||
|
return result
|
||||||
|
else
|
||||||
|
return fail
|
||||||
|
|
||||||
|
behave = {
|
||||||
|
-- Leaf Node
|
||||||
|
:Node
|
||||||
|
|
||||||
|
-- Composite Nodes
|
||||||
|
:Sequence
|
||||||
|
:Selector
|
||||||
|
:Random
|
||||||
|
:RandomSequence
|
||||||
|
:RandomSelector
|
||||||
|
|
||||||
|
-- Decorator Nodes
|
||||||
|
:Repeat
|
||||||
|
:Succeed
|
||||||
|
:Fail
|
||||||
|
:Invert
|
||||||
|
:RunOnce
|
||||||
|
|
||||||
|
-- Return Values
|
||||||
|
:success
|
||||||
|
:running
|
||||||
|
:fail
|
||||||
|
}
|
||||||
|
|
||||||
|
behave.clone = (object) ->
|
||||||
|
local new
|
||||||
|
cls = getmetatable(object).__class.__name
|
||||||
|
|
||||||
|
if cls == "Repeat"
|
||||||
|
new = behave[cls] object.cycles, object
|
||||||
|
else
|
||||||
|
new = behave[cls] object
|
||||||
|
|
||||||
|
if object.nodes
|
||||||
|
nodes = {}
|
||||||
|
for k,v in pairs object.nodes
|
||||||
|
nodes[k] = behave.clone v
|
||||||
|
new.nodes = nodes
|
||||||
|
elseif object.node
|
||||||
|
new.node = behave.clone object.node
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
|
return behave
|
Loading…
Reference in New Issue
Block a user