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