UNPKG

behaviortree

Version:

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

226 lines (225 loc) 9.83 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 */ /* eslint-disable @typescript-eslint/no-explicit-any */ const constants_1 = require("./constants"); const Parallel_1 = __importDefault(require("./Parallel")); const Task_1 = __importDefault(require("./Task")); describe('Parallel', () => { 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; } }); const switchTask = new Task_1.default({ run: function (blackboard) { ++blackboard.switchCounter; return blackboard.switchResult; } }); const switchTask2 = new Task_1.default({ run: function (blackboard) { ++blackboard.switchCounter2; return blackboard.switchResult2; } }); const switchTask3 = new Task_1.default({ run: function (blackboard) { ++blackboard.switchCounter3; return blackboard.switchResult3; } }); beforeEach(() => { countSuccess = 0; countFail = 0; countRunning = 0; }); it('runs all child nodes and returns running child index as long as one node is running and none are failing', () => { const parallel = new Parallel_1.default({ nodes: [successTask, runningTask, successTask] }); const result = parallel.run(); expect(countRunning).toEqual(1); expect(countSuccess).toEqual(2); expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.SUCCESS, constants_1.RUNNING, constants_1.SUCCESS] }); }); it('returns failure if one task is failing', () => { const parallel = new Parallel_1.default({ nodes: [successTask, runningTask, failTask] }); const result = parallel.run(); expect(countSuccess).toEqual(1); expect(countRunning).toEqual(1); expect(countFail).toEqual(1); expect(result).toEqual(constants_1.FAILURE); }); it('returns success if all tasks are success', () => { const parallel = new Parallel_1.default({ nodes: [successTask, successTask] }); const result = parallel.run(); expect(countSuccess).toEqual(2); expect(result).toEqual(constants_1.SUCCESS); }); describe('running tasks', () => { const parallel = new Parallel_1.default({ nodes: [switchTask, switchTask2, switchTask3] }); it('resumes tasks that where running and stops as soon as one task returns failure', () => { const blackboard = { switchCounter: 0, switchResult: constants_1.RUNNING, switchCounter2: 0, switchResult2: constants_1.RUNNING, switchCounter3: 0, switchResult3: constants_1.RUNNING }; let result = parallel.run(blackboard); expect(blackboard.switchCounter).toEqual(1); expect(blackboard.switchCounter2).toEqual(1); expect(blackboard.switchCounter3).toEqual(1); expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.RUNNING, constants_1.RUNNING, constants_1.RUNNING] }); blackboard.switchResult2 = constants_1.SUCCESS; result = parallel.run(blackboard, { lastRun: result }); expect(blackboard.switchCounter).toEqual(2); expect(blackboard.switchCounter2).toEqual(2); expect(blackboard.switchCounter3).toEqual(2); expect(result).toEqual({ total: constants_1.RUNNING, state: [constants_1.RUNNING, constants_1.SUCCESS, constants_1.RUNNING] }); blackboard.switchResult3 = constants_1.FAILURE; result = parallel.run(blackboard, { lastRun: result }); // counter 2 did not run anymore expect(blackboard.switchCounter).toEqual(3); expect(blackboard.switchCounter2).toEqual(2); expect(blackboard.switchCounter3).toEqual(3); expect(result).toEqual(constants_1.FAILURE); }); }); describe('deeper nesting', () => { it('works as with shallow nodes', () => { const innerParallel = new Parallel_1.default({ nodes: [switchTask, switchTask2] }); const parallel = new Parallel_1.default({ nodes: [innerParallel, switchTask3] }); const blackboard = { switchCounter: 0, switchResult: constants_1.RUNNING, switchCounter2: 0, switchResult2: constants_1.RUNNING, switchCounter3: 0, switchResult3: constants_1.RUNNING }; let result = parallel.run(blackboard); expect(blackboard.switchCounter).toEqual(1); expect(blackboard.switchCounter2).toEqual(1); expect(blackboard.switchCounter3).toEqual(1); expect(result).toEqual({ total: constants_1.RUNNING, state: [{ total: constants_1.RUNNING, state: [constants_1.RUNNING, constants_1.RUNNING] }, constants_1.RUNNING] }); blackboard.switchResult2 = constants_1.SUCCESS; result = parallel.run(blackboard, { lastRun: result }); expect(blackboard.switchCounter).toEqual(2); expect(blackboard.switchCounter2).toEqual(2); expect(blackboard.switchCounter3).toEqual(2); expect(result).toEqual({ total: constants_1.RUNNING, state: [{ total: constants_1.RUNNING, state: [constants_1.RUNNING, constants_1.SUCCESS] }, constants_1.RUNNING] }); blackboard.switchResult = constants_1.FAILURE; result = parallel.run(blackboard, { lastRun: result }); expect(blackboard.switchCounter).toEqual(3); expect(blackboard.switchCounter2).toEqual(2); expect(blackboard.switchCounter3).toEqual(3); expect(result).toEqual(constants_1.FAILURE); }); it('works when only one node is running', () => { const innerParallel = new Parallel_1.default({ nodes: [switchTask, switchTask2] }); const parallel = new Parallel_1.default({ nodes: [innerParallel, switchTask3] }); const blackboard = { switchCounter: 0, switchResult: constants_1.RUNNING, switchCounter2: 0, switchResult2: constants_1.SUCCESS, switchCounter3: 0, switchResult3: constants_1.SUCCESS }; let result = parallel.run(blackboard); expect(blackboard.switchCounter).toEqual(1); expect(blackboard.switchCounter2).toEqual(1); expect(blackboard.switchCounter3).toEqual(1); expect(result).toEqual({ total: constants_1.RUNNING, state: [{ total: constants_1.RUNNING, state: [constants_1.RUNNING, constants_1.SUCCESS] }, constants_1.SUCCESS] }); blackboard.switchResult = constants_1.SUCCESS; result = parallel.run(blackboard, { lastRun: result }); expect(blackboard.switchCounter).toEqual(2); expect(blackboard.switchCounter2).toEqual(1); expect(blackboard.switchCounter3).toEqual(1); expect(result).toEqual(constants_1.SUCCESS); }); }); 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 parallel = new Parallel_1.default({ nodes: [aTask, aTask, switchTask, aTask] }); const blackboard = { switchResult: constants_1.RUNNING, start: 0, run: 0, end: 0 }; const result = parallel.run(blackboard); expect(blackboard.start).toEqual(1); expect(blackboard.run).toEqual(1); expect(blackboard.end).toEqual(0); const result2 = parallel.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; parallel.run(blackboard, { lastRun: result2, rerun: true }); expect(blackboard.start).toEqual(1); expect(blackboard.run).toEqual(3); expect(blackboard.end).toEqual(1); parallel.run(blackboard); expect(blackboard.start).toEqual(2); expect(blackboard.run).toEqual(4); expect(blackboard.end).toEqual(2); }); }); });