behaviortree
Version:
A JavaScript implementation of Behavior Trees. They are useful for implementing AIs. For Browsers and NodeJS.
254 lines (253 loc) • 11.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-env jest */
const constants_1 = require("./constants");
const Sequence_1 = __importDefault(require("./Sequence"));
const Task_1 = __importDefault(require("./Task"));
describe('Sequence', () => {
let countSuccess = 0;
const successTask = new Task_1.default({
run: function () {
++countSuccess;
return constants_1.SUCCESS;
}
});
let countFail = 0;
const failTask = new Task_1.default({
run: function () {
++countFail;
return constants_1.FAILURE;
}
});
let countRunning = 0;
const runningTask = new Task_1.default({
run: function () {
++countRunning;
return constants_1.RUNNING;
}
});
beforeEach(() => {
countSuccess = 0;
countFail = 0;
countRunning = 0;
});
it('stops immediately at a failing node', () => {
const selector = new Sequence_1.default({
nodes: [failTask, successTask]
});
selector.run();
expect(countSuccess).toEqual(0);
expect(countFail).toEqual(1);
});
it('stops at a failing node', () => {
const selector = new Sequence_1.default({
nodes: [successTask, successTask, failTask, failTask, successTask]
});
selector.run();
expect(countSuccess).toEqual(2);
expect(countFail).toEqual(1);
});
it('calls all tasks if all are successful', () => {
const selector = new Sequence_1.default({
nodes: [successTask, successTask, successTask, successTask]
});
selector.run();
expect(countSuccess).toEqual(4);
expect(countFail).toEqual(0);
});
it('does not call tasks after running task', () => {
const selector = new Sequence_1.default({
nodes: [successTask, successTask, runningTask, successTask]
});
selector.run();
expect(countSuccess).toEqual(2);
expect(countRunning).toEqual(1);
});
describe('result values', () => {
it('returns SUCCESS if all task succeeds', () => {
const selector = new Sequence_1.default({
nodes: [successTask, successTask, successTask]
});
expect(selector.run()).toEqual(constants_1.SUCCESS);
});
it('returns FAILURE if one task fails', () => {
const selector = new Sequence_1.default({
nodes: [successTask, failTask, successTask, successTask]
});
expect(selector.run()).toEqual(constants_1.FAILURE);
});
it('returns the index of still running task as array of running lastRun', () => {
const selector = new Sequence_1.default({
nodes: [successTask, successTask, runningTask, successTask]
});
expect(selector.run()).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, constants_1.RUNNING] });
});
});
describe('blackboard', () => {
const aTask = new Task_1.default({
run: function (blackboard) {
++blackboard.counter;
return constants_1.SUCCESS;
}
});
const bTask = new Task_1.default({
run: function (blackboard) {
blackboard.counter += 10;
return constants_1.SUCCESS;
}
});
it('can be passed to a task, and stores data between different tasks', () => {
const selector = new Sequence_1.default({
nodes: [aTask, bTask]
});
const blackboard = { counter: 0 };
selector.run(blackboard);
expect(blackboard.counter).toEqual(11);
});
});
describe('nested selectors', () => {
const aTask = new Task_1.default({
run: function (blackboard) {
++blackboard.aCounter;
return constants_1.SUCCESS;
}
});
const bTask = new Task_1.default({
run: function (blackboard) {
++blackboard.bCounter;
return constants_1.SUCCESS;
}
});
it('calls all nested tasks if all are succeeding', () => {
const selector = new Sequence_1.default({
nodes: [
aTask,
aTask,
new Sequence_1.default({
nodes: [bTask, bTask, bTask]
})
]
});
const blackboard = { aCounter: 0, bCounter: 0 };
selector.run(blackboard);
expect(blackboard.aCounter).toEqual(2);
expect(blackboard.bCounter).toEqual(3);
});
describe('running tasks', () => {
const switchTask = new Task_1.default({
run: function (blackboard) {
++blackboard.switchCounter;
return blackboard.switchResult;
}
});
const selector = new Sequence_1.default({
nodes: [
aTask,
aTask,
new Sequence_1.default({
nodes: [bTask, switchTask, bTask]
}),
aTask
]
});
it('returns lastRun if tasks are running', () => {
const blackboard = { aCounter: 0, bCounter: 0, switchCounter: 0, switchResult: constants_1.RUNNING };
const result = selector.run(blackboard);
expect(blackboard.aCounter).toEqual(2);
expect(blackboard.bCounter).toEqual(1);
expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, { total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING] }] });
});
it('resumes tasks where we left off', () => {
const blackboard = { aCounter: 0, bCounter: 0, switchCounter: 0, switchResult: constants_1.RUNNING };
let result = selector.run(blackboard);
expect(blackboard.switchCounter).toEqual(1);
expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, { total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING] }] });
result = selector.run(blackboard, { lastRun: result });
expect(blackboard.switchCounter).toEqual(2);
expect(blackboard.aCounter).toEqual(2);
expect(blackboard.bCounter).toEqual(1);
expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, { total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING] }] });
});
it('after resuming in can progress, if tasks allow', () => {
const blackboard = { aCounter: 0, bCounter: 0, switchCounter: 0, switchResult: constants_1.RUNNING };
let result = selector.run(blackboard);
expect(blackboard.switchCounter).toEqual(1);
expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, { total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING] }] });
blackboard.switchResult = constants_1.SUCCESS;
result = selector.run(blackboard, { lastRun: result });
expect(blackboard.switchCounter).toEqual(2);
expect(blackboard.aCounter).toEqual(3);
expect(blackboard.bCounter).toEqual(2);
expect(result).toEqual(constants_1.SUCCESS);
// expect(result).toEqual({
// total: SUCCESS,
// state: [SUCCESS, SUCCESS, { total: SUCCESS, state: [SUCCESS, SUCCESS, SUCCESS] }, SUCCESS]
// });
});
it('after resuming in can progress, if tasks allow it', () => {
const blackboard = { aCounter: 0, bCounter: 0, switchCounter: 0, switchResult: constants_1.RUNNING };
let result = selector.run(blackboard);
expect(blackboard.switchCounter).toEqual(1);
expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.SUCCESS, { total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING] }] });
blackboard.switchResult = constants_1.FAILURE;
result = selector.run(blackboard, { lastRun: result });
expect(blackboard.switchCounter).toEqual(2);
// No count increments because of success
expect(blackboard.aCounter).toEqual(2);
expect(blackboard.bCounter).toEqual(1);
expect(result).toEqual(constants_1.FAILURE);
// expect(result).toEqual({ total: FAILURE, state: [SUCCESS, SUCCESS, { total: FAILURE, state: [SUCCESS, FAILURE] }] });
});
});
describe('start and end callbacks', () => {
const aTask = new Task_1.default({
run: function () {
return constants_1.SUCCESS;
}
});
const switchTask = new Task_1.default({
start: function (blackboard) {
++blackboard.start;
},
run: function (blackboard) {
++blackboard.run;
return blackboard.switchResult;
},
end: function (blackboard) {
++blackboard.end;
}
});
it('start is not called again on further running node', () => {
const selector = new Sequence_1.default({
nodes: [aTask, aTask, switchTask, aTask]
});
const blackboard = {
switchResult: constants_1.RUNNING,
start: 0,
run: 0,
end: 0
};
const result = selector.run(blackboard);
expect(blackboard.start).toEqual(1);
expect(blackboard.run).toEqual(1);
expect(blackboard.end).toEqual(0);
const result2 = selector.run(blackboard, { lastRun: result, rerun: true });
expect(blackboard.start).toEqual(1);
expect(blackboard.run).toEqual(2);
expect(blackboard.end).toEqual(0);
blackboard.switchResult = constants_1.SUCCESS;
selector.run(blackboard, { lastRun: result2, rerun: true });
expect(blackboard.start).toEqual(1);
expect(blackboard.run).toEqual(3);
expect(blackboard.end).toEqual(1);
selector.run(blackboard);
expect(blackboard.start).toEqual(2);
expect(blackboard.run).toEqual(4);
expect(blackboard.end).toEqual(2);
});
});
});
});