Skip to content

Instantly share code, notes, and snippets.

@josephg
Last active April 17, 2018 03:25
Show Gist options
  • Save josephg/97d99ab290bf21f776cb5f112348fd75 to your computer and use it in GitHub Desktop.
Save josephg/97d99ab290bf21f776cb5f112348fd75 to your computer and use it in GitHub Desktop.
Behaviour trees using generators
// Some example behaviours
// Helper to wait X frames
function *wait(framecount) {
while(--framecount) yield // Each call to yield will wait 1 frame
}
// Dance function will
function *dance() {
console.log('\\_o_\\')
yield wait(10) // wait 10 frames (1 second)
console.log('/_o_/')
yield wait(5)
console.log('\\_o_/')
}
// *** Plumbing.
// The state is a stack of iterators. Each tick we'll take the top of the stack
// (the stack's last element) and iterate it.
let state = [dance()]
// Grab a reference to GeneratorFunction so we can detect nested iterators.
const GeneratorFunction = (function *(){})().constructor
const next = (iter) => {
// Used to get values out of nested generators
let lastVal = null
while (state.length) {
const iter = state[state.length - 1]
const {value, done} = iter.next(lastVal)
//console.log('iter next', value, done)
if (done) {
// value is the return value of the function. We'll pass that back to the
// yield statement of the caller. We could instead force the caller to
// return or something to save on error handling.
state.pop()
lastVal = value
} else if (typeof value === 'object' && value.constructor === GeneratorFunction) {
state.push(value)
lastVal = null
} else return null
}
return true // success.
}
let timer = setInterval(tick, 100)
function tick() {
const val = next()
//console.log('tick', val)
// We're done
if (val === true) clearInterval(timer)
}
$ node bt.js
\_o_\ # ... then 1 second pause
/_o_/ # then 500ms pause
\_o_/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment