Skip to content

Commit

Permalink
Property interface. Do not store importance when not needed.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neloreck committed Nov 2, 2023
1 parent 1465717 commit 7f226ac
Show file tree
Hide file tree
Showing 20 changed files with 76 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/AbstractAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, expect, it, jest } from "@jest/globals";
import { TestUnit } from "#/fixtures/mocks";

import { AbstractAction } from "@/AbstractAction";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { IUnit } from "@/unit/IUnit";

describe("AbstractAction class", () => {
Expand Down
6 changes: 3 additions & 3 deletions src/AbstractAction.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Properties } from "@/alias";
import { Property } from "@/Property";
import { IProperty } from "@/property";
import { PropertyId } from "@/types";
import { IUnit } from "@/unit/IUnit";

Expand All @@ -22,7 +22,7 @@ export abstract class AbstractAction<T = any> {
/**
* @param property - new precondition property to add
*/
public addPrecondition(property: Property): typeof this {
public addPrecondition(property: IProperty): typeof this {
if (this.preconditions.findIndex((it) => it.id === property.id) === -1) {
this.preconditions.push(property);
}
Expand Down Expand Up @@ -51,7 +51,7 @@ export abstract class AbstractAction<T = any> {
/**
* @param property - world precondition to remove from the action
*/
public addEffect(property: Property): typeof this {
public addEffect(property: IProperty): typeof this {
if (this.effects.findIndex((it) => it.id === property.id) === -1) {
this.effects.push(property);
}
Expand Down
2 changes: 1 addition & 1 deletion src/agent/AbstractAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AbstractAgent } from "@/agent/AbstractAgent";
import { Plan } from "@/alias";
import { GenericPlanner } from "@/planner/GenericPlanner";
import { IPlanner } from "@/planner/IPlanner";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { FiniteStateMachine } from "@/state_machine/FiniteStateMachine";
import { IdleState } from "@/state_machine/IdleState";
import { MoveToState } from "@/state_machine/MoveToState";
Expand Down
2 changes: 1 addition & 1 deletion src/agent/AbstractAgent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IAgent } from "@/agent/IAgent";
import { Plan } from "@/alias";
import { IPlanner } from "@/planner/IPlanner";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { FiniteStateMachine } from "@/state_machine/FiniteStateMachine";
import { IdleState } from "@/state_machine/IdleState";
import { RunActionState } from "@/state_machine/RunActionState";
Expand Down
4 changes: 2 additions & 2 deletions src/alias.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { AbstractAction } from "@/AbstractAction";
import { Property } from "@/Property";
import { IProperty } from "@/property";
import { Queue } from "@/types";

/**
* List of properties.
*/
export type Properties = Array<Property>;
export type Properties = Array<IProperty>;

/**
* Generic actions plan queue.
Expand Down
2 changes: 1 addition & 1 deletion src/event/IImportantUnitChangeEventListener.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Property } from "@/Property";
import { Property } from "@/property/Property";

export interface IImportantUnitChangeEventListener {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export * from "@/utils/path";
export * from "@/types";

export * from "@/AbstractAction";
export * from "@/Property";
export * from "@/property/Property";
2 changes: 1 addition & 1 deletion src/planner/AbstractPlanner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Plan } from "@/alias";
import { DirectedWeightedGraph, IWeightedEdge, IWeightedGraph } from "@/graph";
import { AbstractPlanner } from "@/planner/AbstractPlanner";
import { GraphNode } from "@/planner/GraphNode";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { AbstractUnit } from "@/unit/AbstractUnit";

describe("GenericPlanner class", () => {
Expand Down
9 changes: 7 additions & 2 deletions src/planner/AbstractPlanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { GraphNode } from "@/planner/GraphNode";
import { Optional, Queue } from "@/types";
import { IUnit } from "@/unit/IUnit";
import { createWeightedPath } from "@/utils/path";
import { addNodeToGraphPathEnd, areAllPreconditionsMet, extractActionsFromGraphPath } from "@/utils/planner";
import {
addNodeToGraphPathEnd,
areAllPreconditionsMet,
extractActionsFromGraphPath,
goalComparator,
} from "@/utils/planner";

/**
* Class for generating a queue of goap actions.
Expand Down Expand Up @@ -60,7 +65,7 @@ export abstract class AbstractPlanner {
this.endNodes = [];

try {
const goal: Properties = this.unit.getGoalState().sort((first, second) => second.importance - first.importance);
const goal: Properties = this.unit.getGoalState().sort(goalComparator);

// The Integer.MaxValue indicates that the goal was passed by the changeGoalImmediatly function.
// An empty Queue is returned instead of null because null would result in the IdleState to call this
Expand Down
8 changes: 1 addition & 7 deletions src/planner/GraphNode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Properties } from "@/alias";
import { DirectedWeightedGraph, IWeightedEdge } from "@/graph";
import { IWeightedPath } from "@/graph/IWeightedPath";
import { GraphNode } from "@/planner/GraphNode";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { createWeightedPath } from "@/utils/path";
import { addNodeToGraphPathEnd } from "@/utils/planner";

Expand Down Expand Up @@ -117,7 +117,6 @@ describe("GraphNode class", () => {
expect(weapon.states.get(pickWeaponPath)).toEqual([
{
id: "has_weapon",
importance: 0,
value: true,
},
]);
Expand All @@ -126,30 +125,25 @@ describe("GraphNode class", () => {
expect(ammo.states.get(pickAmmoPath)).toEqual([
{
id: "has_weapon",
importance: 0,
value: true,
},
{
id: "has_ammo",
importance: 0,
value: true,
},
]);

expect(shoot.states.get(startShootingPath)).toEqual([
{
id: "has_weapon",
importance: 0,
value: true,
},
{
id: "has_ammo",
importance: 0,
value: true,
},
{
id: "is_shooting",
importance: 0,
value: true,
},
]);
Expand Down
9 changes: 9 additions & 0 deletions src/property/IProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { PropertyId } from "@/types";

export interface IProperty<Id extends PropertyId = PropertyId, Value = any> {
readonly id: Id;
readonly value: Value;

// Used for goal properties to define which one is more important.
importance?: number;
}
2 changes: 1 addition & 1 deletion src/Property.test.ts → src/property/Property.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "@jest/globals";

import { Property } from "@/Property";
import { Property } from "@/property/Property";

describe("Property class", () => {
it("should correctly initialize", () => {
Expand Down
18 changes: 7 additions & 11 deletions src/Property.ts → src/property/Property.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import { IProperty } from "@/property/IProperty";
import { PropertyId } from "@/types";

/**
* Properties which the GOAP planner use to build a graph.
*/
export class Property<IdType = PropertyId, ValueType = any> {
public readonly id: IdType;
public readonly value: ValueType;

public importance: number = 0;

export class Property<Id extends PropertyId = PropertyId, Value = any> implements IProperty<Id, Value> {
/**
* @param id - the id the property has
* @param value - the value of the property
* @param importance - the importance of the state being reached. Only necessary if the state is used to define
* a worldState. Has no effect in actions being taken. Do NOT set this to Infinity since this causes the goal to be
* removed from the set by the planner
*/
public constructor(id: IdType, value: ValueType, importance?: number) {
this.id = id;
this.value = value;
this.importance = !importance || importance < 0 ? 0 : importance;
}
public constructor(
public readonly id: Id,
public readonly value: Value,
public importance?: number
) {}
}
2 changes: 2 additions & 0 deletions src/property/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "@/property/IProperty";
export * from "@/property/Property";
2 changes: 1 addition & 1 deletion src/unit/AbstractUnit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GenericAction } from "#/fixtures/mocks";
import { AbstractAction } from "@/AbstractAction";
import { Plan, Properties } from "@/alias";
import { IImportantUnitChangeEventListener } from "@/event/IImportantUnitChangeEventListener";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { Queue } from "@/types";
import { AbstractUnit } from "@/unit/AbstractUnit";

Expand Down
2 changes: 1 addition & 1 deletion src/unit/AbstractUnit.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AbstractAction } from "@/AbstractAction";
import { Properties } from "@/alias";
import { IImportantUnitChangeEventListener } from "@/event/IImportantUnitChangeEventListener";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { AnyObject, PropertyId } from "@/types";
import { IUnit } from "@/unit/IUnit";
import { removeFromArray } from "@/utils/array";
Expand Down
2 changes: 1 addition & 1 deletion src/utils/path.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GenericAction } from "#/fixtures/mocks";

import { DirectedWeightedGraph, IEdge, IPath } from "@/graph";
import { GraphNode } from "@/planner";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { Optional } from "@/types";
import { createPath, isOneOfPaths, mergePathEffectsTogether } from "@/utils/path";

Expand Down
24 changes: 22 additions & 2 deletions src/utils/planner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,32 @@ import { AbstractAction } from "@/AbstractAction";
import { DirectedWeightedGraph, IEdge, IPath, IWeightedEdge } from "@/graph";
import { IWeightedPath } from "@/graph/IWeightedPath";
import { GraphNode } from "@/planner";
import { Property } from "@/Property";
import { Property } from "@/property/Property";
import { Optional } from "@/types";
import { createPath, createWeightedPath } from "@/utils/path";
import { addNodeToGraphPathEnd, areAllPreconditionsMet, extractActionsFromGraphPath } from "@/utils/planner";
import {
addNodeToGraphPathEnd,
areAllPreconditionsMet,
extractActionsFromGraphPath,
goalComparator,
} from "@/utils/planner";

describe("planner utils module", () => {
it("goalComparator should correctly sort properties by order", () => {
expect(
[
new Property("a", true, 100),
new Property("b", false),
new Property("z", false, 0),
new Property("c", 1, Infinity),
{ id: "d", value: "e", importance: 1 },
{ id: "x", value: "e", importance: -Infinity },
]
.sort(goalComparator)
.map((it) => it.importance)
).toEqual([Infinity, 100, 1, 0, undefined, -Infinity]);
});

it("extractActionsFromGraphPath should correctly extract actions from graph path", () => {
const start: GraphNode = new GraphNode([], [], new GenericAction(1));
const second: GraphNode = new GraphNode([], [], new GenericAction(2));
Expand Down
12 changes: 12 additions & 0 deletions src/utils/planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,21 @@ import { AbstractAction } from "@/AbstractAction";
import { Properties } from "@/alias";
import { IWeightedPath } from "@/graph/IWeightedPath";
import { GraphNode } from "@/planner/GraphNode";
import { IProperty } from "@/property";
import { Optional } from "@/types";
import { createWeightedPath } from "@/utils/path";

/**
* Sorting goal properties by importance.
*
* @param first - first property to compare
* @param second - second property to compare
* @returns comparator result for properties based on importance
*/
export function goalComparator(first: IProperty, second: IProperty): number {
return (second.importance ?? -1) - (first.importance ?? -1);
}

/**
* Function for extracting all actions from a provided path.
*
Expand Down
2 changes: 1 addition & 1 deletion test/scenarios/shooting_with_ammo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GenericAction, TestUnit } from "#/fixtures/mocks";

import { AbstractAction } from "@/AbstractAction";
import { GenericPlanner } from "@/planner";
import { Property } from "@/Property";
import { Property } from "@/property/Property";

describe("shooting scenario", () => {
const pickAmmo: AbstractAction = new GenericAction("pick_ammo");
Expand Down

0 comments on commit 7f226ac

Please sign in to comment.