From cf84399fe30581af2a97f6cdb1417a0bb3574f35 Mon Sep 17 00:00:00 2001 From: Nikolas Howard Date: Fri, 29 Sep 2023 08:43:58 +0100 Subject: [PATCH] Tidying --- dist/Agent.d.ts | 12 + dist/BehaviourTree.d.ts | 89 + dist/BehaviourTreeOptions.d.ts | 15 + dist/Lookup.d.ts | 63 + dist/RootAstNodesBuilder.d.ts | 104 + dist/State.d.ts | 12 + dist/attributes/Attribute.d.ts | 36 + dist/attributes/callbacks/Callback.d.ts | 36 + dist/attributes/callbacks/Entry.d.ts | 18 + dist/attributes/callbacks/Exit.d.ts | 20 + dist/attributes/callbacks/Step.d.ts | 18 + dist/attributes/guards/Guard.d.ts | 37 + dist/attributes/guards/GuardPath.d.ts | 23 + .../guards/GuardUnsatisifedException.d.ts | 17 + dist/attributes/guards/Until.d.ts | 19 + dist/attributes/guards/While.d.ts | 19 + dist/bundle.js | 1860 ++++++++++++++++ dist/bundle.js.map | 7 + dist/dsl/DSLDefinitionParser.d.ts | 163 ++ dist/dsl/DSLNodeArgumentParser.d.ts | 36 + dist/dsl/DSLUtilities.d.ts | 7 + dist/index.d.ts | 4 + dist/index.js | 1863 +++++++++++++++++ dist/index.js.map | 7 + dist/nodes/Node.d.ts | 111 + dist/nodes/composite/Composite.d.ts | 32 + dist/nodes/composite/Lotto.d.ts | 33 + dist/nodes/composite/Parallel.d.ts | 26 + dist/nodes/composite/Selector.d.ts | 27 + dist/nodes/composite/Sequence.d.ts | 27 + dist/nodes/decorator/Decorator.d.ts | 32 + dist/nodes/decorator/Fail.d.ts | 26 + dist/nodes/decorator/Flip.d.ts | 26 + dist/nodes/decorator/Repeat.d.ts | 58 + dist/nodes/decorator/Retry.d.ts | 58 + dist/nodes/decorator/Root.d.ts | 26 + dist/nodes/decorator/Succeed.d.ts | 26 + dist/nodes/leaf/Action.d.ts | 46 + dist/nodes/leaf/Condition.d.ts | 29 + dist/nodes/leaf/Leaf.d.ts | 10 + dist/nodes/leaf/Wait.d.ts | 42 + src/BehaviourTree.ts | 2 +- src/dsl/DSLDefinitionParser.ts | 75 +- src/dsl/DSLNodeArgumentParser.ts | 8 +- src/dsl/DSLUtilities.ts | 4 +- 45 files changed, 5173 insertions(+), 36 deletions(-) create mode 100644 dist/Agent.d.ts create mode 100644 dist/BehaviourTree.d.ts create mode 100644 dist/BehaviourTreeOptions.d.ts create mode 100644 dist/Lookup.d.ts create mode 100644 dist/RootAstNodesBuilder.d.ts create mode 100644 dist/State.d.ts create mode 100644 dist/attributes/Attribute.d.ts create mode 100644 dist/attributes/callbacks/Callback.d.ts create mode 100644 dist/attributes/callbacks/Entry.d.ts create mode 100644 dist/attributes/callbacks/Exit.d.ts create mode 100644 dist/attributes/callbacks/Step.d.ts create mode 100644 dist/attributes/guards/Guard.d.ts create mode 100644 dist/attributes/guards/GuardPath.d.ts create mode 100644 dist/attributes/guards/GuardUnsatisifedException.d.ts create mode 100644 dist/attributes/guards/Until.d.ts create mode 100644 dist/attributes/guards/While.d.ts create mode 100644 dist/bundle.js create mode 100644 dist/bundle.js.map create mode 100644 dist/dsl/DSLDefinitionParser.d.ts create mode 100644 dist/dsl/DSLNodeArgumentParser.d.ts create mode 100644 dist/dsl/DSLUtilities.d.ts create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 dist/nodes/Node.d.ts create mode 100644 dist/nodes/composite/Composite.d.ts create mode 100644 dist/nodes/composite/Lotto.d.ts create mode 100644 dist/nodes/composite/Parallel.d.ts create mode 100644 dist/nodes/composite/Selector.d.ts create mode 100644 dist/nodes/composite/Sequence.d.ts create mode 100644 dist/nodes/decorator/Decorator.d.ts create mode 100644 dist/nodes/decorator/Fail.d.ts create mode 100644 dist/nodes/decorator/Flip.d.ts create mode 100644 dist/nodes/decorator/Repeat.d.ts create mode 100644 dist/nodes/decorator/Retry.d.ts create mode 100644 dist/nodes/decorator/Root.d.ts create mode 100644 dist/nodes/decorator/Succeed.d.ts create mode 100644 dist/nodes/leaf/Action.d.ts create mode 100644 dist/nodes/leaf/Condition.d.ts create mode 100644 dist/nodes/leaf/Leaf.d.ts create mode 100644 dist/nodes/leaf/Wait.d.ts diff --git a/dist/Agent.d.ts b/dist/Agent.d.ts new file mode 100644 index 0000000..abde15d --- /dev/null +++ b/dist/Agent.d.ts @@ -0,0 +1,12 @@ +import { CompleteState } from "./State"; +export type Agent = { + [actionName: string]: AgentFunction; +}; +export type ExitFunctionArg = { + succeeded: boolean; + aborted: boolean; +}; +export type FunctionArg = number | string | boolean | null | ExitFunctionArg; +export type ActionResult = CompleteState | Promise | boolean; +export type AgentFunction = (this: Agent, ...args: FunctionArg[]) => ActionResult; +export type GlobalFunction = (agent: Agent, ...args: FunctionArg[]) => ActionResult; diff --git a/dist/BehaviourTree.d.ts b/dist/BehaviourTree.d.ts new file mode 100644 index 0000000..5b62d16 --- /dev/null +++ b/dist/BehaviourTree.d.ts @@ -0,0 +1,89 @@ +import { AnyArgument } from "./RootAstNodesBuilder"; +import { AnyState } from "./State"; +import Root from "./nodes/decorator/Root"; +import { Agent, GlobalFunction } from "./Agent"; +import { CallbackAttributeDetails } from "./attributes/callbacks/Callback"; +import { GuardAttributeDetails } from "./attributes/guards/Guard"; +import { BehaviourTreeOptions } from "./BehaviourTreeOptions"; +export type FlattenedTreeNode = { + id: string; + type: string; + caption: string; + state: AnyState; + guards: GuardAttributeDetails[]; + callbacks: CallbackAttributeDetails[]; + args: AnyArgument[]; + parentId: string | null; +}; +/** + * A representation of a behaviour tree. + */ +export declare class BehaviourTree { + private agent; + private options; + /** + * The main root tree node. + */ + readonly rootNode: Root; + /** + * Creates a new instance of the BehaviourTree class. + * @param definition The behaviour tree definition. + * @param agent The agent instance that this behaviour tree is modelling behaviour for. + * @param options The behaviour tree options object. + */ + constructor(definition: string, agent: Agent, options?: BehaviourTreeOptions); + /** + * Gets whether the tree is in the RUNNING state. + * @returns true if the tree is in the RUNNING state, otherwise false. + */ + isRunning(): boolean; + /** + * Gets the current tree state of SUCCEEDED, FAILED, READY or RUNNING. + * @returns The current tree state. + */ + getState(): AnyState; + /** + * Step the tree. + * Carries out a node update that traverses the tree from the root node outwards to any child nodes, skipping those that are already in a resolved state of SUCCEEDED or FAILED. + * After being updated, leaf nodes will have a state of SUCCEEDED, FAILED or RUNNING. Leaf nodes that are left in the RUNNING state as part of a tree step will be revisited each + * subsequent step until they move into a resolved state of either SUCCEEDED or FAILED, after which execution will move through the tree to the next node with a state of READY. + * + * Calling this method when the tree is already in a resolved state of SUCCEEDED or FAILED will cause it to be reset before tree traversal begins. + */ + step(): void; + /** + * Resets the tree from the root node outwards to each nested node, giving each a state of READY. + */ + reset(): void; + /** + * Gets the flattened details of every node in the tree. + * @returns The flattened details of every node in the tree. + */ + getFlattenedNodeDetails(): FlattenedTreeNode[]; + /** + * Registers the action/condition/guard/callback function or subtree with the given name. + * @param name The name of the function or subtree to register. + * @param value The function or subtree definition to register. + */ + static register(name: string, value: GlobalFunction | string): void; + /** + * Unregisters the registered action/condition/guard/callback function or subtree with the given name. + * @param name The name of the registered action/condition/guard/callback function or subtree to unregister. + */ + static unregister(name: string): void; + /** + * Unregister all registered action/condition/guard/callback functions and subtrees. + */ + static unregisterAll(): void; + /** + * Parses a behaviour tree definition and creates a tree of behaviour tree nodes. + * @param {string} definition The behaviour tree definition. + * @returns The root behaviour tree node. + */ + private static _createRootNode; + /** + * Applies a guard path to every leaf of the tree to evaluate as part of each update. + * @param rootNode The main root tree node. + */ + private static _applyLeafNodeGuardPaths; +} diff --git a/dist/BehaviourTreeOptions.d.ts b/dist/BehaviourTreeOptions.d.ts new file mode 100644 index 0000000..138cc72 --- /dev/null +++ b/dist/BehaviourTreeOptions.d.ts @@ -0,0 +1,15 @@ +/** + * The options object that can be passed as an argument when instantiating the BehaviourTree class. + */ +export interface BehaviourTreeOptions { + /** + * Gets a delta time in seconds that is used to calculate the elapsed duration of any wait nodes. + * @returns The delta time to use in seconds. + */ + getDeltaTime?(): number; + /** + * Gets a pseudo-random floating-point number between 0 (inclusive) and 1 (exclusive) for use in the selection of active children for any lotto nodes. + * @returns A floating-point number between 0 (inclusive) and 1 (exclusive) + */ + random?(): number; +} diff --git a/dist/Lookup.d.ts b/dist/Lookup.d.ts new file mode 100644 index 0000000..8efbc28 --- /dev/null +++ b/dist/Lookup.d.ts @@ -0,0 +1,63 @@ +import { ActionResult, Agent, ExitFunctionArg, GlobalFunction } from "./Agent"; +import { AnyArgument, RootAstNode } from "./RootAstNodesBuilder"; +type ExitResultArg = { + value: ExitFunctionArg; +}; +export type AnyExitArgument = AnyArgument | ExitResultArg; +export type InvokerFunction = (args: AnyExitArgument[]) => ActionResult; +/** + * A singleton used to store and lookup registered functions and subtrees. + */ +export default class Lookup { + /** + * The object holding any registered functions keyed on function name. + */ + private static functionTable; + /** + * The object holding any registered sub-trees keyed on tree name. + */ + private static subtreeTable; + /** + * Gets the function with the specified name. + * @param name The name of the function. + * @returns The function with the specified name. + */ + static getFunc(name: string): GlobalFunction; + /** + * Sets the function with the specified name for later lookup. + * @param name The name of the function. + * @param func The function. + */ + static setFunc(name: string, func: GlobalFunction): void; + /** + * Gets the function invoker for the specified agent and function name. + * If a function with the specified name exists on the agent object then it will + * be returned, otherwise we will then check the registered functions for a match. + * @param agent The agent instance that this behaviour tree is modelling behaviour for. + * @param name The function name. + * @returns The function invoker for the specified agent and function name. + */ + static getFuncInvoker(agent: Agent, name: string): InvokerFunction | null; + /** + * Gets the subtree with the specified name. + * @param name The name of the subtree. + * @returns The subtree with the specified name. + */ + static getSubtree(name: string): RootAstNode; + /** + * Sets the subtree with the specified name for later lookup. + * @param name The name of the subtree. + * @param subtree The subtree. + */ + static setSubtree(name: string, subtree: RootAstNode): void; + /** + * Removes the registered function or subtree with the specified name. + * @param name The name of the registered function or subtree. + */ + static remove(name: string): void; + /** + * Remove all registered functions and subtrees. + */ + static empty(): void; +} +export {}; diff --git a/dist/RootAstNodesBuilder.d.ts b/dist/RootAstNodesBuilder.d.ts new file mode 100644 index 0000000..23c835b --- /dev/null +++ b/dist/RootAstNodesBuilder.d.ts @@ -0,0 +1,104 @@ +import Action from "./nodes/leaf/Action"; +import Condition from "./nodes/leaf/Condition"; +import Wait from "./nodes/leaf/Wait"; +import Root from "./nodes/decorator/Root"; +import Repeat from "./nodes/decorator/Repeat"; +import Retry from "./nodes/decorator/Retry"; +import Lotto from "./nodes/composite/Lotto"; +import Node from "./nodes/Node"; +import Attribute from "./attributes/Attribute"; +import Composite from "./nodes/composite/Composite"; +import Decorator from "./nodes/decorator/Decorator"; +import Leaf from "./nodes/leaf/Leaf"; +export type Argument = { + value: T; + type: string; +}; +type NullArgument = Argument & { + type: "null"; +}; +type BooleanArgument = Argument & { + type: "boolean"; +}; +type NumberArgument = Argument & { + type: "number"; + isInteger: boolean; +}; +type StringPlaceholderArgument = Argument & { + type: "string"; +}; +type IdentifierArgument = Argument & { + type: "identifier"; +}; +export type AnyArgument = NullArgument | BooleanArgument | NumberArgument | StringPlaceholderArgument | IdentifierArgument; +type Validatable = { + children?: AstNode[]; + validate: (depth: number) => void; +}; +type NodeInstanceCreator = (namedRootNodeProvider: (name: string) => RootAstNode, visitedBranches: string[]) => T; +export type AstNode = Validatable & { + type: string; + createNodeInstance: NodeInstanceCreator; +}; +export type LeafAstNode = AstNode & { + type: "action" | "condition" | "wait"; + attributes: Attribute[]; +}; +export type CompositeAstNode = AstNode & { + type: "lotto" | "parallel" | "selector" | "sequence"; + attributes: Attribute[]; + children: AstNode[]; +}; +export type DecoratorAstNode = AstNode & { + type: "fail" | "flip" | "repeat" | "retry" | "root" | "succeed"; + attributes: Attribute[]; + children: AstNode[]; +}; +export type BranchAstNode = AstNode & { + type: "branch"; + branchName: "" | string; +}; +export type LottoAstNode = CompositeAstNode & { + type: "lotto"; + tickets: number[]; +}; +export type RootAstNode = DecoratorAstNode & { + type: "root"; + name: null | string; +}; +export type RepeatAstNode = DecoratorAstNode & { + type: "repeat"; + iterations: number | null; + iterationsMin: number | null; + iterationsMax: number | null; +}; +export type RetryAstNode = DecoratorAstNode & { + type: "retry"; + attempts: number | null; + attemptsMin: number | null; + attemptsMax: number | null; +}; +export type ActionAstNode = LeafAstNode & { + type: "action"; + actionName: string; + actionArguments: AnyArgument[]; +}; +export type ConditionAstNode = LeafAstNode & { + type: "condition"; + conditionName: string; + conditionArguments: AnyArgument[]; +}; +export type WaitAstNode = LeafAstNode & { + type: "wait"; + duration: number | null; + durationMin: number | null; + durationMax: number | null; +}; +export type AnyAstNode = BranchAstNode | CompositeAstNode | LottoAstNode | DecoratorAstNode | RootAstNode | RepeatAstNode | RetryAstNode | LeafAstNode | ActionAstNode | ConditionAstNode | WaitAstNode; +/** + * Create an array of root AST nodes based on the given definition. + * @param definition The definition to parse the AST nodes from. + * @returns The base definition AST nodes. + */ +export default function buildRootASTNodes(definition: string): RootAstNode[]; +export {}; diff --git a/dist/State.d.ts b/dist/State.d.ts new file mode 100644 index 0000000..18da32d --- /dev/null +++ b/dist/State.d.ts @@ -0,0 +1,12 @@ +/** + * Enumeration of node state types. + */ +export declare enum State { + READY = "mistreevous.ready", + RUNNING = "mistreevous.running", + SUCCEEDED = "mistreevous.succeeded", + FAILED = "mistreevous.failed" +} +export { State as default }; +export type CompleteState = State.SUCCEEDED | State.FAILED; +export type AnyState = State.READY | State.RUNNING | CompleteState; diff --git a/dist/attributes/Attribute.d.ts b/dist/attributes/Attribute.d.ts new file mode 100644 index 0000000..6fb7bb3 --- /dev/null +++ b/dist/attributes/Attribute.d.ts @@ -0,0 +1,36 @@ +import { AnyArgument } from "../RootAstNodesBuilder"; +import Guard from "./guards/Guard"; +export type AttributeDetails = { + /** The attribute type. */ + type: string; + /** The attribute arguments. */ + args: AnyArgument[]; +}; +/** + * A base node attribute. + */ +export default abstract class Attribute { + protected type: string; + protected args: AnyArgument[]; + /** + * @param type The node attribute type. + * @param args The array of attribute argument definitions. + */ + constructor(type: string, args: AnyArgument[]); + /** + * Gets the type of the attribute. + */ + getType: () => string; + /** + * Gets the array of attribute argument definitions. + */ + getArguments: () => AnyArgument[]; + /** + * Gets the attribute details. + */ + abstract getDetails(): TAttributeDetails; + /** + * Gets whether this attribute is a guard. + */ + abstract isGuard: () => this is Guard; +} diff --git a/dist/attributes/callbacks/Callback.d.ts b/dist/attributes/callbacks/Callback.d.ts new file mode 100644 index 0000000..6a294b7 --- /dev/null +++ b/dist/attributes/callbacks/Callback.d.ts @@ -0,0 +1,36 @@ +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +import Attribute, { AttributeDetails } from "../Attribute"; +export type CallbackAttributeDetails = { + /** The name of the agent function that is called. */ + functionName: string; +} & AttributeDetails; +/** + * A base node callback attribute. + */ +export default abstract class Callback extends Attribute { + private functionName; + /** + * @param type The node attribute type. + * @param args The array of decorator argument definitions. + * @param functionName The name of the agent function to call. + */ + constructor(type: string, args: AnyArgument[], functionName: string); + /** + * Gets the name of the agent function to call. + */ + getFunctionName: () => string; + /** + * Gets whether this attribute is a guard. + */ + isGuard: () => boolean; + /** + * Gets the attribute details. + */ + getDetails(): CallbackAttributeDetails; + /** + * Attempt to call the agent function that this callback refers to. + * @param agent The agent. + */ + abstract callAgentFunction: (agent: Agent, isSuccess: boolean, isAborted: boolean) => void; +} diff --git a/dist/attributes/callbacks/Entry.d.ts b/dist/attributes/callbacks/Entry.d.ts new file mode 100644 index 0000000..0db55e9 --- /dev/null +++ b/dist/attributes/callbacks/Entry.d.ts @@ -0,0 +1,18 @@ +import Callback from "./Callback"; +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +/** + * An ENTRY callback which defines an agent function to call when the associated node is updated and moves out of running state. + */ +export default class Entry extends Callback { + /** + * @param functionName The name of the agent function to call. + * @param args The array of callback argument definitions. + */ + constructor(functionName: string, args: AnyArgument[]); + /** + * Attempt to call the agent function that this callback refers to. + * @param agent The agent. + */ + callAgentFunction: (agent: Agent) => void; +} diff --git a/dist/attributes/callbacks/Exit.d.ts b/dist/attributes/callbacks/Exit.d.ts new file mode 100644 index 0000000..7c3bbe8 --- /dev/null +++ b/dist/attributes/callbacks/Exit.d.ts @@ -0,0 +1,20 @@ +import Callback from "./Callback"; +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +/** + * An EXIT callback which defines an agent function to call when the associated node is updated and moves to a finished state or is aborted. + */ +export default class Exit extends Callback { + /** + * @param functionName The name of the agent function to call. + * @param args The array of callback argument definitions. + */ + constructor(functionName: string, args: AnyArgument[]); + /** + * Attempt to call the agent function that this callback refers to. + * @param agent The agent. + * @param isSuccess Whether the decorated node was left with a success state. + * @param isAborted Whether the decorated node was aborted. + */ + callAgentFunction: (agent: Agent, isSuccess: boolean, isAborted: boolean) => void; +} diff --git a/dist/attributes/callbacks/Step.d.ts b/dist/attributes/callbacks/Step.d.ts new file mode 100644 index 0000000..d4e0a1c --- /dev/null +++ b/dist/attributes/callbacks/Step.d.ts @@ -0,0 +1,18 @@ +import Callback from "./Callback"; +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +/** + * A STEP callback which defines an agent function to call when the associated node is updated. + */ +export default class Step extends Callback { + /** + * @param functionName The name of the agent function to call. + * @param args The array of callback argument definitions. + */ + constructor(functionName: string, args: AnyArgument[]); + /** + * Attempt to call the agent function that this callback refers to. + * @param agent The agent. + */ + callAgentFunction: (agent: Agent) => void; +} diff --git a/dist/attributes/guards/Guard.d.ts b/dist/attributes/guards/Guard.d.ts new file mode 100644 index 0000000..4332517 --- /dev/null +++ b/dist/attributes/guards/Guard.d.ts @@ -0,0 +1,37 @@ +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +import Attribute, { AttributeDetails } from "../Attribute"; +export type GuardAttributeDetails = { + /** The name of the condition function that determines whether the guard is satisfied. */ + condition: string; +} & AttributeDetails; +/** + * A base node guard attribute. + */ +export default abstract class Guard extends Attribute { + private condition; + /** + * @param type The node attribute type. + * @param args The array of decorator argument definitions. + * @param condition The name of the condition function that determines whether the guard is satisfied. + */ + constructor(type: string, args: AnyArgument[], condition: string); + /** + * Gets the name of the condition function that determines whether the guard is satisfied. + */ + getCondition: () => string; + /** + * Gets whether this attribute is a guard. + */ + isGuard: () => boolean; + /** + * Gets the attribute details. + */ + getDetails(): GuardAttributeDetails; + /** + * Gets whether the guard is satisfied. + * @param agent The agent. + * @returns Whether the guard is satisfied. + */ + abstract isSatisfied(agent: Agent): boolean; +} diff --git a/dist/attributes/guards/GuardPath.d.ts b/dist/attributes/guards/GuardPath.d.ts new file mode 100644 index 0000000..a6fea20 --- /dev/null +++ b/dist/attributes/guards/GuardPath.d.ts @@ -0,0 +1,23 @@ +import { Agent } from "../../Agent"; +import Guard from "./Guard"; +import Node from "../../nodes/Node"; +export type GuardPathPart = { + node: Node; + guards: Guard[]; +}; +/** + * Represents a path of node guards along a root-to-leaf tree path. + */ +export default class GuardPath { + private nodes; + /** + * @param nodes An array of objects defining a node instance -> guard link, ordered by node depth. + */ + constructor(nodes: GuardPathPart[]); + /** + * Evaluate guard conditions for all guards in the tree path, moving outwards from the root. + * @param agent The agent, required for guard evaluation. + * @returns An evaluation results object. + */ + evaluate: (agent: Agent) => void; +} diff --git a/dist/attributes/guards/GuardUnsatisifedException.d.ts b/dist/attributes/guards/GuardUnsatisifedException.d.ts new file mode 100644 index 0000000..71c51f5 --- /dev/null +++ b/dist/attributes/guards/GuardUnsatisifedException.d.ts @@ -0,0 +1,17 @@ +import Node from "../../nodes/Node"; +/** + * An exception thrown when evaluating node guard path conditions and a conditions fails. + */ +export default class GuardUnsatisifedException extends Error { + private source; + /** + * @param source The node at which a guard condition failed. + */ + constructor(source: Node); + /** + * Gets whether the specified node is the node at which a guard condition failed. + * @param node The node to check against the source node. + * @returns Whether the specified node is the node at which a guard condition failed. + */ + isSourceNode: (node: Node) => boolean; +} diff --git a/dist/attributes/guards/Until.d.ts b/dist/attributes/guards/Until.d.ts new file mode 100644 index 0000000..5ec505f --- /dev/null +++ b/dist/attributes/guards/Until.d.ts @@ -0,0 +1,19 @@ +import Guard from "./Guard"; +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +/** + * An UNTIL guard which is satisfied as long as the given condition remains false. + */ +export default class Until extends Guard { + /** + * @param condition The name of the condition function that determines whether the guard is satisfied. + * @param args The array of decorator argument definitions. + */ + constructor(condition: string, args: AnyArgument[]); + /** + * Gets whether the guard is satisfied. + * @param agent The agent. + * @returns Whether the guard is satisfied. + */ + isSatisfied: (agent: Agent) => boolean; +} diff --git a/dist/attributes/guards/While.d.ts b/dist/attributes/guards/While.d.ts new file mode 100644 index 0000000..dd4f6bf --- /dev/null +++ b/dist/attributes/guards/While.d.ts @@ -0,0 +1,19 @@ +import Guard from "./Guard"; +import { Agent } from "../../Agent"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +/** + * A WHILE guard which is satisfied as long as the given condition remains true. + */ +export default class While extends Guard { + /** + * @param condition The name of the condition function that determines whether the guard is satisfied. + * @param args The array of decorator argument definitions. + */ + constructor(condition: string, args: AnyArgument[]); + /** + * Gets whether the guard is satisfied. + * @param agent The agent. + * @returns Whether the guard is satisfied. + */ + isSatisfied: (agent: Agent) => boolean; +} diff --git a/dist/bundle.js b/dist/bundle.js new file mode 100644 index 0000000..12265f3 --- /dev/null +++ b/dist/bundle.js @@ -0,0 +1,1860 @@ +"use strict"; +var mistreevous = (() => { + var __create = Object.create; + var __defProp = Object.defineProperty; + var __getOwnPropDesc = Object.getOwnPropertyDescriptor; + var __getOwnPropNames = Object.getOwnPropertyNames; + var __getProtoOf = Object.getPrototypeOf; + var __hasOwnProp = Object.prototype.hasOwnProperty; + var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; + var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); + }; + var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; + }; + var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod + )); + var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; + }; + + // node_modules/lotto-draw/dist/Participant.js + var require_Participant = __commonJS({ + "node_modules/lotto-draw/dist/Participant.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Participant = void 0; + var Participant = function() { + function Participant2(participant, tickets) { + if (tickets === void 0) { + tickets = 1; + } + this._participant = participant; + this._tickets = tickets; + } + Object.defineProperty(Participant2.prototype, "participant", { + get: function() { + return this._participant; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Participant2.prototype, "tickets", { + get: function() { + return this._tickets; + }, + set: function(value) { + this._tickets = value; + }, + enumerable: false, + configurable: true + }); + return Participant2; + }(); + exports.Participant = Participant; + } + }); + + // node_modules/lotto-draw/dist/Utilities.js + var require_Utilities = __commonJS({ + "node_modules/lotto-draw/dist/Utilities.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isNaturalNumber = exports.isNullOrUndefined = void 0; + function isNullOrUndefined(value) { + return value === null || value === void 0; + } + exports.isNullOrUndefined = isNullOrUndefined; + function isNaturalNumber(value) { + return typeof value === "number" && value >= 1 && Math.floor(value) === value; + } + exports.isNaturalNumber = isNaturalNumber; + } + }); + + // node_modules/lotto-draw/dist/Lotto.js + var require_Lotto = __commonJS({ + "node_modules/lotto-draw/dist/Lotto.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Lotto = void 0; + var Participant_1 = require_Participant(); + var Utilities_1 = require_Utilities(); + var Lotto2 = function() { + function Lotto3(customRandom) { + this._participants = []; + this._customRandom = customRandom; + } + Lotto3.prototype.add = function(participant, tickets) { + if (tickets === void 0) { + tickets = 1; + } + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + var existingParticipant = this._participants.find(function(part) { + return part.participant === participant; + }); + if (existingParticipant) { + existingParticipant.tickets += tickets; + } else { + this._participants.push(new Participant_1.Participant(participant, tickets)); + } + return this; + }; + Lotto3.prototype.remove = function(participant, tickets) { + var existingParticipant = this._participants.find(function(part) { + return part.participant === participant; + }); + if (!existingParticipant) { + return this; + } + if (tickets !== void 0) { + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + existingParticipant.tickets -= tickets; + if (existingParticipant.tickets < 1) { + this._participants = this._participants.filter(function(part) { + return part !== existingParticipant; + }); + } + } else { + this._participants = this._participants.filter(function(part) { + return part !== existingParticipant; + }); + } + return this; + }; + Lotto3.prototype.draw = function(options) { + if (options === void 0) { + options = {}; + } + if (this._participants.length === 0) { + return null; + } + var redrawable = (0, Utilities_1.isNullOrUndefined)(options.redrawable) ? true : options.redrawable; + var pickable = []; + this._participants.forEach(function(_a) { + var participant = _a.participant, tickets = _a.tickets; + for (var ticketCount = 0; ticketCount < tickets; ticketCount++) { + pickable.push(participant); + } + }); + var random; + if (this._customRandom) { + random = this._customRandom(); + if (typeof random !== "number" || random < 0 || random >= 1) { + throw new Error("the 'random' function provided did not return a number between 0 (inclusive) and 1"); + } + } else { + random = Math.random(); + } + var winner = pickable[Math.floor(random * pickable.length)]; + if (!redrawable) { + this.remove(winner, 1); + } + return winner; + }; + Lotto3.prototype.drawMultiple = function(tickets, options) { + if (options === void 0) { + options = {}; + } + var uniqueResults = (0, Utilities_1.isNullOrUndefined)(options.unique) ? false : options.unique; + if (tickets === 0) { + return []; + } + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + var result = []; + while (result.length < tickets && this._participants.length > 0) { + result.push(this.draw(options)); + } + if (uniqueResults) { + var unique = []; + for (var _i = 0, result_1 = result; _i < result_1.length; _i++) { + var participant = result_1[_i]; + if (unique.indexOf(participant) === -1) { + unique.push(participant); + } + } + result = unique; + } + return result; + }; + return Lotto3; + }(); + exports.Lotto = Lotto2; + } + }); + + // node_modules/lotto-draw/dist/createLotto.js + var require_createLotto = __commonJS({ + "node_modules/lotto-draw/dist/createLotto.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.createLotto = void 0; + var Lotto_1 = require_Lotto(); + function createLotto2(participantsOrOptions) { + if (!participantsOrOptions) { + return new Lotto_1.Lotto(); + } + if (Array.isArray(participantsOrOptions)) { + var participants = participantsOrOptions; + var lotto_1 = new Lotto_1.Lotto(); + participants.forEach(function(_a) { + var participant = _a[0], tokens = _a[1]; + return lotto_1.add(participant, tokens); + }); + return lotto_1; + } else { + var random = participantsOrOptions.random, participants = participantsOrOptions.participants; + var lotto_2 = new Lotto_1.Lotto(random); + if (participants) { + participants.forEach(function(_a) { + var participant = _a[0], tokens = _a[1]; + return lotto_2.add(participant, tokens); + }); + } + return lotto_2; + } + } + exports.createLotto = createLotto2; + } + }); + + // node_modules/lotto-draw/dist/index.js + var require_dist = __commonJS({ + "node_modules/lotto-draw/dist/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var createLotto_1 = require_createLotto(); + exports.default = createLotto_1.createLotto; + } + }); + + // src/index.ts + var src_exports = {}; + __export(src_exports, { + BehaviourTree: () => BehaviourTree, + State: () => State + }); + + // src/attributes/guards/GuardUnsatisifedException.ts + var GuardUnsatisifedException = class extends Error { + constructor(source) { + super("A guard path condition has failed"); + this.source = source; + } + isSourceNode = (node) => node === this.source; + }; + + // src/attributes/guards/GuardPath.ts + var GuardPath = class { + constructor(nodes) { + this.nodes = nodes; + } + evaluate = (agent) => { + for (const details of this.nodes) { + for (const guard of details.guards) { + if (!guard.isSatisfied(agent)) { + throw new GuardUnsatisifedException(details.node); + } + } + } + }; + }; + + // src/State.ts + var State = /* @__PURE__ */ ((State2) => { + State2["READY"] = "mistreevous.ready"; + State2["RUNNING"] = "mistreevous.running"; + State2["SUCCEEDED"] = "mistreevous.succeeded"; + State2["FAILED"] = "mistreevous.failed"; + return State2; + })(State || {}); + + // src/nodes/Node.ts + var Node = class { + constructor(type, attributes, args) { + this.type = type; + this.attributes = attributes; + this.args = args; + } + uid = createNodeUid(); + state = "mistreevous.ready" /* READY */; + guardPath; + getState = () => this.state; + setState = (value) => { + this.state = value; + }; + getUid = () => this.uid; + getType = () => this.type; + getAttributes = () => this.attributes; + getArguments = () => this.args; + getAttribute(type) { + return this.getAttributes().filter((decorator) => decorator.getType().toUpperCase() === type.toUpperCase())[0] || null; + } + getGuardAttributes = () => this.getAttributes().filter((decorator) => decorator.isGuard()); + setGuardPath = (value) => this.guardPath = value; + hasGuardPath = () => !!this.guardPath; + is(value) { + return this.state === value; + } + reset() { + this.setState("mistreevous.ready" /* READY */); + } + abort(agent) { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + } + update(agent, options) { + if (this.is("mistreevous.succeeded" /* SUCCEEDED */) || this.is("mistreevous.failed" /* FAILED */)) { + return; + } + try { + this.guardPath.evaluate(agent); + if (this.is("mistreevous.ready" /* READY */)) { + this.getAttribute("entry")?.callAgentFunction(agent); + } + this.getAttribute("step")?.callAgentFunction(agent); + this.onUpdate(agent, options); + if (this.is("mistreevous.succeeded" /* SUCCEEDED */) || this.is("mistreevous.failed" /* FAILED */)) { + this.getAttribute("exit")?.callAgentFunction(agent, this.is("mistreevous.succeeded" /* SUCCEEDED */), false); + } + } catch (error) { + if (error instanceof GuardUnsatisifedException && error.isSourceNode(this)) { + this.abort(agent); + this.setState("mistreevous.failed" /* FAILED */); + } else { + throw error; + } + } + } + }; + function createNodeUid() { + var S4 = function() { + return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1); + }; + return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4(); + } + + // src/nodes/leaf/Leaf.ts + var Leaf = class extends Node { + isLeafNode = () => true; + }; + + // src/Lookup.ts + var Lookup = class { + static getFunc(name) { + return this.functionTable[name]; + } + static setFunc(name, func) { + this.functionTable[name] = func; + } + static getFuncInvoker(agent, name) { + const foundOnAgent = agent[name]; + if (foundOnAgent && typeof foundOnAgent === "function") { + return (args) => foundOnAgent.apply( + agent, + args.map((arg) => arg.value) + ); + } + if (this.functionTable[name] && typeof this.functionTable[name] === "function") { + return (args) => this.functionTable[name](agent, ...args.map((arg) => arg.value)); + } + return null; + } + static getSubtree(name) { + return this.subtreeTable[name]; + } + static setSubtree(name, subtree) { + this.subtreeTable[name] = subtree; + } + static remove(name) { + delete this.functionTable[name]; + delete this.subtreeTable[name]; + } + static empty() { + this.functionTable = {}; + this.subtreeTable = {}; + } + }; + __publicField(Lookup, "functionTable", {}); + __publicField(Lookup, "subtreeTable", {}); + + // src/nodes/leaf/Action.ts + var Action = class extends Leaf { + constructor(attributes, actionName, actionArguments) { + super("action", attributes, actionArguments); + this.actionName = actionName; + this.actionArguments = actionArguments; + } + isUsingUpdatePromise = false; + updatePromiseStateResult = null; + onUpdate(agent, options) { + if (this.isUsingUpdatePromise) { + if (this.updatePromiseStateResult) { + this.setState(this.updatePromiseStateResult); + } + return; + } + const actionFuncInvoker = Lookup.getFuncInvoker(agent, this.actionName); + if (actionFuncInvoker === null) { + throw new Error( + `cannot update action node as the action '${this.actionName}' function is not defined on the agent and has not been registered` + ); + } + const updateResult = actionFuncInvoker(this.actionArguments); + if (updateResult instanceof Promise) { + updateResult.then( + (result) => { + if (!this.isUsingUpdatePromise) { + return; + } + if (result !== "mistreevous.succeeded" /* SUCCEEDED */ && result !== "mistreevous.failed" /* FAILED */) { + throw new Error( + "action node promise resolved with an invalid value, expected a State.SUCCEEDED or State.FAILED value to be returned" + ); + } + this.updatePromiseStateResult = result; + }, + (reason) => { + if (!this.isUsingUpdatePromise) { + return; + } + throw new Error(reason); + } + ); + this.setState("mistreevous.running" /* RUNNING */); + this.isUsingUpdatePromise = true; + } else { + this.validateUpdateResult(updateResult); + this.setState(updateResult || "mistreevous.running" /* RUNNING */); + } + } + getName = () => this.actionName; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.isUsingUpdatePromise = false; + this.updatePromiseStateResult = null; + }; + validateUpdateResult = (result) => { + switch (result) { + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + case void 0: + return; + default: + throw new Error( + `action '${this.actionName}' 'onUpdate' returned an invalid response, expected an optional State.SUCCEEDED or State.FAILED value to be returned` + ); + } + }; + }; + + // src/nodes/leaf/Condition.ts + var Condition = class extends Leaf { + constructor(attributes, conditionName, conditionArguments) { + super("condition", attributes, conditionArguments); + this.conditionName = conditionName; + this.conditionArguments = conditionArguments; + } + onUpdate(agent, options) { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.conditionName); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot update condition node as the condition '${this.conditionName}' function is not defined on the agent and has not been registered` + ); + } + this.setState(!!conditionFuncInvoker(this.conditionArguments) ? "mistreevous.succeeded" /* SUCCEEDED */ : "mistreevous.failed" /* FAILED */); + } + getName = () => this.conditionName; + }; + + // src/nodes/leaf/Wait.ts + var Wait = class extends Leaf { + constructor(attributes, duration, durationMin, durationMax) { + super("wait", attributes, []); + this.duration = duration; + this.durationMin = durationMin; + this.durationMax = durationMax; + } + initialUpdateTime = 0; + totalDuration = null; + waitedDuration = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.initialUpdateTime = new Date().getTime(); + this.waitedDuration = 0; + if (this.duration !== null) { + this.totalDuration = this.duration; + } else if (this.durationMin !== null && this.durationMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.totalDuration = Math.floor( + random() * (this.durationMax - this.durationMin + 1) + this.durationMin + ); + } else { + this.totalDuration = null; + } + this.setState("mistreevous.running" /* RUNNING */); + } + if (this.totalDuration === null) { + return; + } + if (typeof options.getDeltaTime === "function") { + const deltaTime = options.getDeltaTime(); + if (typeof deltaTime !== "number" || isNaN(deltaTime)) { + throw new Error("The delta time must be a valid number and not NaN."); + } + this.waitedDuration += deltaTime * 1e3; + } else { + this.waitedDuration = new Date().getTime() - this.initialUpdateTime; + } + if (this.waitedDuration >= this.totalDuration) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + } + } + getName = () => { + if (this.duration !== null) { + return `WAIT ${this.duration}ms`; + } else if (this.durationMin !== null && this.durationMax !== null) { + return `WAIT ${this.durationMin}ms-${this.durationMax}ms`; + } else { + return "WAIT"; + } + }; + }; + + // src/nodes/decorator/Decorator.ts + var Decorator = class extends Node { + constructor(type, attributes, child) { + super(type, attributes, []); + this.child = child; + } + isLeafNode = () => false; + getChildren = () => [this.child]; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.child.reset(); + }; + abort = (agent) => { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.child.abort(agent); + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + }; + }; + + // src/nodes/decorator/Root.ts + var Root = class extends Decorator { + constructor(attributes, child) { + super("root", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + this.setState(this.child.getState()); + } + getName = () => "ROOT"; + }; + + // src/nodes/decorator/Repeat.ts + var Repeat = class extends Decorator { + constructor(attributes, iterations, iterationsMin, iterationsMax, child) { + super("repeat", attributes, child); + this.iterations = iterations; + this.iterationsMin = iterationsMin; + this.iterationsMax = iterationsMax; + } + targetIterationCount = null; + currentIterationCount = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.child.reset(); + this.currentIterationCount = 0; + this.setTargetIterationCount(options); + } + if (this.canIterate()) { + this.setState("mistreevous.running" /* RUNNING */); + if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.child.reset(); + } + this.child.update(agent, options); + if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } else if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.currentIterationCount += 1; + } + } else { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + } + } + getName = () => { + if (this.iterations !== null) { + return `REPEAT ${this.iterations}x`; + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + return `REPEAT ${this.iterationsMin}x-${this.iterationsMax}x`; + } else { + return "REPEAT"; + } + }; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.currentIterationCount = 0; + this.child.reset(); + }; + canIterate = () => { + if (this.targetIterationCount !== null) { + return this.currentIterationCount < this.targetIterationCount; + } + return true; + }; + setTargetIterationCount = (options) => { + if (this.iterations !== null) { + this.targetIterationCount = this.iterations; + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.targetIterationCount = Math.floor( + random() * (this.iterationsMax - this.iterationsMin + 1) + this.iterationsMin + ); + } else { + this.targetIterationCount = null; + } + }; + }; + + // src/nodes/decorator/Retry.ts + var Retry = class extends Decorator { + constructor(attributes, attempts, attemptsMin, attemptsMax, child) { + super("retry", attributes, child); + this.attempts = attempts; + this.attemptsMin = attemptsMin; + this.attemptsMax = attemptsMax; + } + targetAttemptCount = null; + currentAttemptCount = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.child.reset(); + this.currentAttemptCount = 0; + this.setTargetAttemptCount(options); + } + if (this.canAttempt()) { + this.setState("mistreevous.running" /* RUNNING */); + if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.child.reset(); + } + this.child.update(agent, options); + if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } else if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.currentAttemptCount += 1; + } + } else { + this.setState("mistreevous.failed" /* FAILED */); + } + } + getName = () => { + if (this.attempts !== null) { + return `RETRY ${this.attempts}x`; + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + return `RETRY ${this.attemptsMin}x-${this.attemptsMax}x`; + } else { + return "RETRY"; + } + }; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.currentAttemptCount = 0; + this.child.reset(); + }; + canAttempt = () => { + if (this.targetAttemptCount !== null) { + return this.currentAttemptCount < this.targetAttemptCount; + } + return true; + }; + setTargetAttemptCount = (options) => { + if (this.attempts !== null) { + this.targetAttemptCount = this.attempts; + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.targetAttemptCount = Math.floor( + random() * (this.attemptsMax - this.attemptsMin + 1) + this.attemptsMin + ); + } else { + this.targetAttemptCount = null; + } + }; + }; + + // src/nodes/decorator/Flip.ts + var Flip = class extends Decorator { + constructor(attributes, child) { + super("flip", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + this.setState("mistreevous.failed" /* FAILED */); + break; + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "FLIP"; + }; + + // src/nodes/decorator/Succeed.ts + var Succeed = class extends Decorator { + constructor(attributes, child) { + super("succeed", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "SUCCEED"; + }; + + // src/nodes/decorator/Fail.ts + var Fail = class extends Decorator { + constructor(attributes, child) { + super("fail", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.failed" /* FAILED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "FAIL"; + }; + + // src/nodes/composite/Lotto.ts + var import_lotto_draw = __toESM(require_dist()); + + // src/nodes/composite/Composite.ts + var Composite = class extends Node { + constructor(type, attributes, children) { + super(type, attributes, []); + this.children = children; + } + isLeafNode = () => false; + getChildren = () => this.children; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.getChildren().forEach((child) => child.reset()); + }; + abort = (agent) => { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.getChildren().forEach((child) => child.abort(agent)); + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + }; + }; + + // src/nodes/composite/Lotto.ts + var Lotto = class extends Composite { + constructor(attributes, tickets, children) { + super("lotto", attributes, children); + this.tickets = tickets; + } + selectedChild; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + const lottoDraw = (0, import_lotto_draw.default)({ + random: options.random, + participants: this.children.map((child, index) => [child, this.tickets[index] || 1]) + }); + this.selectedChild = lottoDraw.draw() || void 0; + } + if (!this.selectedChild) { + throw new Error("failed to update lotto node as it has no active child"); + } + if (this.selectedChild.getState() === "mistreevous.ready" /* READY */ || this.selectedChild.getState() === "mistreevous.running" /* RUNNING */) { + this.selectedChild.update(agent, options); + } + this.setState(this.selectedChild.getState()); + } + getName = () => this.tickets.length ? `LOTTO [${this.tickets.join(",")}]` : "LOTTO"; + }; + + // src/nodes/composite/Selector.ts + var Selector = class extends Composite { + constructor(attributes, children) { + super("selector", attributes, children); + this.children = children; + } + onUpdate(agent, options) { + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + if (this.children.indexOf(child) === this.children.length - 1) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } else { + continue; + } + } + if (child.getState() === "mistreevous.running" /* RUNNING */) { + this.setState("mistreevous.running" /* RUNNING */); + return; + } + throw new Error("child node was not in an expected state."); + } + } + getName = () => "SELECTOR"; + }; + + // src/nodes/composite/Sequence.ts + var Sequence = class extends Composite { + constructor(attributes, children) { + super("sequence", attributes, children); + this.children = children; + } + onUpdate(agent, options) { + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + if (this.children.indexOf(child) === this.children.length - 1) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } else { + continue; + } + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } + if (child.getState() === "mistreevous.running" /* RUNNING */) { + this.setState("mistreevous.running" /* RUNNING */); + return; + } + throw new Error("child node was not in an expected state."); + } + } + getName = () => "SEQUENCE"; + }; + + // src/nodes/composite/Parallel.ts + var Parallel = class extends Composite { + constructor(attributes, children) { + super("parallel", attributes, children); + } + onUpdate(agent, options) { + let succeededCount = 0; + let hasChildFailed = false; + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + succeededCount++; + continue; + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + hasChildFailed = true; + break; + } + if (child.getState() !== "mistreevous.running" /* RUNNING */) { + throw new Error("child node was not in an expected state."); + } + } + if (hasChildFailed) { + this.setState("mistreevous.failed" /* FAILED */); + for (const child of this.children) { + if (child.getState() === "mistreevous.running" /* RUNNING */) { + child.abort(agent); + } + } + } else { + this.setState(succeededCount === this.children.length ? "mistreevous.succeeded" /* SUCCEEDED */ : "mistreevous.running" /* RUNNING */); + } + } + getName = () => "PARALLEL"; + }; + + // src/attributes/Attribute.ts + var Attribute = class { + constructor(type, args) { + this.type = type; + this.args = args; + } + getType = () => this.type; + getArguments = () => this.args; + }; + + // src/attributes/guards/Guard.ts + var Guard = class extends Attribute { + constructor(type, args, condition) { + super(type, args); + this.condition = condition; + } + getCondition = () => this.condition; + isGuard = () => true; + getDetails() { + return { + type: this.getType(), + args: this.getArguments(), + condition: this.getCondition() + }; + } + }; + + // src/attributes/guards/While.ts + var While = class extends Guard { + constructor(condition, args) { + super("while", args, condition); + } + isSatisfied = (agent) => { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition()); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered` + ); + } + return !!conditionFuncInvoker(this.args); + }; + }; + + // src/attributes/guards/Until.ts + var Until = class extends Guard { + constructor(condition, args) { + super("until", args, condition); + } + isSatisfied = (agent) => { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition()); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered` + ); + } + return !!!conditionFuncInvoker(this.args); + }; + }; + + // src/attributes/callbacks/Callback.ts + var Callback = class extends Attribute { + constructor(type, args, functionName) { + super(type, args); + this.functionName = functionName; + } + getFunctionName = () => this.functionName; + isGuard = () => false; + getDetails() { + return { + type: this.getType(), + args: this.getArguments(), + functionName: this.getFunctionName() + }; + } + }; + + // src/attributes/callbacks/Entry.ts + var Entry = class extends Callback { + constructor(functionName, args) { + super("entry", args, functionName); + } + callAgentFunction = (agent) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call entry function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker(this.args); + }; + }; + + // src/attributes/callbacks/Exit.ts + var Exit = class extends Callback { + constructor(functionName, args) { + super("exit", args, functionName); + } + callAgentFunction = (agent, isSuccess, isAborted) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call exit function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker([{ value: { succeeded: isSuccess, aborted: isAborted } }, ...this.args]); + }; + }; + + // src/attributes/callbacks/Step.ts + var Step = class extends Callback { + constructor(functionName, args) { + super("step", args, functionName); + } + callAgentFunction = (agent) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call step function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker(this.args); + }; + }; + + // src/RootAstNodesBuilder.ts + var AttributeFactories = { + WHILE: (condition, attributeArguments) => new While(condition, attributeArguments), + UNTIL: (condition, attributeArguments) => new Until(condition, attributeArguments), + ENTRY: (functionName, attributeArguments) => new Entry(functionName, attributeArguments), + EXIT: (functionName, attributeArguments) => new Exit(functionName, attributeArguments), + STEP: (functionName, attributeArguments) => new Step(functionName, attributeArguments) + }; + var ASTNodeFactories = { + ROOT: () => ({ + type: "root", + attributes: [], + name: null, + children: [], + validate(depth) { + if (depth > 1) { + throw new Error("a root node cannot be the child of another node"); + } + if (this.children.length !== 1) { + throw new Error("a root node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Root( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + BRANCH: () => ({ + type: "branch", + branchName: "", + validate() { + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + const targetRootNode = namedRootNodeProvider(this.branchName); + if (visitedBranches.indexOf(this.branchName) !== -1) { + throw new Error(`circular dependency found in branch node references for branch '${this.branchName}'`); + } + if (targetRootNode) { + return targetRootNode.createNodeInstance(namedRootNodeProvider, visitedBranches.concat(this.branchName)).getChildren()[0]; + } else { + throw new Error(`branch references root node '${this.branchName}' which has not been defined`); + } + } + }), + SELECTOR: () => ({ + type: "selector", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a selector node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Selector( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + SEQUENCE: () => ({ + type: "sequence", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a sequence node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Sequence( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + PARALLEL: () => ({ + type: "parallel", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a parallel node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Parallel( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + LOTTO: () => ({ + type: "lotto", + attributes: [], + children: [], + tickets: [], + validate() { + if (this.children.length < 1) { + throw new Error("a lotto node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Lotto( + this.attributes, + this.tickets, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + REPEAT: () => ({ + type: "repeat", + attributes: [], + iterations: null, + iterationsMin: null, + iterationsMax: null, + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a repeat node must have a single child"); + } + if (this.iterations !== null) { + if (this.iterations < 0) { + throw new Error("a repeat node must have a positive number of iterations if defined"); + } + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + if (this.iterationsMin < 0 || this.iterationsMax < 0) { + throw new Error( + "a repeat node must have a positive minimum and maximum iteration count if defined" + ); + } + if (this.iterationsMin > this.iterationsMax) { + throw new Error( + "a repeat node must not have a minimum iteration count that exceeds the maximum iteration count" + ); + } + } else { + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Repeat( + this.attributes, + this.iterations, + this.iterationsMin, + this.iterationsMax, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + RETRY: () => ({ + type: "retry", + attributes: [], + attempts: null, + attemptsMin: null, + attemptsMax: null, + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a retry node must have a single child"); + } + if (this.attempts !== null) { + if (this.attempts < 0) { + throw new Error("a retry node must have a positive number of attempts if defined"); + } + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + if (this.attemptsMin < 0 || this.attemptsMax < 0) { + throw new Error("a retry node must have a positive minimum and maximum attempt count if defined"); + } + if (this.attemptsMin > this.attemptsMax) { + throw new Error( + "a retry node must not have a minimum attempt count that exceeds the maximum attempt count" + ); + } + } else { + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Retry( + this.attributes, + this.attempts, + this.attemptsMin, + this.attemptsMax, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + FLIP: () => ({ + type: "flip", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a flip node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Flip( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + SUCCEED: () => ({ + type: "succeed", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a succeed node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Succeed( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + FAIL: () => ({ + type: "fail", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a fail node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Fail( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + WAIT: () => ({ + type: "wait", + attributes: [], + duration: null, + durationMin: null, + durationMax: null, + validate() { + if (this.duration !== null) { + if (this.duration < 0) { + throw new Error("a wait node must have a positive duration"); + } + } else if (this.durationMin !== null && this.durationMax !== null) { + if (this.durationMin < 0 || this.durationMax < 0) { + throw new Error("a wait node must have a positive minimum and maximum duration"); + } + if (this.durationMin > this.durationMax) { + throw new Error("a wait node must not have a minimum duration that exceeds the maximum duration"); + } + } else { + } + }, + createNodeInstance() { + return new Wait(this.attributes, this.duration, this.durationMin, this.durationMax); + } + }), + ACTION: () => ({ + type: "action", + attributes: [], + actionName: "", + actionArguments: [], + validate() { + }, + createNodeInstance() { + return new Action(this.attributes, this.actionName, this.actionArguments); + } + }), + CONDITION: () => ({ + type: "condition", + attributes: [], + conditionName: "", + conditionArguments: [], + validate() { + }, + createNodeInstance() { + return new Condition(this.attributes, this.conditionName, this.conditionArguments); + } + }) + }; + function buildRootASTNodes(definition) { + const { placeholders, processedDefinition } = substituteStringLiterals(definition); + const tokens = parseTokensFromDefinition(processedDefinition); + if (tokens.length < 3) { + throw new Error("invalid token count"); + } + if (tokens.filter((token) => token === "{").length !== tokens.filter((token) => token === "}").length) { + throw new Error("scope character mismatch"); + } + const stack = [[]]; + const rootScope = stack[0]; + while (tokens.length) { + const token = tokens.shift(); + const currentScope = stack[stack.length - 1]; + switch (token.toUpperCase()) { + case "ROOT": { + const node = ASTNodeFactories.ROOT(); + rootScope.push(node); + if (tokens[0] === "[") { + const rootArguments = getArguments(tokens, placeholders); + if (rootArguments.length === 1 && rootArguments[0].type === "identifier") { + node.name = rootArguments[0].value; + } else { + throw new Error("expected single root name argument"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "BRANCH": { + const node = ASTNodeFactories.BRANCH(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected single branch name argument"); + } + const branchArguments = getArguments(tokens, placeholders); + if (branchArguments.length === 1 && branchArguments[0].type === "identifier") { + node.branchName = branchArguments[0].value; + } else { + throw new Error("expected single branch name argument"); + } + break; + } + case "SELECTOR": { + const node = ASTNodeFactories.SELECTOR(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "SEQUENCE": { + const node = ASTNodeFactories.SEQUENCE(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "PARALLEL": { + const node = ASTNodeFactories.PARALLEL(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "LOTTO": { + const node = ASTNodeFactories.LOTTO(); + currentScope.push(node); + if (tokens[0] === "[") { + node.tickets = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "lotto node ticket counts must be integer values" + ).map((argument) => argument.value); + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "CONDITION": { + const node = ASTNodeFactories.CONDITION(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected condition name identifier argument"); + } + const conditionArguments = getArguments(tokens, placeholders); + if (conditionArguments.length && conditionArguments[0].type === "identifier") { + node.conditionName = conditionArguments.shift().value; + } else { + throw new Error("expected condition name identifier argument"); + } + conditionArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid condition node argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + node.conditionArguments = conditionArguments; + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "FLIP": { + const node = ASTNodeFactories.FLIP(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "SUCCEED": { + const node = ASTNodeFactories.SUCCEED(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "FAIL": { + const node = ASTNodeFactories.FAIL(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "WAIT": { + const node = ASTNodeFactories.WAIT(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "wait node durations must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.duration = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.durationMin = nodeArguments[0]; + node.durationMax = nodeArguments[1]; + } else if (nodeArguments.length > 2) { + throw new Error("invalid number of wait node duration arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "REPEAT": { + const node = ASTNodeFactories.REPEAT(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "repeat node iteration counts must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.iterations = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.iterationsMin = nodeArguments[0]; + node.iterationsMax = nodeArguments[1]; + } else { + throw new Error("invalid number of repeat node iteration count arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "RETRY": { + const node = ASTNodeFactories.RETRY(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "retry node attempt counts must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.attempts = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.attemptsMin = nodeArguments[0]; + node.attemptsMax = nodeArguments[1]; + } else { + throw new Error("invalid number of retry node attempt count arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "ACTION": { + const node = ASTNodeFactories.ACTION(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected action name identifier argument"); + } + const actionArguments = getArguments(tokens, placeholders); + if (actionArguments.length && actionArguments[0].type === "identifier") { + node.actionName = actionArguments.shift().value; + } else { + throw new Error("expected action name identifier argument"); + } + actionArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid action node argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + node.actionArguments = actionArguments; + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "}": { + stack.pop(); + break; + } + default: { + throw new Error(`unexpected token '${token}'`); + } + } + } + const validateASTNode = (node, depth) => { + node.validate(depth); + (node.children || []).forEach((child) => validateASTNode(child, depth + 1)); + }; + validateASTNode( + { + children: stack[0], + validate() { + if (this.children.length === 0) { + throw new Error("expected root node to have been defined"); + } + for (const definitionLevelNode of this.children) { + if (definitionLevelNode.type !== "root") { + throw new Error("expected root node at base of definition"); + } + } + if (this.children.filter((definitionLevelNode) => definitionLevelNode.name === null).length !== 1) { + throw new Error("expected single unnamed root node at base of definition to act as main root"); + } + const rootNodeNames = []; + for (const definitionLevelNode of this.children) { + if (rootNodeNames.indexOf(definitionLevelNode.name) !== -1) { + throw new Error(`multiple root nodes found with duplicate name '${definitionLevelNode.name}'`); + } else { + rootNodeNames.push(definitionLevelNode.name); + } + } + } + }, + 0 + ); + return stack[0]; + } + function popAndCheck(tokens, expected) { + const popped = tokens.shift(); + if (popped === void 0) { + throw new Error("unexpected end of definition"); + } + if (expected !== void 0) { + var tokenMatchesExpectation = [].concat(expected).some((item) => popped.toUpperCase() === item.toUpperCase()); + if (!tokenMatchesExpectation) { + const expectationString = [].concat(expected).map((item) => "'" + item + "'").join(" or "); + throw new Error(`unexpected token found. Expected '${expectationString}' but got '${popped}'`); + } + } + return popped; + } + function getArguments(tokens, stringArgumentPlaceholders, argumentValidator, validationFailedMessage) { + const closer = popAndCheck(tokens, ["[", "("]) === "[" ? "]" : ")"; + const argumentListTokens = []; + const argumentList = []; + while (tokens.length && tokens[0] !== closer) { + argumentListTokens.push(tokens.shift()); + } + argumentListTokens.forEach((token, index) => { + const shouldBeArgumentToken = !(index & 1); + if (shouldBeArgumentToken) { + const argumentDefinition = getArgumentDefinition(token, stringArgumentPlaceholders); + if (argumentValidator && !argumentValidator(argumentDefinition)) { + throw new Error(validationFailedMessage); + } + argumentList.push(argumentDefinition); + } else { + if (token !== ",") { + throw new Error(`invalid argument list, expected ',' or ']' but got '${token}'`); + } + } + }); + popAndCheck(tokens, closer); + return argumentList; + } + function getArgumentDefinition(token, stringArgumentPlaceholders) { + if (token === "null") { + return { + value: null, + type: "null" + }; + } + if (token === "true" || token === "false") { + return { + value: token === "true", + type: "boolean" + }; + } + if (!isNaN(token)) { + return { + value: parseFloat(token), + isInteger: parseFloat(token) === parseInt(token, 10), + type: "number" + }; + } + if (token.match(/^@@\d+@@$/g)) { + return { + value: stringArgumentPlaceholders[token].replace('\\"', '"'), + type: "string" + }; + } + return { + value: token, + type: "identifier" + }; + } + function getAttributes(tokens, stringArgumentPlaceholders) { + const attributes = []; + const attributesFound = []; + let attributeFactory = AttributeFactories[(tokens[0] || "").toUpperCase()]; + while (attributeFactory) { + if (attributesFound.indexOf(tokens[0].toUpperCase()) !== -1) { + throw new Error(`duplicate attribute '${tokens[0].toUpperCase()}' found for node`); + } + attributesFound.push(tokens.shift().toUpperCase()); + const attributeArguments = getArguments(tokens, stringArgumentPlaceholders); + if (attributeArguments.length === 0 || attributeArguments[0].type !== "identifier") { + throw new Error("expected agent function name identifier argument for attribute"); + } + const attributeFunctionName = attributeArguments.shift(); + attributeArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid attribute argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + attributes.push(attributeFactory(attributeFunctionName.value, attributeArguments)); + attributeFactory = AttributeFactories[(tokens[0] || "").toUpperCase()]; + } + return attributes; + } + function substituteStringLiterals(definition) { + const placeholders = {}; + const processedDefinition = definition.replace(/\"(\\.|[^"\\])*\"/g, (match) => { + var strippedMatch = match.substring(1, match.length - 1); + var placeholder = Object.keys(placeholders).find((key) => placeholders[key] === strippedMatch); + if (!placeholder) { + placeholder = `@@${Object.keys(placeholders).length}@@`; + placeholders[placeholder] = strippedMatch; + } + return placeholder; + }); + return { placeholders, processedDefinition }; + } + function parseTokensFromDefinition(definition) { + definition = definition.replace(/\(/g, " ( "); + definition = definition.replace(/\)/g, " ) "); + definition = definition.replace(/\{/g, " { "); + definition = definition.replace(/\}/g, " } "); + definition = definition.replace(/\]/g, " ] "); + definition = definition.replace(/\[/g, " [ "); + definition = definition.replace(/\,/g, " , "); + return definition.replace(/\s+/g, " ").trim().split(" "); + } + + // src/BehaviourTree.ts + var BehaviourTree = class { + constructor(definition, agent, options = {}) { + this.agent = agent; + this.options = options; + if (typeof definition !== "string") { + throw new Error("the tree definition must be a string"); + } + if (typeof agent !== "object" || agent === null) { + throw new Error("the agent must be defined and not null"); + } + this.rootNode = BehaviourTree._createRootNode(definition); + } + rootNode; + isRunning() { + return this.rootNode.getState() === "mistreevous.running" /* RUNNING */; + } + getState() { + return this.rootNode.getState(); + } + step() { + if (this.rootNode.getState() === "mistreevous.succeeded" /* SUCCEEDED */ || this.rootNode.getState() === "mistreevous.failed" /* FAILED */) { + this.rootNode.reset(); + } + try { + this.rootNode.update(this.agent, this.options); + } catch (exception) { + throw new Error(`error stepping tree: ${exception.message}`); + } + } + reset() { + this.rootNode.reset(); + } + getFlattenedNodeDetails() { + const flattenedTreeNodes = []; + const processNode = (node, parentUid) => { + const guards = node.getAttributes().filter((attribute) => attribute.isGuard()).map((attribute) => attribute.getDetails()); + const callbacks = node.getAttributes().filter((attribute) => !attribute.isGuard()).map((attribute) => attribute.getDetails()); + flattenedTreeNodes.push({ + id: node.getUid(), + type: node.getType(), + caption: node.getName(), + state: node.getState(), + guards, + callbacks, + args: node.getArguments(), + parentId: parentUid + }); + if (!node.isLeafNode()) { + node.getChildren().forEach((child) => processNode(child, node.getUid())); + } + }; + processNode(this.rootNode, null); + return flattenedTreeNodes; + } + static register(name, value) { + if (typeof value === "function") { + Lookup.setFunc(name, value); + } else if (typeof value === "string") { + let rootASTNodes; + try { + rootASTNodes = buildRootASTNodes(value); + } catch (exception) { + throw new Error(`error registering definition: ${exception.message}`); + } + if (rootASTNodes.length != 1 || rootASTNodes[0].name !== null) { + throw new Error("error registering definition: expected a single unnamed root node"); + } + Lookup.setSubtree(name, rootASTNodes[0]); + } else { + throw new Error("unexpected value, expected string definition or function"); + } + } + static unregister(name) { + Lookup.remove(name); + } + static unregisterAll() { + Lookup.empty(); + } + static _createRootNode(definition) { + try { + } catch (exception) { + console.log(exception); + } + try { + const rootASTNodes = buildRootASTNodes(definition); + const mainRootNodeKey = Symbol("__root__"); + const rootNodeMap = {}; + for (const rootASTNode of rootASTNodes) { + rootNodeMap[rootASTNode.name === null ? mainRootNodeKey : rootASTNode.name] = rootASTNode; + } + const rootNode = rootNodeMap[mainRootNodeKey].createNodeInstance( + (name) => rootNodeMap[name] ? rootNodeMap[name] : Lookup.getSubtree(name), + [] + ); + BehaviourTree._applyLeafNodeGuardPaths(rootNode); + return rootNode; + } catch (exception) { + throw new Error(`error parsing tree: ${exception.message}`); + } + } + static _applyLeafNodeGuardPaths(rootNode) { + const nodePaths = []; + const findLeafNodes = (path, node) => { + path = path.concat(node); + if (node.isLeafNode()) { + nodePaths.push(path); + } else { + node.getChildren().forEach((child) => findLeafNodes(path, child)); + } + }; + findLeafNodes([], rootNode); + nodePaths.forEach((path) => { + for (let depth = 0; depth < path.length; depth++) { + const currentNode = path[depth]; + if (currentNode.hasGuardPath()) { + continue; + } + const guardPath = new GuardPath( + path.slice(0, depth + 1).map((node) => ({ node, guards: node.getGuardAttributes() })).filter((details) => details.guards.length > 0) + ); + currentNode.setGuardPath(guardPath); + } + }); + } + }; + return __toCommonJS(src_exports); +})(); +//# sourceMappingURL=bundle.js.map diff --git a/dist/bundle.js.map b/dist/bundle.js.map new file mode 100644 index 0000000..b76ce81 --- /dev/null +++ b/dist/bundle.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../node_modules/lotto-draw/dist/Participant.js", "../node_modules/lotto-draw/dist/Utilities.js", "../node_modules/lotto-draw/dist/Lotto.js", "../node_modules/lotto-draw/dist/createLotto.js", "../node_modules/lotto-draw/dist/index.js", "../src/index.ts", "../src/attributes/guards/GuardUnsatisifedException.ts", "../src/attributes/guards/GuardPath.ts", "../src/State.ts", "../src/nodes/Node.ts", "../src/nodes/leaf/Leaf.ts", "../src/Lookup.ts", "../src/nodes/leaf/Action.ts", "../src/nodes/leaf/Condition.ts", "../src/nodes/leaf/Wait.ts", "../src/nodes/decorator/Decorator.ts", "../src/nodes/decorator/Root.ts", "../src/nodes/decorator/Repeat.ts", "../src/nodes/decorator/Retry.ts", "../src/nodes/decorator/Flip.ts", "../src/nodes/decorator/Succeed.ts", "../src/nodes/decorator/Fail.ts", "../src/nodes/composite/Lotto.ts", "../src/nodes/composite/Composite.ts", "../src/nodes/composite/Selector.ts", "../src/nodes/composite/Sequence.ts", "../src/nodes/composite/Parallel.ts", "../src/attributes/Attribute.ts", "../src/attributes/guards/Guard.ts", "../src/attributes/guards/While.ts", "../src/attributes/guards/Until.ts", "../src/attributes/callbacks/Callback.ts", "../src/attributes/callbacks/Entry.ts", "../src/attributes/callbacks/Exit.ts", "../src/attributes/callbacks/Step.ts", "../src/RootAstNodesBuilder.ts", "../src/BehaviourTree.ts"], + "sourcesContent": ["\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.Participant = void 0;\r\n/**\r\n * A participant that holds a number of tickets.\r\n */\r\nvar Participant = /** @class */ (function () {\r\n /**\r\n * Creates an instance of the Participant class.\r\n * @param participant The actual participant.\r\n * @param tickets The number of tickets held by the participant.\r\n */\r\n function Participant(participant, tickets) {\r\n if (tickets === void 0) { tickets = 1; }\r\n this._participant = participant;\r\n this._tickets = tickets;\r\n }\r\n Object.defineProperty(Participant.prototype, \"participant\", {\r\n /** Gets the actual participant. */\r\n get: function () {\r\n return this._participant;\r\n },\r\n enumerable: false,\r\n configurable: true\r\n });\r\n Object.defineProperty(Participant.prototype, \"tickets\", {\r\n /** Gets or sets the number of tickets held by the participant. */\r\n get: function () {\r\n return this._tickets;\r\n },\r\n set: function (value) {\r\n this._tickets = value;\r\n },\r\n enumerable: false,\r\n configurable: true\r\n });\r\n return Participant;\r\n}());\r\nexports.Participant = Participant;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.isNaturalNumber = exports.isNullOrUndefined = void 0;\r\n/**\r\n * Gets whether the value provided is null or undefined.\r\n * @param value The value to check.\r\n * @returns Whether the value provided is null or undefined.\r\n */\r\nfunction isNullOrUndefined(value) {\r\n return value === null || value === undefined;\r\n}\r\nexports.isNullOrUndefined = isNullOrUndefined;\r\n/**\r\n * Gets whether the value provided is a natural number.\r\n * @param value The value to check.\r\n * @returns Whether the value provided is a natural number.\r\n */\r\nfunction isNaturalNumber(value) {\r\n return typeof value === \"number\" && value >= 1 && Math.floor(value) === value;\r\n}\r\nexports.isNaturalNumber = isNaturalNumber;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.Lotto = void 0;\r\nvar Participant_1 = require(\"./Participant\");\r\nvar Utilities_1 = require(\"./Utilities\");\r\n/**\r\n * Represents a lotto consisting of a number of pickable ticket-holding participants.\r\n */\r\nvar Lotto = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of Lotto.\r\n * @param customRandom The custom RNG to use in place of Math.random().\r\n */\r\n function Lotto(customRandom) {\r\n /** The array of participants that are holding tickets in the lotto. */\r\n this._participants = [];\r\n this._customRandom = customRandom;\r\n }\r\n /**\r\n * Adds a participant with the specified number of tickets, or adds to the participant ticket count if the participant already holds tickets.\r\n * @param participant The participant to add or to increase the ticket count for if they already hold tickets.\r\n * @param tickets The number of tickets, defaults to 1.\r\n * @returns The Lotto instance.\r\n */\r\n Lotto.prototype.add = function (participant, tickets) {\r\n if (tickets === void 0) { tickets = 1; }\r\n // Check that we have a valid ticket count.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n // Check whether this participant has already been added.\r\n var existingParticipant = this._participants.find(function (part) { return part.participant === participant; });\r\n if (existingParticipant) {\r\n // The participant has already been added to the lotto so just add to their ticket count.\r\n existingParticipant.tickets += tickets;\r\n }\r\n else {\r\n // The participant is not part of the lotto so we should add them.\r\n this._participants.push(new Participant_1.Participant(participant, tickets));\r\n }\r\n return this;\r\n };\r\n /**\r\n * Removes the specified number of tickets for the given participant from the draw, or all tickets if a ticket number is not defined.\r\n * @param participant The participant to remove tickets for.\r\n * @param tickets The number of tickets to remove, or undefined if all tickets are to be removed.\r\n * @returns The Lotto instance.\r\n */\r\n Lotto.prototype.remove = function (participant, tickets) {\r\n // Attempt to get the existing participant.\r\n var existingParticipant = this._participants.find(function (part) { return part.participant === participant; });\r\n // There is nothing to do if the specified participant isn't even part of the lotto.\r\n if (!existingParticipant) {\r\n return this;\r\n }\r\n // Check whether a tickets value was given.\r\n if (tickets !== undefined) {\r\n // Check that we have a valid ticket count.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n existingParticipant.tickets -= tickets;\r\n // If the participant no longer holds any tickets then they should be removed.\r\n if (existingParticipant.tickets < 1) {\r\n this._participants = this._participants.filter(function (part) { return part !== existingParticipant; });\r\n }\r\n }\r\n else {\r\n // We are removing all tickets for the participant so just remove them from the lotto.\r\n this._participants = this._participants.filter(function (part) { return part !== existingParticipant; });\r\n }\r\n return this;\r\n };\r\n /**\r\n * Draw a winning ticket and return the participant that holds the ticket.\r\n * @param options The draw options.\r\n * @returns The participant that holds the winning ticket.\r\n */\r\n Lotto.prototype.draw = function (options) {\r\n if (options === void 0) { options = {}; }\r\n // If we have no participants then just return null.\r\n if (this._participants.length === 0) {\r\n return null;\r\n }\r\n var redrawable = (0, Utilities_1.isNullOrUndefined)(options.redrawable) ? true : options.redrawable;\r\n var pickable = [];\r\n this._participants.forEach(function (_a) {\r\n var participant = _a.participant, tickets = _a.tickets;\r\n for (var ticketCount = 0; ticketCount < tickets; ticketCount++) {\r\n pickable.push(participant);\r\n }\r\n });\r\n var random;\r\n // We need a random floating-point number between 0 (inclusive) and 1 to scale up to pick our winner.\r\n // If a custom random function exists then we should use that or fall back to Math.random().\r\n if (this._customRandom) {\r\n // Call our custom random function to get a random floating-point number.\r\n random = this._customRandom();\r\n // Verify that the result of calling our custom random function is a number between 0 (inclusive) and 1.\r\n if (typeof random !== \"number\" || random < 0 || random >= 1) {\r\n throw new Error(\"the 'random' function provided did not return a number between 0 (inclusive) and 1\");\r\n }\r\n }\r\n else {\r\n // No custom random function was defined so just use good ol' Math.random().\r\n random = Math.random();\r\n }\r\n // Pick a winning participant.\r\n var winner = pickable[Math.floor(random * pickable.length)];\r\n // If the ticket isn't redrawable then we should remove a ticket from the winning participants ticket count.\r\n if (!redrawable) {\r\n this.remove(winner, 1);\r\n }\r\n // Return the winning participant.\r\n return winner;\r\n };\r\n /**\r\n * Draws multiple winning tickets and return an array of the participants that hold the winning tickets.\r\n * @param tickets The number of winning tickets to draw.\r\n * @param options The draw multiple options.\r\n * @returns An array of the participants that hold the winning tickets.\r\n */\r\n Lotto.prototype.drawMultiple = function (tickets, options) {\r\n if (options === void 0) { options = {}; }\r\n var uniqueResults = (0, Utilities_1.isNullOrUndefined)(options.unique) ? false : options.unique;\r\n // Handle cases where the user has asked for zero tickets (no idea why they would do this be we should trust them).\r\n if (tickets === 0) {\r\n return [];\r\n }\r\n // Now that we know out tickets value is not zero we should check that it is a valid natural number.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n var result = [];\r\n // Keep drawing tickets until we either reach the number of required tickets or we simply run out of tickets to draw.\r\n // We can run out of tickets to draw if 'options.redrawable' is explicity 'false' or we just had no participants when 'drawMultiple' was called.\r\n while (result.length < tickets && this._participants.length > 0) {\r\n result.push(this.draw(options));\r\n }\r\n // If the 'unique' draw option is set then we need to remove duplicates from the result list.\r\n if (uniqueResults) {\r\n // Create an array to store our unique results.\r\n var unique = [];\r\n // Iterate over all of our participants (with potential duplicates) and populate our array of unique values.\r\n for (var _i = 0, result_1 = result; _i < result_1.length; _i++) {\r\n var participant = result_1[_i];\r\n if (unique.indexOf(participant) === -1) {\r\n unique.push(participant);\r\n }\r\n }\r\n result = unique;\r\n }\r\n return result;\r\n };\r\n return Lotto;\r\n}());\r\nexports.Lotto = Lotto;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.createLotto = void 0;\r\nvar Lotto_1 = require(\"./Lotto\");\r\n/**\r\n * A function that creates and returns a Lotto instance.\r\n * @param participantsOrOptions An array of initial participants or options relating to the creation of a Lotto instance.\r\n * @returns A new Lotto instance.\r\n */\r\nfunction createLotto(participantsOrOptions) {\r\n // If no initial participants or lotto options were provided as an argument then we can just return a new lotto instance now.\r\n if (!participantsOrOptions) {\r\n return new Lotto_1.Lotto();\r\n }\r\n // Check whether we were provided with an array of initial participants or a lotto options object.\r\n if (Array.isArray(participantsOrOptions)) {\r\n // We are dealing with a pre-defined array of participants.\r\n var participants = participantsOrOptions;\r\n var lotto_1 = new Lotto_1.Lotto();\r\n // If the lotto participants have been defined upfront then we will need to add them all to our lotto instance now.\r\n participants.forEach(function (_a) {\r\n var participant = _a[0], tokens = _a[1];\r\n return lotto_1.add(participant, tokens);\r\n });\r\n // Return the Lotto instance.\r\n return lotto_1;\r\n }\r\n else {\r\n // We are dealing with some lotto options.\r\n var random = participantsOrOptions.random, participants = participantsOrOptions.participants;\r\n // Create a Lotto instance passing the custom RNG function to use in place of Math.random() (which could be undefined).\r\n var lotto_2 = new Lotto_1.Lotto(random);\r\n // If the lotto participants have been defined upfront as part of the options then we will need to add them all to our lotto instance now.\r\n if (participants) {\r\n participants.forEach(function (_a) {\r\n var participant = _a[0], tokens = _a[1];\r\n return lotto_2.add(participant, tokens);\r\n });\r\n }\r\n // Return the Lotto instance.\r\n return lotto_2;\r\n }\r\n}\r\nexports.createLotto = createLotto;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nvar createLotto_1 = require(\"./createLotto\");\r\nexports.default = createLotto_1.createLotto;\r\n", "import { BehaviourTree, FlattenedTreeNode } from \"./BehaviourTree\";\nimport State from \"./State\";\n\nexport { BehaviourTree, State };\nexport type { FlattenedTreeNode };\n", "import Node from \"../../nodes/Node\";\n\n/**\n * An exception thrown when evaluating node guard path conditions and a conditions fails.\n */\nexport default class GuardUnsatisifedException extends Error {\n /**\n * @param source The node at which a guard condition failed.\n */\n constructor(private source: Node) {\n super(\"A guard path condition has failed\");\n }\n\n /**\n * Gets whether the specified node is the node at which a guard condition failed.\n * @param node The node to check against the source node.\n * @returns Whether the specified node is the node at which a guard condition failed.\n */\n isSourceNode = (node: Node) => node === this.source;\n}\n", "import { Agent } from \"../../Agent\";\nimport Guard from \"./Guard\";\nimport Node from \"../../nodes/Node\";\nimport GuardUnsatisifedException from \"./GuardUnsatisifedException\";\n\nexport type GuardPathPart = {\n node: Node;\n guards: Guard[];\n};\n\n/**\n * Represents a path of node guards along a root-to-leaf tree path.\n */\nexport default class GuardPath {\n /**\n * @param nodes An array of objects defining a node instance -> guard link, ordered by node depth.\n */\n constructor(private nodes: GuardPathPart[]) {}\n\n /**\n * Evaluate guard conditions for all guards in the tree path, moving outwards from the root.\n * @param agent The agent, required for guard evaluation.\n * @returns An evaluation results object.\n */\n evaluate = (agent: Agent) => {\n // We need to evaluate guard conditions for nodes up the tree, moving outwards from the root.\n for (const details of this.nodes) {\n // There can be multiple guards per node.\n for (const guard of details.guards) {\n // Check whether the guard condition passes, and throw an exception if not.\n if (!guard.isSatisfied(agent)) {\n throw new GuardUnsatisifedException(details.node);\n }\n }\n }\n };\n}\n", "/**\n * Enumeration of node state types.\n */\nexport enum State {\n READY = \"mistreevous.ready\",\n RUNNING = \"mistreevous.running\",\n SUCCEEDED = \"mistreevous.succeeded\",\n FAILED = \"mistreevous.failed\"\n}\n\nexport { State as default };\n\nexport type CompleteState = State.SUCCEEDED | State.FAILED;\nexport type AnyState = State.READY | State.RUNNING | CompleteState;\n", "import { Agent } from \"../Agent\";\nimport Attribute from \"../attributes/Attribute\";\nimport Entry from \"../attributes/callbacks/Entry\";\nimport Exit from \"../attributes/callbacks/Exit\";\nimport Step from \"../attributes/callbacks/Step\";\nimport Guard from \"../attributes/guards/Guard\";\nimport GuardPath from \"../attributes/guards/GuardPath\";\nimport GuardUnsatisifedException from \"../attributes/guards/GuardUnsatisifedException\";\nimport { BehaviourTreeOptions } from \"../BehaviourTreeOptions\";\nimport { AnyArgument } from \"../RootAstNodesBuilder\";\nimport State, { AnyState } from \"../State\";\nimport Leaf from \"./leaf/Leaf\";\n\n/**\n * A base node.\n */\nexport default abstract class Node {\n /**\n * The node uid.\n */\n private readonly uid: string = createNodeUid();\n /**\n * The node state.\n */\n private state: AnyState = State.READY;\n /**\n * The guard path to evaluate as part of a node update.\n */\n private guardPath: GuardPath | undefined;\n\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param args The node argument definitions.\n */\n constructor(private type: string, private attributes: Attribute[], private args: AnyArgument[]) {}\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected abstract onUpdate(agent: Agent, options: BehaviourTreeOptions): void;\n\n /**\n * Gets the name of the node.\n */\n public abstract getName(): string;\n\n /**\n * Gets whether this node is a leaf node.\n */\n public abstract isLeafNode: () => this is Leaf;\n\n /**\n * Gets/Sets the state of the node.\n */\n getState = (): AnyState => this.state;\n setState = (value: AnyState): void => {\n this.state = value;\n };\n\n /**\n * Gets the unique id of the node.\n */\n getUid = () => this.uid;\n\n /**\n * Gets the type of the node.\n */\n getType = () => this.type;\n\n /**\n * Gets the node attributes.\n */\n getAttributes = () => this.attributes;\n\n /**\n * Gets the node arguments.\n */\n getArguments = () => this.args;\n\n /**\n * Gets the node attribute with the specified type, or null if it does not exist.\n */\n getAttribute(type: \"entry\" | \"ENTRY\"): Entry;\n getAttribute(type: \"exit\" | \"EXIT\"): Exit;\n getAttribute(type: \"step\" | \"STEP\"): Step;\n getAttribute(type: string): Attribute {\n return (\n this.getAttributes().filter((decorator) => decorator.getType().toUpperCase() === type.toUpperCase())[0] ||\n null\n );\n }\n\n /**\n * Gets the node attributes.\n */\n getGuardAttributes = (): Guard[] => this.getAttributes().filter((decorator) => decorator.isGuard()) as Guard[];\n\n /**\n * Sets the guard path to evaluate as part of a node update.\n */\n setGuardPath = (value: GuardPath) => (this.guardPath = value);\n\n /**\n * Gets whether a guard path is assigned to this node.\n */\n hasGuardPath = () => !!this.guardPath;\n\n /**\n * Gets whether this node is in the specified state.\n * @param value The value to compare to the node state.\n */\n public is(value: AnyState): boolean {\n return this.state === value;\n }\n\n /**\n * Reset the state of the node.\n */\n public reset(): void {\n this.setState(State.READY);\n }\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n public abort(agent: Agent): void {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n }\n\n /**\n * Update the node.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n * @returns The result of the update.\n */\n public update(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is already in a 'SUCCEEDED' or 'FAILED' state then there is nothing to do.\n if (this.is(State.SUCCEEDED) || this.is(State.FAILED)) {\n return;\n }\n\n try {\n // Evaluate all of the guard path conditions for the current tree path.\n this.guardPath!.evaluate(agent);\n\n // If this node is in the READY state then call the ENTRY for this node if it exists.\n if (this.is(State.READY)) {\n this.getAttribute(\"entry\")?.callAgentFunction(agent);\n }\n\n this.getAttribute(\"step\")?.callAgentFunction(agent);\n\n // Do the actual update.\n this.onUpdate(agent, options);\n\n // If this node is now in a 'SUCCEEDED' or 'FAILED' state then call the EXIT for this node if it exists.\n if (this.is(State.SUCCEEDED) || this.is(State.FAILED)) {\n this.getAttribute(\"exit\")?.callAgentFunction(agent, this.is(State.SUCCEEDED), false);\n }\n } catch (error) {\n // If the error is a GuardUnsatisfiedException then we need to determine if this node is the source.\n if (error instanceof GuardUnsatisifedException && error.isSourceNode(this)) {\n // Abort the current node.\n this.abort(agent);\n\n // Any node that is the source of an abort will be a failed node.\n this.setState(State.FAILED);\n } else {\n throw error;\n }\n }\n }\n}\n\n/**\n * Create a randomly generated node uid.\n * @returns A randomly generated node uid.\n */\nfunction createNodeUid(): string {\n var S4 = function () {\n return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);\n };\n return S4() + S4() + \"-\" + S4() + \"-\" + S4() + \"-\" + S4() + \"-\" + S4() + S4() + S4();\n}\n", "import Node from \"../Node\";\n\n/**\n * A leaf node.\n */\nexport default abstract class Leaf extends Node {\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => true;\n}\n", "import { ActionResult, Agent, ExitFunctionArg, FunctionArg, GlobalFunction } from \"./Agent\";\nimport { AnyArgument, RootAstNode } from \"./RootAstNodesBuilder\";\n\n// Exit callbacks receive their own special type of argument.\n// There's probably stricter ways to represent this but it feels overly complex right now.\ntype ExitResultArg = { value: ExitFunctionArg };\nexport type AnyExitArgument = AnyArgument | ExitResultArg;\n\nexport type InvokerFunction = (args: AnyExitArgument[]) => ActionResult;\n\n/**\n * A singleton used to store and lookup registered functions and subtrees.\n */\nexport default class Lookup {\n /**\n * The object holding any registered functions keyed on function name.\n */\n private static functionTable: { [key: string]: GlobalFunction } = {};\n /**\n * The object holding any registered sub-trees keyed on tree name.\n */\n private static subtreeTable: { [key: string]: RootAstNode } = {};\n\n /**\n * Gets the function with the specified name.\n * @param name The name of the function.\n * @returns The function with the specified name.\n */\n public static getFunc(name: string): GlobalFunction {\n return this.functionTable[name];\n }\n\n /**\n * Sets the function with the specified name for later lookup.\n * @param name The name of the function.\n * @param func The function.\n */\n public static setFunc(name: string, func: GlobalFunction): void {\n this.functionTable[name] = func;\n }\n\n /**\n * Gets the function invoker for the specified agent and function name.\n * If a function with the specified name exists on the agent object then it will\n * be returned, otherwise we will then check the registered functions for a match.\n * @param agent The agent instance that this behaviour tree is modelling behaviour for.\n * @param name The function name.\n * @returns The function invoker for the specified agent and function name.\n */\n static getFuncInvoker(agent: Agent, name: string): InvokerFunction | null {\n // Check whether the agent contains the specified function.\n const foundOnAgent = agent[name];\n if (foundOnAgent && typeof foundOnAgent === \"function\") {\n return (args: AnyExitArgument[]): boolean | ActionResult =>\n foundOnAgent.apply(\n agent,\n args.map((arg) => arg.value)\n );\n }\n\n // The agent does not contain the specified function but it may have been registered at some point.\n if (this.functionTable[name] && typeof this.functionTable[name] === \"function\") {\n return (args: AnyExitArgument[]) => this.functionTable[name](agent, ...args.map((arg) => arg.value));\n }\n\n // We have no function to invoke.\n return null;\n }\n\n /**\n * Gets the subtree with the specified name.\n * @param name The name of the subtree.\n * @returns The subtree with the specified name.\n */\n static getSubtree(name: string): RootAstNode {\n return this.subtreeTable[name];\n }\n\n /**\n * Sets the subtree with the specified name for later lookup.\n * @param name The name of the subtree.\n * @param subtree The subtree.\n */\n static setSubtree(name: string, subtree: RootAstNode) {\n this.subtreeTable[name] = subtree;\n }\n\n /**\n * Removes the registered function or subtree with the specified name.\n * @param name The name of the registered function or subtree.\n */\n static remove(name: string) {\n delete this.functionTable[name];\n delete this.subtreeTable[name];\n }\n\n /**\n * Remove all registered functions and subtrees.\n */\n static empty() {\n this.functionTable = {};\n this.subtreeTable = {};\n }\n}\n", "import Leaf from \"./Leaf\";\nimport State, { CompleteState } from \"../../State\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * An Action leaf node.\n * This represents an immediate or ongoing state of behaviour.\n */\nexport default class Action extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param actionName The action name.\n * @param actionArguments The array of action argument definitions.\n */\n constructor(attributes: Attribute[], private actionName: string, private actionArguments: AnyArgument[]) {\n super(\"action\", attributes, actionArguments);\n }\n\n /**\n * Whether there is a pending update promise.\n */\n private isUsingUpdatePromise = false;\n\n /**\n * The finished state result of an update promise.\n */\n private updatePromiseStateResult: CompleteState | null = null;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the result of this action depends on an update promise then there is nothing to do until\n // it resolves, unless there has been a value set as a result of the update promise resolving.\n if (this.isUsingUpdatePromise) {\n // Check whether the update promise has resolved with a state value.\n if (this.updatePromiseStateResult) {\n // Set the state of this node to match the state returned by the promise.\n this.setState(this.updatePromiseStateResult);\n }\n\n return;\n }\n\n // Attempt to get the invoker for the action function.\n const actionFuncInvoker = Lookup.getFuncInvoker(agent, this.actionName);\n\n // The action function should be defined.\n if (actionFuncInvoker === null) {\n throw new Error(\n `cannot update action node as the action '${this.actionName}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the action function, the result of which may be:\n // - The finished state of this action node.\n // - A promise to return a finished node state.\n // - Undefined if the node should remain in the running state.\n const updateResult = actionFuncInvoker(this.actionArguments) as CompleteState | Promise;\n\n if (updateResult instanceof Promise) {\n updateResult.then(\n (result) => {\n // If 'isUpdatePromisePending' is null then the promise was cleared as it was resolving, probably via an abort of reset.\n if (!this.isUsingUpdatePromise) {\n return;\n }\n\n // Check to make sure the result is a valid finished state.\n if (result !== State.SUCCEEDED && result !== State.FAILED) {\n throw new Error(\n \"action node promise resolved with an invalid value, expected a State.SUCCEEDED or State.FAILED value to be returned\"\n );\n }\n\n // Set pending update promise state result to be processed on next update.\n this.updatePromiseStateResult = result;\n },\n (reason) => {\n // If 'isUpdatePromisePending' is null then the promise was cleared as it was resolving, probably via an abort of reset.\n if (!this.isUsingUpdatePromise) {\n return;\n }\n\n // Just throw whatever was returned as the rejection argument.\n throw new Error(reason);\n }\n );\n\n // This node will be in the 'RUNNING' state until the update promise resolves.\n this.setState(State.RUNNING);\n\n // We are now waiting for the promise returned by the use to resolve before we know what state this node is in.\n this.isUsingUpdatePromise = true;\n } else {\n // Validate the returned value.\n this.validateUpdateResult(updateResult);\n\n // Set the state of this node, this may be undefined, which just means that the node is still in the 'RUNNING' state.\n this.setState(updateResult || State.RUNNING);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => this.actionName;\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // There is no longer an update promise that we care about.\n this.isUsingUpdatePromise = false;\n this.updatePromiseStateResult = null;\n };\n\n /**\n * Validate the result of an update function call.\n * @param result The result of an update function call.\n */\n private validateUpdateResult = (result: CompleteState | boolean) => {\n switch (result) {\n case State.SUCCEEDED:\n case State.FAILED:\n case undefined:\n return;\n default:\n throw new Error(\n `action '${this.actionName}' 'onUpdate' returned an invalid response, expected an optional State.SUCCEEDED or State.FAILED value to be returned`\n );\n }\n };\n}\n", "import Leaf from \"./Leaf\";\nimport State from \"../../State\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Condition leaf node.\n * This will succeed or fail immediately based on an agent predicate, without moving to the 'RUNNING' state.\n */\nexport default class Condition extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param conditionName The name of the condition function.\n * @param conditionArguments The array of condition argument definitions.\n */\n constructor(attributes: Attribute[], private conditionName: string, private conditionArguments: AnyArgument[]) {\n super(\"condition\", attributes, conditionArguments);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.conditionName);\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot update condition node as the condition '${this.conditionName}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine the state of this node.\n this.setState(!!conditionFuncInvoker(this.conditionArguments) ? State.SUCCEEDED : State.FAILED);\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => this.conditionName;\n}\n", "import Leaf from \"./Leaf\";\nimport State from \"../../State\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { Agent } from \"../../Agent\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A WAIT node.\n * The state of this node will change to SUCCEEDED after a duration of time\n */\nexport default class Wait extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param duration The duration that this node will wait to succeed in milliseconds.\n * @param durationMin The minimum possible duration in milliseconds that this node will wait to succeed.\n * @param durationMax The maximum possible duration in milliseconds that this node will wait to succeed.\n */\n constructor(\n attributes: Attribute[],\n private duration: number | null,\n private durationMin: number | null,\n private durationMax: number | null\n ) {\n super(\"wait\", attributes, []);\n }\n\n /**\n * The time in milliseconds at which this node was first updated.\n */\n private initialUpdateTime: number = 0;\n\n /**\n * The total duration in milliseconds that this node will be waiting for.\n */\n private totalDuration: number | null = null;\n\n /**\n * The duration in milliseconds that this node has been waiting for.\n */\n private waitedDuration: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to set the initial update time.\n if (this.is(State.READY)) {\n // Set the initial update time.\n this.initialUpdateTime = new Date().getTime();\n\n // Set the initial waited duration.\n this.waitedDuration = 0;\n\n // Are we dealing with an explicit duration or will we be randomly picking a duration between the min and max duration.\n if (this.duration !== null) {\n this.totalDuration = this.duration;\n } else if (this.durationMin !== null && this.durationMax !== null) {\n // We will be picking a random duration between a min and max duration, if the optional 'random' behaviour tree\n // function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random duration between a min and max duration.\n this.totalDuration = Math.floor(\n random() * (this.durationMax - this.durationMin + 1) + this.durationMin\n );\n } else {\n this.totalDuration = null;\n }\n\n // The node is now running until we finish waiting.\n this.setState(State.RUNNING);\n }\n\n // If we have no total duration then this wait node will wait indefinitely until it is aborted.\n if (this.totalDuration === null) {\n return;\n }\n\n // If we have a 'getDeltaTime' function defined as part of our options then we will use it to figure out how long we have waited for.\n if (typeof options.getDeltaTime === \"function\") {\n // Get the delta time.\n const deltaTime = options.getDeltaTime();\n\n // Our delta time must be a valid number and cannot be NaN.\n if (typeof deltaTime !== \"number\" || isNaN(deltaTime)) {\n throw new Error(\"The delta time must be a valid number and not NaN.\");\n }\n\n // Update the amount of time that this node has been waiting for based on the delta time.\n this.waitedDuration += deltaTime * 1000;\n } else {\n // We are not using a delta time, so we will just work out hom much time has passed since the first update.\n this.waitedDuration = new Date().getTime() - this.initialUpdateTime;\n }\n\n // Have we waited long enough?\n if (this.waitedDuration >= this.totalDuration) {\n // We have finished waiting!\n this.setState(State.SUCCEEDED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.duration !== null) {\n return `WAIT ${this.duration}ms`;\n } else if (this.durationMin !== null && this.durationMax !== null) {\n return `WAIT ${this.durationMin}ms-${this.durationMax}ms`;\n } else {\n return \"WAIT\";\n }\n };\n}\n", "import Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\n\n/**\n * A decorator node that wraps a single child node.\n */\nexport default abstract class Decorator extends Node {\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(type: string, attributes: Attribute[], protected child: Node) {\n super(type, attributes, []);\n }\n\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => false;\n\n /**\n * Gets the children of this node.\n */\n getChildren = () => [this.child];\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the state of the child node.\n this.child.reset();\n };\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n abort = (agent: Agent) => {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Abort the child node.\n this.child.abort(agent);\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n };\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Root node.\n * The root node will have a single child.\n */\nexport default class Root extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"root\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n // Update the child of this node.\n this.child.update(agent, options);\n }\n\n // The state of the root node is the state of its child.\n this.setState(this.child.getState());\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"ROOT\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A REPEAT node.\n * The node has a single child which can have:\n * -- A number of iterations for which to repeat the child node.\n * -- An infinite repeat loop if neither an iteration count or a condition function is defined.\n * The REPEAT node will stop and have a 'FAILED' state if its child is ever in a 'FAILED' state after an update.\n * The REPEAT node will attempt to move on to the next iteration if its child is ever in a 'SUCCEEDED' state.\n */\nexport default class Repeat extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param iterations The number of iterations to repeat the child node.\n * @param iterationsMin The minimum possible number of iterations to repeat the child node.\n * @param iterationsMax The maximum possible number of iterations to repeat the child node.\n * @param child The child node.\n */\n constructor(\n attributes: Attribute[],\n private iterations: number | null,\n private iterationsMin: number | null,\n private iterationsMax: number | null,\n child: Node\n ) {\n super(\"repeat\", attributes, child);\n }\n\n /**\n * The number of target iterations to make.\n */\n private targetIterationCount: number | null = null;\n\n /**\n * The current iteration count.\n */\n private currentIterationCount: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to reset the child and the target iteration count.\n if (this.is(State.READY)) {\n // Reset the child node.\n this.child.reset();\n\n // Reset the current iteration count.\n this.currentIterationCount = 0;\n\n // Set the target iteration count.\n this.setTargetIterationCount(options);\n }\n\n // Do a check to see if we can iterate. If we can then this node will move into the 'RUNNING' state.\n // If we cannot iterate then we have hit our target iteration count, which means that the node has succeeded.\n if (this.canIterate()) {\n // This node is in the running state and can do its initial iteration.\n this.setState(State.RUNNING);\n\n // We may have already completed an iteration, meaning that the child node will be in the SUCCEEDED state.\n // If this is the case then we will have to reset the child node now.\n if (this.child.getState() === State.SUCCEEDED) {\n this.child.reset();\n }\n\n // Update the child of this node.\n this.child.update(agent, options);\n\n // If the child moved into the FAILED state when we updated it then there is nothing left to do and this node has also failed.\n // If it has moved into the SUCCEEDED state then we have completed the current iteration.\n if (this.child.getState() === State.FAILED) {\n // The child has failed, meaning that this node has failed.\n this.setState(State.FAILED);\n\n return;\n } else if (this.child.getState() === State.SUCCEEDED) {\n // We have completed an iteration.\n this.currentIterationCount += 1;\n }\n } else {\n // This node is in the 'SUCCEEDED' state as we cannot iterate any more.\n this.setState(State.SUCCEEDED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.iterations !== null) {\n return `REPEAT ${this.iterations}x`;\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n return `REPEAT ${this.iterationsMin}x-${this.iterationsMax}x`;\n } else {\n return \"REPEAT\";\n }\n };\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the current iteration count.\n this.currentIterationCount = 0;\n\n // Reset the child node.\n this.child.reset();\n };\n\n /**\n * Gets whether an iteration can be made.\n * @returns Whether an iteration can be made.\n */\n private canIterate = () => {\n if (this.targetIterationCount !== null) {\n // We can iterate as long as we have not reached our target iteration count.\n return this.currentIterationCount < this.targetIterationCount;\n }\n\n // If neither an iteration count or a condition function were defined then we can iterate indefinitely.\n return true;\n };\n\n /**\n * Sets the target iteration count.\n * @param options The behaviour tree options object.\n */\n private setTargetIterationCount = (options: BehaviourTreeOptions) => {\n // Are we dealing with an explicit iteration count or will we be randomly picking a iteration count between the min and max iteration count.\n if (this.iterations !== null) {\n this.targetIterationCount = this.iterations;\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n // We will be picking a random iteration count between a min and max iteration count, if the optional 'random'\n // behaviour tree function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random iteration count between a min and max iteration count.\n this.targetIterationCount = Math.floor(\n random() * (this.iterationsMax - this.iterationsMin + 1) + this.iterationsMin\n );\n } else {\n this.targetIterationCount = null;\n }\n };\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A RETRY node.\n * The node has a single child which can have:\n * -- A number of iterations for which to repeat the child node.\n * -- An infinite repeat loop if neither an iteration count or a condition function is defined.\n * The RETRY node will stop and have a 'SUCCEEDED' state if its child is ever in a 'SUCCEEDED' state after an update.\n * The RETRY node will attempt to move on to the next iteration if its child is ever in a 'FAILED' state.\n */\nexport default class Retry extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param attempts The number of attempts to retry the child node.\n * @param attemptsMin The minimum possible number of attempts to retry the child node.\n * @param attemptsMax The maximum possible number of attempts to retry the child node.\n * @param child The child node.\n */\n constructor(\n attributes: Attribute[],\n private attempts: number | null,\n private attemptsMin: number | null,\n private attemptsMax: number | null,\n child: Node\n ) {\n super(\"retry\", attributes, child);\n }\n\n /**\n * The number of target attempts to make.\n */\n private targetAttemptCount: number | null = null;\n\n /**\n * The current attempt count.\n */\n private currentAttemptCount: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to reset the child and the target attempt count.\n if (this.is(State.READY)) {\n // Reset the child node.\n this.child.reset();\n\n // Reset the current attempt count.\n this.currentAttemptCount = 0;\n\n // Set the target attempt count.\n this.setTargetAttemptCount(options);\n }\n\n // Do a check to see if we can attempt. If we can then this node will move into the 'RUNNING' state.\n // If we cannot attempt then we have hit our target attempt count, which means that the node has succeeded.\n if (this.canAttempt()) {\n // This node is in the running state and can do its initial attempt.\n this.setState(State.RUNNING);\n\n // We may have already completed an attempt, meaning that the child node will be in the FAILED state.\n // If this is the case then we will have to reset the child node now.\n if (this.child.getState() === State.FAILED) {\n this.child.reset();\n }\n\n // Update the child of this node.\n this.child.update(agent, options);\n\n // If the child moved into the SUCCEEDED state when we updated it then there is nothing left to do and this node has also succeeded.\n // If it has moved into the FAILED state then we have completed the current attempt.\n if (this.child.getState() === State.SUCCEEDED) {\n // The child has succeeded, meaning that this node has succeeded.\n this.setState(State.SUCCEEDED);\n\n return;\n } else if (this.child.getState() === State.FAILED) {\n // We have completed an attempt.\n this.currentAttemptCount += 1;\n }\n } else {\n // This node is in the 'FAILED' state as we cannot iterate any more.\n this.setState(State.FAILED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.attempts !== null) {\n return `RETRY ${this.attempts}x`;\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n return `RETRY ${this.attemptsMin}x-${this.attemptsMax}x`;\n } else {\n return \"RETRY\";\n }\n };\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the current attempt count.\n this.currentAttemptCount = 0;\n\n // Reset the child node.\n this.child.reset();\n };\n\n /**\n * Gets whether an attempt can be made.\n * @returns Whether an attempt can be made.\n */\n canAttempt = () => {\n if (this.targetAttemptCount !== null) {\n // We can attempt as long as we have not reached our target attempt count.\n return this.currentAttemptCount < this.targetAttemptCount;\n }\n\n // If neither an attempt count or a condition function were defined then we can attempt indefinitely.\n return true;\n };\n\n /**\n * Sets the target attempt count.\n * @param options The behaviour tree options object.\n */\n setTargetAttemptCount = (options: BehaviourTreeOptions) => {\n // Are we dealing with an explicit attempt count or will we be randomly picking an attempt count between the min and max attempt count.\n if (this.attempts !== null) {\n this.targetAttemptCount = this.attempts;\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n // We will be picking a random attempt count between a min and max attempt count, if the optional 'random'\n // behaviour tree function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random attempt count between a min and max attempt count.\n this.targetAttemptCount = Math.floor(\n random() * (this.attemptsMax - this.attemptsMin + 1) + this.attemptsMin\n );\n } else {\n this.targetAttemptCount = null;\n }\n };\n}\n", "import Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport Node from \"../Node\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Flip node.\n * This node wraps a single child and will flip the state of the child state.\n */\nexport default class Flip extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"flip\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n this.setState(State.FAILED);\n break;\n\n case State.FAILED:\n this.setState(State.SUCCEEDED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"FLIP\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Succeed node.\n * This node wraps a single child and will always move to the 'SUCCEEDED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state.\n */\nexport default class Succeed extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"succeed\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n case State.FAILED:\n this.setState(State.SUCCEEDED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SUCCEED\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Fail node.\n * This node wraps a single child and will always move to the 'FAILED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state.\n */\nexport default class Fail extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"fail\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n case State.FAILED:\n this.setState(State.FAILED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"FAIL\";\n}\n", "import createLotto from \"lotto-draw\";\n\nimport Node from \"../Node\";\nimport Composite from \"./Composite\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A LOTTO node.\n * A winning child is picked on the initial update of this node, based on ticket weighting.\n * The state of this node will match the state of the winning child.\n */\nexport default class Lotto extends Composite {\n /**\n * @param attributes The node attributes.\n * @param tickets The child node tickets.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], private tickets: number[], children: Node[]) {\n super(\"lotto\", attributes, children);\n }\n\n /**\n * The child node selected to be the active one.\n */\n private selectedChild: Node | undefined;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to pick a winning child node.\n if (this.is(State.READY)) {\n // Create a lotto draw with which to randomly pick a child node to become the active one.\n const lottoDraw = createLotto({\n // Hook up the optional 'random' behaviour tree function option to the one used by 'lotto-draw'.\n random: options.random,\n // Pass in each child node as a participant in the lotto draw with their respective ticket count.\n participants: this.children.map((child, index) => [child, this.tickets[index] || 1])\n });\n\n // Randomly pick a child based on ticket weighting, this will become the active child for this composite node.\n this.selectedChild = lottoDraw.draw() || undefined;\n }\n\n // If something went wrong and we don't have an active child then we should throw an error.\n if (!this.selectedChild) {\n throw new Error(\"failed to update lotto node as it has no active child\");\n }\n\n // If the selected child has never been updated or is running then we will need to update it now.\n if (this.selectedChild.getState() === State.READY || this.selectedChild.getState() === State.RUNNING) {\n this.selectedChild.update(agent, options);\n }\n\n // The state of the lotto node is the state of its selected child.\n this.setState(this.selectedChild.getState());\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => (this.tickets.length ? `LOTTO [${this.tickets.join(\",\")}]` : \"LOTTO\");\n}\n", "import Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\n\n/**\n * A composite node that wraps child nodes.\n */\nexport default abstract class Composite extends Node {\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(type: string, attributes: Attribute[], protected children: Node[]) {\n super(type, attributes, []);\n }\n\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => false;\n\n /**\n * Gets the children of this node.\n */\n getChildren = () => this.children;\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the state of any child nodes.\n this.getChildren().forEach((child) => child.reset());\n };\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n abort = (agent: Agent) => {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Abort any child nodes.\n this.getChildren().forEach((child) => child.abort(agent));\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n };\n}\n", "import Composite from \"./Composite\";\nimport Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A SELECTOR node.\n * The child nodes are executed in sequence until one succeeds or all fail.\n */\nexport default class Selector extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], protected children: Node[]) {\n super(\"selector\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then this node is also a 'SUCCEEDED' node.\n if (child.getState() === State.SUCCEEDED) {\n // This node is a 'SUCCEEDED' node.\n this.setState(State.SUCCEEDED);\n\n // There is no need to check the rest of the selector nodes.\n return;\n }\n\n // If the current child has a state of 'FAILED' then we should move on to the next child.\n if (child.getState() === State.FAILED) {\n // Find out if the current child is the last one in the selector.\n // If it is then this sequence node has also failed.\n if (this.children.indexOf(child) === this.children.length - 1) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // There is no need to check the rest of the selector as we have completed it.\n return;\n } else {\n // The child node failed, try the next one.\n continue;\n }\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() === State.RUNNING) {\n // This node is a 'RUNNING' node.\n this.setState(State.RUNNING);\n\n // There is no need to check the rest of the selector as the current child is still running.\n return;\n }\n\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SELECTOR\";\n}\n", "import Composite from \"./Composite\";\nimport Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A SEQUENCE node.\n * The child nodes are executed in sequence until one fails or all succeed.\n */\nexport default class Sequence extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], protected children: Node[]) {\n super(\"sequence\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then we should move on to the next child.\n if (child.getState() === State.SUCCEEDED) {\n // Find out if the current child is the last one in the sequence.\n // If it is then this sequence node has also succeeded.\n if (this.children.indexOf(child) === this.children.length - 1) {\n // This node is a 'SUCCEEDED' node.\n this.setState(State.SUCCEEDED);\n\n // There is no need to check the rest of the sequence as we have completed it.\n return;\n } else {\n // The child node succeeded, but we have not finished the sequence yet.\n continue;\n }\n }\n\n // If the current child has a state of 'FAILED' then this node is also a 'FAILED' node.\n if (child.getState() === State.FAILED) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // There is no need to check the rest of the sequence.\n return;\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() === State.RUNNING) {\n // This node is a 'RUNNING' node.\n this.setState(State.RUNNING);\n\n // There is no need to check the rest of the sequence as the current child is still running.\n return;\n }\n\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SEQUENCE\";\n}\n", "import Composite from \"./Composite\";\nimport State from \"../../State\";\nimport Node from \"../Node\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A PARALLEL node.\n * The child nodes are executed concurrently until one fails or all succeed.\n */\nexport default class Parallel extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], children: Node[]) {\n super(\"parallel\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Keep a count of the number of succeeded child nodes.\n let succeededCount = 0;\n\n let hasChildFailed = false;\n\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then we should move on to the next child.\n if (child.getState() === State.SUCCEEDED) {\n // The child node has succeeded, keep track of this to determine if all children have.\n succeededCount++;\n\n // The child node succeeded, but we have not finished checking every child node yet.\n continue;\n }\n\n // If the current child has a state of 'FAILED' then this node is also a 'FAILED' node.\n if (child.getState() === State.FAILED) {\n hasChildFailed = true;\n\n // There is no need to check the rest of the children.\n break;\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() !== State.RUNNING) {\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n if (hasChildFailed) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // Abort every running child.\n for (const child of this.children) {\n if (child.getState() === State.RUNNING) {\n child.abort(agent);\n }\n }\n } else {\n // If all children have succeeded then this node has also succeeded, otherwise it is still running.\n this.setState(succeededCount === this.children.length ? State.SUCCEEDED : State.RUNNING);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"PARALLEL\";\n}\n", "import { AnyArgument } from \"../RootAstNodesBuilder\";\nimport Guard from \"./guards/Guard\";\n\nexport type AttributeDetails = {\n /** The attribute type. */\n type: string;\n\n /** The attribute arguments. */\n args: AnyArgument[];\n};\n\n/**\n * A base node attribute.\n */\nexport default abstract class Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of attribute argument definitions.\n */\n constructor(protected type: string, protected args: AnyArgument[]) {}\n\n /**\n * Gets the type of the attribute.\n */\n getType = () => this.type;\n\n /**\n * Gets the array of attribute argument definitions.\n */\n getArguments = () => this.args;\n\n /**\n * Gets the attribute details.\n */\n abstract getDetails(): TAttributeDetails;\n\n /**\n * Gets whether this attribute is a guard.\n */\n abstract isGuard: () => this is Guard;\n}\n", "import { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport Attribute, { AttributeDetails } from \"../Attribute\";\n\nexport type GuardAttributeDetails = {\n /** The name of the condition function that determines whether the guard is satisfied. */\n condition: string;\n} & AttributeDetails;\n\n/**\n * A base node guard attribute.\n */\nexport default abstract class Guard extends Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of decorator argument definitions.\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n */\n constructor(type: string, args: AnyArgument[], private condition: string) {\n super(type, args);\n }\n\n /**\n * Gets the name of the condition function that determines whether the guard is satisfied.\n */\n getCondition = () => this.condition;\n\n /**\n * Gets whether this attribute is a guard.\n */\n isGuard = () => true;\n\n /**\n * Gets the attribute details.\n */\n getDetails(): GuardAttributeDetails {\n return {\n type: this.getType(),\n args: this.getArguments(),\n condition: this.getCondition()\n };\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n abstract isSatisfied(agent: Agent): boolean;\n}\n", "import Guard from \"./Guard\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * A WHILE guard which is satisfied as long as the given condition remains true.\n */\nexport default class While extends Guard {\n /**\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n * @param args The array of decorator argument definitions.\n */\n constructor(condition: string, args: AnyArgument[]) {\n super(\"while\", args, condition);\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n isSatisfied = (agent: Agent) => {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition());\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine whether this guard is satisfied.\n return !!conditionFuncInvoker(this.args);\n };\n}\n", "import Guard from \"./Guard\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An UNTIL guard which is satisfied as long as the given condition remains false.\n */\nexport default class Until extends Guard {\n /**\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n * @param args The array of decorator argument definitions.\n */\n constructor(condition: string, args: AnyArgument[]) {\n super(\"until\", args, condition);\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n isSatisfied = (agent: Agent) => {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition());\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine whether this guard is satisfied.\n return !!!conditionFuncInvoker(this.args);\n };\n}\n", "import { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport Attribute, { AttributeDetails } from \"../Attribute\";\n\nexport type CallbackAttributeDetails = {\n /** The name of the agent function that is called. */\n functionName: string;\n} & AttributeDetails;\n\n/**\n * A base node callback attribute.\n */\nexport default abstract class Callback extends Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of decorator argument definitions.\n * @param functionName The name of the agent function to call.\n */\n constructor(type: string, args: AnyArgument[], private functionName: string) {\n super(type, args);\n }\n\n /**\n * Gets the name of the agent function to call.\n */\n getFunctionName = () => this.functionName;\n\n /**\n * Gets whether this attribute is a guard.\n */\n isGuard = () => false;\n\n /**\n * Gets the attribute details.\n */\n getDetails(): CallbackAttributeDetails {\n return {\n type: this.getType(),\n args: this.getArguments(),\n functionName: this.getFunctionName()\n };\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n abstract callAgentFunction: (agent: Agent, isSuccess: boolean, isAborted: boolean) => void;\n}\n", "import Callback from \"./Callback\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An ENTRY callback which defines an agent function to call when the associated node is updated and moves out of running state.\n */\nexport default class Entry extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"entry\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n callAgentFunction = (agent: Agent) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call entry function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function.\n callbackFuncInvoker(this.args);\n };\n}\n", "import Callback from \"./Callback\";\nimport Lookup, { AnyExitArgument } from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An EXIT callback which defines an agent function to call when the associated node is updated and moves to a finished state or is aborted.\n */\nexport default class Exit extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"exit\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n * @param isSuccess Whether the decorated node was left with a success state.\n * @param isAborted Whether the decorated node was aborted.\n */\n callAgentFunction = (agent: Agent, isSuccess: boolean, isAborted: boolean) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call exit function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function\n callbackFuncInvoker([{ value: { succeeded: isSuccess, aborted: isAborted } }, ...this.args]);\n };\n}\n", "import Callback from \"./Callback\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * A STEP callback which defines an agent function to call when the associated node is updated.\n */\nexport default class Step extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"step\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n callAgentFunction = (agent: Agent) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call step function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function.\n callbackFuncInvoker(this.args);\n };\n}\n", "import Action from \"./nodes/leaf/Action\";\nimport Condition from \"./nodes/leaf/Condition\";\nimport Wait from \"./nodes/leaf/Wait\";\nimport Root from \"./nodes/decorator/Root\";\nimport Repeat from \"./nodes/decorator/Repeat\";\nimport Retry from \"./nodes/decorator/Retry\";\nimport Flip from \"./nodes/decorator/Flip\";\nimport Succeed from \"./nodes/decorator/Succeed\";\nimport Fail from \"./nodes/decorator/Fail\";\nimport Lotto from \"./nodes/composite/Lotto\";\nimport Selector from \"./nodes/composite/Selector\";\nimport Sequence from \"./nodes/composite/Sequence\";\nimport Parallel from \"./nodes/composite/Parallel\";\nimport Node from \"./nodes/Node\";\nimport While from \"./attributes/guards/While\";\nimport Until from \"./attributes/guards/Until\";\nimport Entry from \"./attributes/callbacks/Entry\";\nimport Exit from \"./attributes/callbacks/Exit\";\nimport Step from \"./attributes/callbacks/Step\";\nimport Callback from \"./attributes/callbacks/Callback\";\nimport Guard from \"./attributes/guards/Guard\";\nimport Attribute from \"./attributes/Attribute\";\nimport Composite from \"./nodes/composite/Composite\";\nimport Decorator from \"./nodes/decorator/Decorator\";\nimport Leaf from \"./nodes/leaf/Leaf\";\n\nexport type Argument = {\n value: T;\n type: string; // Used for validation.\n};\ntype NullArgument = Argument & {\n type: \"null\";\n};\ntype BooleanArgument = Argument & {\n type: \"boolean\";\n};\ntype NumberArgument = Argument & {\n type: \"number\";\n isInteger: boolean; // Used for validation.\n};\ntype StringPlaceholderArgument = Argument & {\n type: \"string\";\n};\ntype IdentifierArgument = Argument & {\n type: \"identifier\";\n};\nexport type AnyArgument =\n | NullArgument\n | BooleanArgument\n | NumberArgument\n | StringPlaceholderArgument\n | IdentifierArgument;\n\n/**\n * The node attribute factories.\n */\nconst AttributeFactories: {\n [key: string]: (functionName: string, attributeArguments: AnyArgument[]) => Callback | Guard;\n} = {\n WHILE: (condition: string, attributeArguments: AnyArgument[]) => new While(condition, attributeArguments),\n UNTIL: (condition: string, attributeArguments: AnyArgument[]) => new Until(condition, attributeArguments),\n ENTRY: (functionName: string, attributeArguments: AnyArgument[]) => new Entry(functionName, attributeArguments),\n EXIT: (functionName: string, attributeArguments: AnyArgument[]) => new Exit(functionName, attributeArguments),\n STEP: (functionName: string, attributeArguments: AnyArgument[]) => new Step(functionName, attributeArguments)\n};\n\ntype Validatable = {\n children?: AstNode[];\n validate: (depth: number) => void;\n};\n\ntype NodeInstanceCreator = (\n namedRootNodeProvider: (name: string) => RootAstNode,\n visitedBranches: string[]\n) => T;\n\nexport type AstNode = Validatable & {\n type: string;\n createNodeInstance: NodeInstanceCreator;\n};\n\nexport type LeafAstNode = AstNode & {\n type: \"action\" | \"condition\" | \"wait\";\n attributes: Attribute[];\n};\n\nexport type CompositeAstNode = AstNode & {\n type: \"lotto\" | \"parallel\" | \"selector\" | \"sequence\";\n attributes: Attribute[];\n children: AstNode[];\n};\n\nexport type DecoratorAstNode = AstNode & {\n type: \"fail\" | \"flip\" | \"repeat\" | \"retry\" | \"root\" | \"succeed\";\n attributes: Attribute[];\n children: AstNode[];\n};\n\nexport type BranchAstNode = AstNode & {\n type: \"branch\";\n branchName: \"\" | string;\n};\n\nexport type LottoAstNode = CompositeAstNode & {\n type: \"lotto\";\n tickets: number[];\n};\n\nexport type RootAstNode = DecoratorAstNode & {\n type: \"root\";\n name: null | string;\n};\n\nexport type RepeatAstNode = DecoratorAstNode & {\n type: \"repeat\";\n iterations: number | null;\n iterationsMin: number | null;\n iterationsMax: number | null;\n};\n\nexport type RetryAstNode = DecoratorAstNode & {\n type: \"retry\";\n attempts: number | null;\n attemptsMin: number | null;\n attemptsMax: number | null;\n};\n\nexport type ActionAstNode = LeafAstNode & {\n type: \"action\";\n actionName: string;\n actionArguments: AnyArgument[];\n};\n\nexport type ConditionAstNode = LeafAstNode & {\n type: \"condition\";\n conditionName: string;\n conditionArguments: AnyArgument[];\n};\n\nexport type WaitAstNode = LeafAstNode & {\n type: \"wait\";\n duration: number | null;\n durationMin: number | null;\n durationMax: number | null;\n};\n\nexport type AnyAstNode =\n | BranchAstNode\n | CompositeAstNode\n | LottoAstNode\n | DecoratorAstNode\n | RootAstNode\n | RepeatAstNode\n | RetryAstNode\n | LeafAstNode\n | ActionAstNode\n | ConditionAstNode\n | WaitAstNode;\n\n/**\n * The AST node factories.\n */\nconst ASTNodeFactories = {\n ROOT: (): RootAstNode => ({\n type: \"root\",\n attributes: [],\n name: null,\n children: [],\n validate(depth: number) {\n // A root node cannot be the child of another node.\n if (depth > 1) {\n throw new Error(\"a root node cannot be the child of another node\");\n }\n\n // A root node must have a single child node.\n if (this.children.length !== 1) {\n throw new Error(\"a root node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Root(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n BRANCH: (): BranchAstNode => ({\n type: \"branch\",\n branchName: \"\",\n validate() {},\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n // Try to find the root node with a matching branch name.\n const targetRootNode = namedRootNodeProvider(this.branchName);\n\n // If we have already visited this branch then we have a circular dependency.\n if (visitedBranches.indexOf(this.branchName) !== -1) {\n throw new Error(`circular dependency found in branch node references for branch '${this.branchName}'`);\n }\n\n // If we have a target root node, then the node instance we want will be the first and only child of the referenced root node.\n if (targetRootNode) {\n return targetRootNode\n .createNodeInstance(namedRootNodeProvider, visitedBranches.concat(this.branchName))\n .getChildren()[0];\n } else {\n throw new Error(`branch references root node '${this.branchName}' which has not been defined`);\n }\n }\n }),\n SELECTOR: (): CompositeAstNode => ({\n type: \"selector\",\n attributes: [],\n children: [],\n validate() {\n // A selector node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a selector node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Selector(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n SEQUENCE: (): CompositeAstNode => ({\n type: \"sequence\",\n attributes: [],\n children: [],\n validate() {\n // A sequence node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a sequence node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Sequence(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n PARALLEL: (): CompositeAstNode => ({\n type: \"parallel\",\n attributes: [],\n children: [],\n validate() {\n // A parallel node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a parallel node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Parallel(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n LOTTO: (): LottoAstNode => ({\n type: \"lotto\",\n attributes: [],\n children: [],\n tickets: [],\n validate() {\n // A lotto node must have at least a single node.\n if (this.children!.length < 1) {\n throw new Error(\"a lotto node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Lotto(\n this.attributes,\n this.tickets!,\n this.children!.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n REPEAT: (): RepeatAstNode => ({\n type: \"repeat\",\n attributes: [],\n iterations: null,\n iterationsMin: null,\n iterationsMax: null,\n children: [],\n validate() {\n // A repeat node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a repeat node must have a single child\");\n }\n\n if (this.iterations !== null) {\n // A repeat node must have a positive number of iterations if defined.\n if (this.iterations < 0) {\n throw new Error(\"a repeat node must have a positive number of iterations if defined\");\n }\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n // A repeat node must have a positive min and max iteration count if they are defined.\n if (this.iterationsMin < 0 || this.iterationsMax < 0) {\n throw new Error(\n \"a repeat node must have a positive minimum and maximum iteration count if defined\"\n );\n }\n\n // A repeat node must not have an minimum iteration count that exceeds the maximum iteration count.\n if (this.iterationsMin > this.iterationsMax) {\n throw new Error(\n \"a repeat node must not have a minimum iteration count that exceeds the maximum iteration count\"\n );\n }\n } else {\n // If we have no explicit iteration count or a minimum and maximum iteration count set then we are dealing with a repeat node that iterates indefinitely.\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Repeat(\n this.attributes,\n this.iterations,\n this.iterationsMin,\n this.iterationsMax,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n RETRY: (): RetryAstNode => ({\n type: \"retry\",\n attributes: [],\n attempts: null,\n attemptsMin: null,\n attemptsMax: null,\n children: [],\n validate() {\n // A retry node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a retry node must have a single child\");\n }\n\n if (this.attempts !== null) {\n // A retry node must have a positive number of attempts if defined.\n if (this.attempts < 0) {\n throw new Error(\"a retry node must have a positive number of attempts if defined\");\n }\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n // A retry node must have a positive min and max attempts count if they are defined.\n if (this.attemptsMin < 0 || this.attemptsMax < 0) {\n throw new Error(\"a retry node must have a positive minimum and maximum attempt count if defined\");\n }\n\n // A retry node must not have a minimum attempt count that exceeds the maximum attempt count.\n if (this.attemptsMin > this.attemptsMax) {\n throw new Error(\n \"a retry node must not have a minimum attempt count that exceeds the maximum attempt count\"\n );\n }\n } else {\n // If we have no explicit attempt count or a minimum and maximum attempt count set then we are dealing with a retry node that attempts indefinitely.\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Retry(\n this.attributes,\n this.attempts,\n this.attemptsMin,\n this.attemptsMax,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n FLIP: (): DecoratorAstNode => ({\n type: \"flip\",\n attributes: [],\n children: [],\n validate() {\n // A flip node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a flip node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Flip(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n SUCCEED: (): DecoratorAstNode => ({\n type: \"succeed\",\n attributes: [],\n children: [],\n validate() {\n // A succeed node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a succeed node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Succeed(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n FAIL: (): DecoratorAstNode => ({\n type: \"fail\",\n attributes: [],\n children: [],\n validate() {\n // A fail node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a fail node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Fail(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n WAIT: (): WaitAstNode => ({\n type: \"wait\",\n attributes: [],\n duration: null,\n durationMin: null,\n durationMax: null,\n validate() {\n if (this.duration !== null) {\n // If an explict duration was defined then it must be a positive number.\n if (this.duration < 0) {\n throw new Error(\"a wait node must have a positive duration\");\n }\n } else if (this.durationMin !== null && this.durationMax !== null) {\n // A wait node must have a positive min and max duration.\n if (this.durationMin < 0 || this.durationMax < 0) {\n throw new Error(\"a wait node must have a positive minimum and maximum duration\");\n }\n\n // A wait node must not have a minimum duration that exceeds the maximum duration.\n if (this.durationMin > this.durationMax) {\n throw new Error(\"a wait node must not have a minimum duration that exceeds the maximum duration\");\n }\n } else {\n // If we have no explicit duration or duration bounds set then we are dealing with a wait node that waits indefinitely.\n }\n },\n createNodeInstance() {\n return new Wait(this.attributes, this.duration, this.durationMin, this.durationMax);\n }\n }),\n ACTION: (): ActionAstNode => ({\n type: \"action\",\n attributes: [],\n actionName: \"\",\n actionArguments: [],\n validate() {},\n createNodeInstance() {\n return new Action(this.attributes, this.actionName!, this.actionArguments!);\n }\n }),\n CONDITION: (): ConditionAstNode => ({\n type: \"condition\",\n attributes: [],\n conditionName: \"\",\n conditionArguments: [],\n validate() {},\n createNodeInstance() {\n return new Condition(this.attributes, this.conditionName!, this.conditionArguments!);\n }\n })\n};\n\ntype OtherAstNodes = AstNode[];\n\n/**\n * Create an array of root AST nodes based on the given definition.\n * @param definition The definition to parse the AST nodes from.\n * @returns The base definition AST nodes.\n */\nexport default function buildRootASTNodes(definition: string): RootAstNode[] {\n // Swap out any node/attribute argument string literals with a placeholder and get a mapping of placeholders to original values as well as the processed definition.\n const { placeholders, processedDefinition } = substituteStringLiterals(definition);\n\n // Convert the processed definition (with substituted string literals) into an array of raw tokens.\n const tokens = parseTokensFromDefinition(processedDefinition);\n\n // There must be at least 3 tokens for the tree definition to be valid. 'ROOT', '{' and '}'.\n if (tokens.length < 3) {\n throw new Error(\"invalid token count\");\n }\n\n // We should have a matching number of '{' and '}' tokens. If not, then there are scopes that have not been properly closed.\n if (tokens.filter((token) => token === \"{\").length !== tokens.filter((token) => token === \"}\").length) {\n throw new Error(\"scope character mismatch\");\n }\n\n // Create a stack of node children arrays, starting with a definition scope.\n const stack: [RootAstNode[], ...OtherAstNodes[]] = [[]];\n const rootScope = stack[0];\n\n // We should keep processing the raw tokens until we run out of them.\n while (tokens.length) {\n // Grab the next token.\n const token = tokens.shift();\n\n const currentScope = stack[stack.length - 1] as OtherAstNodes;\n\n // How we create the next AST token depends on the current raw token value.\n switch (token!.toUpperCase()) {\n case \"ROOT\": {\n // Create a ROOT AST node.\n const node = ASTNodeFactories.ROOT();\n\n // Push the ROOT node into the current scope.\n rootScope.push(node);\n\n // We may have a root node name defined as an argument.\n if (tokens[0] === \"[\") {\n const rootArguments = getArguments(tokens, placeholders);\n\n // We should have only a single argument that is not an empty string for a root node, which is the root name identifier.\n if (rootArguments.length === 1 && rootArguments[0].type === \"identifier\") {\n // The root name will be the first and only node argument.\n node.name = rootArguments[0].value as string;\n } else {\n throw new Error(\"expected single root name argument\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new ROOT nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"BRANCH\": {\n // Create a BRANCH AST node.\n const node = ASTNodeFactories.BRANCH();\n\n // Push the BRANCH node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require a branch name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected single branch name argument\");\n }\n\n // The branch name will be defined as a node argument.\n const branchArguments = getArguments(tokens, placeholders);\n\n // We should have only a single identifer argument for a branch node, which is the branch name.\n if (branchArguments.length === 1 && branchArguments[0].type === \"identifier\") {\n // The branch name will be the first and only node argument.\n node.branchName = branchArguments[0].value as string;\n } else {\n throw new Error(\"expected single branch name argument\");\n }\n break;\n }\n\n case \"SELECTOR\": {\n // Create a SELECTOR AST node.\n const node = ASTNodeFactories.SELECTOR();\n\n // Push the SELECTOR node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new SELECTOR nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"SEQUENCE\": {\n // Create a SEQUENCE AST node.\n const node = ASTNodeFactories.SEQUENCE();\n\n // Push the SEQUENCE node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new SEQUENCE nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"PARALLEL\": {\n // Create a PARALLEL AST node.\n const node = ASTNodeFactories.PARALLEL();\n\n // Push the PARALLEL node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new PARALLEL nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"LOTTO\": {\n // Create a LOTTO AST node.\n const node = ASTNodeFactories.LOTTO();\n\n // Push the LOTTO node into the current scope.\n currentScope.push(node);\n\n // If the next token is a '[' character then some ticket counts have been defined as arguments.\n if (tokens[0] === \"[\") {\n // Get the ticket count arguments, each argument must be a number.\n node.tickets = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"lotto node ticket counts must be integer values\"\n ).map((argument) => argument.value as number);\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new LOTTO nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"CONDITION\": {\n // Create a CONDITION AST node.\n const node = ASTNodeFactories.CONDITION();\n\n // Push the CONDITION node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require a condition function name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected condition name identifier argument\");\n }\n\n // Grab the condition node arguments.\n const conditionArguments = getArguments(tokens, placeholders);\n\n // We should have at least a single identifier argument for a condition node, which is the condition function name.\n if (conditionArguments.length && conditionArguments[0].type === \"identifier\") {\n // The condition function name will be the first node argument.\n node.conditionName = conditionArguments.shift()!.value as string;\n } else {\n throw new Error(\"expected condition name identifier argument\");\n }\n\n // Only the first argument should have been an identifier, all following arguments must be string, number, boolean or null.\n conditionArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid condition node argument value '\" +\n arg.value +\n \"', must be string, number, boolean or null\"\n );\n });\n\n // Any node arguments that follow the condition name identifier will be treated as condition function arguments.\n node.conditionArguments = conditionArguments;\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"FLIP\": {\n // Create a FLIP AST node.\n const node = ASTNodeFactories.FLIP();\n\n // Push the Flip node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new FLIP nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"SUCCEED\": {\n // Create a SUCCEED AST node.\n const node = ASTNodeFactories.SUCCEED();\n\n // Push the Succeed node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new Succeed nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"FAIL\": {\n // Create a FAIL AST node.\n const node = ASTNodeFactories.FAIL();\n\n // Push the Fail node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new Fail nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"WAIT\": {\n // Create a WAIT AST node.\n const node = ASTNodeFactories.WAIT();\n\n // Push the WAIT node into the current scope.\n currentScope.push(node);\n\n // The arguments of a wait node are optional. We may have:\n // - No node arguments, in which case the wait will be indefinite until it is aborted.\n // - One node argument which will be the explicit duration of the wait.\n // - Two node arguments which define the min and max duration bounds from which a random duration will be picked.\n if (tokens[0] === \"[\") {\n // Get the optional duration and longest duration of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"wait node durations must be integer values\"\n ).map((argument) => argument.value);\n\n // We may have:\n // - One node argument which will be the explicit duration of the wait.\n // - Two node arguments which define the min and max duration bounds from which a random duration will be picked.\n // - Too many arguments, which is not valid.\n if (nodeArguments.length === 1) {\n // An explicit duration was defined.\n node.duration = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // Min and max duration bounds were defined from which a random duration will be picked.\n node.durationMin = nodeArguments[0] as number;\n node.durationMax = nodeArguments[1] as number;\n } else if (nodeArguments.length > 2) {\n // An incorrect number of durations was defined.\n throw new Error(\"invalid number of wait node duration arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"REPEAT\": {\n // Create a REPEAT AST node.\n const node = ASTNodeFactories.REPEAT();\n\n // Push the REPEAT node into the current scope.\n currentScope.push(node);\n\n // The arguments of a repeat node are optional. We may have:\n // - No node arguments, in which case the repeat note will iterate indefinitely.\n // - One node argument which will be the explicit number of iterations to make.\n // - Two node arguments which define the min and max iteration bounds from which a random iteration count will be picked.\n if (tokens[0] === \"[\") {\n // An iteration count has been defined. Get the iteration and potential maximum iteration of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"repeat node iteration counts must be integer values\"\n ).map((argument) => argument.value);\n\n // We should have got one or two iteration counts.\n if (nodeArguments.length === 1) {\n // A static iteration count was defined.\n node.iterations = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // A minimum and maximum iteration count was defined.\n node.iterationsMin = nodeArguments[0] as number;\n node.iterationsMax = nodeArguments[1] as number;\n } else {\n // An incorrect number of iteration counts was defined.\n throw new Error(\"invalid number of repeat node iteration count arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new REPEAT nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"RETRY\": {\n // Create a RETRY AST node.\n const node = ASTNodeFactories.RETRY();\n\n // Push the RETRY node into the current scope.\n currentScope.push(node);\n\n // The arguments of a retry node are optional. We may have:\n // - No node arguments, in which case the retry note will attempt indefinitely.\n // - One node argument which will be the explicit number of attempts to make.\n // - Two node arguments which define the min and max attempt bounds from which a random attempt count will be picked.\n if (tokens[0] === \"[\") {\n // An attempt count has been defined. Get the attempt count and potential maximum attempt count of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"retry node attempt counts must be integer values\"\n ).map((argument) => argument.value);\n\n // We should have got one or two attempt counts.\n if (nodeArguments.length === 1) {\n // A static attempt count was defined.\n node.attempts = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // A minimum and maximum attempt count was defined.\n node.attemptsMin = nodeArguments[0] as number;\n node.attemptsMax = nodeArguments[1] as number;\n } else {\n // An incorrect number of attempt counts was defined.\n throw new Error(\"invalid number of retry node attempt count arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new RETRY nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"ACTION\": {\n // Create a ACTION AST node.\n const node = ASTNodeFactories.ACTION();\n\n // Push the ACTION node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require an action name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected action name identifier argument\");\n }\n\n // The action name will be defined as a node argument.\n const actionArguments = getArguments(tokens, placeholders);\n\n // We should have at least one identifer argument for an action node, which is the action name.\n if (actionArguments.length && actionArguments[0].type === \"identifier\") {\n // The action name will be the first and only node argument.\n node.actionName = actionArguments.shift()!.value as string;\n } else {\n throw new Error(\"expected action name identifier argument\");\n }\n\n // Only the first argument should have been an identifier, all following arguments must be string, number, boolean or null.\n actionArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid action node argument value '\" +\n arg.value +\n \"', must be string, number, boolean or null\"\n );\n });\n\n // Any node arguments that follow the action name identifier will be treated as action function arguments.\n node.actionArguments = actionArguments;\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"}\": {\n // The '}' character closes the current scope.\n stack.pop();\n break;\n }\n\n default: {\n throw new Error(`unexpected token '${token}'`);\n }\n }\n }\n\n // A function to recursively validate each of the nodes in the AST.\n const validateASTNode = (node: Validatable, depth: number): void => {\n // Validate the node.\n node.validate(depth);\n\n // Validate each child of the node.\n (node.children || []).forEach((child) => validateASTNode(child, depth + 1));\n };\n\n // Start node validation from the definition root.\n validateASTNode(\n {\n children: stack[0] as RootAstNode[],\n validate(this: { children: RootAstNode[] }) {\n // We must have at least one node defined as the definition scope, which should be a root node.\n if (this.children.length === 0) {\n throw new Error(\"expected root node to have been defined\");\n }\n\n // Each node at the base of the definition scope MUST be a root node.\n for (const definitionLevelNode of this.children) {\n if (definitionLevelNode.type !== \"root\") {\n throw new Error(\"expected root node at base of definition\");\n }\n }\n\n // Exactly one root node must not have a name defined. This will be the main root, others will have to be referenced via branch nodes.\n if (this.children.filter((definitionLevelNode) => definitionLevelNode.name === null).length !== 1) {\n throw new Error(\"expected single unnamed root node at base of definition to act as main root\");\n }\n\n // No two named root nodes can have matching names.\n const rootNodeNames: string[] = [];\n for (const definitionLevelNode of this.children) {\n if (rootNodeNames.indexOf(definitionLevelNode.name!) !== -1) {\n throw new Error(`multiple root nodes found with duplicate name '${definitionLevelNode.name}'`);\n } else {\n rootNodeNames.push(definitionLevelNode.name!);\n }\n }\n }\n },\n 0\n );\n\n // Return the root AST nodes.\n return stack[0];\n}\n\n/**\n * Pop the next raw token off of the stack and throw an error if it wasn't the expected one.\n * @param tokens The array of remaining tokens.\n * @param expected An optional string or array or items, one of which must match the next popped token.\n * @returns The popped token.\n */\nfunction popAndCheck(tokens: string[], expected: string | string[]) {\n // Get and remove the next token.\n const popped = tokens.shift();\n\n // We were expecting another token.\n if (popped === undefined) {\n throw new Error(\"unexpected end of definition\");\n }\n\n // Do we have an expected token/tokens array?\n if (expected !== undefined) {\n // Check whether the popped token matches at least one of our expected items.\n var tokenMatchesExpectation = ([] as string[])\n .concat(expected)\n .some((item) => popped.toUpperCase() === item.toUpperCase());\n\n // Throw an error if the popped token didn't match any of our expected items.\n if (!tokenMatchesExpectation) {\n const expectationString = ([] as string[])\n .concat(expected)\n .map((item) => \"'\" + item + \"'\")\n .join(\" or \");\n\n throw new Error(`unexpected token found. Expected '${expectationString}' but got '${popped}'`);\n }\n }\n\n // Return the popped token.\n return popped;\n}\n\ntype Placeholders = { [key: string]: string };\n\n/**\n * Pull an argument definition list off of the token stack.\n * @param tokens The array of remaining tokens.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @param argumentValidator The argument validator function.\n * @param validationFailedMessage The exception message to throw if argument validation fails.\n * @returns The argument definition list.\n */\nfunction getArguments(\n tokens: string[],\n stringArgumentPlaceholders: Placeholders,\n argumentValidator?: (arg: AnyArgument) => boolean,\n validationFailedMessage?: string\n) {\n // Any lists of arguments will always be wrapped in '[]' for node arguments or '()' for attribute arguments.\n // We are looking for a '[' or '(' opener that wraps the argument tokens and the relevant closer.\n const closer = popAndCheck(tokens, [\"[\", \"(\"]) === \"[\" ? \"]\" : \")\";\n\n const argumentListTokens: string[] = [];\n const argumentList: AnyArgument[] = [];\n\n // Grab all tokens between the '[' and ']' or '(' and ')'.\n while (tokens.length && tokens[0] !== closer) {\n // The next token is part of our arguments list.\n argumentListTokens.push(tokens.shift()!);\n }\n\n // Validate the order of the argument tokens. Each token must either be a ',' or a single argument that satisfies the validator.\n argumentListTokens.forEach((token, index) => {\n // Get whether this token should be an actual argument.\n const shouldBeArgumentToken = !(index & 1);\n\n // If the current token should be an actual argument then validate it,otherwise it should be a ',' token.\n if (shouldBeArgumentToken) {\n // Get the argument definition.\n const argumentDefinition = getArgumentDefinition(token!, stringArgumentPlaceholders);\n\n // Try to validate the argument.\n if (argumentValidator && !argumentValidator(argumentDefinition)) {\n throw new Error(validationFailedMessage);\n }\n\n // This is a valid argument!\n argumentList.push(argumentDefinition);\n } else {\n // The current token should be a ',' token.\n if (token !== \",\") {\n throw new Error(`invalid argument list, expected ',' or ']' but got '${token}'`);\n }\n }\n });\n\n // The arguments list should terminate with a ']' or ')' token, depending on the opener.\n popAndCheck(tokens, closer);\n\n // Return the argument list.\n return argumentList;\n}\n\n/**\n * Gets an argument value definition.\n * @param token The argument token.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @returns An argument value definition.\n */\nfunction getArgumentDefinition(token: string, stringArgumentPlaceholders: Placeholders): AnyArgument {\n // Check whether the token represents a null value.\n if (token === \"null\") {\n return {\n value: null,\n type: \"null\"\n } as NullArgument;\n }\n\n // Check whether the token represents a boolean value.\n if (token === \"true\" || token === \"false\") {\n return {\n value: token === \"true\",\n type: \"boolean\"\n } as BooleanArgument;\n }\n\n // Check whether the token represents a number value.\n // TODO: Relies on broken isNaN - see MDN.\n // if (!Number.isNaN(token)) {\n if (!isNaN(token as any)) {\n return {\n value: parseFloat(token),\n isInteger: parseFloat(token) === parseInt(token, 10),\n type: \"number\"\n } as NumberArgument;\n }\n\n // Check whether the token is a placeholder (e.g. @@0@@) representing a string literal.\n if (token.match(/^@@\\d+@@$/g)) {\n return {\n value: stringArgumentPlaceholders[token].replace('\\\\\"', '\"'),\n type: \"string\"\n } as StringPlaceholderArgument;\n }\n\n // The only remaining option is that the argument value is an identifier.\n return {\n value: token,\n type: \"identifier\"\n } as IdentifierArgument;\n}\n\n/**\n * Pull any attributes off of the token stack.\n * @param tokens The array of remaining tokens.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @returns An array of attributes defined by any directly following tokens.\n */\nfunction getAttributes(tokens: string[], stringArgumentPlaceholders: Placeholders) {\n // Create an array to hold any attributes found.\n const attributes: Attribute[] = [];\n\n // Keep track of names of attribute that we have found on the token stack, as we cannot have duplicates.\n const attributesFound: string[] = [];\n\n // Try to get the attribute factory for the next token.\n let attributeFactory = AttributeFactories[(tokens[0] || \"\").toUpperCase()];\n\n // Pull attribute tokens off of the tokens stack until we have no more.\n while (attributeFactory) {\n // Check to make sure that we have not already created a attribute of this type for this node.\n if (attributesFound.indexOf(tokens[0].toUpperCase()) !== -1) {\n throw new Error(`duplicate attribute '${tokens[0].toUpperCase()}' found for node`);\n }\n\n // Add the current attribute type to our array of found attributes.\n attributesFound.push(tokens.shift()!.toUpperCase());\n\n // Grab any attribute arguments.\n const attributeArguments = getArguments(tokens, stringArgumentPlaceholders);\n\n // The first attribute argument has to be an identifer, this will reference an agent function.\n if (attributeArguments.length === 0 || attributeArguments[0].type !== \"identifier\") {\n throw new Error(\"expected agent function name identifier argument for attribute\");\n }\n\n // Grab the first attribute which is an identifier that will reference an agent function.\n const attributeFunctionName = attributeArguments.shift()! as IdentifierArgument;\n\n // Any remaining attribute arguments must have a type of string, number, boolean or null.\n attributeArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid attribute argument value '\" + arg.value + \"', must be string, number, boolean or null\"\n );\n });\n\n // Create the attribute and add it to the array of attributes found.\n attributes.push(attributeFactory(attributeFunctionName.value, attributeArguments));\n\n // Try to get the next attribute name token, as there could be multiple.\n attributeFactory = AttributeFactories[(tokens[0] || \"\").toUpperCase()];\n }\n\n return attributes;\n}\n\n/**\n * Swaps out any node/attribute argument string literals with placeholders.\n * @param definition The definition.\n * @returns An object containing a mapping of placeholders to original string values as well as the processed definition string.\n */\nfunction substituteStringLiterals(definition: string): {\n placeholders: { [key: string]: string };\n processedDefinition: string;\n} {\n // Create an object to hold the mapping of placeholders to original string values.\n const placeholders: Placeholders = {};\n\n // Replace any string literals wrapped with double quotes in our definition with placeholders to be processed later.\n const processedDefinition = definition.replace(/\\\"(\\\\.|[^\"\\\\])*\\\"/g, (match) => {\n var strippedMatch = match.substring(1, match.length - 1);\n var placeholder = Object.keys(placeholders).find((key) => placeholders[key] === strippedMatch);\n\n // If we have no existing string literal match then create a new placeholder.\n if (!placeholder) {\n placeholder = `@@${Object.keys(placeholders).length}@@`;\n placeholders[placeholder] = strippedMatch;\n }\n\n return placeholder;\n });\n\n return { placeholders, processedDefinition };\n}\n\n/**\n * Parse the tree definition into an array of raw tokens.\n * @param definition The definition.\n * @returns An array of tokens parsed from the definition.\n */\nfunction parseTokensFromDefinition(definition: string): string[] {\n // Add some space around various important characters so that they can be plucked out easier as individual tokens.\n definition = definition.replace(/\\(/g, \" ( \");\n definition = definition.replace(/\\)/g, \" ) \");\n definition = definition.replace(/\\{/g, \" { \");\n definition = definition.replace(/\\}/g, \" } \");\n definition = definition.replace(/\\]/g, \" ] \");\n definition = definition.replace(/\\[/g, \" [ \");\n definition = definition.replace(/\\,/g, \" , \");\n\n // Split the definition into raw token form and return it.\n return definition.replace(/\\s+/g, \" \").trim().split(\" \");\n}\n", "import GuardPath, { GuardPathPart } from \"./attributes/guards/GuardPath\";\nimport buildRootASTNodes, { AnyArgument, RootAstNode } from \"./RootAstNodesBuilder\";\nimport State, { AnyState } from \"./State\";\nimport Lookup from \"./Lookup\";\nimport Node from \"./nodes/Node\";\nimport Root from \"./nodes/decorator/Root\";\nimport Composite from \"./nodes/composite/Composite\";\nimport Decorator from \"./nodes/decorator/Decorator\";\nimport { Agent, GlobalFunction } from \"./Agent\";\nimport { CallbackAttributeDetails } from \"./attributes/callbacks/Callback\";\nimport { GuardAttributeDetails } from \"./attributes/guards/Guard\";\nimport { BehaviourTreeOptions } from \"./BehaviourTreeOptions\";\nimport { parseToJSON } from \"./dsl/DSLDefinitionParser\";\n\n// Purely for outside inspection of the tree.\nexport type FlattenedTreeNode = {\n id: string;\n type: string;\n caption: string;\n state: AnyState;\n guards: GuardAttributeDetails[];\n callbacks: CallbackAttributeDetails[];\n args: AnyArgument[];\n parentId: string | null;\n};\n\n/**\n * A representation of a behaviour tree.\n */\nexport class BehaviourTree {\n /**\n * The main root tree node.\n */\n public readonly rootNode: Root;\n\n /**\n * Creates a new instance of the BehaviourTree class.\n * @param definition The behaviour tree definition.\n * @param agent The agent instance that this behaviour tree is modelling behaviour for.\n * @param options The behaviour tree options object.\n */\n constructor(definition: string, private agent: Agent, private options: BehaviourTreeOptions = {}) {\n // The tree definition must be defined and a valid string.\n if (typeof definition !== \"string\") {\n throw new Error(\"the tree definition must be a string\");\n }\n\n // The agent must be defined and not null.\n if (typeof agent !== \"object\" || agent === null) {\n throw new Error(\"the agent must be defined and not null\");\n }\n\n // Parse the behaviour tree definition, create the populated tree of behaviour tree nodes, and get the root.\n this.rootNode = BehaviourTree._createRootNode(definition);\n }\n\n /**\n * Gets whether the tree is in the RUNNING state.\n * @returns true if the tree is in the RUNNING state, otherwise false.\n */\n isRunning() {\n return this.rootNode.getState() === State.RUNNING;\n }\n\n /**\n * Gets the current tree state of SUCCEEDED, FAILED, READY or RUNNING.\n * @returns The current tree state.\n */\n getState() {\n return this.rootNode.getState();\n }\n\n /**\n * Step the tree.\n * Carries out a node update that traverses the tree from the root node outwards to any child nodes, skipping those that are already in a resolved state of SUCCEEDED or FAILED.\n * After being updated, leaf nodes will have a state of SUCCEEDED, FAILED or RUNNING. Leaf nodes that are left in the RUNNING state as part of a tree step will be revisited each\n * subsequent step until they move into a resolved state of either SUCCEEDED or FAILED, after which execution will move through the tree to the next node with a state of READY.\n *\n * Calling this method when the tree is already in a resolved state of SUCCEEDED or FAILED will cause it to be reset before tree traversal begins.\n */\n step() {\n // If the root node has already been stepped to completion then we need to reset it.\n if (this.rootNode.getState() === State.SUCCEEDED || this.rootNode.getState() === State.FAILED) {\n this.rootNode.reset();\n }\n\n try {\n this.rootNode.update(this.agent, this.options);\n } catch (exception) {\n throw new Error(`error stepping tree: ${(exception as Error).message}`);\n }\n }\n\n /**\n * Resets the tree from the root node outwards to each nested node, giving each a state of READY.\n */\n reset() {\n this.rootNode.reset();\n }\n\n /**\n * Gets the flattened details of every node in the tree.\n * @returns The flattened details of every node in the tree.\n */\n getFlattenedNodeDetails(): FlattenedTreeNode[] {\n // Create an empty flattened array of tree nodes.\n const flattenedTreeNodes: FlattenedTreeNode[] = [];\n\n /**\n * Helper function to process a node instance and push details into the flattened tree nodes array.\n * @param node The current node.\n * @param parentUid The UID of the node parent, or null if the node is the main root node.\n */\n const processNode = (node: Node, parentUid: string | null) => {\n // Get the guard and callback attribute details for this node.\n const guards = node\n .getAttributes()\n .filter((attribute) => attribute.isGuard())\n .map((attribute) => attribute.getDetails()) as GuardAttributeDetails[];\n const callbacks = node\n .getAttributes()\n .filter((attribute) => !attribute.isGuard())\n .map((attribute) => attribute.getDetails()) as CallbackAttributeDetails[];\n\n // Push the current node into the flattened nodes array.\n flattenedTreeNodes.push({\n id: node.getUid(),\n type: node.getType(),\n caption: node.getName(),\n state: node.getState(),\n guards,\n callbacks,\n args: node.getArguments(),\n parentId: parentUid\n });\n\n // Process each of the nodes children if it is not a leaf node.\n if (!node.isLeafNode()) {\n (node as Composite | Decorator)\n .getChildren()\n .forEach((child) => processNode(child, (node as Composite | Decorator).getUid()));\n }\n };\n\n // Convert the nested node structure into a flattened array of node details.\n processNode(this.rootNode, null);\n\n return flattenedTreeNodes;\n }\n\n /**\n * Registers the action/condition/guard/callback function or subtree with the given name.\n * @param name The name of the function or subtree to register.\n * @param value The function or subtree definition to register.\n */\n static register(name: string, value: GlobalFunction | string) {\n if (typeof value === \"function\") {\n // We are going to register a action/condition/guard/callback function.\n Lookup.setFunc(name, value);\n } else if (typeof value === \"string\") {\n // We are going to register a subtree.\n let rootASTNodes: RootAstNode[];\n\n try {\n // Try to create the behaviour tree AST based on the definition provided, this could fail if the definition is invalid.\n rootASTNodes = buildRootASTNodes(value);\n } catch (exception) {\n // There was an issue in trying to parse and build the tree definition.\n throw new Error(`error registering definition: ${(exception as Error).message}`);\n }\n\n // This function should only ever be called with a definition containing a single unnamed root node.\n if (rootASTNodes.length != 1 || rootASTNodes[0].name !== null) {\n throw new Error(\"error registering definition: expected a single unnamed root node\");\n }\n\n Lookup.setSubtree(name, rootASTNodes[0]);\n } else {\n throw new Error(\"unexpected value, expected string definition or function\");\n }\n }\n\n /**\n * Unregisters the registered action/condition/guard/callback function or subtree with the given name.\n * @param name The name of the registered action/condition/guard/callback function or subtree to unregister.\n */\n static unregister(name: string): void {\n Lookup.remove(name);\n }\n\n /**\n * Unregister all registered action/condition/guard/callback functions and subtrees.\n */\n static unregisterAll(): void {\n Lookup.empty();\n }\n\n /**\n * Parses a behaviour tree definition and creates a tree of behaviour tree nodes.\n * @param {string} definition The behaviour tree definition.\n * @returns The root behaviour tree node.\n */\n private static _createRootNode(definition: string): Root {\n // TODO Remove!\n try {\n //parseToJSON(definition);\n } catch (exception) {\n console.log(exception);\n }\n\n try {\n // Try to create the behaviour tree AST based on the definition provided, this could fail if the definition is invalid.\n const rootASTNodes = buildRootASTNodes(definition);\n\n // Create a symbol to use as the main root key in our root node mapping.\n const mainRootNodeKey = Symbol(\"__root__\");\n\n // Create a mapping of root node names to root AST tokens. The main root node will have a key of Symbol(\"__root__\").\n const rootNodeMap: { [key: string | symbol]: RootAstNode } = {};\n for (const rootASTNode of rootASTNodes) {\n rootNodeMap[rootASTNode.name === null ? mainRootNodeKey : rootASTNode.name!] = rootASTNode;\n }\n\n // Convert the AST to our actual tree and get the root node.\n const rootNode: Root = rootNodeMap[mainRootNodeKey].createNodeInstance(\n // Create a provider for named root nodes that are part of our definition or have been registered. Prioritising the former.\n (name: string): RootAstNode => (rootNodeMap[name] ? rootNodeMap[name] : Lookup.getSubtree(name)),\n []\n );\n\n // Set a guard path on every leaf of the tree to evaluate as part of its update.\n BehaviourTree._applyLeafNodeGuardPaths(rootNode);\n\n // Return the root node.\n return rootNode;\n } catch (exception) {\n // There was an issue in trying to parse and build the tree definition.\n throw new Error(`error parsing tree: ${(exception as Error).message}`);\n }\n }\n\n /**\n * Applies a guard path to every leaf of the tree to evaluate as part of each update.\n * @param rootNode The main root tree node.\n */\n private static _applyLeafNodeGuardPaths(rootNode: Root) {\n const nodePaths: Node[][] = [];\n\n const findLeafNodes = (path: Node[], node: Node) => {\n // Add the current node to the path.\n path = path.concat(node);\n\n // Check whether the current node is a leaf node.\n if (node.isLeafNode()) {\n nodePaths.push(path);\n } else {\n (node as Composite | Decorator).getChildren().forEach((child) => findLeafNodes(path, child));\n }\n };\n\n // Find all leaf node paths, starting from the root.\n findLeafNodes([], rootNode);\n\n nodePaths.forEach((path) => {\n // Each node in the current path will have to be assigned a guard path, working from the root outwards.\n for (let depth = 0; depth < path.length; depth++) {\n // Get the node in the path at the current depth.\n const currentNode = path[depth];\n\n // The node may already have been assigned a guard path, if so just skip it.\n if (currentNode.hasGuardPath()) {\n continue;\n }\n\n // Create the guard path for the current node.\n const guardPath = new GuardPath(\n path\n .slice(0, depth + 1)\n .map((node) => ({ node, guards: node.getGuardAttributes() }))\n .filter((details) => details.guards.length > 0)\n );\n\n // Assign the guard path to the current node.\n currentNode.setGuardPath(guardPath);\n }\n });\n }\n}\n"], + "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AACA,aAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAC5D,cAAQ,cAAc;AAItB,UAAI,cAA6B,WAAY;AAMzC,iBAASA,aAAY,aAAa,SAAS;AACvC,cAAI,YAAY,QAAQ;AAAE,sBAAU;AAAA,UAAG;AACvC,eAAK,eAAe;AACpB,eAAK,WAAW;AAAA,QACpB;AACA,eAAO,eAAeA,aAAY,WAAW,eAAe;AAAA,UAExD,KAAK,WAAY;AACb,mBAAO,KAAK;AAAA,UAChB;AAAA,UACA,YAAY;AAAA,UACZ,cAAc;AAAA,QAClB,CAAC;AACD,eAAO,eAAeA,aAAY,WAAW,WAAW;AAAA,UAEpD,KAAK,WAAY;AACb,mBAAO,KAAK;AAAA,UAChB;AAAA,UACA,KAAK,SAAU,OAAO;AAClB,iBAAK,WAAW;AAAA,UACpB;AAAA,UACA,YAAY;AAAA,UACZ,cAAc;AAAA,QAClB,CAAC;AACD,eAAOA;AAAA,MACX,EAAE;AACF,cAAQ,cAAc;AAAA;AAAA;;;ACtCtB;AAAA;AAAA;AACA,aAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAC5D,cAAQ,kBAAkB,QAAQ,oBAAoB;AAMtD,eAAS,kBAAkB,OAAO;AAC9B,eAAO,UAAU,QAAQ,UAAU;AAAA,MACvC;AACA,cAAQ,oBAAoB;AAM5B,eAAS,gBAAgB,OAAO;AAC5B,eAAO,OAAO,UAAU,YAAY,SAAS,KAAK,KAAK,MAAM,KAAK,MAAM;AAAA,MAC5E;AACA,cAAQ,kBAAkB;AAAA;AAAA;;;ACpB1B;AAAA;AAAA;AACA,aAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAC5D,cAAQ,QAAQ;AAChB,UAAI,gBAAgB;AACpB,UAAI,cAAc;AAIlB,UAAIC,SAAuB,WAAY;AAKnC,iBAASA,OAAM,cAAc;AAEzB,eAAK,gBAAgB,CAAC;AACtB,eAAK,gBAAgB;AAAA,QACzB;AAOA,QAAAA,OAAM,UAAU,MAAM,SAAU,aAAa,SAAS;AAClD,cAAI,YAAY,QAAQ;AAAE,sBAAU;AAAA,UAAG;AAEvC,cAAI,EAAE,GAAG,YAAY,iBAAiB,OAAO,GAAG;AAC5C,kBAAM,IAAI,MAAM,wCAAwC;AAAA,UAC5D;AAEA,cAAI,sBAAsB,KAAK,cAAc,KAAK,SAAU,MAAM;AAAE,mBAAO,KAAK,gBAAgB;AAAA,UAAa,CAAC;AAC9G,cAAI,qBAAqB;AAErB,gCAAoB,WAAW;AAAA,UACnC,OACK;AAED,iBAAK,cAAc,KAAK,IAAI,cAAc,YAAY,aAAa,OAAO,CAAC;AAAA,UAC/E;AACA,iBAAO;AAAA,QACX;AAOA,QAAAA,OAAM,UAAU,SAAS,SAAU,aAAa,SAAS;AAErD,cAAI,sBAAsB,KAAK,cAAc,KAAK,SAAU,MAAM;AAAE,mBAAO,KAAK,gBAAgB;AAAA,UAAa,CAAC;AAE9G,cAAI,CAAC,qBAAqB;AACtB,mBAAO;AAAA,UACX;AAEA,cAAI,YAAY,QAAW;AAEvB,gBAAI,EAAE,GAAG,YAAY,iBAAiB,OAAO,GAAG;AAC5C,oBAAM,IAAI,MAAM,wCAAwC;AAAA,YAC5D;AACA,gCAAoB,WAAW;AAE/B,gBAAI,oBAAoB,UAAU,GAAG;AACjC,mBAAK,gBAAgB,KAAK,cAAc,OAAO,SAAU,MAAM;AAAE,uBAAO,SAAS;AAAA,cAAqB,CAAC;AAAA,YAC3G;AAAA,UACJ,OACK;AAED,iBAAK,gBAAgB,KAAK,cAAc,OAAO,SAAU,MAAM;AAAE,qBAAO,SAAS;AAAA,YAAqB,CAAC;AAAA,UAC3G;AACA,iBAAO;AAAA,QACX;AAMA,QAAAA,OAAM,UAAU,OAAO,SAAU,SAAS;AACtC,cAAI,YAAY,QAAQ;AAAE,sBAAU,CAAC;AAAA,UAAG;AAExC,cAAI,KAAK,cAAc,WAAW,GAAG;AACjC,mBAAO;AAAA,UACX;AACA,cAAI,cAAc,GAAG,YAAY,mBAAmB,QAAQ,UAAU,IAAI,OAAO,QAAQ;AACzF,cAAI,WAAW,CAAC;AAChB,eAAK,cAAc,QAAQ,SAAU,IAAI;AACrC,gBAAI,cAAc,GAAG,aAAa,UAAU,GAAG;AAC/C,qBAAS,cAAc,GAAG,cAAc,SAAS,eAAe;AAC5D,uBAAS,KAAK,WAAW;AAAA,YAC7B;AAAA,UACJ,CAAC;AACD,cAAI;AAGJ,cAAI,KAAK,eAAe;AAEpB,qBAAS,KAAK,cAAc;AAE5B,gBAAI,OAAO,WAAW,YAAY,SAAS,KAAK,UAAU,GAAG;AACzD,oBAAM,IAAI,MAAM,oFAAoF;AAAA,YACxG;AAAA,UACJ,OACK;AAED,qBAAS,KAAK,OAAO;AAAA,UACzB;AAEA,cAAI,SAAS,SAAS,KAAK,MAAM,SAAS,SAAS,MAAM;AAEzD,cAAI,CAAC,YAAY;AACb,iBAAK,OAAO,QAAQ,CAAC;AAAA,UACzB;AAEA,iBAAO;AAAA,QACX;AAOA,QAAAA,OAAM,UAAU,eAAe,SAAU,SAAS,SAAS;AACvD,cAAI,YAAY,QAAQ;AAAE,sBAAU,CAAC;AAAA,UAAG;AACxC,cAAI,iBAAiB,GAAG,YAAY,mBAAmB,QAAQ,MAAM,IAAI,QAAQ,QAAQ;AAEzF,cAAI,YAAY,GAAG;AACf,mBAAO,CAAC;AAAA,UACZ;AAEA,cAAI,EAAE,GAAG,YAAY,iBAAiB,OAAO,GAAG;AAC5C,kBAAM,IAAI,MAAM,wCAAwC;AAAA,UAC5D;AACA,cAAI,SAAS,CAAC;AAGd,iBAAO,OAAO,SAAS,WAAW,KAAK,cAAc,SAAS,GAAG;AAC7D,mBAAO,KAAK,KAAK,KAAK,OAAO,CAAC;AAAA,UAClC;AAEA,cAAI,eAAe;AAEf,gBAAI,SAAS,CAAC;AAEd,qBAAS,KAAK,GAAG,WAAW,QAAQ,KAAK,SAAS,QAAQ,MAAM;AAC5D,kBAAI,cAAc,SAAS;AAC3B,kBAAI,OAAO,QAAQ,WAAW,MAAM,IAAI;AACpC,uBAAO,KAAK,WAAW;AAAA,cAC3B;AAAA,YACJ;AACA,qBAAS;AAAA,UACb;AACA,iBAAO;AAAA,QACX;AACA,eAAOA;AAAA,MACX,EAAE;AACF,cAAQ,QAAQA;AAAA;AAAA;;;AC5JhB;AAAA;AAAA;AACA,aAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAC5D,cAAQ,cAAc;AACtB,UAAI,UAAU;AAMd,eAASC,aAAY,uBAAuB;AAExC,YAAI,CAAC,uBAAuB;AACxB,iBAAO,IAAI,QAAQ,MAAM;AAAA,QAC7B;AAEA,YAAI,MAAM,QAAQ,qBAAqB,GAAG;AAEtC,cAAI,eAAe;AACnB,cAAI,UAAU,IAAI,QAAQ,MAAM;AAEhC,uBAAa,QAAQ,SAAU,IAAI;AAC/B,gBAAI,cAAc,GAAG,IAAI,SAAS,GAAG;AACrC,mBAAO,QAAQ,IAAI,aAAa,MAAM;AAAA,UAC1C,CAAC;AAED,iBAAO;AAAA,QACX,OACK;AAED,cAAI,SAAS,sBAAsB,QAAQ,eAAe,sBAAsB;AAEhF,cAAI,UAAU,IAAI,QAAQ,MAAM,MAAM;AAEtC,cAAI,cAAc;AACd,yBAAa,QAAQ,SAAU,IAAI;AAC/B,kBAAI,cAAc,GAAG,IAAI,SAAS,GAAG;AACrC,qBAAO,QAAQ,IAAI,aAAa,MAAM;AAAA,YAC1C,CAAC;AAAA,UACL;AAEA,iBAAO;AAAA,QACX;AAAA,MACJ;AACA,cAAQ,cAAcA;AAAA;AAAA;;;AC3CtB;AAAA;AAAA;AACA,aAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;AAC5D,UAAI,gBAAgB;AACpB,cAAQ,UAAU,cAAc;AAAA;AAAA;;;ACHhC;AAAA;AAAA;AAAA;AAAA;;;ACKA,MAAqB,4BAArB,cAAuD,MAAM;AAAA,IAIzD,YAAoB,QAAc;AAC9B,YAAM,mCAAmC;AADzB;AAAA,IAEpB;AAAA,IAOA,eAAe,CAAC,SAAe,SAAS,KAAK;AAAA,EACjD;;;ACNA,MAAqB,YAArB,MAA+B;AAAA,IAI3B,YAAoB,OAAwB;AAAxB;AAAA,IAAyB;AAAA,IAO7C,WAAW,CAAC,UAAiB;AAEzB,iBAAW,WAAW,KAAK,OAAO;AAE9B,mBAAW,SAAS,QAAQ,QAAQ;AAEhC,cAAI,CAAC,MAAM,YAAY,KAAK,GAAG;AAC3B,kBAAM,IAAI,0BAA0B,QAAQ,IAAI;AAAA,UACpD;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;;;ACjCO,MAAK,QAAL,kBAAKC,WAAL;AACH,IAAAA,OAAA,WAAQ;AACR,IAAAA,OAAA,aAAU;AACV,IAAAA,OAAA,eAAY;AACZ,IAAAA,OAAA,YAAS;AAJD,WAAAA;AAAA,KAAA;;;ACaZ,MAA8B,OAA9B,MAAmC;AAAA,IAmB/B,YAAoB,MAAsB,YAAiC,MAAqB;AAA5E;AAAsB;AAAiC;AAAA,IAAsB;AAAA,IAfhF,MAAc,cAAc;AAAA,IAIrC;AAAA,IAIA;AAAA,IA6BR,WAAW,MAAgB,KAAK;AAAA,IAChC,WAAW,CAAC,UAA0B;AAClC,WAAK,QAAQ;AAAA,IACjB;AAAA,IAKA,SAAS,MAAM,KAAK;AAAA,IAKpB,UAAU,MAAM,KAAK;AAAA,IAKrB,gBAAgB,MAAM,KAAK;AAAA,IAK3B,eAAe,MAAM,KAAK;AAAA,IAQ1B,aAAa,MAAyB;AAClC,aACI,KAAK,cAAc,EAAE,OAAO,CAAC,cAAc,UAAU,QAAQ,EAAE,YAAY,MAAM,KAAK,YAAY,CAAC,EAAE,MACrG;AAAA,IAER;AAAA,IAKA,qBAAqB,MAAe,KAAK,cAAc,EAAE,OAAO,CAAC,cAAc,UAAU,QAAQ,CAAC;AAAA,IAKlG,eAAe,CAAC,UAAsB,KAAK,YAAY;AAAA,IAKvD,eAAe,MAAM,CAAC,CAAC,KAAK;AAAA,IAMrB,GAAG,OAA0B;AAChC,aAAO,KAAK,UAAU;AAAA,IAC1B;AAAA,IAKO,QAAc;AACjB,WAAK,wCAAoB;AAAA,IAC7B;AAAA,IAMO,MAAM,OAAoB;AAE7B,UAAI,CAAC,KAAK,sCAAgB,GAAG;AACzB;AAAA,MACJ;AAGA,WAAK,MAAM;AAEX,WAAK,aAAa,MAAM,GAAG,kBAAkB,OAAO,OAAO,IAAI;AAAA,IACnE;AAAA,IAQO,OAAO,OAAc,SAAqC;AAE7D,UAAI,KAAK,0CAAkB,KAAK,KAAK,oCAAe,GAAG;AACnD;AAAA,MACJ;AAEA,UAAI;AAEA,aAAK,UAAW,SAAS,KAAK;AAG9B,YAAI,KAAK,kCAAc,GAAG;AACtB,eAAK,aAAa,OAAO,GAAG,kBAAkB,KAAK;AAAA,QACvD;AAEA,aAAK,aAAa,MAAM,GAAG,kBAAkB,KAAK;AAGlD,aAAK,SAAS,OAAO,OAAO;AAG5B,YAAI,KAAK,0CAAkB,KAAK,KAAK,oCAAe,GAAG;AACnD,eAAK,aAAa,MAAM,GAAG,kBAAkB,OAAO,KAAK,0CAAkB,GAAG,KAAK;AAAA,QACvF;AAAA,MACJ,SAAS,OAAP;AAEE,YAAI,iBAAiB,6BAA6B,MAAM,aAAa,IAAI,GAAG;AAExE,eAAK,MAAM,KAAK;AAGhB,eAAK,0CAAqB;AAAA,QAC9B,OAAO;AACH,gBAAM;AAAA,QACV;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AAMA,WAAS,gBAAwB;AAC7B,QAAI,KAAK,WAAY;AACjB,eAAU,IAAI,KAAK,OAAO,KAAK,QAAW,GAAG,SAAS,EAAE,EAAE,UAAU,CAAC;AAAA,IACzE;AACA,WAAO,GAAG,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,MAAM,GAAG,IAAI,MAAM,GAAG,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG;AAAA,EACvF;;;AC9LA,MAA8B,OAA9B,cAA2C,KAAK;AAAA,IAI5C,aAAa,MAAM;AAAA,EACvB;;;ACGA,MAAqB,SAArB,MAA4B;AAAA,IAexB,OAAc,QAAQ,MAA8B;AAChD,aAAO,KAAK,cAAc;AAAA,IAC9B;AAAA,IAOA,OAAc,QAAQ,MAAc,MAA4B;AAC5D,WAAK,cAAc,QAAQ;AAAA,IAC/B;AAAA,IAUA,OAAO,eAAe,OAAc,MAAsC;AAEtE,YAAM,eAAe,MAAM;AAC3B,UAAI,gBAAgB,OAAO,iBAAiB,YAAY;AACpD,eAAO,CAAC,SACJ,aAAa;AAAA,UACT;AAAA,UACA,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK;AAAA,QAC/B;AAAA,MACR;AAGA,UAAI,KAAK,cAAc,SAAS,OAAO,KAAK,cAAc,UAAU,YAAY;AAC5E,eAAO,CAAC,SAA4B,KAAK,cAAc,MAAM,OAAO,GAAG,KAAK,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;AAAA,MACvG;AAGA,aAAO;AAAA,IACX;AAAA,IAOA,OAAO,WAAW,MAA2B;AACzC,aAAO,KAAK,aAAa;AAAA,IAC7B;AAAA,IAOA,OAAO,WAAW,MAAc,SAAsB;AAClD,WAAK,aAAa,QAAQ;AAAA,IAC9B;AAAA,IAMA,OAAO,OAAO,MAAc;AACxB,aAAO,KAAK,cAAc;AAC1B,aAAO,KAAK,aAAa;AAAA,IAC7B;AAAA,IAKA,OAAO,QAAQ;AACX,WAAK,gBAAgB,CAAC;AACtB,WAAK,eAAe,CAAC;AAAA,IACzB;AAAA,EACJ;AAtFI,gBAJiB,QAIF,iBAAmD,CAAC;AAInE,gBARiB,QAQF,gBAA+C,CAAC;;;ACTnE,MAAqB,SAArB,cAAoC,KAAK;AAAA,IAMrC,YAAY,YAAiC,YAA4B,iBAAgC;AACrG,YAAM,UAAU,YAAY,eAAe;AADF;AAA4B;AAAA,IAEzE;AAAA,IAKQ,uBAAuB;AAAA,IAKvB,2BAAiD;AAAA,IAO/C,SAAS,OAAc,SAAqC;AAGlE,UAAI,KAAK,sBAAsB;AAE3B,YAAI,KAAK,0BAA0B;AAE/B,eAAK,SAAS,KAAK,wBAAwB;AAAA,QAC/C;AAEA;AAAA,MACJ;AAGA,YAAM,oBAAoB,OAAO,eAAe,OAAO,KAAK,UAAU;AAGtE,UAAI,sBAAsB,MAAM;AAC5B,cAAM,IAAI;AAAA,UACN,4CAA4C,KAAK;AAAA,QACrD;AAAA,MACJ;AAMA,YAAM,eAAe,kBAAkB,KAAK,eAAe;AAE3D,UAAI,wBAAwB,SAAS;AACjC,qBAAa;AAAA,UACT,CAAC,WAAW;AAER,gBAAI,CAAC,KAAK,sBAAsB;AAC5B;AAAA,YACJ;AAGA,gBAAI,sDAA8B,8CAAyB;AACvD,oBAAM,IAAI;AAAA,gBACN;AAAA,cACJ;AAAA,YACJ;AAGA,iBAAK,2BAA2B;AAAA,UACpC;AAAA,UACA,CAAC,WAAW;AAER,gBAAI,CAAC,KAAK,sBAAsB;AAC5B;AAAA,YACJ;AAGA,kBAAM,IAAI,MAAM,MAAM;AAAA,UAC1B;AAAA,QACJ;AAGA,aAAK,4CAAsB;AAG3B,aAAK,uBAAuB;AAAA,MAChC,OAAO;AAEH,aAAK,qBAAqB,YAAY;AAGtC,aAAK,SAAS,mDAA6B;AAAA,MAC/C;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM,KAAK;AAAA,IAKrB,QAAQ,MAAM;AAEV,WAAK,wCAAoB;AAGzB,WAAK,uBAAuB;AAC5B,WAAK,2BAA2B;AAAA,IACpC;AAAA,IAMQ,uBAAuB,CAAC,WAAoC;AAChE,cAAQ,QAAQ;AAAA,QACZ;AAAA,QACA;AAAA,QACA,KAAK;AACD;AAAA,QACJ;AACI,gBAAM,IAAI;AAAA,YACN,WAAW,KAAK;AAAA,UACpB;AAAA,MACR;AAAA,IACJ;AAAA,EACJ;;;AClIA,MAAqB,YAArB,cAAuC,KAAK;AAAA,IAMxC,YAAY,YAAiC,eAA+B,oBAAmC;AAC3G,YAAM,aAAa,YAAY,kBAAkB;AADR;AAA+B;AAAA,IAE5E;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,YAAM,uBAAuB,OAAO,eAAe,OAAO,KAAK,aAAa;AAG5E,UAAI,yBAAyB,MAAM;AAC/B,cAAM,IAAI;AAAA,UACN,kDAAkD,KAAK;AAAA,QAC3D;AAAA,MACJ;AAGA,WAAK,SAAS,CAAC,CAAC,qBAAqB,KAAK,kBAAkB,+EAAkC;AAAA,IAClG;AAAA,IAKA,UAAU,MAAM,KAAK;AAAA,EACzB;;;ACpCA,MAAqB,OAArB,cAAkC,KAAK;AAAA,IAOnC,YACI,YACQ,UACA,aACA,aACV;AACE,YAAM,QAAQ,YAAY,CAAC,CAAC;AAJpB;AACA;AACA;AAAA,IAGZ;AAAA,IAKQ,oBAA4B;AAAA,IAK5B,gBAA+B;AAAA,IAK/B,iBAAyB;AAAA,IAOvB,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,kCAAc,GAAG;AAEtB,aAAK,oBAAoB,IAAI,KAAK,EAAE,QAAQ;AAG5C,aAAK,iBAAiB;AAGtB,YAAI,KAAK,aAAa,MAAM;AACxB,eAAK,gBAAgB,KAAK;AAAA,QAC9B,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAG/D,gBAAM,SAAS,OAAO,QAAQ,WAAW,aAAa,QAAQ,SAAS,KAAK;AAG5E,eAAK,gBAAgB,KAAK;AAAA,YACtB,OAAO,KAAK,KAAK,cAAc,KAAK,cAAc,KAAK,KAAK;AAAA,UAChE;AAAA,QACJ,OAAO;AACH,eAAK,gBAAgB;AAAA,QACzB;AAGA,aAAK,4CAAsB;AAAA,MAC/B;AAGA,UAAI,KAAK,kBAAkB,MAAM;AAC7B;AAAA,MACJ;AAGA,UAAI,OAAO,QAAQ,iBAAiB,YAAY;AAE5C,cAAM,YAAY,QAAQ,aAAa;AAGvC,YAAI,OAAO,cAAc,YAAY,MAAM,SAAS,GAAG;AACnD,gBAAM,IAAI,MAAM,oDAAoD;AAAA,QACxE;AAGA,aAAK,kBAAkB,YAAY;AAAA,MACvC,OAAO;AAEH,aAAK,iBAAiB,IAAI,KAAK,EAAE,QAAQ,IAAI,KAAK;AAAA,MACtD;AAGA,UAAI,KAAK,kBAAkB,KAAK,eAAe;AAE3C,aAAK,gDAAwB;AAAA,MACjC;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AACZ,UAAI,KAAK,aAAa,MAAM;AACxB,eAAO,QAAQ,KAAK;AAAA,MACxB,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAC/D,eAAO,QAAQ,KAAK,iBAAiB,KAAK;AAAA,MAC9C,OAAO;AACH,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,EACJ;;;AC5GA,MAA8B,YAA9B,cAAgD,KAAK;AAAA,IAMjD,YAAY,MAAc,YAAmC,OAAa;AACtE,YAAM,MAAM,YAAY,CAAC,CAAC;AAD+B;AAAA,IAE7D;AAAA,IAKA,aAAa,MAAM;AAAA,IAKnB,cAAc,MAAM,CAAC,KAAK,KAAK;AAAA,IAK/B,QAAQ,MAAM;AAEV,WAAK,wCAAoB;AAGzB,WAAK,MAAM,MAAM;AAAA,IACrB;AAAA,IAMA,QAAQ,CAAC,UAAiB;AAEtB,UAAI,CAAC,KAAK,sCAAgB,GAAG;AACzB;AAAA,MACJ;AAGA,WAAK,MAAM,MAAM,KAAK;AAGtB,WAAK,MAAM;AAEX,WAAK,aAAa,MAAM,GAAG,kBAAkB,OAAO,OAAO,IAAI;AAAA,IACnE;AAAA,EACJ;;;AC9CA,MAAqB,OAArB,cAAkC,UAAU;AAAA,IAKxC,YAAY,YAAyB,OAAa;AAC9C,YAAM,QAAQ,YAAY,KAAK;AAAA,IACnC;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,MAAM,SAAS,yCAAqB,KAAK,MAAM,SAAS,2CAAqB;AAElF,aAAK,MAAM,OAAO,OAAO,OAAO;AAAA,MACpC;AAGA,WAAK,SAAS,KAAK,MAAM,SAAS,CAAC;AAAA,IACvC;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;ACzBA,MAAqB,SAArB,cAAoC,UAAU;AAAA,IAQ1C,YACI,YACQ,YACA,eACA,eACR,OACF;AACE,YAAM,UAAU,YAAY,KAAK;AALzB;AACA;AACA;AAAA,IAIZ;AAAA,IAKQ,uBAAsC;AAAA,IAKtC,wBAAgC;AAAA,IAO9B,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,kCAAc,GAAG;AAEtB,aAAK,MAAM,MAAM;AAGjB,aAAK,wBAAwB;AAG7B,aAAK,wBAAwB,OAAO;AAAA,MACxC;AAIA,UAAI,KAAK,WAAW,GAAG;AAEnB,aAAK,4CAAsB;AAI3B,YAAI,KAAK,MAAM,SAAS,+CAAuB;AAC3C,eAAK,MAAM,MAAM;AAAA,QACrB;AAGA,aAAK,MAAM,OAAO,OAAO,OAAO;AAIhC,YAAI,KAAK,MAAM,SAAS,yCAAoB;AAExC,eAAK,0CAAqB;AAE1B;AAAA,QACJ,WAAW,KAAK,MAAM,SAAS,+CAAuB;AAElD,eAAK,yBAAyB;AAAA,QAClC;AAAA,MACJ,OAAO;AAEH,aAAK,gDAAwB;AAAA,MACjC;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AACZ,UAAI,KAAK,eAAe,MAAM;AAC1B,eAAO,UAAU,KAAK;AAAA,MAC1B,WAAW,KAAK,kBAAkB,QAAQ,KAAK,kBAAkB,MAAM;AACnE,eAAO,UAAU,KAAK,kBAAkB,KAAK;AAAA,MACjD,OAAO;AACH,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,IAKA,QAAQ,MAAM;AAEV,WAAK,wCAAoB;AAGzB,WAAK,wBAAwB;AAG7B,WAAK,MAAM,MAAM;AAAA,IACrB;AAAA,IAMQ,aAAa,MAAM;AACvB,UAAI,KAAK,yBAAyB,MAAM;AAEpC,eAAO,KAAK,wBAAwB,KAAK;AAAA,MAC7C;AAGA,aAAO;AAAA,IACX;AAAA,IAMQ,0BAA0B,CAAC,YAAkC;AAEjE,UAAI,KAAK,eAAe,MAAM;AAC1B,aAAK,uBAAuB,KAAK;AAAA,MACrC,WAAW,KAAK,kBAAkB,QAAQ,KAAK,kBAAkB,MAAM;AAGnE,cAAM,SAAS,OAAO,QAAQ,WAAW,aAAa,QAAQ,SAAS,KAAK;AAG5E,aAAK,uBAAuB,KAAK;AAAA,UAC7B,OAAO,KAAK,KAAK,gBAAgB,KAAK,gBAAgB,KAAK,KAAK;AAAA,QACpE;AAAA,MACJ,OAAO;AACH,aAAK,uBAAuB;AAAA,MAChC;AAAA,IACJ;AAAA,EACJ;;;AC5IA,MAAqB,QAArB,cAAmC,UAAU;AAAA,IAQzC,YACI,YACQ,UACA,aACA,aACR,OACF;AACE,YAAM,SAAS,YAAY,KAAK;AALxB;AACA;AACA;AAAA,IAIZ;AAAA,IAKQ,qBAAoC;AAAA,IAKpC,sBAA8B;AAAA,IAO5B,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,kCAAc,GAAG;AAEtB,aAAK,MAAM,MAAM;AAGjB,aAAK,sBAAsB;AAG3B,aAAK,sBAAsB,OAAO;AAAA,MACtC;AAIA,UAAI,KAAK,WAAW,GAAG;AAEnB,aAAK,4CAAsB;AAI3B,YAAI,KAAK,MAAM,SAAS,yCAAoB;AACxC,eAAK,MAAM,MAAM;AAAA,QACrB;AAGA,aAAK,MAAM,OAAO,OAAO,OAAO;AAIhC,YAAI,KAAK,MAAM,SAAS,+CAAuB;AAE3C,eAAK,gDAAwB;AAE7B;AAAA,QACJ,WAAW,KAAK,MAAM,SAAS,yCAAoB;AAE/C,eAAK,uBAAuB;AAAA,QAChC;AAAA,MACJ,OAAO;AAEH,aAAK,0CAAqB;AAAA,MAC9B;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AACZ,UAAI,KAAK,aAAa,MAAM;AACxB,eAAO,SAAS,KAAK;AAAA,MACzB,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAC/D,eAAO,SAAS,KAAK,gBAAgB,KAAK;AAAA,MAC9C,OAAO;AACH,eAAO;AAAA,MACX;AAAA,IACJ;AAAA,IAKA,QAAQ,MAAM;AAEV,WAAK,wCAAoB;AAGzB,WAAK,sBAAsB;AAG3B,WAAK,MAAM,MAAM;AAAA,IACrB;AAAA,IAMA,aAAa,MAAM;AACf,UAAI,KAAK,uBAAuB,MAAM;AAElC,eAAO,KAAK,sBAAsB,KAAK;AAAA,MAC3C;AAGA,aAAO;AAAA,IACX;AAAA,IAMA,wBAAwB,CAAC,YAAkC;AAEvD,UAAI,KAAK,aAAa,MAAM;AACxB,aAAK,qBAAqB,KAAK;AAAA,MACnC,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAG/D,cAAM,SAAS,OAAO,QAAQ,WAAW,aAAa,QAAQ,SAAS,KAAK;AAG5E,aAAK,qBAAqB,KAAK;AAAA,UAC3B,OAAO,KAAK,KAAK,cAAc,KAAK,cAAc,KAAK,KAAK;AAAA,QAChE;AAAA,MACJ,OAAO;AACH,aAAK,qBAAqB;AAAA,MAC9B;AAAA,IACJ;AAAA,EACJ;;;AChJA,MAAqB,OAArB,cAAkC,UAAU;AAAA,IAKxC,YAAY,YAAyB,OAAa;AAC9C,YAAM,QAAQ,YAAY,KAAK;AAAA,IACnC;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,MAAM,SAAS,yCAAqB,KAAK,MAAM,SAAS,2CAAqB;AAClF,aAAK,MAAM,OAAO,OAAO,OAAO;AAAA,MACpC;AAGA,cAAQ,KAAK,MAAM,SAAS,GAAG;AAAA,QAC3B;AACI,eAAK,4CAAsB;AAC3B;AAAA,QAEJ;AACI,eAAK,0CAAqB;AAC1B;AAAA,QAEJ;AACI,eAAK,gDAAwB;AAC7B;AAAA,QAEJ;AACI,eAAK,wCAAoB;AAAA,MACjC;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;AC3CA,MAAqB,UAArB,cAAqC,UAAU;AAAA,IAK3C,YAAY,YAAyB,OAAa;AAC9C,YAAM,WAAW,YAAY,KAAK;AAAA,IACtC;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,MAAM,SAAS,yCAAqB,KAAK,MAAM,SAAS,2CAAqB;AAClF,aAAK,MAAM,OAAO,OAAO,OAAO;AAAA,MACpC;AAGA,cAAQ,KAAK,MAAM,SAAS,GAAG;AAAA,QAC3B;AACI,eAAK,4CAAsB;AAC3B;AAAA,QAEJ;AAAA,QACA;AACI,eAAK,gDAAwB;AAC7B;AAAA,QAEJ;AACI,eAAK,wCAAoB;AAAA,MACjC;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;ACxCA,MAAqB,OAArB,cAAkC,UAAU;AAAA,IAKxC,YAAY,YAAyB,OAAa;AAC9C,YAAM,QAAQ,YAAY,KAAK;AAAA,IACnC;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,MAAM,SAAS,yCAAqB,KAAK,MAAM,SAAS,2CAAqB;AAClF,aAAK,MAAM,OAAO,OAAO,OAAO;AAAA,MACpC;AAGA,cAAQ,KAAK,MAAM,SAAS,GAAG;AAAA,QAC3B;AACI,eAAK,4CAAsB;AAC3B;AAAA,QAEJ;AAAA,QACA;AACI,eAAK,0CAAqB;AAC1B;AAAA,QAEJ;AACI,eAAK,wCAAoB;AAAA,MACjC;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;ACnDA,0BAAwB;;;ACQxB,MAA8B,YAA9B,cAAgD,KAAK;AAAA,IAMjD,YAAY,MAAc,YAAmC,UAAkB;AAC3E,YAAM,MAAM,YAAY,CAAC,CAAC;AAD+B;AAAA,IAE7D;AAAA,IAKA,aAAa,MAAM;AAAA,IAKnB,cAAc,MAAM,KAAK;AAAA,IAKzB,QAAQ,MAAM;AAEV,WAAK,wCAAoB;AAGzB,WAAK,YAAY,EAAE,QAAQ,CAAC,UAAU,MAAM,MAAM,CAAC;AAAA,IACvD;AAAA,IAMA,QAAQ,CAAC,UAAiB;AAEtB,UAAI,CAAC,KAAK,sCAAgB,GAAG;AACzB;AAAA,MACJ;AAGA,WAAK,YAAY,EAAE,QAAQ,CAAC,UAAU,MAAM,MAAM,KAAK,CAAC;AAGxD,WAAK,MAAM;AAEX,WAAK,aAAa,MAAM,GAAG,kBAAkB,OAAO,OAAO,IAAI;AAAA,IACnE;AAAA,EACJ;;;AD3CA,MAAqB,QAArB,cAAmC,UAAU;AAAA,IAMzC,YAAY,YAAiC,SAAmB,UAAkB;AAC9E,YAAM,SAAS,YAAY,QAAQ;AADM;AAAA,IAE7C;AAAA,IAKQ;AAAA,IAOE,SAAS,OAAc,SAAqC;AAElE,UAAI,KAAK,kCAAc,GAAG;AAEtB,cAAM,gBAAY,kBAAAC,SAAkB;AAAA,UAEhC,QAAQ,QAAQ;AAAA,UAEhB,cAAc,KAAK,SAAS,IAAI,CAAC,OAAO,UAAU,CAAC,OAAO,KAAK,QAAQ,UAAU,CAAC,CAAC;AAAA,QACvF,CAAC;AAGD,aAAK,gBAAgB,UAAU,KAAK,KAAK;AAAA,MAC7C;AAGA,UAAI,CAAC,KAAK,eAAe;AACrB,cAAM,IAAI,MAAM,uDAAuD;AAAA,MAC3E;AAGA,UAAI,KAAK,cAAc,SAAS,yCAAqB,KAAK,cAAc,SAAS,2CAAqB;AAClG,aAAK,cAAc,OAAO,OAAO,OAAO;AAAA,MAC5C;AAGA,WAAK,SAAS,KAAK,cAAc,SAAS,CAAC;AAAA,IAC/C;AAAA,IAKA,UAAU,MAAO,KAAK,QAAQ,SAAS,UAAU,KAAK,QAAQ,KAAK,GAAG,OAAO;AAAA,EACjF;;;AExDA,MAAqB,WAArB,cAAsC,UAAU;AAAA,IAK5C,YAAY,YAAmC,UAAkB;AAC7D,YAAM,YAAY,YAAY,QAAQ;AADK;AAAA,IAE/C;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,iBAAW,SAAS,KAAK,UAAU;AAE/B,YAAI,MAAM,SAAS,yCAAqB,MAAM,SAAS,2CAAqB;AAExE,gBAAM,OAAO,OAAO,OAAO;AAAA,QAC/B;AAGA,YAAI,MAAM,SAAS,+CAAuB;AAEtC,eAAK,gDAAwB;AAG7B;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,yCAAoB;AAGnC,cAAI,KAAK,SAAS,QAAQ,KAAK,MAAM,KAAK,SAAS,SAAS,GAAG;AAE3D,iBAAK,0CAAqB;AAG1B;AAAA,UACJ,OAAO;AAEH;AAAA,UACJ;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,2CAAqB;AAEpC,eAAK,4CAAsB;AAG3B;AAAA,QACJ;AAGA,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC9D;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;AClEA,MAAqB,WAArB,cAAsC,UAAU;AAAA,IAK5C,YAAY,YAAmC,UAAkB;AAC7D,YAAM,YAAY,YAAY,QAAQ;AADK;AAAA,IAE/C;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,iBAAW,SAAS,KAAK,UAAU;AAE/B,YAAI,MAAM,SAAS,yCAAqB,MAAM,SAAS,2CAAqB;AAExE,gBAAM,OAAO,OAAO,OAAO;AAAA,QAC/B;AAGA,YAAI,MAAM,SAAS,+CAAuB;AAGtC,cAAI,KAAK,SAAS,QAAQ,KAAK,MAAM,KAAK,SAAS,SAAS,GAAG;AAE3D,iBAAK,gDAAwB;AAG7B;AAAA,UACJ,OAAO;AAEH;AAAA,UACJ;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,yCAAoB;AAEnC,eAAK,0CAAqB;AAG1B;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,2CAAqB;AAEpC,eAAK,4CAAsB;AAG3B;AAAA,QACJ;AAGA,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC9D;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;AClEA,MAAqB,WAArB,cAAsC,UAAU;AAAA,IAK5C,YAAY,YAAyB,UAAkB;AACnD,YAAM,YAAY,YAAY,QAAQ;AAAA,IAC1C;AAAA,IAOU,SAAS,OAAc,SAAqC;AAElE,UAAI,iBAAiB;AAErB,UAAI,iBAAiB;AAGrB,iBAAW,SAAS,KAAK,UAAU;AAE/B,YAAI,MAAM,SAAS,yCAAqB,MAAM,SAAS,2CAAqB;AAExE,gBAAM,OAAO,OAAO,OAAO;AAAA,QAC/B;AAGA,YAAI,MAAM,SAAS,+CAAuB;AAEtC;AAGA;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,yCAAoB;AACnC,2BAAiB;AAGjB;AAAA,QACJ;AAGA,YAAI,MAAM,SAAS,2CAAqB;AAEpC,gBAAM,IAAI,MAAM,0CAA0C;AAAA,QAC9D;AAAA,MACJ;AAEA,UAAI,gBAAgB;AAEhB,aAAK,0CAAqB;AAG1B,mBAAW,SAAS,KAAK,UAAU;AAC/B,cAAI,MAAM,SAAS,2CAAqB;AACpC,kBAAM,MAAM,KAAK;AAAA,UACrB;AAAA,QACJ;AAAA,MACJ,OAAO;AAEH,aAAK,SAAS,mBAAmB,KAAK,SAAS,sFAAwC;AAAA,MAC3F;AAAA,IACJ;AAAA,IAKA,UAAU,MAAM;AAAA,EACpB;;;ACrEA,MAA8B,YAA9B,MAAuG;AAAA,IAKnG,YAAsB,MAAwB,MAAqB;AAA7C;AAAwB;AAAA,IAAsB;AAAA,IAKpE,UAAU,MAAM,KAAK;AAAA,IAKrB,eAAe,MAAM,KAAK;AAAA,EAW9B;;;AC5BA,MAA8B,QAA9B,cAA4C,UAAiC;AAAA,IAMzE,YAAY,MAAc,MAA6B,WAAmB;AACtE,YAAM,MAAM,IAAI;AADmC;AAAA,IAEvD;AAAA,IAKA,eAAe,MAAM,KAAK;AAAA,IAK1B,UAAU,MAAM;AAAA,IAKhB,aAAoC;AAChC,aAAO;AAAA,QACH,MAAM,KAAK,QAAQ;AAAA,QACnB,MAAM,KAAK,aAAa;AAAA,QACxB,WAAW,KAAK,aAAa;AAAA,MACjC;AAAA,IACJ;AAAA,EAQJ;;;ACzCA,MAAqB,QAArB,cAAmC,MAAM;AAAA,IAKrC,YAAY,WAAmB,MAAqB;AAChD,YAAM,SAAS,MAAM,SAAS;AAAA,IAClC;AAAA,IAOA,cAAc,CAAC,UAAiB;AAE5B,YAAM,uBAAuB,OAAO,eAAe,OAAO,KAAK,aAAa,CAAC;AAG7E,UAAI,yBAAyB,MAAM;AAC/B,cAAM,IAAI;AAAA,UACN,gDAAgD,KAAK,aAAa;AAAA,QACtE;AAAA,MACJ;AAGA,aAAO,CAAC,CAAC,qBAAqB,KAAK,IAAI;AAAA,IAC3C;AAAA,EACJ;;;AC5BA,MAAqB,QAArB,cAAmC,MAAM;AAAA,IAKrC,YAAY,WAAmB,MAAqB;AAChD,YAAM,SAAS,MAAM,SAAS;AAAA,IAClC;AAAA,IAOA,cAAc,CAAC,UAAiB;AAE5B,YAAM,uBAAuB,OAAO,eAAe,OAAO,KAAK,aAAa,CAAC;AAG7E,UAAI,yBAAyB,MAAM;AAC/B,cAAM,IAAI;AAAA,UACN,gDAAgD,KAAK,aAAa;AAAA,QACtE;AAAA,MACJ;AAGA,aAAO,CAAC,CAAC,CAAC,qBAAqB,KAAK,IAAI;AAAA,IAC5C;AAAA,EACJ;;;ACxBA,MAA8B,WAA9B,cAA+C,UAAoC;AAAA,IAM/E,YAAY,MAAc,MAA6B,cAAsB;AACzE,YAAM,MAAM,IAAI;AADmC;AAAA,IAEvD;AAAA,IAKA,kBAAkB,MAAM,KAAK;AAAA,IAK7B,UAAU,MAAM;AAAA,IAKhB,aAAuC;AACnC,aAAO;AAAA,QACH,MAAM,KAAK,QAAQ;AAAA,QACnB,MAAM,KAAK,aAAa;AAAA,QACxB,cAAc,KAAK,gBAAgB;AAAA,MACvC;AAAA,IACJ;AAAA,EAOJ;;;ACxCA,MAAqB,QAArB,cAAmC,SAAS;AAAA,IAKxC,YAAY,cAAsB,MAAqB;AACnD,YAAM,SAAS,MAAM,YAAY;AAAA,IACrC;AAAA,IAMA,oBAAoB,CAAC,UAAiB;AAElC,YAAM,sBAAsB,OAAO,eAAe,OAAO,KAAK,gBAAgB,CAAC;AAG/E,UAAI,wBAAwB,MAAM;AAC9B,cAAM,IAAI;AAAA,UACN,+BAA+B,KAAK,gBAAgB;AAAA,QACxD;AAAA,MACJ;AAGA,0BAAoB,KAAK,IAAI;AAAA,IACjC;AAAA,EACJ;;;AC3BA,MAAqB,OAArB,cAAkC,SAAS;AAAA,IAKvC,YAAY,cAAsB,MAAqB;AACnD,YAAM,QAAQ,MAAM,YAAY;AAAA,IACpC;AAAA,IAQA,oBAAoB,CAAC,OAAc,WAAoB,cAAuB;AAE1E,YAAM,sBAAsB,OAAO,eAAe,OAAO,KAAK,gBAAgB,CAAC;AAG/E,UAAI,wBAAwB,MAAM;AAC9B,cAAM,IAAI;AAAA,UACN,8BAA8B,KAAK,gBAAgB;AAAA,QACvD;AAAA,MACJ;AAGA,0BAAoB,CAAC,EAAE,OAAO,EAAE,WAAW,WAAW,SAAS,UAAU,EAAE,GAAG,GAAG,KAAK,IAAI,CAAC;AAAA,IAC/F;AAAA,EACJ;;;AC7BA,MAAqB,OAArB,cAAkC,SAAS;AAAA,IAKvC,YAAY,cAAsB,MAAqB;AACnD,YAAM,QAAQ,MAAM,YAAY;AAAA,IACpC;AAAA,IAMA,oBAAoB,CAAC,UAAiB;AAElC,YAAM,sBAAsB,OAAO,eAAe,OAAO,KAAK,gBAAgB,CAAC;AAG/E,UAAI,wBAAwB,MAAM;AAC9B,cAAM,IAAI;AAAA,UACN,8BAA8B,KAAK,gBAAgB;AAAA,QACvD;AAAA,MACJ;AAGA,0BAAoB,KAAK,IAAI;AAAA,IACjC;AAAA,EACJ;;;ACqBA,MAAM,qBAEF;AAAA,IACA,OAAO,CAAC,WAAmB,uBAAsC,IAAI,MAAM,WAAW,kBAAkB;AAAA,IACxG,OAAO,CAAC,WAAmB,uBAAsC,IAAI,MAAM,WAAW,kBAAkB;AAAA,IACxG,OAAO,CAAC,cAAsB,uBAAsC,IAAI,MAAM,cAAc,kBAAkB;AAAA,IAC9G,MAAM,CAAC,cAAsB,uBAAsC,IAAI,KAAK,cAAc,kBAAkB;AAAA,IAC5G,MAAM,CAAC,cAAsB,uBAAsC,IAAI,KAAK,cAAc,kBAAkB;AAAA,EAChH;AAkGA,MAAM,mBAAmB;AAAA,IACrB,MAAM,OAAoB;AAAA,MACtB,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,MACX,SAAS,OAAe;AAEpB,YAAI,QAAQ,GAAG;AACX,gBAAM,IAAI,MAAM,iDAAiD;AAAA,QACrE;AAGA,YAAI,KAAK,SAAS,WAAW,GAAG;AAC5B,gBAAM,IAAI,MAAM,sCAAsC;AAAA,QAC1D;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,QAAQ,OAAsB;AAAA,MAC1B,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,WAAW;AAAA,MAAC;AAAA,MACZ,mBAAmB,uBAAuB,iBAAiB;AAEvD,cAAM,iBAAiB,sBAAsB,KAAK,UAAU;AAG5D,YAAI,gBAAgB,QAAQ,KAAK,UAAU,MAAM,IAAI;AACjD,gBAAM,IAAI,MAAM,mEAAmE,KAAK,aAAa;AAAA,QACzG;AAGA,YAAI,gBAAgB;AAChB,iBAAO,eACF,mBAAmB,uBAAuB,gBAAgB,OAAO,KAAK,UAAU,CAAC,EACjF,YAAY,EAAE;AAAA,QACvB,OAAO;AACH,gBAAM,IAAI,MAAM,gCAAgC,KAAK,wCAAwC;AAAA,QACjG;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,UAAU,OAAyB;AAAA,MAC/B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,gBAAM,IAAI,MAAM,mDAAmD;AAAA,QACvE;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAS,IAAI,CAAC,UAAU,MAAM,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC,CAAC;AAAA,QACzG;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,UAAU,OAAyB;AAAA,MAC/B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,gBAAM,IAAI,MAAM,mDAAmD;AAAA,QACvE;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAS,IAAI,CAAC,UAAU,MAAM,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC,CAAC;AAAA,QACzG;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,UAAU,OAAyB;AAAA,MAC/B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,gBAAM,IAAI,MAAM,mDAAmD;AAAA,QACvE;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAS,IAAI,CAAC,UAAU,MAAM,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC,CAAC;AAAA,QACzG;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,OAAO,OAAqB;AAAA,MACxB,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,SAAS,CAAC;AAAA,MACV,WAAW;AAEP,YAAI,KAAK,SAAU,SAAS,GAAG;AAC3B,gBAAM,IAAI,MAAM,gDAAgD;AAAA,QACpE;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK,SAAU,IAAI,CAAC,UAAU,MAAM,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC,CAAC;AAAA,QAC1G;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,QAAQ,OAAsB;AAAA,MAC1B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,eAAe;AAAA,MACf,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAU,WAAW,GAAG;AAC7B,gBAAM,IAAI,MAAM,wCAAwC;AAAA,QAC5D;AAEA,YAAI,KAAK,eAAe,MAAM;AAE1B,cAAI,KAAK,aAAa,GAAG;AACrB,kBAAM,IAAI,MAAM,oEAAoE;AAAA,UACxF;AAAA,QACJ,WAAW,KAAK,kBAAkB,QAAQ,KAAK,kBAAkB,MAAM;AAEnE,cAAI,KAAK,gBAAgB,KAAK,KAAK,gBAAgB,GAAG;AAClD,kBAAM,IAAI;AAAA,cACN;AAAA,YACJ;AAAA,UACJ;AAGA,cAAI,KAAK,gBAAgB,KAAK,eAAe;AACzC,kBAAM,IAAI;AAAA,cACN;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ,OAAO;AAAA,QAEP;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,OAAO,OAAqB;AAAA,MACxB,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,aAAa;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAU,WAAW,GAAG;AAC7B,gBAAM,IAAI,MAAM,uCAAuC;AAAA,QAC3D;AAEA,YAAI,KAAK,aAAa,MAAM;AAExB,cAAI,KAAK,WAAW,GAAG;AACnB,kBAAM,IAAI,MAAM,iEAAiE;AAAA,UACrF;AAAA,QACJ,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAE/D,cAAI,KAAK,cAAc,KAAK,KAAK,cAAc,GAAG;AAC9C,kBAAM,IAAI,MAAM,gFAAgF;AAAA,UACpG;AAGA,cAAI,KAAK,cAAc,KAAK,aAAa;AACrC,kBAAM,IAAI;AAAA,cACN;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ,OAAO;AAAA,QAEP;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,MAAM,OAAyB;AAAA,MAC3B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAU,WAAW,GAAG;AAC7B,gBAAM,IAAI,MAAM,sCAAsC;AAAA,QAC1D;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,SAAS,OAAyB;AAAA,MAC9B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAU,WAAW,GAAG;AAC7B,gBAAM,IAAI,MAAM,yCAAyC;AAAA,QAC7D;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,MAAM,OAAyB;AAAA,MAC3B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU,CAAC;AAAA,MACX,WAAW;AAEP,YAAI,KAAK,SAAU,WAAW,GAAG;AAC7B,gBAAM,IAAI,MAAM,sCAAsC;AAAA,QAC1D;AAAA,MACJ;AAAA,MACA,mBAAmB,uBAAuB,iBAAiB;AACvD,eAAO,IAAI;AAAA,UACP,KAAK;AAAA,UACL,KAAK,SAAU,GAAG,mBAAmB,uBAAuB,gBAAgB,MAAM,CAAC;AAAA,QACvF;AAAA,MACJ;AAAA,IACJ;AAAA,IACA,MAAM,OAAoB;AAAA,MACtB,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA,MACb,aAAa;AAAA,MACb,WAAW;AACP,YAAI,KAAK,aAAa,MAAM;AAExB,cAAI,KAAK,WAAW,GAAG;AACnB,kBAAM,IAAI,MAAM,2CAA2C;AAAA,UAC/D;AAAA,QACJ,WAAW,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,MAAM;AAE/D,cAAI,KAAK,cAAc,KAAK,KAAK,cAAc,GAAG;AAC9C,kBAAM,IAAI,MAAM,+DAA+D;AAAA,UACnF;AAGA,cAAI,KAAK,cAAc,KAAK,aAAa;AACrC,kBAAM,IAAI,MAAM,gFAAgF;AAAA,UACpG;AAAA,QACJ,OAAO;AAAA,QAEP;AAAA,MACJ;AAAA,MACA,qBAAqB;AACjB,eAAO,IAAI,KAAK,KAAK,YAAY,KAAK,UAAU,KAAK,aAAa,KAAK,WAAW;AAAA,MACtF;AAAA,IACJ;AAAA,IACA,QAAQ,OAAsB;AAAA,MAC1B,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,YAAY;AAAA,MACZ,iBAAiB,CAAC;AAAA,MAClB,WAAW;AAAA,MAAC;AAAA,MACZ,qBAAqB;AACjB,eAAO,IAAI,OAAO,KAAK,YAAY,KAAK,YAAa,KAAK,eAAgB;AAAA,MAC9E;AAAA,IACJ;AAAA,IACA,WAAW,OAAyB;AAAA,MAChC,MAAM;AAAA,MACN,YAAY,CAAC;AAAA,MACb,eAAe;AAAA,MACf,oBAAoB,CAAC;AAAA,MACrB,WAAW;AAAA,MAAC;AAAA,MACZ,qBAAqB;AACjB,eAAO,IAAI,UAAU,KAAK,YAAY,KAAK,eAAgB,KAAK,kBAAmB;AAAA,MACvF;AAAA,IACJ;AAAA,EACJ;AASe,WAAR,kBAAmC,YAAmC;AAEzE,UAAM,EAAE,cAAc,oBAAoB,IAAI,yBAAyB,UAAU;AAGjF,UAAM,SAAS,0BAA0B,mBAAmB;AAG5D,QAAI,OAAO,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACzC;AAGA,QAAI,OAAO,OAAO,CAAC,UAAU,UAAU,GAAG,EAAE,WAAW,OAAO,OAAO,CAAC,UAAU,UAAU,GAAG,EAAE,QAAQ;AACnG,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC9C;AAGA,UAAM,QAA6C,CAAC,CAAC,CAAC;AACtD,UAAM,YAAY,MAAM;AAGxB,WAAO,OAAO,QAAQ;AAElB,YAAM,QAAQ,OAAO,MAAM;AAE3B,YAAM,eAAe,MAAM,MAAM,SAAS;AAG1C,cAAQ,MAAO,YAAY,GAAG;AAAA,QAC1B,KAAK,QAAQ;AAET,gBAAM,OAAO,iBAAiB,KAAK;AAGnC,oBAAU,KAAK,IAAI;AAGnB,cAAI,OAAO,OAAO,KAAK;AACnB,kBAAM,gBAAgB,aAAa,QAAQ,YAAY;AAGvD,gBAAI,cAAc,WAAW,KAAK,cAAc,GAAG,SAAS,cAAc;AAEtE,mBAAK,OAAO,cAAc,GAAG;AAAA,YACjC,OAAO;AACH,oBAAM,IAAI,MAAM,oCAAoC;AAAA,YACxD;AAAA,UACJ;AAGA,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,UAAU;AAEX,gBAAM,OAAO,iBAAiB,OAAO;AAGrC,uBAAa,KAAK,IAAI;AAGtB,cAAI,OAAO,OAAO,KAAK;AACnB,kBAAM,IAAI,MAAM,sCAAsC;AAAA,UAC1D;AAGA,gBAAM,kBAAkB,aAAa,QAAQ,YAAY;AAGzD,cAAI,gBAAgB,WAAW,KAAK,gBAAgB,GAAG,SAAS,cAAc;AAE1E,iBAAK,aAAa,gBAAgB,GAAG;AAAA,UACzC,OAAO;AACH,kBAAM,IAAI,MAAM,sCAAsC;AAAA,UAC1D;AACA;AAAA,QACJ;AAAA,QAEA,KAAK,YAAY;AAEb,gBAAM,OAAO,iBAAiB,SAAS;AAGvC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,YAAY;AAEb,gBAAM,OAAO,iBAAiB,SAAS;AAGvC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,YAAY;AAEb,gBAAM,OAAO,iBAAiB,SAAS;AAGvC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,SAAS;AAEV,gBAAM,OAAO,iBAAiB,MAAM;AAGpC,uBAAa,KAAK,IAAI;AAGtB,cAAI,OAAO,OAAO,KAAK;AAEnB,iBAAK,UAAU;AAAA,cACX;AAAA,cACA;AAAA,cACA,CAAC,QAAQ,IAAI,SAAS,YAAY,CAAC,CAAC,IAAI;AAAA,cACxC;AAAA,YACJ,EAAE,IAAI,CAAC,aAAa,SAAS,KAAe;AAAA,UAChD;AAGA,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,aAAa;AAEd,gBAAM,OAAO,iBAAiB,UAAU;AAGxC,uBAAa,KAAK,IAAI;AAGtB,cAAI,OAAO,OAAO,KAAK;AACnB,kBAAM,IAAI,MAAM,6CAA6C;AAAA,UACjE;AAGA,gBAAM,qBAAqB,aAAa,QAAQ,YAAY;AAG5D,cAAI,mBAAmB,UAAU,mBAAmB,GAAG,SAAS,cAAc;AAE1E,iBAAK,gBAAgB,mBAAmB,MAAM,EAAG;AAAA,UACrD,OAAO;AACH,kBAAM,IAAI,MAAM,6CAA6C;AAAA,UACjE;AAGA,6BACK,OAAO,CAAC,QAAQ,IAAI,SAAS,YAAY,EACzC,QAAQ,CAAC,QAAQ;AACd,kBAAM,IAAI;AAAA,cACN,4CACI,IAAI,QACJ;AAAA,YACR;AAAA,UACJ,CAAC;AAGL,eAAK,qBAAqB;AAG1B,eAAK,aAAa,cAAc,QAAQ,YAAY;AACpD;AAAA,QACJ;AAAA,QAEA,KAAK,QAAQ;AAET,gBAAM,OAAO,iBAAiB,KAAK;AAGnC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,WAAW;AAEZ,gBAAM,OAAO,iBAAiB,QAAQ;AAGtC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,QAAQ;AAET,gBAAM,OAAO,iBAAiB,KAAK;AAGnC,uBAAa,KAAK,IAAI;AAGtB,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,QAAQ;AAET,gBAAM,OAAO,iBAAiB,KAAK;AAGnC,uBAAa,KAAK,IAAI;AAMtB,cAAI,OAAO,OAAO,KAAK;AAEnB,kBAAM,gBAAgB;AAAA,cAClB;AAAA,cACA;AAAA,cACA,CAAC,QAAQ,IAAI,SAAS,YAAY,CAAC,CAAC,IAAI;AAAA,cACxC;AAAA,YACJ,EAAE,IAAI,CAAC,aAAa,SAAS,KAAK;AAMlC,gBAAI,cAAc,WAAW,GAAG;AAE5B,mBAAK,WAAW,cAAc;AAAA,YAClC,WAAW,cAAc,WAAW,GAAG;AAEnC,mBAAK,cAAc,cAAc;AACjC,mBAAK,cAAc,cAAc;AAAA,YACrC,WAAW,cAAc,SAAS,GAAG;AAEjC,oBAAM,IAAI,MAAM,wDAAwD;AAAA,YAC5E;AAAA,UACJ;AAGA,eAAK,aAAa,cAAc,QAAQ,YAAY;AACpD;AAAA,QACJ;AAAA,QAEA,KAAK,UAAU;AAEX,gBAAM,OAAO,iBAAiB,OAAO;AAGrC,uBAAa,KAAK,IAAI;AAMtB,cAAI,OAAO,OAAO,KAAK;AAEnB,kBAAM,gBAAgB;AAAA,cAClB;AAAA,cACA;AAAA,cACA,CAAC,QAAQ,IAAI,SAAS,YAAY,CAAC,CAAC,IAAI;AAAA,cACxC;AAAA,YACJ,EAAE,IAAI,CAAC,aAAa,SAAS,KAAK;AAGlC,gBAAI,cAAc,WAAW,GAAG;AAE5B,mBAAK,aAAa,cAAc;AAAA,YACpC,WAAW,cAAc,WAAW,GAAG;AAEnC,mBAAK,gBAAgB,cAAc;AACnC,mBAAK,gBAAgB,cAAc;AAAA,YACvC,OAAO;AAEH,oBAAM,IAAI,MAAM,iEAAiE;AAAA,YACrF;AAAA,UACJ;AAGA,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,SAAS;AAEV,gBAAM,OAAO,iBAAiB,MAAM;AAGpC,uBAAa,KAAK,IAAI;AAMtB,cAAI,OAAO,OAAO,KAAK;AAEnB,kBAAM,gBAAgB;AAAA,cAClB;AAAA,cACA;AAAA,cACA,CAAC,QAAQ,IAAI,SAAS,YAAY,CAAC,CAAC,IAAI;AAAA,cACxC;AAAA,YACJ,EAAE,IAAI,CAAC,aAAa,SAAS,KAAK;AAGlC,gBAAI,cAAc,WAAW,GAAG;AAE5B,mBAAK,WAAW,cAAc;AAAA,YAClC,WAAW,cAAc,WAAW,GAAG;AAEnC,mBAAK,cAAc,cAAc;AACjC,mBAAK,cAAc,cAAc;AAAA,YACrC,OAAO;AAEH,oBAAM,IAAI,MAAM,8DAA8D;AAAA,YAClF;AAAA,UACJ;AAGA,eAAK,aAAa,cAAc,QAAQ,YAAY;AAEpD,sBAAY,QAAQ,GAAG;AAGvB,gBAAM,KAAK,KAAK,QAAS;AACzB;AAAA,QACJ;AAAA,QAEA,KAAK,UAAU;AAEX,gBAAM,OAAO,iBAAiB,OAAO;AAGrC,uBAAa,KAAK,IAAI;AAGtB,cAAI,OAAO,OAAO,KAAK;AACnB,kBAAM,IAAI,MAAM,0CAA0C;AAAA,UAC9D;AAGA,gBAAM,kBAAkB,aAAa,QAAQ,YAAY;AAGzD,cAAI,gBAAgB,UAAU,gBAAgB,GAAG,SAAS,cAAc;AAEpE,iBAAK,aAAa,gBAAgB,MAAM,EAAG;AAAA,UAC/C,OAAO;AACH,kBAAM,IAAI,MAAM,0CAA0C;AAAA,UAC9D;AAGA,0BACK,OAAO,CAAC,QAAQ,IAAI,SAAS,YAAY,EACzC,QAAQ,CAAC,QAAQ;AACd,kBAAM,IAAI;AAAA,cACN,yCACI,IAAI,QACJ;AAAA,YACR;AAAA,UACJ,CAAC;AAGL,eAAK,kBAAkB;AAGvB,eAAK,aAAa,cAAc,QAAQ,YAAY;AACpD;AAAA,QACJ;AAAA,QAEA,KAAK,KAAK;AAEN,gBAAM,IAAI;AACV;AAAA,QACJ;AAAA,QAEA,SAAS;AACL,gBAAM,IAAI,MAAM,qBAAqB,QAAQ;AAAA,QACjD;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,kBAAkB,CAAC,MAAmB,UAAwB;AAEhE,WAAK,SAAS,KAAK;AAGnB,OAAC,KAAK,YAAY,CAAC,GAAG,QAAQ,CAAC,UAAU,gBAAgB,OAAO,QAAQ,CAAC,CAAC;AAAA,IAC9E;AAGA;AAAA,MACI;AAAA,QACI,UAAU,MAAM;AAAA,QAChB,WAA4C;AAExC,cAAI,KAAK,SAAS,WAAW,GAAG;AAC5B,kBAAM,IAAI,MAAM,yCAAyC;AAAA,UAC7D;AAGA,qBAAW,uBAAuB,KAAK,UAAU;AAC7C,gBAAI,oBAAoB,SAAS,QAAQ;AACrC,oBAAM,IAAI,MAAM,0CAA0C;AAAA,YAC9D;AAAA,UACJ;AAGA,cAAI,KAAK,SAAS,OAAO,CAAC,wBAAwB,oBAAoB,SAAS,IAAI,EAAE,WAAW,GAAG;AAC/F,kBAAM,IAAI,MAAM,6EAA6E;AAAA,UACjG;AAGA,gBAAM,gBAA0B,CAAC;AACjC,qBAAW,uBAAuB,KAAK,UAAU;AAC7C,gBAAI,cAAc,QAAQ,oBAAoB,IAAK,MAAM,IAAI;AACzD,oBAAM,IAAI,MAAM,kDAAkD,oBAAoB,OAAO;AAAA,YACjG,OAAO;AACH,4BAAc,KAAK,oBAAoB,IAAK;AAAA,YAChD;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,MACA;AAAA,IACJ;AAGA,WAAO,MAAM;AAAA,EACjB;AAQA,WAAS,YAAY,QAAkB,UAA6B;AAEhE,UAAM,SAAS,OAAO,MAAM;AAG5B,QAAI,WAAW,QAAW;AACtB,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAClD;AAGA,QAAI,aAAa,QAAW;AAExB,UAAI,0BAA2B,CAAC,EAC3B,OAAO,QAAQ,EACf,KAAK,CAAC,SAAS,OAAO,YAAY,MAAM,KAAK,YAAY,CAAC;AAG/D,UAAI,CAAC,yBAAyB;AAC1B,cAAM,oBAAqB,CAAC,EACvB,OAAO,QAAQ,EACf,IAAI,CAAC,SAAS,MAAM,OAAO,GAAG,EAC9B,KAAK,MAAM;AAEhB,cAAM,IAAI,MAAM,qCAAqC,+BAA+B,SAAS;AAAA,MACjG;AAAA,IACJ;AAGA,WAAO;AAAA,EACX;AAYA,WAAS,aACL,QACA,4BACA,mBACA,yBACF;AAGE,UAAM,SAAS,YAAY,QAAQ,CAAC,KAAK,GAAG,CAAC,MAAM,MAAM,MAAM;AAE/D,UAAM,qBAA+B,CAAC;AACtC,UAAM,eAA8B,CAAC;AAGrC,WAAO,OAAO,UAAU,OAAO,OAAO,QAAQ;AAE1C,yBAAmB,KAAK,OAAO,MAAM,CAAE;AAAA,IAC3C;AAGA,uBAAmB,QAAQ,CAAC,OAAO,UAAU;AAEzC,YAAM,wBAAwB,EAAE,QAAQ;AAGxC,UAAI,uBAAuB;AAEvB,cAAM,qBAAqB,sBAAsB,OAAQ,0BAA0B;AAGnF,YAAI,qBAAqB,CAAC,kBAAkB,kBAAkB,GAAG;AAC7D,gBAAM,IAAI,MAAM,uBAAuB;AAAA,QAC3C;AAGA,qBAAa,KAAK,kBAAkB;AAAA,MACxC,OAAO;AAEH,YAAI,UAAU,KAAK;AACf,gBAAM,IAAI,MAAM,uDAAuD,QAAQ;AAAA,QACnF;AAAA,MACJ;AAAA,IACJ,CAAC;AAGD,gBAAY,QAAQ,MAAM;AAG1B,WAAO;AAAA,EACX;AAQA,WAAS,sBAAsB,OAAe,4BAAuD;AAEjG,QAAI,UAAU,QAAQ;AAClB,aAAO;AAAA,QACH,OAAO;AAAA,QACP,MAAM;AAAA,MACV;AAAA,IACJ;AAGA,QAAI,UAAU,UAAU,UAAU,SAAS;AACvC,aAAO;AAAA,QACH,OAAO,UAAU;AAAA,QACjB,MAAM;AAAA,MACV;AAAA,IACJ;AAKA,QAAI,CAAC,MAAM,KAAY,GAAG;AACtB,aAAO;AAAA,QACH,OAAO,WAAW,KAAK;AAAA,QACvB,WAAW,WAAW,KAAK,MAAM,SAAS,OAAO,EAAE;AAAA,QACnD,MAAM;AAAA,MACV;AAAA,IACJ;AAGA,QAAI,MAAM,MAAM,YAAY,GAAG;AAC3B,aAAO;AAAA,QACH,OAAO,2BAA2B,OAAO,QAAQ,OAAO,GAAG;AAAA,QAC3D,MAAM;AAAA,MACV;AAAA,IACJ;AAGA,WAAO;AAAA,MACH,OAAO;AAAA,MACP,MAAM;AAAA,IACV;AAAA,EACJ;AAQA,WAAS,cAAc,QAAkB,4BAA0C;AAE/E,UAAM,aAA0B,CAAC;AAGjC,UAAM,kBAA4B,CAAC;AAGnC,QAAI,mBAAmB,oBAAoB,OAAO,MAAM,IAAI,YAAY;AAGxE,WAAO,kBAAkB;AAErB,UAAI,gBAAgB,QAAQ,OAAO,GAAG,YAAY,CAAC,MAAM,IAAI;AACzD,cAAM,IAAI,MAAM,wBAAwB,OAAO,GAAG,YAAY,mBAAmB;AAAA,MACrF;AAGA,sBAAgB,KAAK,OAAO,MAAM,EAAG,YAAY,CAAC;AAGlD,YAAM,qBAAqB,aAAa,QAAQ,0BAA0B;AAG1E,UAAI,mBAAmB,WAAW,KAAK,mBAAmB,GAAG,SAAS,cAAc;AAChF,cAAM,IAAI,MAAM,gEAAgE;AAAA,MACpF;AAGA,YAAM,wBAAwB,mBAAmB,MAAM;AAGvD,yBACK,OAAO,CAAC,QAAQ,IAAI,SAAS,YAAY,EACzC,QAAQ,CAAC,QAAQ;AACd,cAAM,IAAI;AAAA,UACN,uCAAuC,IAAI,QAAQ;AAAA,QACvD;AAAA,MACJ,CAAC;AAGL,iBAAW,KAAK,iBAAiB,sBAAsB,OAAO,kBAAkB,CAAC;AAGjF,yBAAmB,oBAAoB,OAAO,MAAM,IAAI,YAAY;AAAA,IACxE;AAEA,WAAO;AAAA,EACX;AAOA,WAAS,yBAAyB,YAGhC;AAEE,UAAM,eAA6B,CAAC;AAGpC,UAAM,sBAAsB,WAAW,QAAQ,sBAAsB,CAAC,UAAU;AAC5E,UAAI,gBAAgB,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC;AACvD,UAAI,cAAc,OAAO,KAAK,YAAY,EAAE,KAAK,CAAC,QAAQ,aAAa,SAAS,aAAa;AAG7F,UAAI,CAAC,aAAa;AACd,sBAAc,KAAK,OAAO,KAAK,YAAY,EAAE;AAC7C,qBAAa,eAAe;AAAA,MAChC;AAEA,aAAO;AAAA,IACX,CAAC;AAED,WAAO,EAAE,cAAc,oBAAoB;AAAA,EAC/C;AAOA,WAAS,0BAA0B,YAA8B;AAE7D,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAC5C,iBAAa,WAAW,QAAQ,OAAO,KAAK;AAG5C,WAAO,WAAW,QAAQ,QAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG;AAAA,EAC3D;;;ACrqCO,MAAM,gBAAN,MAAoB;AAAA,IAYvB,YAAY,YAA4B,OAAsB,UAAgC,CAAC,GAAG;AAA1D;AAAsB;AAE1D,UAAI,OAAO,eAAe,UAAU;AAChC,cAAM,IAAI,MAAM,sCAAsC;AAAA,MAC1D;AAGA,UAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC7C,cAAM,IAAI,MAAM,wCAAwC;AAAA,MAC5D;AAGA,WAAK,WAAW,cAAc,gBAAgB,UAAU;AAAA,IAC5D;AAAA,IArBgB;AAAA,IA2BhB,YAAY;AACR,aAAO,KAAK,SAAS,SAAS;AAAA,IAClC;AAAA,IAMA,WAAW;AACP,aAAO,KAAK,SAAS,SAAS;AAAA,IAClC;AAAA,IAUA,OAAO;AAEH,UAAI,KAAK,SAAS,SAAS,iDAAyB,KAAK,SAAS,SAAS,yCAAoB;AAC3F,aAAK,SAAS,MAAM;AAAA,MACxB;AAEA,UAAI;AACA,aAAK,SAAS,OAAO,KAAK,OAAO,KAAK,OAAO;AAAA,MACjD,SAAS,WAAP;AACE,cAAM,IAAI,MAAM,wBAAyB,UAAoB,SAAS;AAAA,MAC1E;AAAA,IACJ;AAAA,IAKA,QAAQ;AACJ,WAAK,SAAS,MAAM;AAAA,IACxB;AAAA,IAMA,0BAA+C;AAE3C,YAAM,qBAA0C,CAAC;AAOjD,YAAM,cAAc,CAAC,MAAY,cAA6B;AAE1D,cAAM,SAAS,KACV,cAAc,EACd,OAAO,CAAC,cAAc,UAAU,QAAQ,CAAC,EACzC,IAAI,CAAC,cAAc,UAAU,WAAW,CAAC;AAC9C,cAAM,YAAY,KACb,cAAc,EACd,OAAO,CAAC,cAAc,CAAC,UAAU,QAAQ,CAAC,EAC1C,IAAI,CAAC,cAAc,UAAU,WAAW,CAAC;AAG9C,2BAAmB,KAAK;AAAA,UACpB,IAAI,KAAK,OAAO;AAAA,UAChB,MAAM,KAAK,QAAQ;AAAA,UACnB,SAAS,KAAK,QAAQ;AAAA,UACtB,OAAO,KAAK,SAAS;AAAA,UACrB;AAAA,UACA;AAAA,UACA,MAAM,KAAK,aAAa;AAAA,UACxB,UAAU;AAAA,QACd,CAAC;AAGD,YAAI,CAAC,KAAK,WAAW,GAAG;AACpB,UAAC,KACI,YAAY,EACZ,QAAQ,CAAC,UAAU,YAAY,OAAQ,KAA+B,OAAO,CAAC,CAAC;AAAA,QACxF;AAAA,MACJ;AAGA,kBAAY,KAAK,UAAU,IAAI;AAE/B,aAAO;AAAA,IACX;AAAA,IAOA,OAAO,SAAS,MAAc,OAAgC;AAC1D,UAAI,OAAO,UAAU,YAAY;AAE7B,eAAO,QAAQ,MAAM,KAAK;AAAA,MAC9B,WAAW,OAAO,UAAU,UAAU;AAElC,YAAI;AAEJ,YAAI;AAEA,yBAAe,kBAAkB,KAAK;AAAA,QAC1C,SAAS,WAAP;AAEE,gBAAM,IAAI,MAAM,iCAAkC,UAAoB,SAAS;AAAA,QACnF;AAGA,YAAI,aAAa,UAAU,KAAK,aAAa,GAAG,SAAS,MAAM;AAC3D,gBAAM,IAAI,MAAM,mEAAmE;AAAA,QACvF;AAEA,eAAO,WAAW,MAAM,aAAa,EAAE;AAAA,MAC3C,OAAO;AACH,cAAM,IAAI,MAAM,0DAA0D;AAAA,MAC9E;AAAA,IACJ;AAAA,IAMA,OAAO,WAAW,MAAoB;AAClC,aAAO,OAAO,IAAI;AAAA,IACtB;AAAA,IAKA,OAAO,gBAAsB;AACzB,aAAO,MAAM;AAAA,IACjB;AAAA,IAOA,OAAe,gBAAgB,YAA0B;AAErD,UAAI;AAAA,MAEJ,SAAS,WAAP;AACE,gBAAQ,IAAI,SAAS;AAAA,MACzB;AAEA,UAAI;AAEA,cAAM,eAAe,kBAAkB,UAAU;AAGjD,cAAM,kBAAkB,OAAO,UAAU;AAGzC,cAAM,cAAuD,CAAC;AAC9D,mBAAW,eAAe,cAAc;AACpC,sBAAY,YAAY,SAAS,OAAO,kBAAkB,YAAY,QAAS;AAAA,QACnF;AAGA,cAAM,WAAiB,YAAY,iBAAiB;AAAA,UAEhD,CAAC,SAA+B,YAAY,QAAQ,YAAY,QAAQ,OAAO,WAAW,IAAI;AAAA,UAC9F,CAAC;AAAA,QACL;AAGA,sBAAc,yBAAyB,QAAQ;AAG/C,eAAO;AAAA,MACX,SAAS,WAAP;AAEE,cAAM,IAAI,MAAM,uBAAwB,UAAoB,SAAS;AAAA,MACzE;AAAA,IACJ;AAAA,IAMA,OAAe,yBAAyB,UAAgB;AACpD,YAAM,YAAsB,CAAC;AAE7B,YAAM,gBAAgB,CAAC,MAAc,SAAe;AAEhD,eAAO,KAAK,OAAO,IAAI;AAGvB,YAAI,KAAK,WAAW,GAAG;AACnB,oBAAU,KAAK,IAAI;AAAA,QACvB,OAAO;AACH,UAAC,KAA+B,YAAY,EAAE,QAAQ,CAAC,UAAU,cAAc,MAAM,KAAK,CAAC;AAAA,QAC/F;AAAA,MACJ;AAGA,oBAAc,CAAC,GAAG,QAAQ;AAE1B,gBAAU,QAAQ,CAAC,SAAS;AAExB,iBAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;AAE9C,gBAAM,cAAc,KAAK;AAGzB,cAAI,YAAY,aAAa,GAAG;AAC5B;AAAA,UACJ;AAGA,gBAAM,YAAY,IAAI;AAAA,YAClB,KACK,MAAM,GAAG,QAAQ,CAAC,EAClB,IAAmB,CAAC,UAAU,EAAE,MAAM,QAAQ,KAAK,mBAAmB,EAAE,EAAE,EAC1E,OAAO,CAAC,YAAY,QAAQ,OAAO,SAAS,CAAC;AAAA,UACtD;AAGA,sBAAY,aAAa,SAAS;AAAA,QACtC;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;", + "names": ["Participant", "Lotto", "createLotto", "State", "createLotto"] +} diff --git a/dist/dsl/DSLDefinitionParser.d.ts b/dist/dsl/DSLDefinitionParser.d.ts new file mode 100644 index 0000000..023bdfe --- /dev/null +++ b/dist/dsl/DSLDefinitionParser.d.ts @@ -0,0 +1,163 @@ +/** + * A type defining the arguments that can be passed to an agent function. + */ +export type AgentFunctionArguments = (string | number | boolean | null | undefined)[]; +/** + * A guard attribute for a node. + */ +export type GuardAttributeDefinition = { + call: string; + args?: AgentFunctionArguments; +}; +/** + * A callback attribute for a node. + */ +export type CallbackAttributeDefinition = { + call: string; + args?: AgentFunctionArguments; +}; +/** + * A type defining a general node definition. + */ +export type NodeDefinition = { + type: string; + while?: GuardAttributeDefinition; + until?: GuardAttributeDefinition; + entry?: CallbackAttributeDefinition; + exit?: CallbackAttributeDefinition; + step?: CallbackAttributeDefinition; +}; +/** + * A composite node that can contain any number of child nodes. + */ +export type CompositeDefinition = NodeDefinition & { + children: AnyChildNode[]; +}; +/** + * A decorator node, a composite with only a single child node. + */ +export type DecoratorDefinition = NodeDefinition & { + child: AnyChildNode; +}; +/** + * A branch node. + */ +export type BranchDefinition = NodeDefinition & { + type: "branch"; + ref: string; +}; +/** + * An action node. + */ +export type ActionDefinition = NodeDefinition & { + type: "action"; + call: string; + args?: AgentFunctionArguments; +}; +/** + * A condition node. + */ +export type ConditionDefinition = NodeDefinition & { + type: "condition"; + call: string; + args?: AgentFunctionArguments; +}; +/** + * A wait node. + */ +export type WaitDefinition = NodeDefinition & { + type: "wait"; + duration: number | [number, number]; +}; +/** + * A sequence node. + */ +export type SequenceDefinition = CompositeDefinition & { + type: "sequence"; +}; +/** + * A selector node. + */ +export type SelectorDefinition = CompositeDefinition & { + type: "selector"; +}; +/** + * A lotto node. + */ +export type LottoDefinition = CompositeDefinition & { + type: "lotto"; + weights?: number[]; +}; +/** + * A parallel node. + */ +export type ParallelDefinition = CompositeDefinition & { + type: "parallel"; +}; +/** + * A root node. + */ +export type RootDefinition = DecoratorDefinition & { + type: "root"; + id?: string; +}; +/** + * A repeat node. + */ +export type RepeatDefinition = DecoratorDefinition & { + type: "repeat"; + iterations?: number | [number, number]; +}; +/** + * A retry node. + */ +export type RetryDefinition = DecoratorDefinition & { + type: "retry"; + attempts?: number | [number, number]; +}; +/** + * A flip node. + */ +export type FlipDefinition = DecoratorDefinition & { + type: "flip"; +}; +/** + * A succeed node. + */ +export type SucceedDefinition = DecoratorDefinition & { + type: "succeed"; +}; +/** + * A fail node. + */ +export type FailDefinition = DecoratorDefinition & { + type: "fail"; +}; +/** + * A type defining any node type. + */ +export type AnyNode = BranchDefinition | ActionDefinition | ConditionDefinition | WaitDefinition | SequenceDefinition | SelectorDefinition | LottoDefinition | ParallelDefinition | RootDefinition | RepeatDefinition | RetryDefinition | FlipDefinition | SucceedDefinition | FailDefinition; +/** + * A type defining any node type that can be a child of composite parent node. + */ +export type AnyChildNode = Exclude; +/** + * A type defining an object that holds a reference to substitued string literals parsed from the definition. + */ +type StringLiteralPlaceholders = { + [key: string]: string; +}; +/** + * Parse the tree definition string into a JSON definition. + * @param definition The tree definition string. + * @returns The root node JSON definitions. + */ +export declare function parseToJSON(definition: string): RootDefinition[]; +/** + * Converts the specified tree definition tokens into a JSON definition. + * @param tokens The tree definition tokens. + * @param placeholders The substituted string literal placeholders. + * @returns The root node JSON definitions. + */ +export declare function convertTokensToJSONDefinition(tokens: string[], placeholders: StringLiteralPlaceholders, processedDefinition: string): RootDefinition[]; +export {}; diff --git a/dist/dsl/DSLNodeArgumentParser.d.ts b/dist/dsl/DSLNodeArgumentParser.d.ts new file mode 100644 index 0000000..d9a29b1 --- /dev/null +++ b/dist/dsl/DSLNodeArgumentParser.d.ts @@ -0,0 +1,36 @@ +type Placeholders = { + [key: string]: string; +}; +export type Argument = { + /** The argument value. */ + value: T; + /** The argument type, used for validation. */ + type: string; +}; +type NullArgument = Argument & { + type: "null"; +}; +type BooleanArgument = Argument & { + type: "boolean"; +}; +type NumberArgument = Argument & { + type: "number"; + isInteger: boolean; +}; +type StringPlaceholderArgument = Argument & { + type: "string"; +}; +type IdentifierArgument = Argument & { + type: "identifier"; +}; +export type AnyArgument = NullArgument | BooleanArgument | NumberArgument | StringPlaceholderArgument | IdentifierArgument; +/** + * Parse an array of argument definitions from the specified tokens array. + * @param tokens The array tokens to parse the argument definitions from. + * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values. + * @param argumentValidator The argument validator function. + * @param validationFailedMessage The exception message to throw if argument validation fails. + * @returns An array of argument definitions parsed from the specified tokens array. + */ +export declare function getArguments(tokens: string[], stringArgumentPlaceholders: Placeholders, argumentValidator?: (arg: AnyArgument) => boolean, validationFailedMessage?: string): AnyArgument[]; +export {}; diff --git a/dist/dsl/DSLUtilities.d.ts b/dist/dsl/DSLUtilities.d.ts new file mode 100644 index 0000000..cdd7220 --- /dev/null +++ b/dist/dsl/DSLUtilities.d.ts @@ -0,0 +1,7 @@ +/** + * Pop the next raw token from the specified array of tokens and throw an error if it wasn't the expected one. + * @param tokens The array of tokens. + * @param expected An optional string or array or items, one of which must match the next popped token. + * @returns The popped token. + */ +export declare function popAndCheck(tokens: string[], expected?: string | string[]): string; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..6adc674 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,4 @@ +import { BehaviourTree, FlattenedTreeNode } from "./BehaviourTree"; +import State from "./State"; +export { BehaviourTree, State }; +export type { FlattenedTreeNode }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..4b59440 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,1863 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +var __publicField = (obj, key, value) => { + __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + return value; +}; + +// node_modules/lotto-draw/dist/Participant.js +var require_Participant = __commonJS({ + "node_modules/lotto-draw/dist/Participant.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Participant = void 0; + var Participant = function() { + function Participant2(participant, tickets) { + if (tickets === void 0) { + tickets = 1; + } + this._participant = participant; + this._tickets = tickets; + } + Object.defineProperty(Participant2.prototype, "participant", { + get: function() { + return this._participant; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Participant2.prototype, "tickets", { + get: function() { + return this._tickets; + }, + set: function(value) { + this._tickets = value; + }, + enumerable: false, + configurable: true + }); + return Participant2; + }(); + exports.Participant = Participant; + } +}); + +// node_modules/lotto-draw/dist/Utilities.js +var require_Utilities = __commonJS({ + "node_modules/lotto-draw/dist/Utilities.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isNaturalNumber = exports.isNullOrUndefined = void 0; + function isNullOrUndefined(value) { + return value === null || value === void 0; + } + exports.isNullOrUndefined = isNullOrUndefined; + function isNaturalNumber(value) { + return typeof value === "number" && value >= 1 && Math.floor(value) === value; + } + exports.isNaturalNumber = isNaturalNumber; + } +}); + +// node_modules/lotto-draw/dist/Lotto.js +var require_Lotto = __commonJS({ + "node_modules/lotto-draw/dist/Lotto.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Lotto = void 0; + var Participant_1 = require_Participant(); + var Utilities_1 = require_Utilities(); + var Lotto2 = function() { + function Lotto3(customRandom) { + this._participants = []; + this._customRandom = customRandom; + } + Lotto3.prototype.add = function(participant, tickets) { + if (tickets === void 0) { + tickets = 1; + } + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + var existingParticipant = this._participants.find(function(part) { + return part.participant === participant; + }); + if (existingParticipant) { + existingParticipant.tickets += tickets; + } else { + this._participants.push(new Participant_1.Participant(participant, tickets)); + } + return this; + }; + Lotto3.prototype.remove = function(participant, tickets) { + var existingParticipant = this._participants.find(function(part) { + return part.participant === participant; + }); + if (!existingParticipant) { + return this; + } + if (tickets !== void 0) { + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + existingParticipant.tickets -= tickets; + if (existingParticipant.tickets < 1) { + this._participants = this._participants.filter(function(part) { + return part !== existingParticipant; + }); + } + } else { + this._participants = this._participants.filter(function(part) { + return part !== existingParticipant; + }); + } + return this; + }; + Lotto3.prototype.draw = function(options) { + if (options === void 0) { + options = {}; + } + if (this._participants.length === 0) { + return null; + } + var redrawable = (0, Utilities_1.isNullOrUndefined)(options.redrawable) ? true : options.redrawable; + var pickable = []; + this._participants.forEach(function(_a) { + var participant = _a.participant, tickets = _a.tickets; + for (var ticketCount = 0; ticketCount < tickets; ticketCount++) { + pickable.push(participant); + } + }); + var random; + if (this._customRandom) { + random = this._customRandom(); + if (typeof random !== "number" || random < 0 || random >= 1) { + throw new Error("the 'random' function provided did not return a number between 0 (inclusive) and 1"); + } + } else { + random = Math.random(); + } + var winner = pickable[Math.floor(random * pickable.length)]; + if (!redrawable) { + this.remove(winner, 1); + } + return winner; + }; + Lotto3.prototype.drawMultiple = function(tickets, options) { + if (options === void 0) { + options = {}; + } + var uniqueResults = (0, Utilities_1.isNullOrUndefined)(options.unique) ? false : options.unique; + if (tickets === 0) { + return []; + } + if (!(0, Utilities_1.isNaturalNumber)(tickets)) { + throw new Error("tickets value must be a natural number"); + } + var result = []; + while (result.length < tickets && this._participants.length > 0) { + result.push(this.draw(options)); + } + if (uniqueResults) { + var unique = []; + for (var _i = 0, result_1 = result; _i < result_1.length; _i++) { + var participant = result_1[_i]; + if (unique.indexOf(participant) === -1) { + unique.push(participant); + } + } + result = unique; + } + return result; + }; + return Lotto3; + }(); + exports.Lotto = Lotto2; + } +}); + +// node_modules/lotto-draw/dist/createLotto.js +var require_createLotto = __commonJS({ + "node_modules/lotto-draw/dist/createLotto.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.createLotto = void 0; + var Lotto_1 = require_Lotto(); + function createLotto2(participantsOrOptions) { + if (!participantsOrOptions) { + return new Lotto_1.Lotto(); + } + if (Array.isArray(participantsOrOptions)) { + var participants = participantsOrOptions; + var lotto_1 = new Lotto_1.Lotto(); + participants.forEach(function(_a) { + var participant = _a[0], tokens = _a[1]; + return lotto_1.add(participant, tokens); + }); + return lotto_1; + } else { + var random = participantsOrOptions.random, participants = participantsOrOptions.participants; + var lotto_2 = new Lotto_1.Lotto(random); + if (participants) { + participants.forEach(function(_a) { + var participant = _a[0], tokens = _a[1]; + return lotto_2.add(participant, tokens); + }); + } + return lotto_2; + } + } + exports.createLotto = createLotto2; + } +}); + +// node_modules/lotto-draw/dist/index.js +var require_dist = __commonJS({ + "node_modules/lotto-draw/dist/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var createLotto_1 = require_createLotto(); + exports.default = createLotto_1.createLotto; + } +}); + +// src/index.ts +var src_exports = {}; +__export(src_exports, { + BehaviourTree: () => BehaviourTree, + State: () => State +}); +module.exports = __toCommonJS(src_exports); + +// src/attributes/guards/GuardUnsatisifedException.ts +var GuardUnsatisifedException = class extends Error { + constructor(source) { + super("A guard path condition has failed"); + this.source = source; + } + isSourceNode = (node) => node === this.source; +}; + +// src/attributes/guards/GuardPath.ts +var GuardPath = class { + constructor(nodes) { + this.nodes = nodes; + } + evaluate = (agent) => { + for (const details of this.nodes) { + for (const guard of details.guards) { + if (!guard.isSatisfied(agent)) { + throw new GuardUnsatisifedException(details.node); + } + } + } + }; +}; + +// src/State.ts +var State = /* @__PURE__ */ ((State2) => { + State2["READY"] = "mistreevous.ready"; + State2["RUNNING"] = "mistreevous.running"; + State2["SUCCEEDED"] = "mistreevous.succeeded"; + State2["FAILED"] = "mistreevous.failed"; + return State2; +})(State || {}); + +// src/nodes/Node.ts +var Node = class { + constructor(type, attributes, args) { + this.type = type; + this.attributes = attributes; + this.args = args; + } + uid = createNodeUid(); + state = "mistreevous.ready" /* READY */; + guardPath; + getState = () => this.state; + setState = (value) => { + this.state = value; + }; + getUid = () => this.uid; + getType = () => this.type; + getAttributes = () => this.attributes; + getArguments = () => this.args; + getAttribute(type) { + return this.getAttributes().filter((decorator) => decorator.getType().toUpperCase() === type.toUpperCase())[0] || null; + } + getGuardAttributes = () => this.getAttributes().filter((decorator) => decorator.isGuard()); + setGuardPath = (value) => this.guardPath = value; + hasGuardPath = () => !!this.guardPath; + is(value) { + return this.state === value; + } + reset() { + this.setState("mistreevous.ready" /* READY */); + } + abort(agent) { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + } + update(agent, options) { + if (this.is("mistreevous.succeeded" /* SUCCEEDED */) || this.is("mistreevous.failed" /* FAILED */)) { + return; + } + try { + this.guardPath.evaluate(agent); + if (this.is("mistreevous.ready" /* READY */)) { + this.getAttribute("entry")?.callAgentFunction(agent); + } + this.getAttribute("step")?.callAgentFunction(agent); + this.onUpdate(agent, options); + if (this.is("mistreevous.succeeded" /* SUCCEEDED */) || this.is("mistreevous.failed" /* FAILED */)) { + this.getAttribute("exit")?.callAgentFunction(agent, this.is("mistreevous.succeeded" /* SUCCEEDED */), false); + } + } catch (error) { + if (error instanceof GuardUnsatisifedException && error.isSourceNode(this)) { + this.abort(agent); + this.setState("mistreevous.failed" /* FAILED */); + } else { + throw error; + } + } + } +}; +function createNodeUid() { + var S4 = function() { + return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1); + }; + return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4(); +} + +// src/nodes/leaf/Leaf.ts +var Leaf = class extends Node { + isLeafNode = () => true; +}; + +// src/Lookup.ts +var Lookup = class { + static getFunc(name) { + return this.functionTable[name]; + } + static setFunc(name, func) { + this.functionTable[name] = func; + } + static getFuncInvoker(agent, name) { + const foundOnAgent = agent[name]; + if (foundOnAgent && typeof foundOnAgent === "function") { + return (args) => foundOnAgent.apply( + agent, + args.map((arg) => arg.value) + ); + } + if (this.functionTable[name] && typeof this.functionTable[name] === "function") { + return (args) => this.functionTable[name](agent, ...args.map((arg) => arg.value)); + } + return null; + } + static getSubtree(name) { + return this.subtreeTable[name]; + } + static setSubtree(name, subtree) { + this.subtreeTable[name] = subtree; + } + static remove(name) { + delete this.functionTable[name]; + delete this.subtreeTable[name]; + } + static empty() { + this.functionTable = {}; + this.subtreeTable = {}; + } +}; +__publicField(Lookup, "functionTable", {}); +__publicField(Lookup, "subtreeTable", {}); + +// src/nodes/leaf/Action.ts +var Action = class extends Leaf { + constructor(attributes, actionName, actionArguments) { + super("action", attributes, actionArguments); + this.actionName = actionName; + this.actionArguments = actionArguments; + } + isUsingUpdatePromise = false; + updatePromiseStateResult = null; + onUpdate(agent, options) { + if (this.isUsingUpdatePromise) { + if (this.updatePromiseStateResult) { + this.setState(this.updatePromiseStateResult); + } + return; + } + const actionFuncInvoker = Lookup.getFuncInvoker(agent, this.actionName); + if (actionFuncInvoker === null) { + throw new Error( + `cannot update action node as the action '${this.actionName}' function is not defined on the agent and has not been registered` + ); + } + const updateResult = actionFuncInvoker(this.actionArguments); + if (updateResult instanceof Promise) { + updateResult.then( + (result) => { + if (!this.isUsingUpdatePromise) { + return; + } + if (result !== "mistreevous.succeeded" /* SUCCEEDED */ && result !== "mistreevous.failed" /* FAILED */) { + throw new Error( + "action node promise resolved with an invalid value, expected a State.SUCCEEDED or State.FAILED value to be returned" + ); + } + this.updatePromiseStateResult = result; + }, + (reason) => { + if (!this.isUsingUpdatePromise) { + return; + } + throw new Error(reason); + } + ); + this.setState("mistreevous.running" /* RUNNING */); + this.isUsingUpdatePromise = true; + } else { + this.validateUpdateResult(updateResult); + this.setState(updateResult || "mistreevous.running" /* RUNNING */); + } + } + getName = () => this.actionName; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.isUsingUpdatePromise = false; + this.updatePromiseStateResult = null; + }; + validateUpdateResult = (result) => { + switch (result) { + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + case void 0: + return; + default: + throw new Error( + `action '${this.actionName}' 'onUpdate' returned an invalid response, expected an optional State.SUCCEEDED or State.FAILED value to be returned` + ); + } + }; +}; + +// src/nodes/leaf/Condition.ts +var Condition = class extends Leaf { + constructor(attributes, conditionName, conditionArguments) { + super("condition", attributes, conditionArguments); + this.conditionName = conditionName; + this.conditionArguments = conditionArguments; + } + onUpdate(agent, options) { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.conditionName); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot update condition node as the condition '${this.conditionName}' function is not defined on the agent and has not been registered` + ); + } + this.setState(!!conditionFuncInvoker(this.conditionArguments) ? "mistreevous.succeeded" /* SUCCEEDED */ : "mistreevous.failed" /* FAILED */); + } + getName = () => this.conditionName; +}; + +// src/nodes/leaf/Wait.ts +var Wait = class extends Leaf { + constructor(attributes, duration, durationMin, durationMax) { + super("wait", attributes, []); + this.duration = duration; + this.durationMin = durationMin; + this.durationMax = durationMax; + } + initialUpdateTime = 0; + totalDuration = null; + waitedDuration = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.initialUpdateTime = new Date().getTime(); + this.waitedDuration = 0; + if (this.duration !== null) { + this.totalDuration = this.duration; + } else if (this.durationMin !== null && this.durationMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.totalDuration = Math.floor( + random() * (this.durationMax - this.durationMin + 1) + this.durationMin + ); + } else { + this.totalDuration = null; + } + this.setState("mistreevous.running" /* RUNNING */); + } + if (this.totalDuration === null) { + return; + } + if (typeof options.getDeltaTime === "function") { + const deltaTime = options.getDeltaTime(); + if (typeof deltaTime !== "number" || isNaN(deltaTime)) { + throw new Error("The delta time must be a valid number and not NaN."); + } + this.waitedDuration += deltaTime * 1e3; + } else { + this.waitedDuration = new Date().getTime() - this.initialUpdateTime; + } + if (this.waitedDuration >= this.totalDuration) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + } + } + getName = () => { + if (this.duration !== null) { + return `WAIT ${this.duration}ms`; + } else if (this.durationMin !== null && this.durationMax !== null) { + return `WAIT ${this.durationMin}ms-${this.durationMax}ms`; + } else { + return "WAIT"; + } + }; +}; + +// src/nodes/decorator/Decorator.ts +var Decorator = class extends Node { + constructor(type, attributes, child) { + super(type, attributes, []); + this.child = child; + } + isLeafNode = () => false; + getChildren = () => [this.child]; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.child.reset(); + }; + abort = (agent) => { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.child.abort(agent); + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + }; +}; + +// src/nodes/decorator/Root.ts +var Root = class extends Decorator { + constructor(attributes, child) { + super("root", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + this.setState(this.child.getState()); + } + getName = () => "ROOT"; +}; + +// src/nodes/decorator/Repeat.ts +var Repeat = class extends Decorator { + constructor(attributes, iterations, iterationsMin, iterationsMax, child) { + super("repeat", attributes, child); + this.iterations = iterations; + this.iterationsMin = iterationsMin; + this.iterationsMax = iterationsMax; + } + targetIterationCount = null; + currentIterationCount = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.child.reset(); + this.currentIterationCount = 0; + this.setTargetIterationCount(options); + } + if (this.canIterate()) { + this.setState("mistreevous.running" /* RUNNING */); + if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.child.reset(); + } + this.child.update(agent, options); + if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } else if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.currentIterationCount += 1; + } + } else { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + } + } + getName = () => { + if (this.iterations !== null) { + return `REPEAT ${this.iterations}x`; + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + return `REPEAT ${this.iterationsMin}x-${this.iterationsMax}x`; + } else { + return "REPEAT"; + } + }; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.currentIterationCount = 0; + this.child.reset(); + }; + canIterate = () => { + if (this.targetIterationCount !== null) { + return this.currentIterationCount < this.targetIterationCount; + } + return true; + }; + setTargetIterationCount = (options) => { + if (this.iterations !== null) { + this.targetIterationCount = this.iterations; + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.targetIterationCount = Math.floor( + random() * (this.iterationsMax - this.iterationsMin + 1) + this.iterationsMin + ); + } else { + this.targetIterationCount = null; + } + }; +}; + +// src/nodes/decorator/Retry.ts +var Retry = class extends Decorator { + constructor(attributes, attempts, attemptsMin, attemptsMax, child) { + super("retry", attributes, child); + this.attempts = attempts; + this.attemptsMin = attemptsMin; + this.attemptsMax = attemptsMax; + } + targetAttemptCount = null; + currentAttemptCount = 0; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + this.child.reset(); + this.currentAttemptCount = 0; + this.setTargetAttemptCount(options); + } + if (this.canAttempt()) { + this.setState("mistreevous.running" /* RUNNING */); + if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.child.reset(); + } + this.child.update(agent, options); + if (this.child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } else if (this.child.getState() === "mistreevous.failed" /* FAILED */) { + this.currentAttemptCount += 1; + } + } else { + this.setState("mistreevous.failed" /* FAILED */); + } + } + getName = () => { + if (this.attempts !== null) { + return `RETRY ${this.attempts}x`; + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + return `RETRY ${this.attemptsMin}x-${this.attemptsMax}x`; + } else { + return "RETRY"; + } + }; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.currentAttemptCount = 0; + this.child.reset(); + }; + canAttempt = () => { + if (this.targetAttemptCount !== null) { + return this.currentAttemptCount < this.targetAttemptCount; + } + return true; + }; + setTargetAttemptCount = (options) => { + if (this.attempts !== null) { + this.targetAttemptCount = this.attempts; + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + const random = typeof options.random === "function" ? options.random : Math.random; + this.targetAttemptCount = Math.floor( + random() * (this.attemptsMax - this.attemptsMin + 1) + this.attemptsMin + ); + } else { + this.targetAttemptCount = null; + } + }; +}; + +// src/nodes/decorator/Flip.ts +var Flip = class extends Decorator { + constructor(attributes, child) { + super("flip", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + this.setState("mistreevous.failed" /* FAILED */); + break; + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "FLIP"; +}; + +// src/nodes/decorator/Succeed.ts +var Succeed = class extends Decorator { + constructor(attributes, child) { + super("succeed", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "SUCCEED"; +}; + +// src/nodes/decorator/Fail.ts +var Fail = class extends Decorator { + constructor(attributes, child) { + super("fail", attributes, child); + } + onUpdate(agent, options) { + if (this.child.getState() === "mistreevous.ready" /* READY */ || this.child.getState() === "mistreevous.running" /* RUNNING */) { + this.child.update(agent, options); + } + switch (this.child.getState()) { + case "mistreevous.running" /* RUNNING */: + this.setState("mistreevous.running" /* RUNNING */); + break; + case "mistreevous.succeeded" /* SUCCEEDED */: + case "mistreevous.failed" /* FAILED */: + this.setState("mistreevous.failed" /* FAILED */); + break; + default: + this.setState("mistreevous.ready" /* READY */); + } + } + getName = () => "FAIL"; +}; + +// src/nodes/composite/Lotto.ts +var import_lotto_draw = __toESM(require_dist()); + +// src/nodes/composite/Composite.ts +var Composite = class extends Node { + constructor(type, attributes, children) { + super(type, attributes, []); + this.children = children; + } + isLeafNode = () => false; + getChildren = () => this.children; + reset = () => { + this.setState("mistreevous.ready" /* READY */); + this.getChildren().forEach((child) => child.reset()); + }; + abort = (agent) => { + if (!this.is("mistreevous.running" /* RUNNING */)) { + return; + } + this.getChildren().forEach((child) => child.abort(agent)); + this.reset(); + this.getAttribute("exit")?.callAgentFunction(agent, false, true); + }; +}; + +// src/nodes/composite/Lotto.ts +var Lotto = class extends Composite { + constructor(attributes, tickets, children) { + super("lotto", attributes, children); + this.tickets = tickets; + } + selectedChild; + onUpdate(agent, options) { + if (this.is("mistreevous.ready" /* READY */)) { + const lottoDraw = (0, import_lotto_draw.default)({ + random: options.random, + participants: this.children.map((child, index) => [child, this.tickets[index] || 1]) + }); + this.selectedChild = lottoDraw.draw() || void 0; + } + if (!this.selectedChild) { + throw new Error("failed to update lotto node as it has no active child"); + } + if (this.selectedChild.getState() === "mistreevous.ready" /* READY */ || this.selectedChild.getState() === "mistreevous.running" /* RUNNING */) { + this.selectedChild.update(agent, options); + } + this.setState(this.selectedChild.getState()); + } + getName = () => this.tickets.length ? `LOTTO [${this.tickets.join(",")}]` : "LOTTO"; +}; + +// src/nodes/composite/Selector.ts +var Selector = class extends Composite { + constructor(attributes, children) { + super("selector", attributes, children); + this.children = children; + } + onUpdate(agent, options) { + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + if (this.children.indexOf(child) === this.children.length - 1) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } else { + continue; + } + } + if (child.getState() === "mistreevous.running" /* RUNNING */) { + this.setState("mistreevous.running" /* RUNNING */); + return; + } + throw new Error("child node was not in an expected state."); + } + } + getName = () => "SELECTOR"; +}; + +// src/nodes/composite/Sequence.ts +var Sequence = class extends Composite { + constructor(attributes, children) { + super("sequence", attributes, children); + this.children = children; + } + onUpdate(agent, options) { + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + if (this.children.indexOf(child) === this.children.length - 1) { + this.setState("mistreevous.succeeded" /* SUCCEEDED */); + return; + } else { + continue; + } + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + this.setState("mistreevous.failed" /* FAILED */); + return; + } + if (child.getState() === "mistreevous.running" /* RUNNING */) { + this.setState("mistreevous.running" /* RUNNING */); + return; + } + throw new Error("child node was not in an expected state."); + } + } + getName = () => "SEQUENCE"; +}; + +// src/nodes/composite/Parallel.ts +var Parallel = class extends Composite { + constructor(attributes, children) { + super("parallel", attributes, children); + } + onUpdate(agent, options) { + let succeededCount = 0; + let hasChildFailed = false; + for (const child of this.children) { + if (child.getState() === "mistreevous.ready" /* READY */ || child.getState() === "mistreevous.running" /* RUNNING */) { + child.update(agent, options); + } + if (child.getState() === "mistreevous.succeeded" /* SUCCEEDED */) { + succeededCount++; + continue; + } + if (child.getState() === "mistreevous.failed" /* FAILED */) { + hasChildFailed = true; + break; + } + if (child.getState() !== "mistreevous.running" /* RUNNING */) { + throw new Error("child node was not in an expected state."); + } + } + if (hasChildFailed) { + this.setState("mistreevous.failed" /* FAILED */); + for (const child of this.children) { + if (child.getState() === "mistreevous.running" /* RUNNING */) { + child.abort(agent); + } + } + } else { + this.setState(succeededCount === this.children.length ? "mistreevous.succeeded" /* SUCCEEDED */ : "mistreevous.running" /* RUNNING */); + } + } + getName = () => "PARALLEL"; +}; + +// src/attributes/Attribute.ts +var Attribute = class { + constructor(type, args) { + this.type = type; + this.args = args; + } + getType = () => this.type; + getArguments = () => this.args; +}; + +// src/attributes/guards/Guard.ts +var Guard = class extends Attribute { + constructor(type, args, condition) { + super(type, args); + this.condition = condition; + } + getCondition = () => this.condition; + isGuard = () => true; + getDetails() { + return { + type: this.getType(), + args: this.getArguments(), + condition: this.getCondition() + }; + } +}; + +// src/attributes/guards/While.ts +var While = class extends Guard { + constructor(condition, args) { + super("while", args, condition); + } + isSatisfied = (agent) => { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition()); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered` + ); + } + return !!conditionFuncInvoker(this.args); + }; +}; + +// src/attributes/guards/Until.ts +var Until = class extends Guard { + constructor(condition, args) { + super("until", args, condition); + } + isSatisfied = (agent) => { + const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition()); + if (conditionFuncInvoker === null) { + throw new Error( + `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered` + ); + } + return !!!conditionFuncInvoker(this.args); + }; +}; + +// src/attributes/callbacks/Callback.ts +var Callback = class extends Attribute { + constructor(type, args, functionName) { + super(type, args); + this.functionName = functionName; + } + getFunctionName = () => this.functionName; + isGuard = () => false; + getDetails() { + return { + type: this.getType(), + args: this.getArguments(), + functionName: this.getFunctionName() + }; + } +}; + +// src/attributes/callbacks/Entry.ts +var Entry = class extends Callback { + constructor(functionName, args) { + super("entry", args, functionName); + } + callAgentFunction = (agent) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call entry function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker(this.args); + }; +}; + +// src/attributes/callbacks/Exit.ts +var Exit = class extends Callback { + constructor(functionName, args) { + super("exit", args, functionName); + } + callAgentFunction = (agent, isSuccess, isAborted) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call exit function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker([{ value: { succeeded: isSuccess, aborted: isAborted } }, ...this.args]); + }; +}; + +// src/attributes/callbacks/Step.ts +var Step = class extends Callback { + constructor(functionName, args) { + super("step", args, functionName); + } + callAgentFunction = (agent) => { + const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName()); + if (callbackFuncInvoker === null) { + throw new Error( + `cannot call step function '${this.getFunctionName()}' as is not defined on the agent and has not been registered` + ); + } + callbackFuncInvoker(this.args); + }; +}; + +// src/RootAstNodesBuilder.ts +var AttributeFactories = { + WHILE: (condition, attributeArguments) => new While(condition, attributeArguments), + UNTIL: (condition, attributeArguments) => new Until(condition, attributeArguments), + ENTRY: (functionName, attributeArguments) => new Entry(functionName, attributeArguments), + EXIT: (functionName, attributeArguments) => new Exit(functionName, attributeArguments), + STEP: (functionName, attributeArguments) => new Step(functionName, attributeArguments) +}; +var ASTNodeFactories = { + ROOT: () => ({ + type: "root", + attributes: [], + name: null, + children: [], + validate(depth) { + if (depth > 1) { + throw new Error("a root node cannot be the child of another node"); + } + if (this.children.length !== 1) { + throw new Error("a root node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Root( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + BRANCH: () => ({ + type: "branch", + branchName: "", + validate() { + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + const targetRootNode = namedRootNodeProvider(this.branchName); + if (visitedBranches.indexOf(this.branchName) !== -1) { + throw new Error(`circular dependency found in branch node references for branch '${this.branchName}'`); + } + if (targetRootNode) { + return targetRootNode.createNodeInstance(namedRootNodeProvider, visitedBranches.concat(this.branchName)).getChildren()[0]; + } else { + throw new Error(`branch references root node '${this.branchName}' which has not been defined`); + } + } + }), + SELECTOR: () => ({ + type: "selector", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a selector node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Selector( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + SEQUENCE: () => ({ + type: "sequence", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a sequence node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Sequence( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + PARALLEL: () => ({ + type: "parallel", + attributes: [], + children: [], + validate() { + if (this.children.length < 1) { + throw new Error("a parallel node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Parallel( + this.attributes, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + LOTTO: () => ({ + type: "lotto", + attributes: [], + children: [], + tickets: [], + validate() { + if (this.children.length < 1) { + throw new Error("a lotto node must have at least a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Lotto( + this.attributes, + this.tickets, + this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice())) + ); + } + }), + REPEAT: () => ({ + type: "repeat", + attributes: [], + iterations: null, + iterationsMin: null, + iterationsMax: null, + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a repeat node must have a single child"); + } + if (this.iterations !== null) { + if (this.iterations < 0) { + throw new Error("a repeat node must have a positive number of iterations if defined"); + } + } else if (this.iterationsMin !== null && this.iterationsMax !== null) { + if (this.iterationsMin < 0 || this.iterationsMax < 0) { + throw new Error( + "a repeat node must have a positive minimum and maximum iteration count if defined" + ); + } + if (this.iterationsMin > this.iterationsMax) { + throw new Error( + "a repeat node must not have a minimum iteration count that exceeds the maximum iteration count" + ); + } + } else { + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Repeat( + this.attributes, + this.iterations, + this.iterationsMin, + this.iterationsMax, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + RETRY: () => ({ + type: "retry", + attributes: [], + attempts: null, + attemptsMin: null, + attemptsMax: null, + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a retry node must have a single child"); + } + if (this.attempts !== null) { + if (this.attempts < 0) { + throw new Error("a retry node must have a positive number of attempts if defined"); + } + } else if (this.attemptsMin !== null && this.attemptsMax !== null) { + if (this.attemptsMin < 0 || this.attemptsMax < 0) { + throw new Error("a retry node must have a positive minimum and maximum attempt count if defined"); + } + if (this.attemptsMin > this.attemptsMax) { + throw new Error( + "a retry node must not have a minimum attempt count that exceeds the maximum attempt count" + ); + } + } else { + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Retry( + this.attributes, + this.attempts, + this.attemptsMin, + this.attemptsMax, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + FLIP: () => ({ + type: "flip", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a flip node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Flip( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + SUCCEED: () => ({ + type: "succeed", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a succeed node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Succeed( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + FAIL: () => ({ + type: "fail", + attributes: [], + children: [], + validate() { + if (this.children.length !== 1) { + throw new Error("a fail node must have a single child"); + } + }, + createNodeInstance(namedRootNodeProvider, visitedBranches) { + return new Fail( + this.attributes, + this.children[0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice()) + ); + } + }), + WAIT: () => ({ + type: "wait", + attributes: [], + duration: null, + durationMin: null, + durationMax: null, + validate() { + if (this.duration !== null) { + if (this.duration < 0) { + throw new Error("a wait node must have a positive duration"); + } + } else if (this.durationMin !== null && this.durationMax !== null) { + if (this.durationMin < 0 || this.durationMax < 0) { + throw new Error("a wait node must have a positive minimum and maximum duration"); + } + if (this.durationMin > this.durationMax) { + throw new Error("a wait node must not have a minimum duration that exceeds the maximum duration"); + } + } else { + } + }, + createNodeInstance() { + return new Wait(this.attributes, this.duration, this.durationMin, this.durationMax); + } + }), + ACTION: () => ({ + type: "action", + attributes: [], + actionName: "", + actionArguments: [], + validate() { + }, + createNodeInstance() { + return new Action(this.attributes, this.actionName, this.actionArguments); + } + }), + CONDITION: () => ({ + type: "condition", + attributes: [], + conditionName: "", + conditionArguments: [], + validate() { + }, + createNodeInstance() { + return new Condition(this.attributes, this.conditionName, this.conditionArguments); + } + }) +}; +function buildRootASTNodes(definition) { + const { placeholders, processedDefinition } = substituteStringLiterals(definition); + const tokens = parseTokensFromDefinition(processedDefinition); + if (tokens.length < 3) { + throw new Error("invalid token count"); + } + if (tokens.filter((token) => token === "{").length !== tokens.filter((token) => token === "}").length) { + throw new Error("scope character mismatch"); + } + const stack = [[]]; + const rootScope = stack[0]; + while (tokens.length) { + const token = tokens.shift(); + const currentScope = stack[stack.length - 1]; + switch (token.toUpperCase()) { + case "ROOT": { + const node = ASTNodeFactories.ROOT(); + rootScope.push(node); + if (tokens[0] === "[") { + const rootArguments = getArguments(tokens, placeholders); + if (rootArguments.length === 1 && rootArguments[0].type === "identifier") { + node.name = rootArguments[0].value; + } else { + throw new Error("expected single root name argument"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "BRANCH": { + const node = ASTNodeFactories.BRANCH(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected single branch name argument"); + } + const branchArguments = getArguments(tokens, placeholders); + if (branchArguments.length === 1 && branchArguments[0].type === "identifier") { + node.branchName = branchArguments[0].value; + } else { + throw new Error("expected single branch name argument"); + } + break; + } + case "SELECTOR": { + const node = ASTNodeFactories.SELECTOR(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "SEQUENCE": { + const node = ASTNodeFactories.SEQUENCE(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "PARALLEL": { + const node = ASTNodeFactories.PARALLEL(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "LOTTO": { + const node = ASTNodeFactories.LOTTO(); + currentScope.push(node); + if (tokens[0] === "[") { + node.tickets = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "lotto node ticket counts must be integer values" + ).map((argument) => argument.value); + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "CONDITION": { + const node = ASTNodeFactories.CONDITION(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected condition name identifier argument"); + } + const conditionArguments = getArguments(tokens, placeholders); + if (conditionArguments.length && conditionArguments[0].type === "identifier") { + node.conditionName = conditionArguments.shift().value; + } else { + throw new Error("expected condition name identifier argument"); + } + conditionArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid condition node argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + node.conditionArguments = conditionArguments; + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "FLIP": { + const node = ASTNodeFactories.FLIP(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "SUCCEED": { + const node = ASTNodeFactories.SUCCEED(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "FAIL": { + const node = ASTNodeFactories.FAIL(); + currentScope.push(node); + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "WAIT": { + const node = ASTNodeFactories.WAIT(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "wait node durations must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.duration = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.durationMin = nodeArguments[0]; + node.durationMax = nodeArguments[1]; + } else if (nodeArguments.length > 2) { + throw new Error("invalid number of wait node duration arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "REPEAT": { + const node = ASTNodeFactories.REPEAT(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "repeat node iteration counts must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.iterations = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.iterationsMin = nodeArguments[0]; + node.iterationsMax = nodeArguments[1]; + } else { + throw new Error("invalid number of repeat node iteration count arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "RETRY": { + const node = ASTNodeFactories.RETRY(); + currentScope.push(node); + if (tokens[0] === "[") { + const nodeArguments = getArguments( + tokens, + placeholders, + (arg) => arg.type === "number" && !!arg.isInteger, + "retry node attempt counts must be integer values" + ).map((argument) => argument.value); + if (nodeArguments.length === 1) { + node.attempts = nodeArguments[0]; + } else if (nodeArguments.length === 2) { + node.attemptsMin = nodeArguments[0]; + node.attemptsMax = nodeArguments[1]; + } else { + throw new Error("invalid number of retry node attempt count arguments defined"); + } + } + node.attributes = getAttributes(tokens, placeholders); + popAndCheck(tokens, "{"); + stack.push(node.children); + break; + } + case "ACTION": { + const node = ASTNodeFactories.ACTION(); + currentScope.push(node); + if (tokens[0] !== "[") { + throw new Error("expected action name identifier argument"); + } + const actionArguments = getArguments(tokens, placeholders); + if (actionArguments.length && actionArguments[0].type === "identifier") { + node.actionName = actionArguments.shift().value; + } else { + throw new Error("expected action name identifier argument"); + } + actionArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid action node argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + node.actionArguments = actionArguments; + node.attributes = getAttributes(tokens, placeholders); + break; + } + case "}": { + stack.pop(); + break; + } + default: { + throw new Error(`unexpected token '${token}'`); + } + } + } + const validateASTNode = (node, depth) => { + node.validate(depth); + (node.children || []).forEach((child) => validateASTNode(child, depth + 1)); + }; + validateASTNode( + { + children: stack[0], + validate() { + if (this.children.length === 0) { + throw new Error("expected root node to have been defined"); + } + for (const definitionLevelNode of this.children) { + if (definitionLevelNode.type !== "root") { + throw new Error("expected root node at base of definition"); + } + } + if (this.children.filter((definitionLevelNode) => definitionLevelNode.name === null).length !== 1) { + throw new Error("expected single unnamed root node at base of definition to act as main root"); + } + const rootNodeNames = []; + for (const definitionLevelNode of this.children) { + if (rootNodeNames.indexOf(definitionLevelNode.name) !== -1) { + throw new Error(`multiple root nodes found with duplicate name '${definitionLevelNode.name}'`); + } else { + rootNodeNames.push(definitionLevelNode.name); + } + } + } + }, + 0 + ); + return stack[0]; +} +function popAndCheck(tokens, expected) { + const popped = tokens.shift(); + if (popped === void 0) { + throw new Error("unexpected end of definition"); + } + if (expected !== void 0) { + var tokenMatchesExpectation = [].concat(expected).some((item) => popped.toUpperCase() === item.toUpperCase()); + if (!tokenMatchesExpectation) { + const expectationString = [].concat(expected).map((item) => "'" + item + "'").join(" or "); + throw new Error(`unexpected token found. Expected '${expectationString}' but got '${popped}'`); + } + } + return popped; +} +function getArguments(tokens, stringArgumentPlaceholders, argumentValidator, validationFailedMessage) { + const closer = popAndCheck(tokens, ["[", "("]) === "[" ? "]" : ")"; + const argumentListTokens = []; + const argumentList = []; + while (tokens.length && tokens[0] !== closer) { + argumentListTokens.push(tokens.shift()); + } + argumentListTokens.forEach((token, index) => { + const shouldBeArgumentToken = !(index & 1); + if (shouldBeArgumentToken) { + const argumentDefinition = getArgumentDefinition(token, stringArgumentPlaceholders); + if (argumentValidator && !argumentValidator(argumentDefinition)) { + throw new Error(validationFailedMessage); + } + argumentList.push(argumentDefinition); + } else { + if (token !== ",") { + throw new Error(`invalid argument list, expected ',' or ']' but got '${token}'`); + } + } + }); + popAndCheck(tokens, closer); + return argumentList; +} +function getArgumentDefinition(token, stringArgumentPlaceholders) { + if (token === "null") { + return { + value: null, + type: "null" + }; + } + if (token === "true" || token === "false") { + return { + value: token === "true", + type: "boolean" + }; + } + if (!isNaN(token)) { + return { + value: parseFloat(token), + isInteger: parseFloat(token) === parseInt(token, 10), + type: "number" + }; + } + if (token.match(/^@@\d+@@$/g)) { + return { + value: stringArgumentPlaceholders[token].replace('\\"', '"'), + type: "string" + }; + } + return { + value: token, + type: "identifier" + }; +} +function getAttributes(tokens, stringArgumentPlaceholders) { + const attributes = []; + const attributesFound = []; + let attributeFactory = AttributeFactories[(tokens[0] || "").toUpperCase()]; + while (attributeFactory) { + if (attributesFound.indexOf(tokens[0].toUpperCase()) !== -1) { + throw new Error(`duplicate attribute '${tokens[0].toUpperCase()}' found for node`); + } + attributesFound.push(tokens.shift().toUpperCase()); + const attributeArguments = getArguments(tokens, stringArgumentPlaceholders); + if (attributeArguments.length === 0 || attributeArguments[0].type !== "identifier") { + throw new Error("expected agent function name identifier argument for attribute"); + } + const attributeFunctionName = attributeArguments.shift(); + attributeArguments.filter((arg) => arg.type === "identifier").forEach((arg) => { + throw new Error( + "invalid attribute argument value '" + arg.value + "', must be string, number, boolean or null" + ); + }); + attributes.push(attributeFactory(attributeFunctionName.value, attributeArguments)); + attributeFactory = AttributeFactories[(tokens[0] || "").toUpperCase()]; + } + return attributes; +} +function substituteStringLiterals(definition) { + const placeholders = {}; + const processedDefinition = definition.replace(/\"(\\.|[^"\\])*\"/g, (match) => { + var strippedMatch = match.substring(1, match.length - 1); + var placeholder = Object.keys(placeholders).find((key) => placeholders[key] === strippedMatch); + if (!placeholder) { + placeholder = `@@${Object.keys(placeholders).length}@@`; + placeholders[placeholder] = strippedMatch; + } + return placeholder; + }); + return { placeholders, processedDefinition }; +} +function parseTokensFromDefinition(definition) { + definition = definition.replace(/\(/g, " ( "); + definition = definition.replace(/\)/g, " ) "); + definition = definition.replace(/\{/g, " { "); + definition = definition.replace(/\}/g, " } "); + definition = definition.replace(/\]/g, " ] "); + definition = definition.replace(/\[/g, " [ "); + definition = definition.replace(/\,/g, " , "); + return definition.replace(/\s+/g, " ").trim().split(" "); +} + +// src/BehaviourTree.ts +var BehaviourTree = class { + constructor(definition, agent, options = {}) { + this.agent = agent; + this.options = options; + if (typeof definition !== "string") { + throw new Error("the tree definition must be a string"); + } + if (typeof agent !== "object" || agent === null) { + throw new Error("the agent must be defined and not null"); + } + this.rootNode = BehaviourTree._createRootNode(definition); + } + rootNode; + isRunning() { + return this.rootNode.getState() === "mistreevous.running" /* RUNNING */; + } + getState() { + return this.rootNode.getState(); + } + step() { + if (this.rootNode.getState() === "mistreevous.succeeded" /* SUCCEEDED */ || this.rootNode.getState() === "mistreevous.failed" /* FAILED */) { + this.rootNode.reset(); + } + try { + this.rootNode.update(this.agent, this.options); + } catch (exception) { + throw new Error(`error stepping tree: ${exception.message}`); + } + } + reset() { + this.rootNode.reset(); + } + getFlattenedNodeDetails() { + const flattenedTreeNodes = []; + const processNode = (node, parentUid) => { + const guards = node.getAttributes().filter((attribute) => attribute.isGuard()).map((attribute) => attribute.getDetails()); + const callbacks = node.getAttributes().filter((attribute) => !attribute.isGuard()).map((attribute) => attribute.getDetails()); + flattenedTreeNodes.push({ + id: node.getUid(), + type: node.getType(), + caption: node.getName(), + state: node.getState(), + guards, + callbacks, + args: node.getArguments(), + parentId: parentUid + }); + if (!node.isLeafNode()) { + node.getChildren().forEach((child) => processNode(child, node.getUid())); + } + }; + processNode(this.rootNode, null); + return flattenedTreeNodes; + } + static register(name, value) { + if (typeof value === "function") { + Lookup.setFunc(name, value); + } else if (typeof value === "string") { + let rootASTNodes; + try { + rootASTNodes = buildRootASTNodes(value); + } catch (exception) { + throw new Error(`error registering definition: ${exception.message}`); + } + if (rootASTNodes.length != 1 || rootASTNodes[0].name !== null) { + throw new Error("error registering definition: expected a single unnamed root node"); + } + Lookup.setSubtree(name, rootASTNodes[0]); + } else { + throw new Error("unexpected value, expected string definition or function"); + } + } + static unregister(name) { + Lookup.remove(name); + } + static unregisterAll() { + Lookup.empty(); + } + static _createRootNode(definition) { + try { + } catch (exception) { + console.log(exception); + } + try { + const rootASTNodes = buildRootASTNodes(definition); + const mainRootNodeKey = Symbol("__root__"); + const rootNodeMap = {}; + for (const rootASTNode of rootASTNodes) { + rootNodeMap[rootASTNode.name === null ? mainRootNodeKey : rootASTNode.name] = rootASTNode; + } + const rootNode = rootNodeMap[mainRootNodeKey].createNodeInstance( + (name) => rootNodeMap[name] ? rootNodeMap[name] : Lookup.getSubtree(name), + [] + ); + BehaviourTree._applyLeafNodeGuardPaths(rootNode); + return rootNode; + } catch (exception) { + throw new Error(`error parsing tree: ${exception.message}`); + } + } + static _applyLeafNodeGuardPaths(rootNode) { + const nodePaths = []; + const findLeafNodes = (path, node) => { + path = path.concat(node); + if (node.isLeafNode()) { + nodePaths.push(path); + } else { + node.getChildren().forEach((child) => findLeafNodes(path, child)); + } + }; + findLeafNodes([], rootNode); + nodePaths.forEach((path) => { + for (let depth = 0; depth < path.length; depth++) { + const currentNode = path[depth]; + if (currentNode.hasGuardPath()) { + continue; + } + const guardPath = new GuardPath( + path.slice(0, depth + 1).map((node) => ({ node, guards: node.getGuardAttributes() })).filter((details) => details.guards.length > 0) + ); + currentNode.setGuardPath(guardPath); + } + }); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + BehaviourTree, + State +}); +//# sourceMappingURL=index.js.map diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..4f0aab2 --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../node_modules/lotto-draw/dist/Participant.js", "../node_modules/lotto-draw/dist/Utilities.js", "../node_modules/lotto-draw/dist/Lotto.js", "../node_modules/lotto-draw/dist/createLotto.js", "../node_modules/lotto-draw/dist/index.js", "../src/index.ts", "../src/attributes/guards/GuardUnsatisifedException.ts", "../src/attributes/guards/GuardPath.ts", "../src/State.ts", "../src/nodes/Node.ts", "../src/nodes/leaf/Leaf.ts", "../src/Lookup.ts", "../src/nodes/leaf/Action.ts", "../src/nodes/leaf/Condition.ts", "../src/nodes/leaf/Wait.ts", "../src/nodes/decorator/Decorator.ts", "../src/nodes/decorator/Root.ts", "../src/nodes/decorator/Repeat.ts", "../src/nodes/decorator/Retry.ts", "../src/nodes/decorator/Flip.ts", "../src/nodes/decorator/Succeed.ts", "../src/nodes/decorator/Fail.ts", "../src/nodes/composite/Lotto.ts", "../src/nodes/composite/Composite.ts", "../src/nodes/composite/Selector.ts", "../src/nodes/composite/Sequence.ts", "../src/nodes/composite/Parallel.ts", "../src/attributes/Attribute.ts", "../src/attributes/guards/Guard.ts", "../src/attributes/guards/While.ts", "../src/attributes/guards/Until.ts", "../src/attributes/callbacks/Callback.ts", "../src/attributes/callbacks/Entry.ts", "../src/attributes/callbacks/Exit.ts", "../src/attributes/callbacks/Step.ts", "../src/RootAstNodesBuilder.ts", "../src/BehaviourTree.ts"], + "sourcesContent": ["\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.Participant = void 0;\r\n/**\r\n * A participant that holds a number of tickets.\r\n */\r\nvar Participant = /** @class */ (function () {\r\n /**\r\n * Creates an instance of the Participant class.\r\n * @param participant The actual participant.\r\n * @param tickets The number of tickets held by the participant.\r\n */\r\n function Participant(participant, tickets) {\r\n if (tickets === void 0) { tickets = 1; }\r\n this._participant = participant;\r\n this._tickets = tickets;\r\n }\r\n Object.defineProperty(Participant.prototype, \"participant\", {\r\n /** Gets the actual participant. */\r\n get: function () {\r\n return this._participant;\r\n },\r\n enumerable: false,\r\n configurable: true\r\n });\r\n Object.defineProperty(Participant.prototype, \"tickets\", {\r\n /** Gets or sets the number of tickets held by the participant. */\r\n get: function () {\r\n return this._tickets;\r\n },\r\n set: function (value) {\r\n this._tickets = value;\r\n },\r\n enumerable: false,\r\n configurable: true\r\n });\r\n return Participant;\r\n}());\r\nexports.Participant = Participant;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.isNaturalNumber = exports.isNullOrUndefined = void 0;\r\n/**\r\n * Gets whether the value provided is null or undefined.\r\n * @param value The value to check.\r\n * @returns Whether the value provided is null or undefined.\r\n */\r\nfunction isNullOrUndefined(value) {\r\n return value === null || value === undefined;\r\n}\r\nexports.isNullOrUndefined = isNullOrUndefined;\r\n/**\r\n * Gets whether the value provided is a natural number.\r\n * @param value The value to check.\r\n * @returns Whether the value provided is a natural number.\r\n */\r\nfunction isNaturalNumber(value) {\r\n return typeof value === \"number\" && value >= 1 && Math.floor(value) === value;\r\n}\r\nexports.isNaturalNumber = isNaturalNumber;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.Lotto = void 0;\r\nvar Participant_1 = require(\"./Participant\");\r\nvar Utilities_1 = require(\"./Utilities\");\r\n/**\r\n * Represents a lotto consisting of a number of pickable ticket-holding participants.\r\n */\r\nvar Lotto = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of Lotto.\r\n * @param customRandom The custom RNG to use in place of Math.random().\r\n */\r\n function Lotto(customRandom) {\r\n /** The array of participants that are holding tickets in the lotto. */\r\n this._participants = [];\r\n this._customRandom = customRandom;\r\n }\r\n /**\r\n * Adds a participant with the specified number of tickets, or adds to the participant ticket count if the participant already holds tickets.\r\n * @param participant The participant to add or to increase the ticket count for if they already hold tickets.\r\n * @param tickets The number of tickets, defaults to 1.\r\n * @returns The Lotto instance.\r\n */\r\n Lotto.prototype.add = function (participant, tickets) {\r\n if (tickets === void 0) { tickets = 1; }\r\n // Check that we have a valid ticket count.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n // Check whether this participant has already been added.\r\n var existingParticipant = this._participants.find(function (part) { return part.participant === participant; });\r\n if (existingParticipant) {\r\n // The participant has already been added to the lotto so just add to their ticket count.\r\n existingParticipant.tickets += tickets;\r\n }\r\n else {\r\n // The participant is not part of the lotto so we should add them.\r\n this._participants.push(new Participant_1.Participant(participant, tickets));\r\n }\r\n return this;\r\n };\r\n /**\r\n * Removes the specified number of tickets for the given participant from the draw, or all tickets if a ticket number is not defined.\r\n * @param participant The participant to remove tickets for.\r\n * @param tickets The number of tickets to remove, or undefined if all tickets are to be removed.\r\n * @returns The Lotto instance.\r\n */\r\n Lotto.prototype.remove = function (participant, tickets) {\r\n // Attempt to get the existing participant.\r\n var existingParticipant = this._participants.find(function (part) { return part.participant === participant; });\r\n // There is nothing to do if the specified participant isn't even part of the lotto.\r\n if (!existingParticipant) {\r\n return this;\r\n }\r\n // Check whether a tickets value was given.\r\n if (tickets !== undefined) {\r\n // Check that we have a valid ticket count.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n existingParticipant.tickets -= tickets;\r\n // If the participant no longer holds any tickets then they should be removed.\r\n if (existingParticipant.tickets < 1) {\r\n this._participants = this._participants.filter(function (part) { return part !== existingParticipant; });\r\n }\r\n }\r\n else {\r\n // We are removing all tickets for the participant so just remove them from the lotto.\r\n this._participants = this._participants.filter(function (part) { return part !== existingParticipant; });\r\n }\r\n return this;\r\n };\r\n /**\r\n * Draw a winning ticket and return the participant that holds the ticket.\r\n * @param options The draw options.\r\n * @returns The participant that holds the winning ticket.\r\n */\r\n Lotto.prototype.draw = function (options) {\r\n if (options === void 0) { options = {}; }\r\n // If we have no participants then just return null.\r\n if (this._participants.length === 0) {\r\n return null;\r\n }\r\n var redrawable = (0, Utilities_1.isNullOrUndefined)(options.redrawable) ? true : options.redrawable;\r\n var pickable = [];\r\n this._participants.forEach(function (_a) {\r\n var participant = _a.participant, tickets = _a.tickets;\r\n for (var ticketCount = 0; ticketCount < tickets; ticketCount++) {\r\n pickable.push(participant);\r\n }\r\n });\r\n var random;\r\n // We need a random floating-point number between 0 (inclusive) and 1 to scale up to pick our winner.\r\n // If a custom random function exists then we should use that or fall back to Math.random().\r\n if (this._customRandom) {\r\n // Call our custom random function to get a random floating-point number.\r\n random = this._customRandom();\r\n // Verify that the result of calling our custom random function is a number between 0 (inclusive) and 1.\r\n if (typeof random !== \"number\" || random < 0 || random >= 1) {\r\n throw new Error(\"the 'random' function provided did not return a number between 0 (inclusive) and 1\");\r\n }\r\n }\r\n else {\r\n // No custom random function was defined so just use good ol' Math.random().\r\n random = Math.random();\r\n }\r\n // Pick a winning participant.\r\n var winner = pickable[Math.floor(random * pickable.length)];\r\n // If the ticket isn't redrawable then we should remove a ticket from the winning participants ticket count.\r\n if (!redrawable) {\r\n this.remove(winner, 1);\r\n }\r\n // Return the winning participant.\r\n return winner;\r\n };\r\n /**\r\n * Draws multiple winning tickets and return an array of the participants that hold the winning tickets.\r\n * @param tickets The number of winning tickets to draw.\r\n * @param options The draw multiple options.\r\n * @returns An array of the participants that hold the winning tickets.\r\n */\r\n Lotto.prototype.drawMultiple = function (tickets, options) {\r\n if (options === void 0) { options = {}; }\r\n var uniqueResults = (0, Utilities_1.isNullOrUndefined)(options.unique) ? false : options.unique;\r\n // Handle cases where the user has asked for zero tickets (no idea why they would do this be we should trust them).\r\n if (tickets === 0) {\r\n return [];\r\n }\r\n // Now that we know out tickets value is not zero we should check that it is a valid natural number.\r\n if (!(0, Utilities_1.isNaturalNumber)(tickets)) {\r\n throw new Error(\"tickets value must be a natural number\");\r\n }\r\n var result = [];\r\n // Keep drawing tickets until we either reach the number of required tickets or we simply run out of tickets to draw.\r\n // We can run out of tickets to draw if 'options.redrawable' is explicity 'false' or we just had no participants when 'drawMultiple' was called.\r\n while (result.length < tickets && this._participants.length > 0) {\r\n result.push(this.draw(options));\r\n }\r\n // If the 'unique' draw option is set then we need to remove duplicates from the result list.\r\n if (uniqueResults) {\r\n // Create an array to store our unique results.\r\n var unique = [];\r\n // Iterate over all of our participants (with potential duplicates) and populate our array of unique values.\r\n for (var _i = 0, result_1 = result; _i < result_1.length; _i++) {\r\n var participant = result_1[_i];\r\n if (unique.indexOf(participant) === -1) {\r\n unique.push(participant);\r\n }\r\n }\r\n result = unique;\r\n }\r\n return result;\r\n };\r\n return Lotto;\r\n}());\r\nexports.Lotto = Lotto;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.createLotto = void 0;\r\nvar Lotto_1 = require(\"./Lotto\");\r\n/**\r\n * A function that creates and returns a Lotto instance.\r\n * @param participantsOrOptions An array of initial participants or options relating to the creation of a Lotto instance.\r\n * @returns A new Lotto instance.\r\n */\r\nfunction createLotto(participantsOrOptions) {\r\n // If no initial participants or lotto options were provided as an argument then we can just return a new lotto instance now.\r\n if (!participantsOrOptions) {\r\n return new Lotto_1.Lotto();\r\n }\r\n // Check whether we were provided with an array of initial participants or a lotto options object.\r\n if (Array.isArray(participantsOrOptions)) {\r\n // We are dealing with a pre-defined array of participants.\r\n var participants = participantsOrOptions;\r\n var lotto_1 = new Lotto_1.Lotto();\r\n // If the lotto participants have been defined upfront then we will need to add them all to our lotto instance now.\r\n participants.forEach(function (_a) {\r\n var participant = _a[0], tokens = _a[1];\r\n return lotto_1.add(participant, tokens);\r\n });\r\n // Return the Lotto instance.\r\n return lotto_1;\r\n }\r\n else {\r\n // We are dealing with some lotto options.\r\n var random = participantsOrOptions.random, participants = participantsOrOptions.participants;\r\n // Create a Lotto instance passing the custom RNG function to use in place of Math.random() (which could be undefined).\r\n var lotto_2 = new Lotto_1.Lotto(random);\r\n // If the lotto participants have been defined upfront as part of the options then we will need to add them all to our lotto instance now.\r\n if (participants) {\r\n participants.forEach(function (_a) {\r\n var participant = _a[0], tokens = _a[1];\r\n return lotto_2.add(participant, tokens);\r\n });\r\n }\r\n // Return the Lotto instance.\r\n return lotto_2;\r\n }\r\n}\r\nexports.createLotto = createLotto;\r\n", "\"use strict\";\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nvar createLotto_1 = require(\"./createLotto\");\r\nexports.default = createLotto_1.createLotto;\r\n", "import { BehaviourTree, FlattenedTreeNode } from \"./BehaviourTree\";\nimport State from \"./State\";\n\nexport { BehaviourTree, State };\nexport type { FlattenedTreeNode };\n", "import Node from \"../../nodes/Node\";\n\n/**\n * An exception thrown when evaluating node guard path conditions and a conditions fails.\n */\nexport default class GuardUnsatisifedException extends Error {\n /**\n * @param source The node at which a guard condition failed.\n */\n constructor(private source: Node) {\n super(\"A guard path condition has failed\");\n }\n\n /**\n * Gets whether the specified node is the node at which a guard condition failed.\n * @param node The node to check against the source node.\n * @returns Whether the specified node is the node at which a guard condition failed.\n */\n isSourceNode = (node: Node) => node === this.source;\n}\n", "import { Agent } from \"../../Agent\";\nimport Guard from \"./Guard\";\nimport Node from \"../../nodes/Node\";\nimport GuardUnsatisifedException from \"./GuardUnsatisifedException\";\n\nexport type GuardPathPart = {\n node: Node;\n guards: Guard[];\n};\n\n/**\n * Represents a path of node guards along a root-to-leaf tree path.\n */\nexport default class GuardPath {\n /**\n * @param nodes An array of objects defining a node instance -> guard link, ordered by node depth.\n */\n constructor(private nodes: GuardPathPart[]) {}\n\n /**\n * Evaluate guard conditions for all guards in the tree path, moving outwards from the root.\n * @param agent The agent, required for guard evaluation.\n * @returns An evaluation results object.\n */\n evaluate = (agent: Agent) => {\n // We need to evaluate guard conditions for nodes up the tree, moving outwards from the root.\n for (const details of this.nodes) {\n // There can be multiple guards per node.\n for (const guard of details.guards) {\n // Check whether the guard condition passes, and throw an exception if not.\n if (!guard.isSatisfied(agent)) {\n throw new GuardUnsatisifedException(details.node);\n }\n }\n }\n };\n}\n", "/**\n * Enumeration of node state types.\n */\nexport enum State {\n READY = \"mistreevous.ready\",\n RUNNING = \"mistreevous.running\",\n SUCCEEDED = \"mistreevous.succeeded\",\n FAILED = \"mistreevous.failed\"\n}\n\nexport { State as default };\n\nexport type CompleteState = State.SUCCEEDED | State.FAILED;\nexport type AnyState = State.READY | State.RUNNING | CompleteState;\n", "import { Agent } from \"../Agent\";\nimport Attribute from \"../attributes/Attribute\";\nimport Entry from \"../attributes/callbacks/Entry\";\nimport Exit from \"../attributes/callbacks/Exit\";\nimport Step from \"../attributes/callbacks/Step\";\nimport Guard from \"../attributes/guards/Guard\";\nimport GuardPath from \"../attributes/guards/GuardPath\";\nimport GuardUnsatisifedException from \"../attributes/guards/GuardUnsatisifedException\";\nimport { BehaviourTreeOptions } from \"../BehaviourTreeOptions\";\nimport { AnyArgument } from \"../RootAstNodesBuilder\";\nimport State, { AnyState } from \"../State\";\nimport Leaf from \"./leaf/Leaf\";\n\n/**\n * A base node.\n */\nexport default abstract class Node {\n /**\n * The node uid.\n */\n private readonly uid: string = createNodeUid();\n /**\n * The node state.\n */\n private state: AnyState = State.READY;\n /**\n * The guard path to evaluate as part of a node update.\n */\n private guardPath: GuardPath | undefined;\n\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param args The node argument definitions.\n */\n constructor(private type: string, private attributes: Attribute[], private args: AnyArgument[]) {}\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected abstract onUpdate(agent: Agent, options: BehaviourTreeOptions): void;\n\n /**\n * Gets the name of the node.\n */\n public abstract getName(): string;\n\n /**\n * Gets whether this node is a leaf node.\n */\n public abstract isLeafNode: () => this is Leaf;\n\n /**\n * Gets/Sets the state of the node.\n */\n getState = (): AnyState => this.state;\n setState = (value: AnyState): void => {\n this.state = value;\n };\n\n /**\n * Gets the unique id of the node.\n */\n getUid = () => this.uid;\n\n /**\n * Gets the type of the node.\n */\n getType = () => this.type;\n\n /**\n * Gets the node attributes.\n */\n getAttributes = () => this.attributes;\n\n /**\n * Gets the node arguments.\n */\n getArguments = () => this.args;\n\n /**\n * Gets the node attribute with the specified type, or null if it does not exist.\n */\n getAttribute(type: \"entry\" | \"ENTRY\"): Entry;\n getAttribute(type: \"exit\" | \"EXIT\"): Exit;\n getAttribute(type: \"step\" | \"STEP\"): Step;\n getAttribute(type: string): Attribute {\n return (\n this.getAttributes().filter((decorator) => decorator.getType().toUpperCase() === type.toUpperCase())[0] ||\n null\n );\n }\n\n /**\n * Gets the node attributes.\n */\n getGuardAttributes = (): Guard[] => this.getAttributes().filter((decorator) => decorator.isGuard()) as Guard[];\n\n /**\n * Sets the guard path to evaluate as part of a node update.\n */\n setGuardPath = (value: GuardPath) => (this.guardPath = value);\n\n /**\n * Gets whether a guard path is assigned to this node.\n */\n hasGuardPath = () => !!this.guardPath;\n\n /**\n * Gets whether this node is in the specified state.\n * @param value The value to compare to the node state.\n */\n public is(value: AnyState): boolean {\n return this.state === value;\n }\n\n /**\n * Reset the state of the node.\n */\n public reset(): void {\n this.setState(State.READY);\n }\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n public abort(agent: Agent): void {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n }\n\n /**\n * Update the node.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n * @returns The result of the update.\n */\n public update(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is already in a 'SUCCEEDED' or 'FAILED' state then there is nothing to do.\n if (this.is(State.SUCCEEDED) || this.is(State.FAILED)) {\n return;\n }\n\n try {\n // Evaluate all of the guard path conditions for the current tree path.\n this.guardPath!.evaluate(agent);\n\n // If this node is in the READY state then call the ENTRY for this node if it exists.\n if (this.is(State.READY)) {\n this.getAttribute(\"entry\")?.callAgentFunction(agent);\n }\n\n this.getAttribute(\"step\")?.callAgentFunction(agent);\n\n // Do the actual update.\n this.onUpdate(agent, options);\n\n // If this node is now in a 'SUCCEEDED' or 'FAILED' state then call the EXIT for this node if it exists.\n if (this.is(State.SUCCEEDED) || this.is(State.FAILED)) {\n this.getAttribute(\"exit\")?.callAgentFunction(agent, this.is(State.SUCCEEDED), false);\n }\n } catch (error) {\n // If the error is a GuardUnsatisfiedException then we need to determine if this node is the source.\n if (error instanceof GuardUnsatisifedException && error.isSourceNode(this)) {\n // Abort the current node.\n this.abort(agent);\n\n // Any node that is the source of an abort will be a failed node.\n this.setState(State.FAILED);\n } else {\n throw error;\n }\n }\n }\n}\n\n/**\n * Create a randomly generated node uid.\n * @returns A randomly generated node uid.\n */\nfunction createNodeUid(): string {\n var S4 = function () {\n return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);\n };\n return S4() + S4() + \"-\" + S4() + \"-\" + S4() + \"-\" + S4() + \"-\" + S4() + S4() + S4();\n}\n", "import Node from \"../Node\";\n\n/**\n * A leaf node.\n */\nexport default abstract class Leaf extends Node {\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => true;\n}\n", "import { ActionResult, Agent, ExitFunctionArg, FunctionArg, GlobalFunction } from \"./Agent\";\nimport { AnyArgument, RootAstNode } from \"./RootAstNodesBuilder\";\n\n// Exit callbacks receive their own special type of argument.\n// There's probably stricter ways to represent this but it feels overly complex right now.\ntype ExitResultArg = { value: ExitFunctionArg };\nexport type AnyExitArgument = AnyArgument | ExitResultArg;\n\nexport type InvokerFunction = (args: AnyExitArgument[]) => ActionResult;\n\n/**\n * A singleton used to store and lookup registered functions and subtrees.\n */\nexport default class Lookup {\n /**\n * The object holding any registered functions keyed on function name.\n */\n private static functionTable: { [key: string]: GlobalFunction } = {};\n /**\n * The object holding any registered sub-trees keyed on tree name.\n */\n private static subtreeTable: { [key: string]: RootAstNode } = {};\n\n /**\n * Gets the function with the specified name.\n * @param name The name of the function.\n * @returns The function with the specified name.\n */\n public static getFunc(name: string): GlobalFunction {\n return this.functionTable[name];\n }\n\n /**\n * Sets the function with the specified name for later lookup.\n * @param name The name of the function.\n * @param func The function.\n */\n public static setFunc(name: string, func: GlobalFunction): void {\n this.functionTable[name] = func;\n }\n\n /**\n * Gets the function invoker for the specified agent and function name.\n * If a function with the specified name exists on the agent object then it will\n * be returned, otherwise we will then check the registered functions for a match.\n * @param agent The agent instance that this behaviour tree is modelling behaviour for.\n * @param name The function name.\n * @returns The function invoker for the specified agent and function name.\n */\n static getFuncInvoker(agent: Agent, name: string): InvokerFunction | null {\n // Check whether the agent contains the specified function.\n const foundOnAgent = agent[name];\n if (foundOnAgent && typeof foundOnAgent === \"function\") {\n return (args: AnyExitArgument[]): boolean | ActionResult =>\n foundOnAgent.apply(\n agent,\n args.map((arg) => arg.value)\n );\n }\n\n // The agent does not contain the specified function but it may have been registered at some point.\n if (this.functionTable[name] && typeof this.functionTable[name] === \"function\") {\n return (args: AnyExitArgument[]) => this.functionTable[name](agent, ...args.map((arg) => arg.value));\n }\n\n // We have no function to invoke.\n return null;\n }\n\n /**\n * Gets the subtree with the specified name.\n * @param name The name of the subtree.\n * @returns The subtree with the specified name.\n */\n static getSubtree(name: string): RootAstNode {\n return this.subtreeTable[name];\n }\n\n /**\n * Sets the subtree with the specified name for later lookup.\n * @param name The name of the subtree.\n * @param subtree The subtree.\n */\n static setSubtree(name: string, subtree: RootAstNode) {\n this.subtreeTable[name] = subtree;\n }\n\n /**\n * Removes the registered function or subtree with the specified name.\n * @param name The name of the registered function or subtree.\n */\n static remove(name: string) {\n delete this.functionTable[name];\n delete this.subtreeTable[name];\n }\n\n /**\n * Remove all registered functions and subtrees.\n */\n static empty() {\n this.functionTable = {};\n this.subtreeTable = {};\n }\n}\n", "import Leaf from \"./Leaf\";\nimport State, { CompleteState } from \"../../State\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * An Action leaf node.\n * This represents an immediate or ongoing state of behaviour.\n */\nexport default class Action extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param actionName The action name.\n * @param actionArguments The array of action argument definitions.\n */\n constructor(attributes: Attribute[], private actionName: string, private actionArguments: AnyArgument[]) {\n super(\"action\", attributes, actionArguments);\n }\n\n /**\n * Whether there is a pending update promise.\n */\n private isUsingUpdatePromise = false;\n\n /**\n * The finished state result of an update promise.\n */\n private updatePromiseStateResult: CompleteState | null = null;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the result of this action depends on an update promise then there is nothing to do until\n // it resolves, unless there has been a value set as a result of the update promise resolving.\n if (this.isUsingUpdatePromise) {\n // Check whether the update promise has resolved with a state value.\n if (this.updatePromiseStateResult) {\n // Set the state of this node to match the state returned by the promise.\n this.setState(this.updatePromiseStateResult);\n }\n\n return;\n }\n\n // Attempt to get the invoker for the action function.\n const actionFuncInvoker = Lookup.getFuncInvoker(agent, this.actionName);\n\n // The action function should be defined.\n if (actionFuncInvoker === null) {\n throw new Error(\n `cannot update action node as the action '${this.actionName}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the action function, the result of which may be:\n // - The finished state of this action node.\n // - A promise to return a finished node state.\n // - Undefined if the node should remain in the running state.\n const updateResult = actionFuncInvoker(this.actionArguments) as CompleteState | Promise;\n\n if (updateResult instanceof Promise) {\n updateResult.then(\n (result) => {\n // If 'isUpdatePromisePending' is null then the promise was cleared as it was resolving, probably via an abort of reset.\n if (!this.isUsingUpdatePromise) {\n return;\n }\n\n // Check to make sure the result is a valid finished state.\n if (result !== State.SUCCEEDED && result !== State.FAILED) {\n throw new Error(\n \"action node promise resolved with an invalid value, expected a State.SUCCEEDED or State.FAILED value to be returned\"\n );\n }\n\n // Set pending update promise state result to be processed on next update.\n this.updatePromiseStateResult = result;\n },\n (reason) => {\n // If 'isUpdatePromisePending' is null then the promise was cleared as it was resolving, probably via an abort of reset.\n if (!this.isUsingUpdatePromise) {\n return;\n }\n\n // Just throw whatever was returned as the rejection argument.\n throw new Error(reason);\n }\n );\n\n // This node will be in the 'RUNNING' state until the update promise resolves.\n this.setState(State.RUNNING);\n\n // We are now waiting for the promise returned by the use to resolve before we know what state this node is in.\n this.isUsingUpdatePromise = true;\n } else {\n // Validate the returned value.\n this.validateUpdateResult(updateResult);\n\n // Set the state of this node, this may be undefined, which just means that the node is still in the 'RUNNING' state.\n this.setState(updateResult || State.RUNNING);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => this.actionName;\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // There is no longer an update promise that we care about.\n this.isUsingUpdatePromise = false;\n this.updatePromiseStateResult = null;\n };\n\n /**\n * Validate the result of an update function call.\n * @param result The result of an update function call.\n */\n private validateUpdateResult = (result: CompleteState | boolean) => {\n switch (result) {\n case State.SUCCEEDED:\n case State.FAILED:\n case undefined:\n return;\n default:\n throw new Error(\n `action '${this.actionName}' 'onUpdate' returned an invalid response, expected an optional State.SUCCEEDED or State.FAILED value to be returned`\n );\n }\n };\n}\n", "import Leaf from \"./Leaf\";\nimport State from \"../../State\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Condition leaf node.\n * This will succeed or fail immediately based on an agent predicate, without moving to the 'RUNNING' state.\n */\nexport default class Condition extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param conditionName The name of the condition function.\n * @param conditionArguments The array of condition argument definitions.\n */\n constructor(attributes: Attribute[], private conditionName: string, private conditionArguments: AnyArgument[]) {\n super(\"condition\", attributes, conditionArguments);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.conditionName);\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot update condition node as the condition '${this.conditionName}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine the state of this node.\n this.setState(!!conditionFuncInvoker(this.conditionArguments) ? State.SUCCEEDED : State.FAILED);\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => this.conditionName;\n}\n", "import Leaf from \"./Leaf\";\nimport State from \"../../State\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { Agent } from \"../../Agent\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A WAIT node.\n * The state of this node will change to SUCCEEDED after a duration of time\n */\nexport default class Wait extends Leaf {\n /**\n * @param attributes The node attributes.\n * @param duration The duration that this node will wait to succeed in milliseconds.\n * @param durationMin The minimum possible duration in milliseconds that this node will wait to succeed.\n * @param durationMax The maximum possible duration in milliseconds that this node will wait to succeed.\n */\n constructor(\n attributes: Attribute[],\n private duration: number | null,\n private durationMin: number | null,\n private durationMax: number | null\n ) {\n super(\"wait\", attributes, []);\n }\n\n /**\n * The time in milliseconds at which this node was first updated.\n */\n private initialUpdateTime: number = 0;\n\n /**\n * The total duration in milliseconds that this node will be waiting for.\n */\n private totalDuration: number | null = null;\n\n /**\n * The duration in milliseconds that this node has been waiting for.\n */\n private waitedDuration: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to set the initial update time.\n if (this.is(State.READY)) {\n // Set the initial update time.\n this.initialUpdateTime = new Date().getTime();\n\n // Set the initial waited duration.\n this.waitedDuration = 0;\n\n // Are we dealing with an explicit duration or will we be randomly picking a duration between the min and max duration.\n if (this.duration !== null) {\n this.totalDuration = this.duration;\n } else if (this.durationMin !== null && this.durationMax !== null) {\n // We will be picking a random duration between a min and max duration, if the optional 'random' behaviour tree\n // function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random duration between a min and max duration.\n this.totalDuration = Math.floor(\n random() * (this.durationMax - this.durationMin + 1) + this.durationMin\n );\n } else {\n this.totalDuration = null;\n }\n\n // The node is now running until we finish waiting.\n this.setState(State.RUNNING);\n }\n\n // If we have no total duration then this wait node will wait indefinitely until it is aborted.\n if (this.totalDuration === null) {\n return;\n }\n\n // If we have a 'getDeltaTime' function defined as part of our options then we will use it to figure out how long we have waited for.\n if (typeof options.getDeltaTime === \"function\") {\n // Get the delta time.\n const deltaTime = options.getDeltaTime();\n\n // Our delta time must be a valid number and cannot be NaN.\n if (typeof deltaTime !== \"number\" || isNaN(deltaTime)) {\n throw new Error(\"The delta time must be a valid number and not NaN.\");\n }\n\n // Update the amount of time that this node has been waiting for based on the delta time.\n this.waitedDuration += deltaTime * 1000;\n } else {\n // We are not using a delta time, so we will just work out hom much time has passed since the first update.\n this.waitedDuration = new Date().getTime() - this.initialUpdateTime;\n }\n\n // Have we waited long enough?\n if (this.waitedDuration >= this.totalDuration) {\n // We have finished waiting!\n this.setState(State.SUCCEEDED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.duration !== null) {\n return `WAIT ${this.duration}ms`;\n } else if (this.durationMin !== null && this.durationMax !== null) {\n return `WAIT ${this.durationMin}ms-${this.durationMax}ms`;\n } else {\n return \"WAIT\";\n }\n };\n}\n", "import Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\n\n/**\n * A decorator node that wraps a single child node.\n */\nexport default abstract class Decorator extends Node {\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(type: string, attributes: Attribute[], protected child: Node) {\n super(type, attributes, []);\n }\n\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => false;\n\n /**\n * Gets the children of this node.\n */\n getChildren = () => [this.child];\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the state of the child node.\n this.child.reset();\n };\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n abort = (agent: Agent) => {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Abort the child node.\n this.child.abort(agent);\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n };\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Root node.\n * The root node will have a single child.\n */\nexport default class Root extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"root\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n // Update the child of this node.\n this.child.update(agent, options);\n }\n\n // The state of the root node is the state of its child.\n this.setState(this.child.getState());\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"ROOT\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A REPEAT node.\n * The node has a single child which can have:\n * -- A number of iterations for which to repeat the child node.\n * -- An infinite repeat loop if neither an iteration count or a condition function is defined.\n * The REPEAT node will stop and have a 'FAILED' state if its child is ever in a 'FAILED' state after an update.\n * The REPEAT node will attempt to move on to the next iteration if its child is ever in a 'SUCCEEDED' state.\n */\nexport default class Repeat extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param iterations The number of iterations to repeat the child node.\n * @param iterationsMin The minimum possible number of iterations to repeat the child node.\n * @param iterationsMax The maximum possible number of iterations to repeat the child node.\n * @param child The child node.\n */\n constructor(\n attributes: Attribute[],\n private iterations: number | null,\n private iterationsMin: number | null,\n private iterationsMax: number | null,\n child: Node\n ) {\n super(\"repeat\", attributes, child);\n }\n\n /**\n * The number of target iterations to make.\n */\n private targetIterationCount: number | null = null;\n\n /**\n * The current iteration count.\n */\n private currentIterationCount: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to reset the child and the target iteration count.\n if (this.is(State.READY)) {\n // Reset the child node.\n this.child.reset();\n\n // Reset the current iteration count.\n this.currentIterationCount = 0;\n\n // Set the target iteration count.\n this.setTargetIterationCount(options);\n }\n\n // Do a check to see if we can iterate. If we can then this node will move into the 'RUNNING' state.\n // If we cannot iterate then we have hit our target iteration count, which means that the node has succeeded.\n if (this.canIterate()) {\n // This node is in the running state and can do its initial iteration.\n this.setState(State.RUNNING);\n\n // We may have already completed an iteration, meaning that the child node will be in the SUCCEEDED state.\n // If this is the case then we will have to reset the child node now.\n if (this.child.getState() === State.SUCCEEDED) {\n this.child.reset();\n }\n\n // Update the child of this node.\n this.child.update(agent, options);\n\n // If the child moved into the FAILED state when we updated it then there is nothing left to do and this node has also failed.\n // If it has moved into the SUCCEEDED state then we have completed the current iteration.\n if (this.child.getState() === State.FAILED) {\n // The child has failed, meaning that this node has failed.\n this.setState(State.FAILED);\n\n return;\n } else if (this.child.getState() === State.SUCCEEDED) {\n // We have completed an iteration.\n this.currentIterationCount += 1;\n }\n } else {\n // This node is in the 'SUCCEEDED' state as we cannot iterate any more.\n this.setState(State.SUCCEEDED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.iterations !== null) {\n return `REPEAT ${this.iterations}x`;\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n return `REPEAT ${this.iterationsMin}x-${this.iterationsMax}x`;\n } else {\n return \"REPEAT\";\n }\n };\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the current iteration count.\n this.currentIterationCount = 0;\n\n // Reset the child node.\n this.child.reset();\n };\n\n /**\n * Gets whether an iteration can be made.\n * @returns Whether an iteration can be made.\n */\n private canIterate = () => {\n if (this.targetIterationCount !== null) {\n // We can iterate as long as we have not reached our target iteration count.\n return this.currentIterationCount < this.targetIterationCount;\n }\n\n // If neither an iteration count or a condition function were defined then we can iterate indefinitely.\n return true;\n };\n\n /**\n * Sets the target iteration count.\n * @param options The behaviour tree options object.\n */\n private setTargetIterationCount = (options: BehaviourTreeOptions) => {\n // Are we dealing with an explicit iteration count or will we be randomly picking a iteration count between the min and max iteration count.\n if (this.iterations !== null) {\n this.targetIterationCount = this.iterations;\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n // We will be picking a random iteration count between a min and max iteration count, if the optional 'random'\n // behaviour tree function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random iteration count between a min and max iteration count.\n this.targetIterationCount = Math.floor(\n random() * (this.iterationsMax - this.iterationsMin + 1) + this.iterationsMin\n );\n } else {\n this.targetIterationCount = null;\n }\n };\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A RETRY node.\n * The node has a single child which can have:\n * -- A number of iterations for which to repeat the child node.\n * -- An infinite repeat loop if neither an iteration count or a condition function is defined.\n * The RETRY node will stop and have a 'SUCCEEDED' state if its child is ever in a 'SUCCEEDED' state after an update.\n * The RETRY node will attempt to move on to the next iteration if its child is ever in a 'FAILED' state.\n */\nexport default class Retry extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param attempts The number of attempts to retry the child node.\n * @param attemptsMin The minimum possible number of attempts to retry the child node.\n * @param attemptsMax The maximum possible number of attempts to retry the child node.\n * @param child The child node.\n */\n constructor(\n attributes: Attribute[],\n private attempts: number | null,\n private attemptsMin: number | null,\n private attemptsMax: number | null,\n child: Node\n ) {\n super(\"retry\", attributes, child);\n }\n\n /**\n * The number of target attempts to make.\n */\n private targetAttemptCount: number | null = null;\n\n /**\n * The current attempt count.\n */\n private currentAttemptCount: number = 0;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to reset the child and the target attempt count.\n if (this.is(State.READY)) {\n // Reset the child node.\n this.child.reset();\n\n // Reset the current attempt count.\n this.currentAttemptCount = 0;\n\n // Set the target attempt count.\n this.setTargetAttemptCount(options);\n }\n\n // Do a check to see if we can attempt. If we can then this node will move into the 'RUNNING' state.\n // If we cannot attempt then we have hit our target attempt count, which means that the node has succeeded.\n if (this.canAttempt()) {\n // This node is in the running state and can do its initial attempt.\n this.setState(State.RUNNING);\n\n // We may have already completed an attempt, meaning that the child node will be in the FAILED state.\n // If this is the case then we will have to reset the child node now.\n if (this.child.getState() === State.FAILED) {\n this.child.reset();\n }\n\n // Update the child of this node.\n this.child.update(agent, options);\n\n // If the child moved into the SUCCEEDED state when we updated it then there is nothing left to do and this node has also succeeded.\n // If it has moved into the FAILED state then we have completed the current attempt.\n if (this.child.getState() === State.SUCCEEDED) {\n // The child has succeeded, meaning that this node has succeeded.\n this.setState(State.SUCCEEDED);\n\n return;\n } else if (this.child.getState() === State.FAILED) {\n // We have completed an attempt.\n this.currentAttemptCount += 1;\n }\n } else {\n // This node is in the 'FAILED' state as we cannot iterate any more.\n this.setState(State.FAILED);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => {\n if (this.attempts !== null) {\n return `RETRY ${this.attempts}x`;\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n return `RETRY ${this.attemptsMin}x-${this.attemptsMax}x`;\n } else {\n return \"RETRY\";\n }\n };\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the current attempt count.\n this.currentAttemptCount = 0;\n\n // Reset the child node.\n this.child.reset();\n };\n\n /**\n * Gets whether an attempt can be made.\n * @returns Whether an attempt can be made.\n */\n canAttempt = () => {\n if (this.targetAttemptCount !== null) {\n // We can attempt as long as we have not reached our target attempt count.\n return this.currentAttemptCount < this.targetAttemptCount;\n }\n\n // If neither an attempt count or a condition function were defined then we can attempt indefinitely.\n return true;\n };\n\n /**\n * Sets the target attempt count.\n * @param options The behaviour tree options object.\n */\n setTargetAttemptCount = (options: BehaviourTreeOptions) => {\n // Are we dealing with an explicit attempt count or will we be randomly picking an attempt count between the min and max attempt count.\n if (this.attempts !== null) {\n this.targetAttemptCount = this.attempts;\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n // We will be picking a random attempt count between a min and max attempt count, if the optional 'random'\n // behaviour tree function option is defined then we will be using that, otherwise we will fall back to using Math.random.\n const random = typeof options.random === \"function\" ? options.random : Math.random;\n\n // Pick a random attempt count between a min and max attempt count.\n this.targetAttemptCount = Math.floor(\n random() * (this.attemptsMax - this.attemptsMin + 1) + this.attemptsMin\n );\n } else {\n this.targetAttemptCount = null;\n }\n };\n}\n", "import Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport Node from \"../Node\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Flip node.\n * This node wraps a single child and will flip the state of the child state.\n */\nexport default class Flip extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"flip\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n this.setState(State.FAILED);\n break;\n\n case State.FAILED:\n this.setState(State.SUCCEEDED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"FLIP\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Succeed node.\n * This node wraps a single child and will always move to the 'SUCCEEDED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state.\n */\nexport default class Succeed extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"succeed\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n case State.FAILED:\n this.setState(State.SUCCEEDED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SUCCEED\";\n}\n", "import Node from \"../Node\";\nimport Decorator from \"./Decorator\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A Fail node.\n * This node wraps a single child and will always move to the 'FAILED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state.\n */\nexport default class Fail extends Decorator {\n /**\n * @param attributes The node attributes.\n * @param child The child node.\n */\n constructor(attributes: Attribute[], child: Node) {\n super(\"fail\", attributes, child);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If the child has never been updated or is running then we will need to update it now.\n if (this.child.getState() === State.READY || this.child.getState() === State.RUNNING) {\n this.child.update(agent, options);\n }\n\n // The state of this node will depend in the state of its child.\n switch (this.child.getState()) {\n case State.RUNNING:\n this.setState(State.RUNNING);\n break;\n\n case State.SUCCEEDED:\n case State.FAILED:\n this.setState(State.FAILED);\n break;\n\n default:\n this.setState(State.READY);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"FAIL\";\n}\n", "import createLotto from \"lotto-draw\";\n\nimport Node from \"../Node\";\nimport Composite from \"./Composite\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A LOTTO node.\n * A winning child is picked on the initial update of this node, based on ticket weighting.\n * The state of this node will match the state of the winning child.\n */\nexport default class Lotto extends Composite {\n /**\n * @param attributes The node attributes.\n * @param tickets The child node tickets.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], private tickets: number[], children: Node[]) {\n super(\"lotto\", attributes, children);\n }\n\n /**\n * The child node selected to be the active one.\n */\n private selectedChild: Node | undefined;\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // If this node is in the READY state then we need to pick a winning child node.\n if (this.is(State.READY)) {\n // Create a lotto draw with which to randomly pick a child node to become the active one.\n const lottoDraw = createLotto({\n // Hook up the optional 'random' behaviour tree function option to the one used by 'lotto-draw'.\n random: options.random,\n // Pass in each child node as a participant in the lotto draw with their respective ticket count.\n participants: this.children.map((child, index) => [child, this.tickets[index] || 1])\n });\n\n // Randomly pick a child based on ticket weighting, this will become the active child for this composite node.\n this.selectedChild = lottoDraw.draw() || undefined;\n }\n\n // If something went wrong and we don't have an active child then we should throw an error.\n if (!this.selectedChild) {\n throw new Error(\"failed to update lotto node as it has no active child\");\n }\n\n // If the selected child has never been updated or is running then we will need to update it now.\n if (this.selectedChild.getState() === State.READY || this.selectedChild.getState() === State.RUNNING) {\n this.selectedChild.update(agent, options);\n }\n\n // The state of the lotto node is the state of its selected child.\n this.setState(this.selectedChild.getState());\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => (this.tickets.length ? `LOTTO [${this.tickets.join(\",\")}]` : \"LOTTO\");\n}\n", "import Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\n\n/**\n * A composite node that wraps child nodes.\n */\nexport default abstract class Composite extends Node {\n /**\n * @param type The node type.\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(type: string, attributes: Attribute[], protected children: Node[]) {\n super(type, attributes, []);\n }\n\n /**\n * Gets whether this node is a leaf node.\n */\n isLeafNode = () => false;\n\n /**\n * Gets the children of this node.\n */\n getChildren = () => this.children;\n\n /**\n * Reset the state of the node.\n */\n reset = () => {\n // Reset the state of this node.\n this.setState(State.READY);\n\n // Reset the state of any child nodes.\n this.getChildren().forEach((child) => child.reset());\n };\n\n /**\n * Abort the running of this node.\n * @param agent The agent.\n */\n abort = (agent: Agent) => {\n // There is nothing to do if this node is not in the running state.\n if (!this.is(State.RUNNING)) {\n return;\n }\n\n // Abort any child nodes.\n this.getChildren().forEach((child) => child.abort(agent));\n\n // Reset the state of this node.\n this.reset();\n\n this.getAttribute(\"exit\")?.callAgentFunction(agent, false, true);\n };\n}\n", "import Composite from \"./Composite\";\nimport Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A SELECTOR node.\n * The child nodes are executed in sequence until one succeeds or all fail.\n */\nexport default class Selector extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], protected children: Node[]) {\n super(\"selector\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then this node is also a 'SUCCEEDED' node.\n if (child.getState() === State.SUCCEEDED) {\n // This node is a 'SUCCEEDED' node.\n this.setState(State.SUCCEEDED);\n\n // There is no need to check the rest of the selector nodes.\n return;\n }\n\n // If the current child has a state of 'FAILED' then we should move on to the next child.\n if (child.getState() === State.FAILED) {\n // Find out if the current child is the last one in the selector.\n // If it is then this sequence node has also failed.\n if (this.children.indexOf(child) === this.children.length - 1) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // There is no need to check the rest of the selector as we have completed it.\n return;\n } else {\n // The child node failed, try the next one.\n continue;\n }\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() === State.RUNNING) {\n // This node is a 'RUNNING' node.\n this.setState(State.RUNNING);\n\n // There is no need to check the rest of the selector as the current child is still running.\n return;\n }\n\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SELECTOR\";\n}\n", "import Composite from \"./Composite\";\nimport Node from \"../Node\";\nimport State from \"../../State\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A SEQUENCE node.\n * The child nodes are executed in sequence until one fails or all succeed.\n */\nexport default class Sequence extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], protected children: Node[]) {\n super(\"sequence\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then we should move on to the next child.\n if (child.getState() === State.SUCCEEDED) {\n // Find out if the current child is the last one in the sequence.\n // If it is then this sequence node has also succeeded.\n if (this.children.indexOf(child) === this.children.length - 1) {\n // This node is a 'SUCCEEDED' node.\n this.setState(State.SUCCEEDED);\n\n // There is no need to check the rest of the sequence as we have completed it.\n return;\n } else {\n // The child node succeeded, but we have not finished the sequence yet.\n continue;\n }\n }\n\n // If the current child has a state of 'FAILED' then this node is also a 'FAILED' node.\n if (child.getState() === State.FAILED) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // There is no need to check the rest of the sequence.\n return;\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() === State.RUNNING) {\n // This node is a 'RUNNING' node.\n this.setState(State.RUNNING);\n\n // There is no need to check the rest of the sequence as the current child is still running.\n return;\n }\n\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"SEQUENCE\";\n}\n", "import Composite from \"./Composite\";\nimport State from \"../../State\";\nimport Node from \"../Node\";\nimport { Agent } from \"../../Agent\";\nimport Attribute from \"../../attributes/Attribute\";\nimport { BehaviourTreeOptions } from \"../../BehaviourTreeOptions\";\n\n/**\n * A PARALLEL node.\n * The child nodes are executed concurrently until one fails or all succeed.\n */\nexport default class Parallel extends Composite {\n /**\n * @param attributes The node attributes.\n * @param children The child nodes.\n */\n constructor(attributes: Attribute[], children: Node[]) {\n super(\"parallel\", attributes, children);\n }\n\n /**\n * Called when the node is being updated.\n * @param agent The agent.\n * @param options The behaviour tree options object.\n */\n protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void {\n // Keep a count of the number of succeeded child nodes.\n let succeededCount = 0;\n\n let hasChildFailed = false;\n\n // Iterate over all of the children of this node.\n for (const child of this.children) {\n // If the child has never been updated or is running then we will need to update it now.\n if (child.getState() === State.READY || child.getState() === State.RUNNING) {\n // Update the child of this node.\n child.update(agent, options);\n }\n\n // If the current child has a state of 'SUCCEEDED' then we should move on to the next child.\n if (child.getState() === State.SUCCEEDED) {\n // The child node has succeeded, keep track of this to determine if all children have.\n succeededCount++;\n\n // The child node succeeded, but we have not finished checking every child node yet.\n continue;\n }\n\n // If the current child has a state of 'FAILED' then this node is also a 'FAILED' node.\n if (child.getState() === State.FAILED) {\n hasChildFailed = true;\n\n // There is no need to check the rest of the children.\n break;\n }\n\n // The node should be in the 'RUNNING' state.\n if (child.getState() !== State.RUNNING) {\n // The child node was not in an expected state.\n throw new Error(\"child node was not in an expected state.\");\n }\n }\n\n if (hasChildFailed) {\n // This node is a 'FAILED' node.\n this.setState(State.FAILED);\n\n // Abort every running child.\n for (const child of this.children) {\n if (child.getState() === State.RUNNING) {\n child.abort(agent);\n }\n }\n } else {\n // If all children have succeeded then this node has also succeeded, otherwise it is still running.\n this.setState(succeededCount === this.children.length ? State.SUCCEEDED : State.RUNNING);\n }\n }\n\n /**\n * Gets the name of the node.\n */\n getName = () => \"PARALLEL\";\n}\n", "import { AnyArgument } from \"../RootAstNodesBuilder\";\nimport Guard from \"./guards/Guard\";\n\nexport type AttributeDetails = {\n /** The attribute type. */\n type: string;\n\n /** The attribute arguments. */\n args: AnyArgument[];\n};\n\n/**\n * A base node attribute.\n */\nexport default abstract class Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of attribute argument definitions.\n */\n constructor(protected type: string, protected args: AnyArgument[]) {}\n\n /**\n * Gets the type of the attribute.\n */\n getType = () => this.type;\n\n /**\n * Gets the array of attribute argument definitions.\n */\n getArguments = () => this.args;\n\n /**\n * Gets the attribute details.\n */\n abstract getDetails(): TAttributeDetails;\n\n /**\n * Gets whether this attribute is a guard.\n */\n abstract isGuard: () => this is Guard;\n}\n", "import { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport Attribute, { AttributeDetails } from \"../Attribute\";\n\nexport type GuardAttributeDetails = {\n /** The name of the condition function that determines whether the guard is satisfied. */\n condition: string;\n} & AttributeDetails;\n\n/**\n * A base node guard attribute.\n */\nexport default abstract class Guard extends Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of decorator argument definitions.\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n */\n constructor(type: string, args: AnyArgument[], private condition: string) {\n super(type, args);\n }\n\n /**\n * Gets the name of the condition function that determines whether the guard is satisfied.\n */\n getCondition = () => this.condition;\n\n /**\n * Gets whether this attribute is a guard.\n */\n isGuard = () => true;\n\n /**\n * Gets the attribute details.\n */\n getDetails(): GuardAttributeDetails {\n return {\n type: this.getType(),\n args: this.getArguments(),\n condition: this.getCondition()\n };\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n abstract isSatisfied(agent: Agent): boolean;\n}\n", "import Guard from \"./Guard\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * A WHILE guard which is satisfied as long as the given condition remains true.\n */\nexport default class While extends Guard {\n /**\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n * @param args The array of decorator argument definitions.\n */\n constructor(condition: string, args: AnyArgument[]) {\n super(\"while\", args, condition);\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n isSatisfied = (agent: Agent) => {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition());\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine whether this guard is satisfied.\n return !!conditionFuncInvoker(this.args);\n };\n}\n", "import Guard from \"./Guard\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An UNTIL guard which is satisfied as long as the given condition remains false.\n */\nexport default class Until extends Guard {\n /**\n * @param condition The name of the condition function that determines whether the guard is satisfied.\n * @param args The array of decorator argument definitions.\n */\n constructor(condition: string, args: AnyArgument[]) {\n super(\"until\", args, condition);\n }\n\n /**\n * Gets whether the guard is satisfied.\n * @param agent The agent.\n * @returns Whether the guard is satisfied.\n */\n isSatisfied = (agent: Agent) => {\n // Attempt to get the invoker for the condition function.\n const conditionFuncInvoker = Lookup.getFuncInvoker(agent, this.getCondition());\n\n // The condition function should be defined.\n if (conditionFuncInvoker === null) {\n throw new Error(\n `cannot evaluate node guard as the condition '${this.getCondition()}' function is not defined on the agent and has not been registered`\n );\n }\n\n // Call the condition function to determine whether this guard is satisfied.\n return !!!conditionFuncInvoker(this.args);\n };\n}\n", "import { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\nimport Attribute, { AttributeDetails } from \"../Attribute\";\n\nexport type CallbackAttributeDetails = {\n /** The name of the agent function that is called. */\n functionName: string;\n} & AttributeDetails;\n\n/**\n * A base node callback attribute.\n */\nexport default abstract class Callback extends Attribute {\n /**\n * @param type The node attribute type.\n * @param args The array of decorator argument definitions.\n * @param functionName The name of the agent function to call.\n */\n constructor(type: string, args: AnyArgument[], private functionName: string) {\n super(type, args);\n }\n\n /**\n * Gets the name of the agent function to call.\n */\n getFunctionName = () => this.functionName;\n\n /**\n * Gets whether this attribute is a guard.\n */\n isGuard = () => false;\n\n /**\n * Gets the attribute details.\n */\n getDetails(): CallbackAttributeDetails {\n return {\n type: this.getType(),\n args: this.getArguments(),\n functionName: this.getFunctionName()\n };\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n abstract callAgentFunction: (agent: Agent, isSuccess: boolean, isAborted: boolean) => void;\n}\n", "import Callback from \"./Callback\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An ENTRY callback which defines an agent function to call when the associated node is updated and moves out of running state.\n */\nexport default class Entry extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"entry\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n callAgentFunction = (agent: Agent) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call entry function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function.\n callbackFuncInvoker(this.args);\n };\n}\n", "import Callback from \"./Callback\";\nimport Lookup, { AnyExitArgument } from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * An EXIT callback which defines an agent function to call when the associated node is updated and moves to a finished state or is aborted.\n */\nexport default class Exit extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"exit\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n * @param isSuccess Whether the decorated node was left with a success state.\n * @param isAborted Whether the decorated node was aborted.\n */\n callAgentFunction = (agent: Agent, isSuccess: boolean, isAborted: boolean) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call exit function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function\n callbackFuncInvoker([{ value: { succeeded: isSuccess, aborted: isAborted } }, ...this.args]);\n };\n}\n", "import Callback from \"./Callback\";\nimport Lookup from \"../../Lookup\";\nimport { Agent } from \"../../Agent\";\nimport { AnyArgument } from \"../../RootAstNodesBuilder\";\n\n/**\n * A STEP callback which defines an agent function to call when the associated node is updated.\n */\nexport default class Step extends Callback {\n /**\n * @param functionName The name of the agent function to call.\n * @param args The array of callback argument definitions.\n */\n constructor(functionName: string, args: AnyArgument[]) {\n super(\"step\", args, functionName);\n }\n\n /**\n * Attempt to call the agent function that this callback refers to.\n * @param agent The agent.\n */\n callAgentFunction = (agent: Agent) => {\n // Attempt to get the invoker for the callback function.\n const callbackFuncInvoker = Lookup.getFuncInvoker(agent, this.getFunctionName());\n\n // The callback function should be defined.\n if (callbackFuncInvoker === null) {\n throw new Error(\n `cannot call step function '${this.getFunctionName()}' as is not defined on the agent and has not been registered`\n );\n }\n\n // Call the callback function.\n callbackFuncInvoker(this.args);\n };\n}\n", "import Action from \"./nodes/leaf/Action\";\nimport Condition from \"./nodes/leaf/Condition\";\nimport Wait from \"./nodes/leaf/Wait\";\nimport Root from \"./nodes/decorator/Root\";\nimport Repeat from \"./nodes/decorator/Repeat\";\nimport Retry from \"./nodes/decorator/Retry\";\nimport Flip from \"./nodes/decorator/Flip\";\nimport Succeed from \"./nodes/decorator/Succeed\";\nimport Fail from \"./nodes/decorator/Fail\";\nimport Lotto from \"./nodes/composite/Lotto\";\nimport Selector from \"./nodes/composite/Selector\";\nimport Sequence from \"./nodes/composite/Sequence\";\nimport Parallel from \"./nodes/composite/Parallel\";\nimport Node from \"./nodes/Node\";\nimport While from \"./attributes/guards/While\";\nimport Until from \"./attributes/guards/Until\";\nimport Entry from \"./attributes/callbacks/Entry\";\nimport Exit from \"./attributes/callbacks/Exit\";\nimport Step from \"./attributes/callbacks/Step\";\nimport Callback from \"./attributes/callbacks/Callback\";\nimport Guard from \"./attributes/guards/Guard\";\nimport Attribute from \"./attributes/Attribute\";\nimport Composite from \"./nodes/composite/Composite\";\nimport Decorator from \"./nodes/decorator/Decorator\";\nimport Leaf from \"./nodes/leaf/Leaf\";\n\nexport type Argument = {\n value: T;\n type: string; // Used for validation.\n};\ntype NullArgument = Argument & {\n type: \"null\";\n};\ntype BooleanArgument = Argument & {\n type: \"boolean\";\n};\ntype NumberArgument = Argument & {\n type: \"number\";\n isInteger: boolean; // Used for validation.\n};\ntype StringPlaceholderArgument = Argument & {\n type: \"string\";\n};\ntype IdentifierArgument = Argument & {\n type: \"identifier\";\n};\nexport type AnyArgument =\n | NullArgument\n | BooleanArgument\n | NumberArgument\n | StringPlaceholderArgument\n | IdentifierArgument;\n\n/**\n * The node attribute factories.\n */\nconst AttributeFactories: {\n [key: string]: (functionName: string, attributeArguments: AnyArgument[]) => Callback | Guard;\n} = {\n WHILE: (condition: string, attributeArguments: AnyArgument[]) => new While(condition, attributeArguments),\n UNTIL: (condition: string, attributeArguments: AnyArgument[]) => new Until(condition, attributeArguments),\n ENTRY: (functionName: string, attributeArguments: AnyArgument[]) => new Entry(functionName, attributeArguments),\n EXIT: (functionName: string, attributeArguments: AnyArgument[]) => new Exit(functionName, attributeArguments),\n STEP: (functionName: string, attributeArguments: AnyArgument[]) => new Step(functionName, attributeArguments)\n};\n\ntype Validatable = {\n children?: AstNode[];\n validate: (depth: number) => void;\n};\n\ntype NodeInstanceCreator = (\n namedRootNodeProvider: (name: string) => RootAstNode,\n visitedBranches: string[]\n) => T;\n\nexport type AstNode = Validatable & {\n type: string;\n createNodeInstance: NodeInstanceCreator;\n};\n\nexport type LeafAstNode = AstNode & {\n type: \"action\" | \"condition\" | \"wait\";\n attributes: Attribute[];\n};\n\nexport type CompositeAstNode = AstNode & {\n type: \"lotto\" | \"parallel\" | \"selector\" | \"sequence\";\n attributes: Attribute[];\n children: AstNode[];\n};\n\nexport type DecoratorAstNode = AstNode & {\n type: \"fail\" | \"flip\" | \"repeat\" | \"retry\" | \"root\" | \"succeed\";\n attributes: Attribute[];\n children: AstNode[];\n};\n\nexport type BranchAstNode = AstNode & {\n type: \"branch\";\n branchName: \"\" | string;\n};\n\nexport type LottoAstNode = CompositeAstNode & {\n type: \"lotto\";\n tickets: number[];\n};\n\nexport type RootAstNode = DecoratorAstNode & {\n type: \"root\";\n name: null | string;\n};\n\nexport type RepeatAstNode = DecoratorAstNode & {\n type: \"repeat\";\n iterations: number | null;\n iterationsMin: number | null;\n iterationsMax: number | null;\n};\n\nexport type RetryAstNode = DecoratorAstNode & {\n type: \"retry\";\n attempts: number | null;\n attemptsMin: number | null;\n attemptsMax: number | null;\n};\n\nexport type ActionAstNode = LeafAstNode & {\n type: \"action\";\n actionName: string;\n actionArguments: AnyArgument[];\n};\n\nexport type ConditionAstNode = LeafAstNode & {\n type: \"condition\";\n conditionName: string;\n conditionArguments: AnyArgument[];\n};\n\nexport type WaitAstNode = LeafAstNode & {\n type: \"wait\";\n duration: number | null;\n durationMin: number | null;\n durationMax: number | null;\n};\n\nexport type AnyAstNode =\n | BranchAstNode\n | CompositeAstNode\n | LottoAstNode\n | DecoratorAstNode\n | RootAstNode\n | RepeatAstNode\n | RetryAstNode\n | LeafAstNode\n | ActionAstNode\n | ConditionAstNode\n | WaitAstNode;\n\n/**\n * The AST node factories.\n */\nconst ASTNodeFactories = {\n ROOT: (): RootAstNode => ({\n type: \"root\",\n attributes: [],\n name: null,\n children: [],\n validate(depth: number) {\n // A root node cannot be the child of another node.\n if (depth > 1) {\n throw new Error(\"a root node cannot be the child of another node\");\n }\n\n // A root node must have a single child node.\n if (this.children.length !== 1) {\n throw new Error(\"a root node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Root(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n BRANCH: (): BranchAstNode => ({\n type: \"branch\",\n branchName: \"\",\n validate() {},\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n // Try to find the root node with a matching branch name.\n const targetRootNode = namedRootNodeProvider(this.branchName);\n\n // If we have already visited this branch then we have a circular dependency.\n if (visitedBranches.indexOf(this.branchName) !== -1) {\n throw new Error(`circular dependency found in branch node references for branch '${this.branchName}'`);\n }\n\n // If we have a target root node, then the node instance we want will be the first and only child of the referenced root node.\n if (targetRootNode) {\n return targetRootNode\n .createNodeInstance(namedRootNodeProvider, visitedBranches.concat(this.branchName))\n .getChildren()[0];\n } else {\n throw new Error(`branch references root node '${this.branchName}' which has not been defined`);\n }\n }\n }),\n SELECTOR: (): CompositeAstNode => ({\n type: \"selector\",\n attributes: [],\n children: [],\n validate() {\n // A selector node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a selector node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Selector(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n SEQUENCE: (): CompositeAstNode => ({\n type: \"sequence\",\n attributes: [],\n children: [],\n validate() {\n // A sequence node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a sequence node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Sequence(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n PARALLEL: (): CompositeAstNode => ({\n type: \"parallel\",\n attributes: [],\n children: [],\n validate() {\n // A parallel node must have at least a single node.\n if (this.children.length < 1) {\n throw new Error(\"a parallel node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Parallel(\n this.attributes,\n this.children.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n LOTTO: (): LottoAstNode => ({\n type: \"lotto\",\n attributes: [],\n children: [],\n tickets: [],\n validate() {\n // A lotto node must have at least a single node.\n if (this.children!.length < 1) {\n throw new Error(\"a lotto node must have at least a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Lotto(\n this.attributes,\n this.tickets!,\n this.children!.map((child) => child.createNodeInstance(namedRootNodeProvider, visitedBranches.slice()))\n );\n }\n }),\n REPEAT: (): RepeatAstNode => ({\n type: \"repeat\",\n attributes: [],\n iterations: null,\n iterationsMin: null,\n iterationsMax: null,\n children: [],\n validate() {\n // A repeat node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a repeat node must have a single child\");\n }\n\n if (this.iterations !== null) {\n // A repeat node must have a positive number of iterations if defined.\n if (this.iterations < 0) {\n throw new Error(\"a repeat node must have a positive number of iterations if defined\");\n }\n } else if (this.iterationsMin !== null && this.iterationsMax !== null) {\n // A repeat node must have a positive min and max iteration count if they are defined.\n if (this.iterationsMin < 0 || this.iterationsMax < 0) {\n throw new Error(\n \"a repeat node must have a positive minimum and maximum iteration count if defined\"\n );\n }\n\n // A repeat node must not have an minimum iteration count that exceeds the maximum iteration count.\n if (this.iterationsMin > this.iterationsMax) {\n throw new Error(\n \"a repeat node must not have a minimum iteration count that exceeds the maximum iteration count\"\n );\n }\n } else {\n // If we have no explicit iteration count or a minimum and maximum iteration count set then we are dealing with a repeat node that iterates indefinitely.\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Repeat(\n this.attributes,\n this.iterations,\n this.iterationsMin,\n this.iterationsMax,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n RETRY: (): RetryAstNode => ({\n type: \"retry\",\n attributes: [],\n attempts: null,\n attemptsMin: null,\n attemptsMax: null,\n children: [],\n validate() {\n // A retry node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a retry node must have a single child\");\n }\n\n if (this.attempts !== null) {\n // A retry node must have a positive number of attempts if defined.\n if (this.attempts < 0) {\n throw new Error(\"a retry node must have a positive number of attempts if defined\");\n }\n } else if (this.attemptsMin !== null && this.attemptsMax !== null) {\n // A retry node must have a positive min and max attempts count if they are defined.\n if (this.attemptsMin < 0 || this.attemptsMax < 0) {\n throw new Error(\"a retry node must have a positive minimum and maximum attempt count if defined\");\n }\n\n // A retry node must not have a minimum attempt count that exceeds the maximum attempt count.\n if (this.attemptsMin > this.attemptsMax) {\n throw new Error(\n \"a retry node must not have a minimum attempt count that exceeds the maximum attempt count\"\n );\n }\n } else {\n // If we have no explicit attempt count or a minimum and maximum attempt count set then we are dealing with a retry node that attempts indefinitely.\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Retry(\n this.attributes,\n this.attempts,\n this.attemptsMin,\n this.attemptsMax,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n FLIP: (): DecoratorAstNode => ({\n type: \"flip\",\n attributes: [],\n children: [],\n validate() {\n // A flip node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a flip node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Flip(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n SUCCEED: (): DecoratorAstNode => ({\n type: \"succeed\",\n attributes: [],\n children: [],\n validate() {\n // A succeed node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a succeed node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Succeed(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n FAIL: (): DecoratorAstNode => ({\n type: \"fail\",\n attributes: [],\n children: [],\n validate() {\n // A fail node must have a single node.\n if (this.children!.length !== 1) {\n throw new Error(\"a fail node must have a single child\");\n }\n },\n createNodeInstance(namedRootNodeProvider, visitedBranches) {\n return new Fail(\n this.attributes,\n this.children![0].createNodeInstance(namedRootNodeProvider, visitedBranches.slice())\n );\n }\n }),\n WAIT: (): WaitAstNode => ({\n type: \"wait\",\n attributes: [],\n duration: null,\n durationMin: null,\n durationMax: null,\n validate() {\n if (this.duration !== null) {\n // If an explict duration was defined then it must be a positive number.\n if (this.duration < 0) {\n throw new Error(\"a wait node must have a positive duration\");\n }\n } else if (this.durationMin !== null && this.durationMax !== null) {\n // A wait node must have a positive min and max duration.\n if (this.durationMin < 0 || this.durationMax < 0) {\n throw new Error(\"a wait node must have a positive minimum and maximum duration\");\n }\n\n // A wait node must not have a minimum duration that exceeds the maximum duration.\n if (this.durationMin > this.durationMax) {\n throw new Error(\"a wait node must not have a minimum duration that exceeds the maximum duration\");\n }\n } else {\n // If we have no explicit duration or duration bounds set then we are dealing with a wait node that waits indefinitely.\n }\n },\n createNodeInstance() {\n return new Wait(this.attributes, this.duration, this.durationMin, this.durationMax);\n }\n }),\n ACTION: (): ActionAstNode => ({\n type: \"action\",\n attributes: [],\n actionName: \"\",\n actionArguments: [],\n validate() {},\n createNodeInstance() {\n return new Action(this.attributes, this.actionName!, this.actionArguments!);\n }\n }),\n CONDITION: (): ConditionAstNode => ({\n type: \"condition\",\n attributes: [],\n conditionName: \"\",\n conditionArguments: [],\n validate() {},\n createNodeInstance() {\n return new Condition(this.attributes, this.conditionName!, this.conditionArguments!);\n }\n })\n};\n\ntype OtherAstNodes = AstNode[];\n\n/**\n * Create an array of root AST nodes based on the given definition.\n * @param definition The definition to parse the AST nodes from.\n * @returns The base definition AST nodes.\n */\nexport default function buildRootASTNodes(definition: string): RootAstNode[] {\n // Swap out any node/attribute argument string literals with a placeholder and get a mapping of placeholders to original values as well as the processed definition.\n const { placeholders, processedDefinition } = substituteStringLiterals(definition);\n\n // Convert the processed definition (with substituted string literals) into an array of raw tokens.\n const tokens = parseTokensFromDefinition(processedDefinition);\n\n // There must be at least 3 tokens for the tree definition to be valid. 'ROOT', '{' and '}'.\n if (tokens.length < 3) {\n throw new Error(\"invalid token count\");\n }\n\n // We should have a matching number of '{' and '}' tokens. If not, then there are scopes that have not been properly closed.\n if (tokens.filter((token) => token === \"{\").length !== tokens.filter((token) => token === \"}\").length) {\n throw new Error(\"scope character mismatch\");\n }\n\n // Create a stack of node children arrays, starting with a definition scope.\n const stack: [RootAstNode[], ...OtherAstNodes[]] = [[]];\n const rootScope = stack[0];\n\n // We should keep processing the raw tokens until we run out of them.\n while (tokens.length) {\n // Grab the next token.\n const token = tokens.shift();\n\n const currentScope = stack[stack.length - 1] as OtherAstNodes;\n\n // How we create the next AST token depends on the current raw token value.\n switch (token!.toUpperCase()) {\n case \"ROOT\": {\n // Create a ROOT AST node.\n const node = ASTNodeFactories.ROOT();\n\n // Push the ROOT node into the current scope.\n rootScope.push(node);\n\n // We may have a root node name defined as an argument.\n if (tokens[0] === \"[\") {\n const rootArguments = getArguments(tokens, placeholders);\n\n // We should have only a single argument that is not an empty string for a root node, which is the root name identifier.\n if (rootArguments.length === 1 && rootArguments[0].type === \"identifier\") {\n // The root name will be the first and only node argument.\n node.name = rootArguments[0].value as string;\n } else {\n throw new Error(\"expected single root name argument\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new ROOT nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"BRANCH\": {\n // Create a BRANCH AST node.\n const node = ASTNodeFactories.BRANCH();\n\n // Push the BRANCH node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require a branch name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected single branch name argument\");\n }\n\n // The branch name will be defined as a node argument.\n const branchArguments = getArguments(tokens, placeholders);\n\n // We should have only a single identifer argument for a branch node, which is the branch name.\n if (branchArguments.length === 1 && branchArguments[0].type === \"identifier\") {\n // The branch name will be the first and only node argument.\n node.branchName = branchArguments[0].value as string;\n } else {\n throw new Error(\"expected single branch name argument\");\n }\n break;\n }\n\n case \"SELECTOR\": {\n // Create a SELECTOR AST node.\n const node = ASTNodeFactories.SELECTOR();\n\n // Push the SELECTOR node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new SELECTOR nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"SEQUENCE\": {\n // Create a SEQUENCE AST node.\n const node = ASTNodeFactories.SEQUENCE();\n\n // Push the SEQUENCE node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new SEQUENCE nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"PARALLEL\": {\n // Create a PARALLEL AST node.\n const node = ASTNodeFactories.PARALLEL();\n\n // Push the PARALLEL node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new PARALLEL nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"LOTTO\": {\n // Create a LOTTO AST node.\n const node = ASTNodeFactories.LOTTO();\n\n // Push the LOTTO node into the current scope.\n currentScope.push(node);\n\n // If the next token is a '[' character then some ticket counts have been defined as arguments.\n if (tokens[0] === \"[\") {\n // Get the ticket count arguments, each argument must be a number.\n node.tickets = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"lotto node ticket counts must be integer values\"\n ).map((argument) => argument.value as number);\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new LOTTO nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"CONDITION\": {\n // Create a CONDITION AST node.\n const node = ASTNodeFactories.CONDITION();\n\n // Push the CONDITION node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require a condition function name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected condition name identifier argument\");\n }\n\n // Grab the condition node arguments.\n const conditionArguments = getArguments(tokens, placeholders);\n\n // We should have at least a single identifier argument for a condition node, which is the condition function name.\n if (conditionArguments.length && conditionArguments[0].type === \"identifier\") {\n // The condition function name will be the first node argument.\n node.conditionName = conditionArguments.shift()!.value as string;\n } else {\n throw new Error(\"expected condition name identifier argument\");\n }\n\n // Only the first argument should have been an identifier, all following arguments must be string, number, boolean or null.\n conditionArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid condition node argument value '\" +\n arg.value +\n \"', must be string, number, boolean or null\"\n );\n });\n\n // Any node arguments that follow the condition name identifier will be treated as condition function arguments.\n node.conditionArguments = conditionArguments;\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"FLIP\": {\n // Create a FLIP AST node.\n const node = ASTNodeFactories.FLIP();\n\n // Push the Flip node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new FLIP nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"SUCCEED\": {\n // Create a SUCCEED AST node.\n const node = ASTNodeFactories.SUCCEED();\n\n // Push the Succeed node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new Succeed nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"FAIL\": {\n // Create a FAIL AST node.\n const node = ASTNodeFactories.FAIL();\n\n // Push the Fail node into the current scope.\n currentScope.push(node);\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new Fail nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"WAIT\": {\n // Create a WAIT AST node.\n const node = ASTNodeFactories.WAIT();\n\n // Push the WAIT node into the current scope.\n currentScope.push(node);\n\n // The arguments of a wait node are optional. We may have:\n // - No node arguments, in which case the wait will be indefinite until it is aborted.\n // - One node argument which will be the explicit duration of the wait.\n // - Two node arguments which define the min and max duration bounds from which a random duration will be picked.\n if (tokens[0] === \"[\") {\n // Get the optional duration and longest duration of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"wait node durations must be integer values\"\n ).map((argument) => argument.value);\n\n // We may have:\n // - One node argument which will be the explicit duration of the wait.\n // - Two node arguments which define the min and max duration bounds from which a random duration will be picked.\n // - Too many arguments, which is not valid.\n if (nodeArguments.length === 1) {\n // An explicit duration was defined.\n node.duration = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // Min and max duration bounds were defined from which a random duration will be picked.\n node.durationMin = nodeArguments[0] as number;\n node.durationMax = nodeArguments[1] as number;\n } else if (nodeArguments.length > 2) {\n // An incorrect number of durations was defined.\n throw new Error(\"invalid number of wait node duration arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"REPEAT\": {\n // Create a REPEAT AST node.\n const node = ASTNodeFactories.REPEAT();\n\n // Push the REPEAT node into the current scope.\n currentScope.push(node);\n\n // The arguments of a repeat node are optional. We may have:\n // - No node arguments, in which case the repeat note will iterate indefinitely.\n // - One node argument which will be the explicit number of iterations to make.\n // - Two node arguments which define the min and max iteration bounds from which a random iteration count will be picked.\n if (tokens[0] === \"[\") {\n // An iteration count has been defined. Get the iteration and potential maximum iteration of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"repeat node iteration counts must be integer values\"\n ).map((argument) => argument.value);\n\n // We should have got one or two iteration counts.\n if (nodeArguments.length === 1) {\n // A static iteration count was defined.\n node.iterations = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // A minimum and maximum iteration count was defined.\n node.iterationsMin = nodeArguments[0] as number;\n node.iterationsMax = nodeArguments[1] as number;\n } else {\n // An incorrect number of iteration counts was defined.\n throw new Error(\"invalid number of repeat node iteration count arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new REPEAT nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"RETRY\": {\n // Create a RETRY AST node.\n const node = ASTNodeFactories.RETRY();\n\n // Push the RETRY node into the current scope.\n currentScope.push(node);\n\n // The arguments of a retry node are optional. We may have:\n // - No node arguments, in which case the retry note will attempt indefinitely.\n // - One node argument which will be the explicit number of attempts to make.\n // - Two node arguments which define the min and max attempt bounds from which a random attempt count will be picked.\n if (tokens[0] === \"[\") {\n // An attempt count has been defined. Get the attempt count and potential maximum attempt count of the wait.\n const nodeArguments = getArguments(\n tokens,\n placeholders,\n (arg) => arg.type === \"number\" && !!arg.isInteger,\n \"retry node attempt counts must be integer values\"\n ).map((argument) => argument.value);\n\n // We should have got one or two attempt counts.\n if (nodeArguments.length === 1) {\n // A static attempt count was defined.\n node.attempts = nodeArguments[0] as number;\n } else if (nodeArguments.length === 2) {\n // A minimum and maximum attempt count was defined.\n node.attemptsMin = nodeArguments[0] as number;\n node.attemptsMax = nodeArguments[1] as number;\n } else {\n // An incorrect number of attempt counts was defined.\n throw new Error(\"invalid number of retry node attempt count arguments defined\");\n }\n }\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n\n popAndCheck(tokens, \"{\");\n\n // The new scope is that of the new RETRY nodes children.\n stack.push(node.children!);\n break;\n }\n\n case \"ACTION\": {\n // Create a ACTION AST node.\n const node = ASTNodeFactories.ACTION();\n\n // Push the ACTION node into the current scope.\n currentScope.push(node);\n\n // We must have arguments defined, as we require an action name argument.\n if (tokens[0] !== \"[\") {\n throw new Error(\"expected action name identifier argument\");\n }\n\n // The action name will be defined as a node argument.\n const actionArguments = getArguments(tokens, placeholders);\n\n // We should have at least one identifer argument for an action node, which is the action name.\n if (actionArguments.length && actionArguments[0].type === \"identifier\") {\n // The action name will be the first and only node argument.\n node.actionName = actionArguments.shift()!.value as string;\n } else {\n throw new Error(\"expected action name identifier argument\");\n }\n\n // Only the first argument should have been an identifier, all following arguments must be string, number, boolean or null.\n actionArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid action node argument value '\" +\n arg.value +\n \"', must be string, number, boolean or null\"\n );\n });\n\n // Any node arguments that follow the action name identifier will be treated as action function arguments.\n node.actionArguments = actionArguments;\n\n // Try to pick any attributes off of the token stack.\n node.attributes = getAttributes(tokens, placeholders);\n break;\n }\n\n case \"}\": {\n // The '}' character closes the current scope.\n stack.pop();\n break;\n }\n\n default: {\n throw new Error(`unexpected token '${token}'`);\n }\n }\n }\n\n // A function to recursively validate each of the nodes in the AST.\n const validateASTNode = (node: Validatable, depth: number): void => {\n // Validate the node.\n node.validate(depth);\n\n // Validate each child of the node.\n (node.children || []).forEach((child) => validateASTNode(child, depth + 1));\n };\n\n // Start node validation from the definition root.\n validateASTNode(\n {\n children: stack[0] as RootAstNode[],\n validate(this: { children: RootAstNode[] }) {\n // We must have at least one node defined as the definition scope, which should be a root node.\n if (this.children.length === 0) {\n throw new Error(\"expected root node to have been defined\");\n }\n\n // Each node at the base of the definition scope MUST be a root node.\n for (const definitionLevelNode of this.children) {\n if (definitionLevelNode.type !== \"root\") {\n throw new Error(\"expected root node at base of definition\");\n }\n }\n\n // Exactly one root node must not have a name defined. This will be the main root, others will have to be referenced via branch nodes.\n if (this.children.filter((definitionLevelNode) => definitionLevelNode.name === null).length !== 1) {\n throw new Error(\"expected single unnamed root node at base of definition to act as main root\");\n }\n\n // No two named root nodes can have matching names.\n const rootNodeNames: string[] = [];\n for (const definitionLevelNode of this.children) {\n if (rootNodeNames.indexOf(definitionLevelNode.name!) !== -1) {\n throw new Error(`multiple root nodes found with duplicate name '${definitionLevelNode.name}'`);\n } else {\n rootNodeNames.push(definitionLevelNode.name!);\n }\n }\n }\n },\n 0\n );\n\n // Return the root AST nodes.\n return stack[0];\n}\n\n/**\n * Pop the next raw token off of the stack and throw an error if it wasn't the expected one.\n * @param tokens The array of remaining tokens.\n * @param expected An optional string or array or items, one of which must match the next popped token.\n * @returns The popped token.\n */\nfunction popAndCheck(tokens: string[], expected: string | string[]) {\n // Get and remove the next token.\n const popped = tokens.shift();\n\n // We were expecting another token.\n if (popped === undefined) {\n throw new Error(\"unexpected end of definition\");\n }\n\n // Do we have an expected token/tokens array?\n if (expected !== undefined) {\n // Check whether the popped token matches at least one of our expected items.\n var tokenMatchesExpectation = ([] as string[])\n .concat(expected)\n .some((item) => popped.toUpperCase() === item.toUpperCase());\n\n // Throw an error if the popped token didn't match any of our expected items.\n if (!tokenMatchesExpectation) {\n const expectationString = ([] as string[])\n .concat(expected)\n .map((item) => \"'\" + item + \"'\")\n .join(\" or \");\n\n throw new Error(`unexpected token found. Expected '${expectationString}' but got '${popped}'`);\n }\n }\n\n // Return the popped token.\n return popped;\n}\n\ntype Placeholders = { [key: string]: string };\n\n/**\n * Pull an argument definition list off of the token stack.\n * @param tokens The array of remaining tokens.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @param argumentValidator The argument validator function.\n * @param validationFailedMessage The exception message to throw if argument validation fails.\n * @returns The argument definition list.\n */\nfunction getArguments(\n tokens: string[],\n stringArgumentPlaceholders: Placeholders,\n argumentValidator?: (arg: AnyArgument) => boolean,\n validationFailedMessage?: string\n) {\n // Any lists of arguments will always be wrapped in '[]' for node arguments or '()' for attribute arguments.\n // We are looking for a '[' or '(' opener that wraps the argument tokens and the relevant closer.\n const closer = popAndCheck(tokens, [\"[\", \"(\"]) === \"[\" ? \"]\" : \")\";\n\n const argumentListTokens: string[] = [];\n const argumentList: AnyArgument[] = [];\n\n // Grab all tokens between the '[' and ']' or '(' and ')'.\n while (tokens.length && tokens[0] !== closer) {\n // The next token is part of our arguments list.\n argumentListTokens.push(tokens.shift()!);\n }\n\n // Validate the order of the argument tokens. Each token must either be a ',' or a single argument that satisfies the validator.\n argumentListTokens.forEach((token, index) => {\n // Get whether this token should be an actual argument.\n const shouldBeArgumentToken = !(index & 1);\n\n // If the current token should be an actual argument then validate it,otherwise it should be a ',' token.\n if (shouldBeArgumentToken) {\n // Get the argument definition.\n const argumentDefinition = getArgumentDefinition(token!, stringArgumentPlaceholders);\n\n // Try to validate the argument.\n if (argumentValidator && !argumentValidator(argumentDefinition)) {\n throw new Error(validationFailedMessage);\n }\n\n // This is a valid argument!\n argumentList.push(argumentDefinition);\n } else {\n // The current token should be a ',' token.\n if (token !== \",\") {\n throw new Error(`invalid argument list, expected ',' or ']' but got '${token}'`);\n }\n }\n });\n\n // The arguments list should terminate with a ']' or ')' token, depending on the opener.\n popAndCheck(tokens, closer);\n\n // Return the argument list.\n return argumentList;\n}\n\n/**\n * Gets an argument value definition.\n * @param token The argument token.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @returns An argument value definition.\n */\nfunction getArgumentDefinition(token: string, stringArgumentPlaceholders: Placeholders): AnyArgument {\n // Check whether the token represents a null value.\n if (token === \"null\") {\n return {\n value: null,\n type: \"null\"\n } as NullArgument;\n }\n\n // Check whether the token represents a boolean value.\n if (token === \"true\" || token === \"false\") {\n return {\n value: token === \"true\",\n type: \"boolean\"\n } as BooleanArgument;\n }\n\n // Check whether the token represents a number value.\n // TODO: Relies on broken isNaN - see MDN.\n // if (!Number.isNaN(token)) {\n if (!isNaN(token as any)) {\n return {\n value: parseFloat(token),\n isInteger: parseFloat(token) === parseInt(token, 10),\n type: \"number\"\n } as NumberArgument;\n }\n\n // Check whether the token is a placeholder (e.g. @@0@@) representing a string literal.\n if (token.match(/^@@\\d+@@$/g)) {\n return {\n value: stringArgumentPlaceholders[token].replace('\\\\\"', '\"'),\n type: \"string\"\n } as StringPlaceholderArgument;\n }\n\n // The only remaining option is that the argument value is an identifier.\n return {\n value: token,\n type: \"identifier\"\n } as IdentifierArgument;\n}\n\n/**\n * Pull any attributes off of the token stack.\n * @param tokens The array of remaining tokens.\n * @param stringArgumentPlaceholders The mapping of string literal node argument placeholders to original values.\n * @returns An array of attributes defined by any directly following tokens.\n */\nfunction getAttributes(tokens: string[], stringArgumentPlaceholders: Placeholders) {\n // Create an array to hold any attributes found.\n const attributes: Attribute[] = [];\n\n // Keep track of names of attribute that we have found on the token stack, as we cannot have duplicates.\n const attributesFound: string[] = [];\n\n // Try to get the attribute factory for the next token.\n let attributeFactory = AttributeFactories[(tokens[0] || \"\").toUpperCase()];\n\n // Pull attribute tokens off of the tokens stack until we have no more.\n while (attributeFactory) {\n // Check to make sure that we have not already created a attribute of this type for this node.\n if (attributesFound.indexOf(tokens[0].toUpperCase()) !== -1) {\n throw new Error(`duplicate attribute '${tokens[0].toUpperCase()}' found for node`);\n }\n\n // Add the current attribute type to our array of found attributes.\n attributesFound.push(tokens.shift()!.toUpperCase());\n\n // Grab any attribute arguments.\n const attributeArguments = getArguments(tokens, stringArgumentPlaceholders);\n\n // The first attribute argument has to be an identifer, this will reference an agent function.\n if (attributeArguments.length === 0 || attributeArguments[0].type !== \"identifier\") {\n throw new Error(\"expected agent function name identifier argument for attribute\");\n }\n\n // Grab the first attribute which is an identifier that will reference an agent function.\n const attributeFunctionName = attributeArguments.shift()! as IdentifierArgument;\n\n // Any remaining attribute arguments must have a type of string, number, boolean or null.\n attributeArguments\n .filter((arg) => arg.type === \"identifier\")\n .forEach((arg) => {\n throw new Error(\n \"invalid attribute argument value '\" + arg.value + \"', must be string, number, boolean or null\"\n );\n });\n\n // Create the attribute and add it to the array of attributes found.\n attributes.push(attributeFactory(attributeFunctionName.value, attributeArguments));\n\n // Try to get the next attribute name token, as there could be multiple.\n attributeFactory = AttributeFactories[(tokens[0] || \"\").toUpperCase()];\n }\n\n return attributes;\n}\n\n/**\n * Swaps out any node/attribute argument string literals with placeholders.\n * @param definition The definition.\n * @returns An object containing a mapping of placeholders to original string values as well as the processed definition string.\n */\nfunction substituteStringLiterals(definition: string): {\n placeholders: { [key: string]: string };\n processedDefinition: string;\n} {\n // Create an object to hold the mapping of placeholders to original string values.\n const placeholders: Placeholders = {};\n\n // Replace any string literals wrapped with double quotes in our definition with placeholders to be processed later.\n const processedDefinition = definition.replace(/\\\"(\\\\.|[^\"\\\\])*\\\"/g, (match) => {\n var strippedMatch = match.substring(1, match.length - 1);\n var placeholder = Object.keys(placeholders).find((key) => placeholders[key] === strippedMatch);\n\n // If we have no existing string literal match then create a new placeholder.\n if (!placeholder) {\n placeholder = `@@${Object.keys(placeholders).length}@@`;\n placeholders[placeholder] = strippedMatch;\n }\n\n return placeholder;\n });\n\n return { placeholders, processedDefinition };\n}\n\n/**\n * Parse the tree definition into an array of raw tokens.\n * @param definition The definition.\n * @returns An array of tokens parsed from the definition.\n */\nfunction parseTokensFromDefinition(definition: string): string[] {\n // Add some space around various important characters so that they can be plucked out easier as individual tokens.\n definition = definition.replace(/\\(/g, \" ( \");\n definition = definition.replace(/\\)/g, \" ) \");\n definition = definition.replace(/\\{/g, \" { \");\n definition = definition.replace(/\\}/g, \" } \");\n definition = definition.replace(/\\]/g, \" ] \");\n definition = definition.replace(/\\[/g, \" [ \");\n definition = definition.replace(/\\,/g, \" , \");\n\n // Split the definition into raw token form and return it.\n return definition.replace(/\\s+/g, \" \").trim().split(\" \");\n}\n", "import GuardPath, { GuardPathPart } from \"./attributes/guards/GuardPath\";\nimport buildRootASTNodes, { AnyArgument, RootAstNode } from \"./RootAstNodesBuilder\";\nimport State, { AnyState } from \"./State\";\nimport Lookup from \"./Lookup\";\nimport Node from \"./nodes/Node\";\nimport Root from \"./nodes/decorator/Root\";\nimport Composite from \"./nodes/composite/Composite\";\nimport Decorator from \"./nodes/decorator/Decorator\";\nimport { Agent, GlobalFunction } from \"./Agent\";\nimport { CallbackAttributeDetails } from \"./attributes/callbacks/Callback\";\nimport { GuardAttributeDetails } from \"./attributes/guards/Guard\";\nimport { BehaviourTreeOptions } from \"./BehaviourTreeOptions\";\nimport { parseToJSON } from \"./dsl/DSLDefinitionParser\";\n\n// Purely for outside inspection of the tree.\nexport type FlattenedTreeNode = {\n id: string;\n type: string;\n caption: string;\n state: AnyState;\n guards: GuardAttributeDetails[];\n callbacks: CallbackAttributeDetails[];\n args: AnyArgument[];\n parentId: string | null;\n};\n\n/**\n * A representation of a behaviour tree.\n */\nexport class BehaviourTree {\n /**\n * The main root tree node.\n */\n public readonly rootNode: Root;\n\n /**\n * Creates a new instance of the BehaviourTree class.\n * @param definition The behaviour tree definition.\n * @param agent The agent instance that this behaviour tree is modelling behaviour for.\n * @param options The behaviour tree options object.\n */\n constructor(definition: string, private agent: Agent, private options: BehaviourTreeOptions = {}) {\n // The tree definition must be defined and a valid string.\n if (typeof definition !== \"string\") {\n throw new Error(\"the tree definition must be a string\");\n }\n\n // The agent must be defined and not null.\n if (typeof agent !== \"object\" || agent === null) {\n throw new Error(\"the agent must be defined and not null\");\n }\n\n // Parse the behaviour tree definition, create the populated tree of behaviour tree nodes, and get the root.\n this.rootNode = BehaviourTree._createRootNode(definition);\n }\n\n /**\n * Gets whether the tree is in the RUNNING state.\n * @returns true if the tree is in the RUNNING state, otherwise false.\n */\n isRunning() {\n return this.rootNode.getState() === State.RUNNING;\n }\n\n /**\n * Gets the current tree state of SUCCEEDED, FAILED, READY or RUNNING.\n * @returns The current tree state.\n */\n getState() {\n return this.rootNode.getState();\n }\n\n /**\n * Step the tree.\n * Carries out a node update that traverses the tree from the root node outwards to any child nodes, skipping those that are already in a resolved state of SUCCEEDED or FAILED.\n * After being updated, leaf nodes will have a state of SUCCEEDED, FAILED or RUNNING. Leaf nodes that are left in the RUNNING state as part of a tree step will be revisited each\n * subsequent step until they move into a resolved state of either SUCCEEDED or FAILED, after which execution will move through the tree to the next node with a state of READY.\n *\n * Calling this method when the tree is already in a resolved state of SUCCEEDED or FAILED will cause it to be reset before tree traversal begins.\n */\n step() {\n // If the root node has already been stepped to completion then we need to reset it.\n if (this.rootNode.getState() === State.SUCCEEDED || this.rootNode.getState() === State.FAILED) {\n this.rootNode.reset();\n }\n\n try {\n this.rootNode.update(this.agent, this.options);\n } catch (exception) {\n throw new Error(`error stepping tree: ${(exception as Error).message}`);\n }\n }\n\n /**\n * Resets the tree from the root node outwards to each nested node, giving each a state of READY.\n */\n reset() {\n this.rootNode.reset();\n }\n\n /**\n * Gets the flattened details of every node in the tree.\n * @returns The flattened details of every node in the tree.\n */\n getFlattenedNodeDetails(): FlattenedTreeNode[] {\n // Create an empty flattened array of tree nodes.\n const flattenedTreeNodes: FlattenedTreeNode[] = [];\n\n /**\n * Helper function to process a node instance and push details into the flattened tree nodes array.\n * @param node The current node.\n * @param parentUid The UID of the node parent, or null if the node is the main root node.\n */\n const processNode = (node: Node, parentUid: string | null) => {\n // Get the guard and callback attribute details for this node.\n const guards = node\n .getAttributes()\n .filter((attribute) => attribute.isGuard())\n .map((attribute) => attribute.getDetails()) as GuardAttributeDetails[];\n const callbacks = node\n .getAttributes()\n .filter((attribute) => !attribute.isGuard())\n .map((attribute) => attribute.getDetails()) as CallbackAttributeDetails[];\n\n // Push the current node into the flattened nodes array.\n flattenedTreeNodes.push({\n id: node.getUid(),\n type: node.getType(),\n caption: node.getName(),\n state: node.getState(),\n guards,\n callbacks,\n args: node.getArguments(),\n parentId: parentUid\n });\n\n // Process each of the nodes children if it is not a leaf node.\n if (!node.isLeafNode()) {\n (node as Composite | Decorator)\n .getChildren()\n .forEach((child) => processNode(child, (node as Composite | Decorator).getUid()));\n }\n };\n\n // Convert the nested node structure into a flattened array of node details.\n processNode(this.rootNode, null);\n\n return flattenedTreeNodes;\n }\n\n /**\n * Registers the action/condition/guard/callback function or subtree with the given name.\n * @param name The name of the function or subtree to register.\n * @param value The function or subtree definition to register.\n */\n static register(name: string, value: GlobalFunction | string) {\n if (typeof value === \"function\") {\n // We are going to register a action/condition/guard/callback function.\n Lookup.setFunc(name, value);\n } else if (typeof value === \"string\") {\n // We are going to register a subtree.\n let rootASTNodes: RootAstNode[];\n\n try {\n // Try to create the behaviour tree AST based on the definition provided, this could fail if the definition is invalid.\n rootASTNodes = buildRootASTNodes(value);\n } catch (exception) {\n // There was an issue in trying to parse and build the tree definition.\n throw new Error(`error registering definition: ${(exception as Error).message}`);\n }\n\n // This function should only ever be called with a definition containing a single unnamed root node.\n if (rootASTNodes.length != 1 || rootASTNodes[0].name !== null) {\n throw new Error(\"error registering definition: expected a single unnamed root node\");\n }\n\n Lookup.setSubtree(name, rootASTNodes[0]);\n } else {\n throw new Error(\"unexpected value, expected string definition or function\");\n }\n }\n\n /**\n * Unregisters the registered action/condition/guard/callback function or subtree with the given name.\n * @param name The name of the registered action/condition/guard/callback function or subtree to unregister.\n */\n static unregister(name: string): void {\n Lookup.remove(name);\n }\n\n /**\n * Unregister all registered action/condition/guard/callback functions and subtrees.\n */\n static unregisterAll(): void {\n Lookup.empty();\n }\n\n /**\n * Parses a behaviour tree definition and creates a tree of behaviour tree nodes.\n * @param {string} definition The behaviour tree definition.\n * @returns The root behaviour tree node.\n */\n private static _createRootNode(definition: string): Root {\n // TODO Remove!\n try {\n //parseToJSON(definition);\n } catch (exception) {\n console.log(exception);\n }\n\n try {\n // Try to create the behaviour tree AST based on the definition provided, this could fail if the definition is invalid.\n const rootASTNodes = buildRootASTNodes(definition);\n\n // Create a symbol to use as the main root key in our root node mapping.\n const mainRootNodeKey = Symbol(\"__root__\");\n\n // Create a mapping of root node names to root AST tokens. The main root node will have a key of Symbol(\"__root__\").\n const rootNodeMap: { [key: string | symbol]: RootAstNode } = {};\n for (const rootASTNode of rootASTNodes) {\n rootNodeMap[rootASTNode.name === null ? mainRootNodeKey : rootASTNode.name!] = rootASTNode;\n }\n\n // Convert the AST to our actual tree and get the root node.\n const rootNode: Root = rootNodeMap[mainRootNodeKey].createNodeInstance(\n // Create a provider for named root nodes that are part of our definition or have been registered. Prioritising the former.\n (name: string): RootAstNode => (rootNodeMap[name] ? rootNodeMap[name] : Lookup.getSubtree(name)),\n []\n );\n\n // Set a guard path on every leaf of the tree to evaluate as part of its update.\n BehaviourTree._applyLeafNodeGuardPaths(rootNode);\n\n // Return the root node.\n return rootNode;\n } catch (exception) {\n // There was an issue in trying to parse and build the tree definition.\n throw new Error(`error parsing tree: ${(exception as Error).message}`);\n }\n }\n\n /**\n * Applies a guard path to every leaf of the tree to evaluate as part of each update.\n * @param rootNode The main root tree node.\n */\n private static _applyLeafNodeGuardPaths(rootNode: Root) {\n const nodePaths: Node[][] = [];\n\n const findLeafNodes = (path: Node[], node: Node) => {\n // Add the current node to the path.\n path = path.concat(node);\n\n // Check whether the current node is a leaf node.\n if (node.isLeafNode()) {\n nodePaths.push(path);\n } else {\n (node as Composite | Decorator).getChildren().forEach((child) => findLeafNodes(path, child));\n }\n };\n\n // Find all leaf node paths, starting from the root.\n findLeafNodes([], rootNode);\n\n nodePaths.forEach((path) => {\n // Each node in the current path will have to be assigned a guard path, working from the root outwards.\n for (let depth = 0; depth < path.length; depth++) {\n // Get the node in the path at the current depth.\n const currentNode = path[depth];\n\n // The node may already have been assigned a guard path, if so just skip it.\n if (currentNode.hasGuardPath()) {\n continue;\n }\n\n // Create the guard path for the current node.\n const guardPath = new GuardPath(\n path\n .slice(0, depth + 1)\n .map((node) => ({ node, guards: node.getGuardAttributes() }))\n .filter((details) => details.guards.length > 0)\n );\n\n // Assign the guard path to the current node.\n currentNode.setGuardPath(guardPath);\n }\n });\n }\n}\n"], + "mappings": "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", + "names": ["Participant", "Lotto", "createLotto", "State", "createLotto"] +} diff --git a/dist/nodes/Node.d.ts b/dist/nodes/Node.d.ts new file mode 100644 index 0000000..864bffc --- /dev/null +++ b/dist/nodes/Node.d.ts @@ -0,0 +1,111 @@ +import { Agent } from "../Agent"; +import Attribute from "../attributes/Attribute"; +import Entry from "../attributes/callbacks/Entry"; +import Exit from "../attributes/callbacks/Exit"; +import Step from "../attributes/callbacks/Step"; +import Guard from "../attributes/guards/Guard"; +import GuardPath from "../attributes/guards/GuardPath"; +import { BehaviourTreeOptions } from "../BehaviourTreeOptions"; +import { AnyArgument } from "../RootAstNodesBuilder"; +import { AnyState } from "../State"; +import Leaf from "./leaf/Leaf"; +/** + * A base node. + */ +export default abstract class Node { + private type; + private attributes; + private args; + /** + * The node uid. + */ + private readonly uid; + /** + * The node state. + */ + private state; + /** + * The guard path to evaluate as part of a node update. + */ + private guardPath; + /** + * @param type The node type. + * @param attributes The node attributes. + * @param args The node argument definitions. + */ + constructor(type: string, attributes: Attribute[], args: AnyArgument[]); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected abstract onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + abstract getName(): string; + /** + * Gets whether this node is a leaf node. + */ + abstract isLeafNode: () => this is Leaf; + /** + * Gets/Sets the state of the node. + */ + getState: () => AnyState; + setState: (value: AnyState) => void; + /** + * Gets the unique id of the node. + */ + getUid: () => string; + /** + * Gets the type of the node. + */ + getType: () => string; + /** + * Gets the node attributes. + */ + getAttributes: () => Attribute[]; + /** + * Gets the node arguments. + */ + getArguments: () => AnyArgument[]; + /** + * Gets the node attribute with the specified type, or null if it does not exist. + */ + getAttribute(type: "entry" | "ENTRY"): Entry; + getAttribute(type: "exit" | "EXIT"): Exit; + getAttribute(type: "step" | "STEP"): Step; + /** + * Gets the node attributes. + */ + getGuardAttributes: () => Guard[]; + /** + * Sets the guard path to evaluate as part of a node update. + */ + setGuardPath: (value: GuardPath) => GuardPath; + /** + * Gets whether a guard path is assigned to this node. + */ + hasGuardPath: () => boolean; + /** + * Gets whether this node is in the specified state. + * @param value The value to compare to the node state. + */ + is(value: AnyState): boolean; + /** + * Reset the state of the node. + */ + reset(): void; + /** + * Abort the running of this node. + * @param agent The agent. + */ + abort(agent: Agent): void; + /** + * Update the node. + * @param agent The agent. + * @param options The behaviour tree options object. + * @returns The result of the update. + */ + update(agent: Agent, options: BehaviourTreeOptions): void; +} diff --git a/dist/nodes/composite/Composite.d.ts b/dist/nodes/composite/Composite.d.ts new file mode 100644 index 0000000..51c075e --- /dev/null +++ b/dist/nodes/composite/Composite.d.ts @@ -0,0 +1,32 @@ +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +/** + * A composite node that wraps child nodes. + */ +export default abstract class Composite extends Node { + protected children: Node[]; + /** + * @param type The node type. + * @param attributes The node attributes. + * @param children The child nodes. + */ + constructor(type: string, attributes: Attribute[], children: Node[]); + /** + * Gets whether this node is a leaf node. + */ + isLeafNode: () => boolean; + /** + * Gets the children of this node. + */ + getChildren: () => Node[]; + /** + * Reset the state of the node. + */ + reset: () => void; + /** + * Abort the running of this node. + * @param agent The agent. + */ + abort: (agent: Agent) => void; +} diff --git a/dist/nodes/composite/Lotto.d.ts b/dist/nodes/composite/Lotto.d.ts new file mode 100644 index 0000000..2a2a001 --- /dev/null +++ b/dist/nodes/composite/Lotto.d.ts @@ -0,0 +1,33 @@ +import Node from "../Node"; +import Composite from "./Composite"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A LOTTO node. + * A winning child is picked on the initial update of this node, based on ticket weighting. + * The state of this node will match the state of the winning child. + */ +export default class Lotto extends Composite { + private tickets; + /** + * @param attributes The node attributes. + * @param tickets The child node tickets. + * @param children The child nodes. + */ + constructor(attributes: Attribute[], tickets: number[], children: Node[]); + /** + * The child node selected to be the active one. + */ + private selectedChild; + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/composite/Parallel.d.ts b/dist/nodes/composite/Parallel.d.ts new file mode 100644 index 0000000..fe8e143 --- /dev/null +++ b/dist/nodes/composite/Parallel.d.ts @@ -0,0 +1,26 @@ +import Composite from "./Composite"; +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A PARALLEL node. + * The child nodes are executed concurrently until one fails or all succeed. + */ +export default class Parallel extends Composite { + /** + * @param attributes The node attributes. + * @param children The child nodes. + */ + constructor(attributes: Attribute[], children: Node[]); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/composite/Selector.d.ts b/dist/nodes/composite/Selector.d.ts new file mode 100644 index 0000000..da73717 --- /dev/null +++ b/dist/nodes/composite/Selector.d.ts @@ -0,0 +1,27 @@ +import Composite from "./Composite"; +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A SELECTOR node. + * The child nodes are executed in sequence until one succeeds or all fail. + */ +export default class Selector extends Composite { + protected children: Node[]; + /** + * @param attributes The node attributes. + * @param children The child nodes. + */ + constructor(attributes: Attribute[], children: Node[]); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/composite/Sequence.d.ts b/dist/nodes/composite/Sequence.d.ts new file mode 100644 index 0000000..31f13d5 --- /dev/null +++ b/dist/nodes/composite/Sequence.d.ts @@ -0,0 +1,27 @@ +import Composite from "./Composite"; +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A SEQUENCE node. + * The child nodes are executed in sequence until one fails or all succeed. + */ +export default class Sequence extends Composite { + protected children: Node[]; + /** + * @param attributes The node attributes. + * @param children The child nodes. + */ + constructor(attributes: Attribute[], children: Node[]); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/decorator/Decorator.d.ts b/dist/nodes/decorator/Decorator.d.ts new file mode 100644 index 0000000..e3c9f24 --- /dev/null +++ b/dist/nodes/decorator/Decorator.d.ts @@ -0,0 +1,32 @@ +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +/** + * A decorator node that wraps a single child node. + */ +export default abstract class Decorator extends Node { + protected child: Node; + /** + * @param type The node type. + * @param attributes The node attributes. + * @param child The child node. + */ + constructor(type: string, attributes: Attribute[], child: Node); + /** + * Gets whether this node is a leaf node. + */ + isLeafNode: () => boolean; + /** + * Gets the children of this node. + */ + getChildren: () => Node[]; + /** + * Reset the state of the node. + */ + reset: () => void; + /** + * Abort the running of this node. + * @param agent The agent. + */ + abort: (agent: Agent) => void; +} diff --git a/dist/nodes/decorator/Fail.d.ts b/dist/nodes/decorator/Fail.d.ts new file mode 100644 index 0000000..9a790f7 --- /dev/null +++ b/dist/nodes/decorator/Fail.d.ts @@ -0,0 +1,26 @@ +import Node from "../Node"; +import Decorator from "./Decorator"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A Fail node. + * This node wraps a single child and will always move to the 'FAILED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state. + */ +export default class Fail extends Decorator { + /** + * @param attributes The node attributes. + * @param child The child node. + */ + constructor(attributes: Attribute[], child: Node); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/decorator/Flip.d.ts b/dist/nodes/decorator/Flip.d.ts new file mode 100644 index 0000000..6e14177 --- /dev/null +++ b/dist/nodes/decorator/Flip.d.ts @@ -0,0 +1,26 @@ +import Decorator from "./Decorator"; +import Node from "../Node"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A Flip node. + * This node wraps a single child and will flip the state of the child state. + */ +export default class Flip extends Decorator { + /** + * @param attributes The node attributes. + * @param child The child node. + */ + constructor(attributes: Attribute[], child: Node); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/decorator/Repeat.d.ts b/dist/nodes/decorator/Repeat.d.ts new file mode 100644 index 0000000..8a99d2d --- /dev/null +++ b/dist/nodes/decorator/Repeat.d.ts @@ -0,0 +1,58 @@ +import Node from "../Node"; +import Decorator from "./Decorator"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A REPEAT node. + * The node has a single child which can have: + * -- A number of iterations for which to repeat the child node. + * -- An infinite repeat loop if neither an iteration count or a condition function is defined. + * The REPEAT node will stop and have a 'FAILED' state if its child is ever in a 'FAILED' state after an update. + * The REPEAT node will attempt to move on to the next iteration if its child is ever in a 'SUCCEEDED' state. + */ +export default class Repeat extends Decorator { + private iterations; + private iterationsMin; + private iterationsMax; + /** + * @param attributes The node attributes. + * @param iterations The number of iterations to repeat the child node. + * @param iterationsMin The minimum possible number of iterations to repeat the child node. + * @param iterationsMax The maximum possible number of iterations to repeat the child node. + * @param child The child node. + */ + constructor(attributes: Attribute[], iterations: number | null, iterationsMin: number | null, iterationsMax: number | null, child: Node); + /** + * The number of target iterations to make. + */ + private targetIterationCount; + /** + * The current iteration count. + */ + private currentIterationCount; + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; + /** + * Reset the state of the node. + */ + reset: () => void; + /** + * Gets whether an iteration can be made. + * @returns Whether an iteration can be made. + */ + private canIterate; + /** + * Sets the target iteration count. + * @param options The behaviour tree options object. + */ + private setTargetIterationCount; +} diff --git a/dist/nodes/decorator/Retry.d.ts b/dist/nodes/decorator/Retry.d.ts new file mode 100644 index 0000000..fd4745a --- /dev/null +++ b/dist/nodes/decorator/Retry.d.ts @@ -0,0 +1,58 @@ +import Node from "../Node"; +import Decorator from "./Decorator"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A RETRY node. + * The node has a single child which can have: + * -- A number of iterations for which to repeat the child node. + * -- An infinite repeat loop if neither an iteration count or a condition function is defined. + * The RETRY node will stop and have a 'SUCCEEDED' state if its child is ever in a 'SUCCEEDED' state after an update. + * The RETRY node will attempt to move on to the next iteration if its child is ever in a 'FAILED' state. + */ +export default class Retry extends Decorator { + private attempts; + private attemptsMin; + private attemptsMax; + /** + * @param attributes The node attributes. + * @param attempts The number of attempts to retry the child node. + * @param attemptsMin The minimum possible number of attempts to retry the child node. + * @param attemptsMax The maximum possible number of attempts to retry the child node. + * @param child The child node. + */ + constructor(attributes: Attribute[], attempts: number | null, attemptsMin: number | null, attemptsMax: number | null, child: Node); + /** + * The number of target attempts to make. + */ + private targetAttemptCount; + /** + * The current attempt count. + */ + private currentAttemptCount; + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; + /** + * Reset the state of the node. + */ + reset: () => void; + /** + * Gets whether an attempt can be made. + * @returns Whether an attempt can be made. + */ + canAttempt: () => boolean; + /** + * Sets the target attempt count. + * @param options The behaviour tree options object. + */ + setTargetAttemptCount: (options: BehaviourTreeOptions) => void; +} diff --git a/dist/nodes/decorator/Root.d.ts b/dist/nodes/decorator/Root.d.ts new file mode 100644 index 0000000..ebbceff --- /dev/null +++ b/dist/nodes/decorator/Root.d.ts @@ -0,0 +1,26 @@ +import Node from "../Node"; +import Decorator from "./Decorator"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A Root node. + * The root node will have a single child. + */ +export default class Root extends Decorator { + /** + * @param attributes The node attributes. + * @param child The child node. + */ + constructor(attributes: Attribute[], child: Node); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/decorator/Succeed.d.ts b/dist/nodes/decorator/Succeed.d.ts new file mode 100644 index 0000000..67dc196 --- /dev/null +++ b/dist/nodes/decorator/Succeed.d.ts @@ -0,0 +1,26 @@ +import Node from "../Node"; +import Decorator from "./Decorator"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A Succeed node. + * This node wraps a single child and will always move to the 'SUCCEEDED' state when the child moves to a 'SUCCEEDED' or 'FAILED' state. + */ +export default class Succeed extends Decorator { + /** + * @param attributes The node attributes. + * @param child The child node. + */ + constructor(attributes: Attribute[], child: Node); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/leaf/Action.d.ts b/dist/nodes/leaf/Action.d.ts new file mode 100644 index 0000000..3b889be --- /dev/null +++ b/dist/nodes/leaf/Action.d.ts @@ -0,0 +1,46 @@ +import Leaf from "./Leaf"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * An Action leaf node. + * This represents an immediate or ongoing state of behaviour. + */ +export default class Action extends Leaf { + private actionName; + private actionArguments; + /** + * @param attributes The node attributes. + * @param actionName The action name. + * @param actionArguments The array of action argument definitions. + */ + constructor(attributes: Attribute[], actionName: string, actionArguments: AnyArgument[]); + /** + * Whether there is a pending update promise. + */ + private isUsingUpdatePromise; + /** + * The finished state result of an update promise. + */ + private updatePromiseStateResult; + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; + /** + * Reset the state of the node. + */ + reset: () => void; + /** + * Validate the result of an update function call. + * @param result The result of an update function call. + */ + private validateUpdateResult; +} diff --git a/dist/nodes/leaf/Condition.d.ts b/dist/nodes/leaf/Condition.d.ts new file mode 100644 index 0000000..e2f08bd --- /dev/null +++ b/dist/nodes/leaf/Condition.d.ts @@ -0,0 +1,29 @@ +import Leaf from "./Leaf"; +import { Agent } from "../../Agent"; +import Attribute from "../../attributes/Attribute"; +import { AnyArgument } from "../../RootAstNodesBuilder"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A Condition leaf node. + * This will succeed or fail immediately based on an agent predicate, without moving to the 'RUNNING' state. + */ +export default class Condition extends Leaf { + private conditionName; + private conditionArguments; + /** + * @param attributes The node attributes. + * @param conditionName The name of the condition function. + * @param conditionArguments The array of condition argument definitions. + */ + constructor(attributes: Attribute[], conditionName: string, conditionArguments: AnyArgument[]); + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/dist/nodes/leaf/Leaf.d.ts b/dist/nodes/leaf/Leaf.d.ts new file mode 100644 index 0000000..c7e7109 --- /dev/null +++ b/dist/nodes/leaf/Leaf.d.ts @@ -0,0 +1,10 @@ +import Node from "../Node"; +/** + * A leaf node. + */ +export default abstract class Leaf extends Node { + /** + * Gets whether this node is a leaf node. + */ + isLeafNode: () => boolean; +} diff --git a/dist/nodes/leaf/Wait.d.ts b/dist/nodes/leaf/Wait.d.ts new file mode 100644 index 0000000..43d08f0 --- /dev/null +++ b/dist/nodes/leaf/Wait.d.ts @@ -0,0 +1,42 @@ +import Leaf from "./Leaf"; +import Attribute from "../../attributes/Attribute"; +import { Agent } from "../../Agent"; +import { BehaviourTreeOptions } from "../../BehaviourTreeOptions"; +/** + * A WAIT node. + * The state of this node will change to SUCCEEDED after a duration of time + */ +export default class Wait extends Leaf { + private duration; + private durationMin; + private durationMax; + /** + * @param attributes The node attributes. + * @param duration The duration that this node will wait to succeed in milliseconds. + * @param durationMin The minimum possible duration in milliseconds that this node will wait to succeed. + * @param durationMax The maximum possible duration in milliseconds that this node will wait to succeed. + */ + constructor(attributes: Attribute[], duration: number | null, durationMin: number | null, durationMax: number | null); + /** + * The time in milliseconds at which this node was first updated. + */ + private initialUpdateTime; + /** + * The total duration in milliseconds that this node will be waiting for. + */ + private totalDuration; + /** + * The duration in milliseconds that this node has been waiting for. + */ + private waitedDuration; + /** + * Called when the node is being updated. + * @param agent The agent. + * @param options The behaviour tree options object. + */ + protected onUpdate(agent: Agent, options: BehaviourTreeOptions): void; + /** + * Gets the name of the node. + */ + getName: () => string; +} diff --git a/src/BehaviourTree.ts b/src/BehaviourTree.ts index 7314728..fefa9b3 100644 --- a/src/BehaviourTree.ts +++ b/src/BehaviourTree.ts @@ -203,7 +203,7 @@ export class BehaviourTree { private static _createRootNode(definition: string): Root { // TODO Remove! try { - parseToJSON(definition); + // parseToJSON(definition); } catch (exception) { console.log(exception); } diff --git a/src/dsl/DSLDefinitionParser.ts b/src/dsl/DSLDefinitionParser.ts index bcec8ac..cce1e0a 100644 --- a/src/dsl/DSLDefinitionParser.ts +++ b/src/dsl/DSLDefinitionParser.ts @@ -11,7 +11,7 @@ export type AgentFunctionArguments = (string | number | boolean | null | undefin export type GuardAttributeDefinition = { call: string; args?: AgentFunctionArguments; -} +}; /** * A callback attribute for a node. @@ -19,7 +19,7 @@ export type GuardAttributeDefinition = { export type CallbackAttributeDefinition = { call: string; args?: AgentFunctionArguments; -} +}; /** * A type defining a general node definition. @@ -31,21 +31,21 @@ export type NodeDefinition = { entry?: CallbackAttributeDefinition; exit?: CallbackAttributeDefinition; step?: CallbackAttributeDefinition; -} +}; /** * A composite node that can contain any number of child nodes. */ export type CompositeDefinition = NodeDefinition & { children: AnyChildNode[]; -} +}; /** * A decorator node, a composite with only a single child node. */ export type DecoratorDefinition = NodeDefinition & { child: AnyChildNode; -} +}; /** * A branch node. @@ -53,7 +53,7 @@ export type DecoratorDefinition = NodeDefinition & { export type BranchDefinition = NodeDefinition & { type: "branch"; ref: string; -} +}; /** * An action node. @@ -62,7 +62,7 @@ export type ActionDefinition = NodeDefinition & { type: "action"; call: string; args?: AgentFunctionArguments; -} +}; /** * A condition node. @@ -71,7 +71,7 @@ export type ConditionDefinition = NodeDefinition & { type: "condition"; call: string; args?: AgentFunctionArguments; -} +}; /** * A wait node. @@ -79,36 +79,36 @@ export type ConditionDefinition = NodeDefinition & { export type WaitDefinition = NodeDefinition & { type: "wait"; duration: number | [number, number]; -} +}; /** * A sequence node. */ export type SequenceDefinition = CompositeDefinition & { type: "sequence"; -} +}; /** * A selector node. */ export type SelectorDefinition = CompositeDefinition & { type: "selector"; -} +}; /** * A lotto node. */ export type LottoDefinition = CompositeDefinition & { type: "lotto"; - weights?: number[] -} + weights?: number[]; +}; /** * A parallel node. */ export type ParallelDefinition = CompositeDefinition & { type: "parallel"; -} +}; /** * A root node. @@ -116,7 +116,7 @@ export type ParallelDefinition = CompositeDefinition & { export type RootDefinition = DecoratorDefinition & { type: "root"; id?: string; -} +}; /** * A repeat node. @@ -124,7 +124,7 @@ export type RootDefinition = DecoratorDefinition & { export type RepeatDefinition = DecoratorDefinition & { type: "repeat"; iterations?: number | [number, number]; -} +}; /** * A retry node. @@ -132,34 +132,47 @@ export type RepeatDefinition = DecoratorDefinition & { export type RetryDefinition = DecoratorDefinition & { type: "retry"; attempts?: number | [number, number]; -} +}; /** * A flip node. */ export type FlipDefinition = DecoratorDefinition & { type: "flip"; -} +}; /** * A succeed node. */ export type SucceedDefinition = DecoratorDefinition & { type: "succeed"; -} +}; /** * A fail node. */ export type FailDefinition = DecoratorDefinition & { type: "fail"; -} +}; /** * A type defining any node type. */ -export type AnyNode = BranchDefinition | ActionDefinition | ConditionDefinition | WaitDefinition | SequenceDefinition | - SelectorDefinition | LottoDefinition | ParallelDefinition | RootDefinition | RepeatDefinition | RetryDefinition | FlipDefinition | SucceedDefinition | FailDefinition; +export type AnyNode = + | BranchDefinition + | ActionDefinition + | ConditionDefinition + | WaitDefinition + | SequenceDefinition + | SelectorDefinition + | LottoDefinition + | ParallelDefinition + | RootDefinition + | RepeatDefinition + | RetryDefinition + | FlipDefinition + | SucceedDefinition + | FailDefinition; /** * A type defining any node type that can be a child of composite parent node. @@ -204,7 +217,11 @@ export function parseToJSON(definition: string): RootDefinition[] { * @param placeholders The substituted string literal placeholders. * @returns The root node JSON definitions. */ -export function convertTokensToJSONDefinition(tokens: string[], placeholders: StringLiteralPlaceholders, processedDefinition: string): RootDefinition[] { +export function convertTokensToJSONDefinition( + tokens: string[], + placeholders: StringLiteralPlaceholders, + processedDefinition: string +): RootDefinition[] { // There must be at least 3 tokens for the tree definition to be valid. 'ROOT', '{' and '}'. if (tokens.length < 3) { throw new Error("invalid token count"); @@ -226,10 +243,10 @@ export function convertTokensToJSONDefinition(tokens: string[], placeholders: St // Add the root node definition to our array of all parsed root node definitions. rootNodes.push(rootNode); - // Add the root node definition to the root of a new tree stack. + // Add the root node definition to the root of a new tree stack. treeStacks.push([rootNode]); - } - + }; + // A helper function used to push non-root node definitions onto the stack. const pushNode = (node: AnyChildNode) => { // Get the current tree stack that we are populating. @@ -258,7 +275,7 @@ export function convertTokensToJSONDefinition(tokens: string[], placeholders: St bottomNode.child = node; } - // If the node we are adding is also a composite or decorator node, then we should push it + // If the node we are adding is also a composite or decorator node, then we should push it // onto the current tree stack, as subsequent nodes will be added as its child/children. if (!isLeafNode(node)) { currentTreeStack.push(node); @@ -275,7 +292,7 @@ export function convertTokensToJSONDefinition(tokens: string[], placeholders: St currentTreeStack.pop(); } - // We don't want any empty tree stacks in our stack of tree stacks. + // We don't want any empty tree stacks in our stack of tree stacks. if (!currentTreeStack.length) { treeStacks.pop(); } @@ -397,4 +414,4 @@ function parseTokensFromDefinition(definition: string): string[] { // Split the definition into raw token form and return it. return definition.replace(/\s+/g, " ").trim().split(" "); -} \ No newline at end of file +} diff --git a/src/dsl/DSLNodeArgumentParser.ts b/src/dsl/DSLNodeArgumentParser.ts index 7f93bd5..60874db 100644 --- a/src/dsl/DSLNodeArgumentParser.ts +++ b/src/dsl/DSLNodeArgumentParser.ts @@ -54,10 +54,10 @@ export function getArguments( const argumentList: AnyArgument[] = []; // If the next token is not a '[' or '(' then we have no arguments to parse. - if (!["[","("].includes(tokens[0])) { + if (!["[", "("].includes(tokens[0])) { return argumentList; - }; - + } + // Any lists of arguments will always be wrapped in '[]' for node arguments or '()' for attribute arguments. // We are looking for a '[' or '(' opener that wraps the argument tokens and the relevant closer. const closer = popAndCheck(tokens, ["[", "("]) === "[" ? "]" : ")"; @@ -149,4 +149,4 @@ function getArgumentDefinition(token: string, stringArgumentPlaceholders: Placeh value: token, type: "identifier" } as IdentifierArgument; -} \ No newline at end of file +} diff --git a/src/dsl/DSLUtilities.ts b/src/dsl/DSLUtilities.ts index d165555..f515a40 100644 --- a/src/dsl/DSLUtilities.ts +++ b/src/dsl/DSLUtilities.ts @@ -16,7 +16,7 @@ export function popAndCheck(tokens: string[], expected?: string | string[]): str // Do we have an expected token/tokens array? if (expected != undefined) { // Get an array of expected values, if the popped token matches any then we are all good. - const expectedValues = (typeof expected === "string") ? [expected] : expected; + const expectedValues = typeof expected === "string" ? [expected] : expected; // Check whether the popped token matches at least one of our expected items. var tokenMatchesExpectation = expectedValues.some((item) => popped.toUpperCase() === item.toUpperCase()); @@ -30,4 +30,4 @@ export function popAndCheck(tokens: string[], expected?: string | string[]): str // Return the popped token. return popped; -} \ No newline at end of file +}