behaviortree
Version:
A JavaScript implementation of Behavior Trees. They are useful for implementing AIs. For Browsers and NodeJS.
105 lines (104 loc) • 4.17 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-env jest */
const sinon_1 = __importDefault(require("sinon"));
const constants_1 = require("./constants");
const BehaviorTree_1 = __importDefault(require("./BehaviorTree"));
const BehaviorTreeImporter_1 = __importDefault(require("./BehaviorTreeImporter"));
const Decorator_1 = __importDefault(require("./Decorator"));
const Task_1 = __importDefault(require("./Task"));
const Introspector_1 = __importDefault(require("./Introspector"));
class EnemyInSightDecorator extends Decorator_1.default {
constructor() {
super(...arguments);
this.nodeType = 'EnemyInSightDecorator';
}
decorate(run, blackboard) {
return blackboard.enemyInSight ? run() : constants_1.FAILURE;
}
}
describe('BehaviorTreeImporter', () => {
let clock;
let blackboard;
let importer;
beforeEach(() => {
clock = sinon_1.default.useFakeTimers();
blackboard = {
timesJumped: 0
};
BehaviorTree_1.default.register('walk', new Task_1.default({
run: function (blackboard) {
blackboard.walking = true;
return constants_1.SUCCESS;
}
}));
BehaviorTree_1.default.register('idle', new Task_1.default({
run: function (blackboard) {
blackboard.walking = false;
return constants_1.SUCCESS;
}
}));
BehaviorTree_1.default.register('jump', new Task_1.default({
run: function (blackboard) {
blackboard.timesJumped++;
return constants_1.SUCCESS;
}
}));
importer = new BehaviorTreeImporter_1.default();
importer.defineType('ifEnemyInSight', EnemyInSightDecorator);
});
describe('importing a complex JSON tree into a usable behavior tree', () => {
const json = {
type: 'selector',
name: 'the root',
nodes: [
{
type: 'ifEnemyInSight',
name: 'handling enemies',
node: { type: 'walk', name: 'go to enemy' }
},
{
type: 'cooldown',
name: 'jumping around',
cooldown: 1,
node: { type: 'jump', name: 'jump up' } // beacuse we can ;)
},
{ type: 'idle', name: 'doing nothing' }
]
};
let bTree;
let introspector;
beforeEach(() => {
introspector = new Introspector_1.default();
bTree = new BehaviorTree_1.default({
tree: importer.parse(json),
blackboard
});
});
it('works', () => {
bTree.step({ introspector });
expect(introspector.lastResult?.name).toEqual('the root');
const selectorNodes = introspector.lastResult?.children || [];
expect(selectorNodes.length).toEqual(2);
expect(selectorNodes.map((x) => x.name)).toEqual(['handling enemies', 'jumping around']);
expect(selectorNodes.map((x) => x.result)).toEqual([false, true]);
});
it('passes in the config as it is supposed to', () => {
bTree.step({ introspector });
bTree.step({ introspector });
let selectorNodes = introspector.lastResult?.children || [];
expect(selectorNodes.map((x) => x.result)).toEqual([false, false, true]);
clock.tick(999);
bTree.step({ introspector });
selectorNodes = introspector.lastResult?.children || [];
expect(selectorNodes.map((x) => x.result)).toEqual([false, false, true]);
clock.tick(1);
bTree.step({ introspector });
selectorNodes = introspector.lastResult?.children || [];
expect(selectorNodes.map((x) => x.result)).toEqual([false, true]);
});
});
});