Skip to content

Commit

Permalink
more work on validator
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikolas Howard committed Nov 8, 2023
1 parent aeafce0 commit 159a5f3
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 9 deletions.
20 changes: 19 additions & 1 deletion src/BehaviourTreeDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export interface BranchNodeDefinition extends NodeDefinition {
* The node type.
*/
type: "branch";
/**
* The reference matching a root node identifier.
*/
ref: string;
}

Expand All @@ -81,7 +84,13 @@ export interface ActionNodeDefinition extends NodeDefinition {
* The node type.
*/
type: "action";
/**
* The name of the agent function to invoke.
*/
call: string;
/**
* An array of arguments to pass when invoking the agent function.
*/
args?: any[];
}

Expand All @@ -93,7 +102,13 @@ export interface ConditionNodeDefinition extends NodeDefinition {
* The node type.
*/
type: "condition";
/**
* The name of the agent function to invoke.
*/
call: string;
/**
* An array of arguments to pass when invoking the agent function.
*/
args?: any[];
}

Expand All @@ -105,7 +120,7 @@ export interface WaitNodeDefinition extends NodeDefinition {
* The node type.
*/
type: "wait";
duration: number | [number, number];
duration?: number | [number, number];
}

/**
Expand Down Expand Up @@ -160,6 +175,9 @@ export interface RootNodeDefinition extends DecoratorNodeDefinition {
* The node type.
*/
type: "root";
/**
* The unique root node identifier.
*/
id?: string;
}

Expand Down
9 changes: 9 additions & 0 deletions src/BehaviourTreeDefinitionUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,12 @@ export function flattenDefinition(nodeDefinition: AnyNode): AnyNode[] {

return nodes;
}

/**
* Determines whether the passed value is an integer.
* @param value The value to check.
* @returns Whether the passed value is an integer.
*/
export function isInteger(value: unknown): boolean {
return typeof value === "number" && Math.floor(value) === value;
}
131 changes: 123 additions & 8 deletions src/BehaviourTreeDefinitionValidator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RootNodeDefinition } from "./BehaviourTreeDefinition";
import { flattenDefinition, isBranchNode } from "./BehaviourTreeDefinitionUtilities";
import { flattenDefinition, isBranchNode, isInteger } from "./BehaviourTreeDefinitionUtilities";
import { parseMDSLToJSON } from "./mdsl/MDSLDefinitionParser";

/**
Expand Down Expand Up @@ -160,22 +160,38 @@ function validateNode(definition: any, depth: number): void {
throw new Error(`node definition is not an object or 'type' property is not a non-empty string at depth '${depth}'`);
}

// How we validate this node will depend on its type.
// How we validate this node definition will depend on its type.
switch (definition.type) {
case "root":
validateRootNode(definition, depth);
case "action":
validateActionNode(definition, depth);
break;

case "condition":
validateConditionNode(definition, depth);
break;

case "wait":
validateWaitNode(definition, depth);
break;

case "branch":
validateBranchNode(definition, depth);
break;

case "action":
validateActionNode(definition, depth);
case "root":
validateRootNode(definition, depth);
break;

case "condition":
validateConditionNode(definition, depth);
case "success":
validateSuccessNode(definition, depth);
break;

case "fail":
validateFailNode(definition, depth);
break;

case "flip":
validateFlipNode(definition, depth);
break;

case "sequence":
Expand Down Expand Up @@ -263,6 +279,75 @@ function validateRootNode(definition: any, depth: number): void {
validateNode(definition.child, depth + 1);
}

/**
* Validate an object that we expect to be a success node definition.
* @param definition An object that we expect to be a success node definition.
* @param depth The depth of the node in the definition tree.
*/
function validateSuccessNode(definition: any, depth: number): void {
// Check that the node type is correct.
if (definition.type !== "success") {
throw new Error(`expected node type of 'success' for success node at depth '${depth}'`);
}

// A success node is a decorator node, so must have a child node defined.
if (typeof definition.child === "undefined") {
throw new Error(`expected property 'child' to be defined for success node at depth '${depth}'`);
}

// Validate the node attributes.
validateNodeAttributes(definition, depth);

// Validate the child node of this decorator node.
validateNode(definition.child, depth + 1);
}

/**
* Validate an object that we expect to be a fail node definition.
* @param definition An object that we expect to be a fail node definition.
* @param depth The depth of the node in the definition tree.
*/
function validateFailNode(definition: any, depth: number): void {
// Check that the node type is correct.
if (definition.type !== "fail") {
throw new Error(`expected node type of 'fail' for fail node at depth '${depth}'`);
}

// A fail node is a decorator node, so must have a child node defined.
if (typeof definition.child === "undefined") {
throw new Error(`expected property 'child' to be defined for fail node at depth '${depth}'`);
}

// Validate the node attributes.
validateNodeAttributes(definition, depth);

// Validate the child node of this decorator node.
validateNode(definition.child, depth + 1);
}

/**
* Validate an object that we expect to be a flip node definition.
* @param definition An object that we expect to be a flip node definition.
* @param depth The depth of the node in the definition tree.
*/
function validateFlipNode(definition: any, depth: number): void {
// Check that the node type is correct.
if (definition.type !== "flip") {
throw new Error(`expected node type of 'flip' for flip node at depth '${depth}'`);
}

// A flip node is a decorator node, so must have a child node defined.
if (typeof definition.child === "undefined") {
throw new Error(`expected property 'child' to be defined for flip node at depth '${depth}'`);
}

// Validate the node attributes.
validateNodeAttributes(definition, depth);

// Validate the child node of this decorator node.
validateNode(definition.child, depth + 1);
}

/**
* Validate an object that we expect to be a branch node definition.
* @param definition An object that we expect to be a branch node definition.
Expand Down Expand Up @@ -344,6 +429,36 @@ function validateConditionNode(definition: any, depth: number): void {
validateNodeAttributes(definition, depth);
}

/**
* Validate an object that we expect to be a wait node definition.
* @param definition An object that we expect to be a wait node definition.
* @param depth The depth of the node in the definition tree.
*/
function validateWaitNode(definition: any, depth: number): void {
// Check that the node type is correct.
if (definition.type !== "wait") {
throw new Error(`expected node type of 'wait' for wait node at depth '${depth}'`);
}

// Check whether a 'duration' property has been defined, it may not have been if this node is to wait indefinitely.
if (typeof definition.duration !== "undefined") {
if (Array.isArray(definition.duration)) {
// Check whether any elements of the array are not integer values.
const containsNonInteger = !!definition.duration.find((value: unknown) => !isInteger(value));

// If the 'duration' property is an array then it MUST contain two integer values.
if (definition.duration.length !== 2 || containsNonInteger) {
throw new Error(`expected array containing two integer values for 'duration' property if defined for wait node at depth '${depth}'`);
}
} else if (!isInteger(definition.duration)) {
throw new Error(`expected integer value or array containing two integer values for 'duration' property if defined for wait node at depth '${depth}'`);
}
}

// Validate the node attributes.
validateNodeAttributes(definition, depth);
}

/**
* Validate an object that we expect to be a sequence node definition.
* @param definition An object that we expect to be a sequence node definition.
Expand Down

0 comments on commit 159a5f3

Please sign in to comment.