Last active
August 29, 2015 14:04
-
-
Save JakeCoxon/65360313a7566ae1a337 to your computer and use it in GitHub Desktop.
Attempt at simple concurrent behaviour trees in Javascript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
</head> | |
<body> | |
<script> | |
var rootContext = { | |
loop: function loop( context, endPromise, times, _ ) { | |
if ( times == 0 ) return endPromise.success( ); | |
var sequence = [ 'seq' ].concat( Array.prototype.slice.call( arguments, 3 ) ); | |
function finish() { | |
loop( context, endPromise, times - 1, sequence ); | |
} | |
var promise = { success: finish, fail: finish }; | |
runTree( context, promise, sequence ); | |
}, | |
selector: function selector( context, endPromise, _ ) { | |
var selects = Array.prototype.slice.call( arguments, 2 ); | |
if ( selects.length == 0 ) { return endPromise.fail( ); } | |
var promise = { | |
success: endPromise.success, | |
fail: function( ) { | |
selector.apply( null, [ context, endPromise ].concat( selects.slice( 1 ) ) ); | |
} | |
}; | |
runTree( context, promise, selects[ 0 ] ); | |
}, | |
seq: function seq( context, endPromise, _ ) { | |
var sequence = Array.prototype.slice.call( arguments, 2 ); | |
if ( sequence.length == 0 ) { return endPromise.success( ); } | |
var promise = { | |
success: function( ) { | |
seq.apply( null, [ context, endPromise ].concat( sequence.slice( 1 ) ) ); | |
}, | |
fail: endPromise.fail | |
}; | |
runTree( context, promise, sequence[ 0 ] ); | |
}, | |
parallel: function( context, endPromise, _ ) { | |
var processes = Array.prototype.slice.call( arguments, 2 ); | |
var numFinished = processes.length; | |
var isFail = false; | |
var promise = { | |
success: function( ) { | |
numFinished --; | |
if ( numFinished == 0 && isFail ) { | |
endPromise.fail( ); | |
} else if ( numFinished == 0 ) { | |
endPromise.success( ); | |
} | |
}, | |
fail: function( ) { | |
isFail = true; | |
numFinished --; | |
if ( numFinished == 0 ) { | |
endPromise.fail( ); | |
} | |
} | |
} | |
for ( var i = 0; i < processes.length; i++ ) { | |
runTree( context, promise, processes[ i ] ); | |
} | |
}, | |
openSignals: {}, | |
signal: function( context, endPromise, signalName ) { | |
var sig = context.openSignals[ signalName ]; | |
if ( sig ) { | |
delete context.openSignals[ signalName ]; | |
sig.success( ); | |
endPromise.success( ); | |
} else { | |
context.openSignals[ signalName ] = endPromise; | |
} | |
}, | |
randomSuccess: function( context, endPromise, randomness ) { | |
if ( Math.random() < ( randomness || 0.5 ) ) { | |
endPromise.success( ); | |
} else { | |
endPromise.fail( ); | |
} | |
}, | |
goToPosition: function( context, endPromise, pos ) { | |
// Simulate NPC walking to a a position | |
console.log( "go to position", pos ); | |
setTimeout( endPromise.success, 1000 ); | |
}, | |
print: function( context, endPromise, text ) { | |
console.log( text ); | |
endPromise.success( ); | |
}, | |
wait: function( context, endPromise, time ) { | |
setTimeout( endPromise.success, time ); | |
} | |
} | |
function runTree( context, promise, expr ) { | |
var func = expr[ 0 ]; | |
var args = expr.slice( 1 ); | |
context[ func ].apply( this, [ context, promise ].concat( args ) ); | |
} | |
var result = { | |
success: function( ) { | |
console.log( "finished with success" ); | |
}, | |
fail: function( ) { | |
console.log( "finished with fail" ); | |
} | |
} | |
function example() { | |
runTree( rootContext, result, | |
["seq", | |
["parallel", | |
["loop", 5, | |
["seq", | |
["wait", 500], | |
["randomSuccess", 0.5], | |
["goToPosition", [4, 5]], | |
["wait", 500], | |
["goToPosition", [1, 4]]]], | |
["loop", 10, | |
["seq", | |
["print", "hello"], | |
["wait", 1000]]]], | |
["print", "Second thing in the sequence"]] ); | |
} | |
function exampleRandomBranch() { | |
runTree( rootContext, result, | |
["loop", 50, | |
["selector", | |
["seq", | |
["randomSuccess", 0.33], | |
["print", "first"]], | |
["seq", | |
["randomSuccess", 0.5], | |
["print", "second"]], | |
["print", "third"]]] ); | |
} | |
function exampleSynchronise() { | |
runTree( rootContext, result, | |
["parallel", | |
["seq", | |
["print", "starting 1"], | |
["wait", 1000], | |
["print", "sending signal"], | |
["signal", "sync1"]], | |
["seq", | |
["print", "starting 2"], | |
["print", "wait for signal"], | |
["signal", "sync1"], | |
["print", "completed wait for signal"]]] ); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment