UNPKG

behaviortree

Version:

A JavaScript implementation of Behavior Trees. They are useful for implementing AIs. For Browsers and NodeJS.

224 lines (223 loc) 10 kB
"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 Selector_1 = __importDefault(require("./Selector")); const Task_1 = __importDefault(require("./Task")); describe('Selector', () => { 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 successfull node', () => { const selector = new Selector_1.default({ nodes: [successTask, failTask] }); selector.run(); expect(countSuccess).toEqual(1); expect(countFail).toEqual(0); }); it('stops at a successfull node', () => { const selector = new Selector_1.default({ nodes: [failTask, successTask, successTask, successTask, failTask] }); selector.run(); expect(countSuccess).toEqual(1); expect(countFail).toEqual(1); }); it('calls all tasks if all fail', () => { const selector = new Selector_1.default({ nodes: [failTask, failTask, failTask, failTask] }); selector.run(); expect(countSuccess).toEqual(0); expect(countFail).toEqual(4); }); it('does not call tasks after running task', () => { const selector = new Selector_1.default({ nodes: [failTask, failTask, runningTask, failTask] }); selector.run(); expect(countFail).toEqual(2); expect(countRunning).toEqual(1); }); describe('result values', () => { it('returns SUCCESS if one task succeeds', () => { const selector = new Selector_1.default({ nodes: [failTask, successTask, failTask] }); expect(selector.run()).toEqual(constants_1.SUCCESS); }); it('returns FAILURE if no task succeeds', () => { const selector = new Selector_1.default({ nodes: [failTask, failTask, failTask, failTask] }); expect(selector.run()).toEqual(constants_1.FAILURE); }); it('returns the index of still running task as array of running lastRun', () => { const selector = new Selector_1.default({ nodes: [failTask, failTask, runningTask, failTask] }); expect(selector.run()).toEqual({ total: constants_1.RUNNING, state: [constants_1.FAILURE, constants_1.FAILURE, constants_1.RUNNING] }); }); }); describe('blackboard', () => { const aTask = new Task_1.default({ run: function (blackboard) { ++blackboard.counter; return constants_1.FAILURE; } }); const bTask = new Task_1.default({ run: function (blackboard) { blackboard.counter += 10; return constants_1.FAILURE; } }); it('can be passed to a task, and stores data between different tasks', () => { const selector = new Selector_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.FAILURE; } }); const bTask = new Task_1.default({ run: function (blackboard) { ++blackboard.bCounter; return constants_1.FAILURE; } }); it('calls all nested tasks if all are failing', () => { const selector = new Selector_1.default({ nodes: [ aTask, aTask, new Selector_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; } }); let selector = new Selector_1.default({ nodes: [ aTask, aTask, new Selector_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.FAILURE, constants_1.FAILURE, { total: constants_1.RUNNING, state: [constants_1.FAILURE, 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.FAILURE, constants_1.FAILURE, { total: constants_1.RUNNING, state: [constants_1.FAILURE, 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.FAILURE, constants_1.FAILURE, { total: constants_1.RUNNING, state: [constants_1.FAILURE, 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.FAILURE, constants_1.FAILURE, { total: constants_1.RUNNING, state: [constants_1.FAILURE, constants_1.RUNNING] }] }); blackboard.switchResult = constants_1.FAILURE; 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.FAILURE); }); 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.FAILURE, constants_1.FAILURE, { total: constants_1.RUNNING, state: [constants_1.FAILURE, constants_1.RUNNING] }] }); blackboard.switchResult = constants_1.SUCCESS; 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.SUCCESS); }); it('does not call start on rerunning running task', () => { const blackboard = { start: 0, end: 0, switchResult: constants_1.RUNNING }; selector = new Selector_1.default({ start: function (blackboard) { ++blackboard.start; }, end: function (blackboard) { ++blackboard.end; }, nodes: [aTask, switchTask] }); selector.run(blackboard); expect(blackboard.start).toEqual(1); expect(blackboard.end).toEqual(0); selector.run(blackboard, { rerun: true }); expect(blackboard.start).toEqual(1); expect(blackboard.end).toEqual(0); blackboard.switchResult = constants_1.FAILURE; selector.run(blackboard, { rerun: true }); expect(blackboard.start).toEqual(1); expect(blackboard.end).toEqual(1); }); }); }); });