Skip to content

Commit

Permalink
Merge pull request #29 from giollord/main
Browse files Browse the repository at this point in the history
Fixed delta cost calculation in ActionAStar.Heuristic
caesuric authored May 22, 2024
2 parents 0413c54 + 2069f54 commit f10b389
Showing 4 changed files with 38 additions and 10 deletions.
4 changes: 4 additions & 0 deletions Examples/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -19,6 +19,10 @@
"comparative happiness": {
"commandName": "Project",
"commandLineArgs": "comparativeHappiness"
},
"car": {
"commandName": "Project",
"commandLineArgs": "car"
}
}
}
13 changes: 11 additions & 2 deletions MountainGoap/Action.cs
Original file line number Diff line number Diff line change
@@ -91,7 +91,8 @@ public class Action {
/// <param name="parameterPostconditions">Parameter postconditions copied to state after the action is successfully executed.</param>
/// <param name="stateMutator">Callback for modifying state after action execution or evaluation.</param>
/// <param name="stateChecker">Callback for checking state before action execution or evaluation.</param>
public Action(string? name = null, Dictionary<string, PermutationSelectorCallback>? permutationSelectors = null, ExecutorCallback? executor = null, float cost = 1f, CostCallback? costCallback = null, Dictionary<string, object?>? preconditions = null, Dictionary<string, ComparisonValuePair>? comparativePreconditions = null, Dictionary<string, object?>? postconditions = null, Dictionary<string, object>? arithmeticPostconditions = null, Dictionary<string, string>? parameterPostconditions = null, StateMutatorCallback? stateMutator = null, StateCheckerCallback? stateChecker = null) {
/// <param name="stateCostDeltaMultiplier">Callback for multiplier for delta value to provide delta cost.</param>
public Action(string? name = null, Dictionary<string, PermutationSelectorCallback>? permutationSelectors = null, ExecutorCallback? executor = null, float cost = 1f, CostCallback? costCallback = null, Dictionary<string, object?>? preconditions = null, Dictionary<string, ComparisonValuePair>? comparativePreconditions = null, Dictionary<string, object?>? postconditions = null, Dictionary<string, object>? arithmeticPostconditions = null, Dictionary<string, string>? parameterPostconditions = null, StateMutatorCallback? stateMutator = null, StateCheckerCallback? stateChecker = null, StateCostDeltaMultiplierCallback? stateCostDeltaMultiplier = null) {
if (permutationSelectors == null) this.permutationSelectors = new();
else this.permutationSelectors = permutationSelectors;
if (executor == null) this.executor = DefaultExecutorCallback;
@@ -106,8 +107,16 @@ public Action(string? name = null, Dictionary<string, PermutationSelectorCallbac
if (parameterPostconditions != null) this.parameterPostconditions = parameterPostconditions;
if (stateMutator != null) this.stateMutator = stateMutator;
if (stateChecker != null) this.stateChecker = stateChecker;
StateCostDeltaMultiplier = stateCostDeltaMultiplier ?? DefaultStateCostDeltaMultiplier;
}

/// <summary>
/// Gets or sets multiplier for delta value to provide delta cost.
/// </summary>
public StateCostDeltaMultiplierCallback? StateCostDeltaMultiplier { get; set; }

public static float DefaultStateCostDeltaMultiplier(Action? action, string stateKey) => 1f;

Check warning on line 118 in MountainGoap/Action.cs

GitHub Actions / build

Missing XML comment for publicly visible type or member 'Action.DefaultStateCostDeltaMultiplier(Action?, string)'

/// <summary>
/// Event that triggers when an action begins executing.
/// </summary>
@@ -128,7 +137,7 @@ public Action(string? name = null, Dictionary<string, PermutationSelectorCallbac
/// </summary>
/// <returns>A copy of the action.</returns>
public Action Copy() {
var newAction = new Action(Name, permutationSelectors, executor, cost, costCallback, preconditions.Copy(), comparativePreconditions.Copy(), postconditions.Copy(), arithmeticPostconditions.CopyNonNullable(), parameterPostconditions.Copy(), stateMutator, stateChecker) {
var newAction = new Action(Name, permutationSelectors, executor, cost, costCallback, preconditions.Copy(), comparativePreconditions.Copy(), postconditions.Copy(), arithmeticPostconditions.CopyNonNullable(), parameterPostconditions.Copy(), stateMutator, stateChecker, StateCostDeltaMultiplier) {
parameters = parameters.Copy()
};
return newAction;
13 changes: 13 additions & 0 deletions MountainGoap/CallbackDelegates/StateCostDeltaMultiplierCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// <copyright file="StateMutatorCallback.cs" company="Chris Muller">

Check warning on line 1 in MountainGoap/CallbackDelegates/StateCostDeltaMultiplierCallback.cs

GitHub Actions / build

File header file name documentation should match file name. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1638.md)
// Copyright (c) Chris Muller. All rights reserved.
// </copyright>

namespace MountainGoap {
/// <summary>
/// Delegate type for a callback that provides multiplier for delta value of the respective key to obtain delta cost to use with ExtremeGoal and ComparativeGoal.
/// </summary>
/// <param name="action">Action being executed or evaluated.</param>
/// <param name="stateKey">Key to provide multiplier for</param>
/// <returns>Multiplier for the delta value to get delta cost</returns>
public delegate float StateCostDeltaMultiplierCallback(Action? action, string stateKey);
}
18 changes: 10 additions & 8 deletions MountainGoap/Internals/ActionAStar.cs
Original file line number Diff line number Diff line change
@@ -76,29 +76,31 @@ private static float Heuristic(ActionNode actionNode, BaseGoal goal, ActionNode
else if (goal is ExtremeGoal extremeGoal) {
foreach (var kvp in extremeGoal.DesiredState) {
var valueDiff = 0f;
var valueDiffMultiplier = (actionNode?.Action?.StateCostDeltaMultiplier ?? Action.DefaultStateCostDeltaMultiplier).Invoke(actionNode?.Action, kvp.Key);
if (actionNode.State.ContainsKey(kvp.Key) && actionNode.State[kvp.Key] == null) {

Check warning on line 80 in MountainGoap/Internals/ActionAStar.cs

GitHub Actions / build

Dereference of a possibly null reference.
cost += float.PositiveInfinity;
continue;
}
if (actionNode.State.ContainsKey(kvp.Key) && extremeGoal.DesiredState.ContainsKey(kvp.Key)) valueDiff = Convert.ToSingle(actionNode.State[kvp.Key]) - Convert.ToSingle(current.State[kvp.Key]);
if (!actionNode.State.ContainsKey(kvp.Key)) cost += float.PositiveInfinity;
else if (!current.State.ContainsKey(kvp.Key)) cost += float.PositiveInfinity;
else if (kvp.Value && actionNode.State[kvp.Key] is object a && current.State[kvp.Key] is object b && IsLowerThanOrEquals(a, b)) cost += 1f / valueDiff;
else if (!kvp.Value && actionNode.State[kvp.Key] is object a2 && current.State[kvp.Key] is object b2 && IsHigherThanOrEquals(a2, b2)) cost += 1f / (1 - valueDiff);
else if (!kvp.Value && actionNode.State[kvp.Key] is object a && current.State[kvp.Key] is object b && IsLowerThanOrEquals(a, b)) cost += valueDiff * valueDiffMultiplier;
else if (kvp.Value && actionNode.State[kvp.Key] is object a2 && current.State[kvp.Key] is object b2 && IsHigherThanOrEquals(a2, b2)) cost -= valueDiff * valueDiffMultiplier;
}
}
else if (goal is ComparativeGoal comparativeGoal) {
foreach (var kvp in comparativeGoal.DesiredState) {
var valueDiff2 = 0f;
if (actionNode.State.ContainsKey(kvp.Key) && comparativeGoal.DesiredState.ContainsKey(kvp.Key)) valueDiff2 = Math.Abs(Convert.ToSingle(kvp.Value.Value) - Convert.ToSingle(actionNode.State[kvp.Key]));
var valueDiffMultiplier = (actionNode?.Action?.StateCostDeltaMultiplier ?? Action.DefaultStateCostDeltaMultiplier).Invoke(actionNode?.Action, kvp.Key);
if (actionNode.State.ContainsKey(kvp.Key) && comparativeGoal.DesiredState.ContainsKey(kvp.Key)) valueDiff2 = Math.Abs(Convert.ToSingle(actionNode.State[kvp.Key]) - Convert.ToSingle(current.State[kvp.Key]));

Check warning on line 95 in MountainGoap/Internals/ActionAStar.cs

GitHub Actions / build

Dereference of a possibly null reference.
if (!actionNode.State.ContainsKey(kvp.Key)) cost += float.PositiveInfinity;
else if (!current.State.ContainsKey(kvp.Key)) cost += float.PositiveInfinity;
else if (kvp.Value.Operator == ComparisonOperator.Undefined) cost += float.PositiveInfinity;
else if (kvp.Value.Operator == ComparisonOperator.Equals && actionNode.State[kvp.Key] is object obj && !obj.Equals(comparativeGoal.DesiredState[kvp.Key].Value)) cost += valueDiff2;
else if (kvp.Value.Operator == ComparisonOperator.LessThan && actionNode.State[kvp.Key] is object a && comparativeGoal.DesiredState[kvp.Key].Value is object b && !IsLowerThan(a, b)) cost += valueDiff2;
else if (kvp.Value.Operator == ComparisonOperator.GreaterThan && actionNode.State[kvp.Key] is object a2 && comparativeGoal.DesiredState[kvp.Key].Value is object b2 && !IsHigherThan(a2, b2)) cost += valueDiff2;
else if (kvp.Value.Operator == ComparisonOperator.LessThanOrEquals && actionNode.State[kvp.Key] is object a3 && comparativeGoal.DesiredState[kvp.Key].Value is object b3 && !IsLowerThanOrEquals(a3, b3)) cost += valueDiff2;
else if (kvp.Value.Operator == ComparisonOperator.GreaterThanOrEquals && actionNode.State[kvp.Key] is object a4 && comparativeGoal.DesiredState[kvp.Key].Value is object b4 && !IsHigherThanOrEquals(a4, b4)) cost += valueDiff2;
else if (kvp.Value.Operator == ComparisonOperator.Equals && actionNode.State[kvp.Key] is object obj && !obj.Equals(comparativeGoal.DesiredState[kvp.Key].Value)) cost += valueDiff2 * valueDiffMultiplier;
else if (kvp.Value.Operator == ComparisonOperator.LessThan && actionNode.State[kvp.Key] is object a && comparativeGoal.DesiredState[kvp.Key].Value is object b && !IsLowerThan(a, b)) cost += valueDiff2 * valueDiffMultiplier;
else if (kvp.Value.Operator == ComparisonOperator.GreaterThan && actionNode.State[kvp.Key] is object a2 && comparativeGoal.DesiredState[kvp.Key].Value is object b2 && !IsHigherThan(a2, b2)) cost += valueDiff2 * valueDiffMultiplier;
else if (kvp.Value.Operator == ComparisonOperator.LessThanOrEquals && actionNode.State[kvp.Key] is object a3 && comparativeGoal.DesiredState[kvp.Key].Value is object b3 && !IsLowerThanOrEquals(a3, b3)) cost += valueDiff2 * valueDiffMultiplier;
else if (kvp.Value.Operator == ComparisonOperator.GreaterThanOrEquals && actionNode.State[kvp.Key] is object a4 && comparativeGoal.DesiredState[kvp.Key].Value is object b4 && !IsHigherThanOrEquals(a4, b4)) cost += valueDiff2 * valueDiffMultiplier;
}
}
return cost;

0 comments on commit f10b389

Please sign in to comment.