From 02547f5e7ced7a3e56d0fedf70bdfd2f1de29f96 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 19:05:06 +0900 Subject: [PATCH 001/108] Add ConditionOperator enum to define comparison operators --- Goap/ConditionOperator.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Goap/ConditionOperator.cs diff --git a/Goap/ConditionOperator.cs b/Goap/ConditionOperator.cs new file mode 100644 index 0000000..9ddd706 --- /dev/null +++ b/Goap/ConditionOperator.cs @@ -0,0 +1,11 @@ +namespace TsunagiModule.Goap +{ + public enum CondiionOperator + { + Larger, + LargerOrEqual, + Smaller, + SmallerOrEqual, + Equal, + } +} From faf0b15be2bedc6b8d4ae31ae8f95f874fdbc8c1 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:02:10 +0900 Subject: [PATCH 002/108] meta files --- Goap.meta | 8 ++++++++ Goap/ConditionOperator.cs.meta | 2 ++ Goap/GoapValue.cs.meta | 2 ++ README.md.meta | 7 +++++++ 4 files changed, 19 insertions(+) create mode 100644 Goap.meta create mode 100644 Goap/ConditionOperator.cs.meta create mode 100644 Goap/GoapValue.cs.meta create mode 100644 README.md.meta diff --git a/Goap.meta b/Goap.meta new file mode 100644 index 0000000..2fdcbd5 --- /dev/null +++ b/Goap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b78773f5467699b4bbc03c3a2be2451d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/ConditionOperator.cs.meta b/Goap/ConditionOperator.cs.meta new file mode 100644 index 0000000..2738543 --- /dev/null +++ b/Goap/ConditionOperator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d56d9e08e417cc94d95a659a1d6da284 \ No newline at end of file diff --git a/Goap/GoapValue.cs.meta b/Goap/GoapValue.cs.meta new file mode 100644 index 0000000..515a1be --- /dev/null +++ b/Goap/GoapValue.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 05a9d8f79c779834a9097c7825fc5ad0 \ No newline at end of file diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..06d9a57 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 86de5bca44e6ae34db8903a877f0033a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 45fe106a4b0a490cf5c22ed6f3c4d5e79341d130 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:05:23 +0900 Subject: [PATCH 003/108] goap value --- Goap/GoapValue.cs | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Goap/GoapValue.cs diff --git a/Goap/GoapValue.cs b/Goap/GoapValue.cs new file mode 100644 index 0000000..52684d0 --- /dev/null +++ b/Goap/GoapValue.cs @@ -0,0 +1,63 @@ +using System; + +namespace TsunagiModule.Goap +{ + /// + /// Value used in Goap system. + /// + /// + /// This is implemented for support integrated control of State Control, being independent of value type. + /// + public struct GoapValue + { + public enum ValueType + { + Int, + Float, + Bool + } + + public float value { get; private set; } + public ValueType type { get; private set; } + + public GoapValue(float value) + { + this.value = value; + type = ValueType.Float; + } + + public GoapValue(int value) + { + this.value = value; + type = ValueType.Int; + } + + public GoapValue(bool value) + { + this.value = value ? 1 : 0; + type = ValueType.Bool; + } + + public bool GetAsBool() + { + return value > 0.5f; + } + + public int GetAsInt() + { + return (int)Math.Round(value); + } + + public float GetAsFloat() + { + if (type == ValueType.Float) + return value; + else if (type == ValueType.Int) + return (int)value; + else if (type == ValueType.Bool) + return value > 0 ? 1 : 0; + else + throw new NotImplementedException("Unknown GoapValue type."); + } + } +} From 06a7cdf12f2a10be3683350e60c362ee404a70c8 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:13:14 +0900 Subject: [PATCH 004/108] add condition class --- Goap/Condition.cs | 54 ++++++++++++++++++++++++++++++++++ Goap/Condition.cs.meta | 2 ++ Goap/ConditionOperator.cs | 11 ------- Goap/ConditionOperator.cs.meta | 2 -- 4 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 Goap/Condition.cs create mode 100644 Goap/Condition.cs.meta delete mode 100644 Goap/ConditionOperator.cs delete mode 100644 Goap/ConditionOperator.cs.meta diff --git a/Goap/Condition.cs b/Goap/Condition.cs new file mode 100644 index 0000000..c834472 --- /dev/null +++ b/Goap/Condition.cs @@ -0,0 +1,54 @@ +using System; +using NUnit.Framework; + +namespace TsunagiModule.Goap +{ + public abstract class Condition + { + public enum ConditionOperator + { + Larger, + LargerOrEqual, + Smaller, + SmallerOrEqual, + Equal, + } + + /// + /// error acceptance value for float comparison. + /// + private const float EPSILON = 0.0001f; + + public string name { get; private set; } + public GoapValue value { get; private set; } + public ConditionOperator conditionOperator { get; private set; } + + public bool IsSatisfied(float valueComparing) + { + switch (conditionOperator) + { + case ConditionOperator.Larger: + return value.GetAsFloat() < valueComparing; + case ConditionOperator.LargerOrEqual: + return value.GetAsFloat() <= valueComparing; + case ConditionOperator.Smaller: + return value.GetAsFloat() > valueComparing; + case ConditionOperator.SmallerOrEqual: + return value.GetAsFloat() >= valueComparing; + case ConditionOperator.Equal: + return IsEqualApproximately(value.GetAsFloat(), valueComparing); + default: + Assert.IsTrue(false, "Unknown condition operator."); + return false; + } + } + + /// + /// Equality check for float values. + /// + private bool IsEqualApproximately(float a, float b) + { + return Math.Abs(a - b) < EPSILON; + } + } +} diff --git a/Goap/Condition.cs.meta b/Goap/Condition.cs.meta new file mode 100644 index 0000000..f2758a3 --- /dev/null +++ b/Goap/Condition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: eaef8e2c64b14954aa8fce230e802c50 \ No newline at end of file diff --git a/Goap/ConditionOperator.cs b/Goap/ConditionOperator.cs deleted file mode 100644 index 9ddd706..0000000 --- a/Goap/ConditionOperator.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace TsunagiModule.Goap -{ - public enum CondiionOperator - { - Larger, - LargerOrEqual, - Smaller, - SmallerOrEqual, - Equal, - } -} diff --git a/Goap/ConditionOperator.cs.meta b/Goap/ConditionOperator.cs.meta deleted file mode 100644 index 2738543..0000000 --- a/Goap/ConditionOperator.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d56d9e08e417cc94d95a659a1d6da284 \ No newline at end of file From 4b598c90055f07f2548e6c83c7b30d2fe7b1749b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:21:16 +0900 Subject: [PATCH 005/108] Refactor GoapValue to use ValueConverter for type conversions and add ValueConverter class for consistent value handling --- Goap/GoapValue.cs | 17 +++++------------ Goap/ValueConverter.cs | 30 ++++++++++++++++++++++++++++++ Goap/ValueConverter.cs.meta | 2 ++ 3 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 Goap/ValueConverter.cs create mode 100644 Goap/ValueConverter.cs.meta diff --git a/Goap/GoapValue.cs b/Goap/GoapValue.cs index 52684d0..f5c995d 100644 --- a/Goap/GoapValue.cs +++ b/Goap/GoapValue.cs @@ -28,36 +28,29 @@ public GoapValue(float value) public GoapValue(int value) { - this.value = value; + this.value = ValueConverter.ToFloat(value); type = ValueType.Int; } public GoapValue(bool value) { - this.value = value ? 1 : 0; + this.value = ValueConverter.ToFloat(value); type = ValueType.Bool; } public bool GetAsBool() { - return value > 0.5f; + return ValueConverter.ToBool(value); } public int GetAsInt() { - return (int)Math.Round(value); + return ValueConverter.ToInt(value); } public float GetAsFloat() { - if (type == ValueType.Float) - return value; - else if (type == ValueType.Int) - return (int)value; - else if (type == ValueType.Bool) - return value > 0 ? 1 : 0; - else - throw new NotImplementedException("Unknown GoapValue type."); + return value; } } } diff --git a/Goap/ValueConverter.cs b/Goap/ValueConverter.cs new file mode 100644 index 0000000..596905f --- /dev/null +++ b/Goap/ValueConverter.cs @@ -0,0 +1,30 @@ +using System; + +namespace TsunagiModule.Goap +{ + /// + /// Convert values for GOAP inner calculation + /// + public static class ValueConverter + { + public static float ToFloat(int value) + { + return value; + } + + public static float ToFloat(bool value) + { + return value ? 1 : 0; + } + + public static int ToInt(float value) + { + return (int)Math.Round(value); + } + + public static bool ToBool(float value) + { + return value >= 0.5f; + } + } +} diff --git a/Goap/ValueConverter.cs.meta b/Goap/ValueConverter.cs.meta new file mode 100644 index 0000000..8dbdcd9 --- /dev/null +++ b/Goap/ValueConverter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f4e445cad5b89ec46ab1eae9702144c2 \ No newline at end of file From 64ea77ae5d04e2cc56d14afb54ff8870ef12db9f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:21:27 +0900 Subject: [PATCH 006/108] Remove unnecessary using directive from GoapValue.cs --- Goap/GoapValue.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Goap/GoapValue.cs b/Goap/GoapValue.cs index f5c995d..306c13c 100644 --- a/Goap/GoapValue.cs +++ b/Goap/GoapValue.cs @@ -1,5 +1,3 @@ -using System; - namespace TsunagiModule.Goap { /// From 6193bd3400159ffc33b89cfc111d09ca3287c550 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:26:57 +0900 Subject: [PATCH 007/108] satisfaction check by type --- Goap/Condition.cs | 56 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/Goap/Condition.cs b/Goap/Condition.cs index c834472..45826b6 100644 --- a/Goap/Condition.cs +++ b/Goap/Condition.cs @@ -1,5 +1,4 @@ using System; -using NUnit.Framework; namespace TsunagiModule.Goap { @@ -24,6 +23,21 @@ public enum ConditionOperator public ConditionOperator conditionOperator { get; private set; } public bool IsSatisfied(float valueComparing) + { + switch (value.type) + { + case GoapValue.ValueType.Float: + return IsSatisfiedFloat(valueComparing); + case GoapValue.ValueType.Int: + return IsSatisfiedInt(ValueConverter.ToInt(valueComparing)); + case GoapValue.ValueType.Bool: + return IsSatisfiedBool(ValueConverter.ToBool(valueComparing)); + default: + throw new NotImplementedException("Unknown GoapValue.ValueType"); + } + } + + private bool IsSatisfiedFloat(float valueComparing) { switch (conditionOperator) { @@ -38,8 +52,44 @@ public bool IsSatisfied(float valueComparing) case ConditionOperator.Equal: return IsEqualApproximately(value.GetAsFloat(), valueComparing); default: - Assert.IsTrue(false, "Unknown condition operator."); - return false; + throw new NotImplementedException("Unknown condition operator."); + } + } + + private bool IsSatisfiedInt(int valueComparing) + { + switch (conditionOperator) + { + case ConditionOperator.Larger: + return value.GetAsInt() < valueComparing; + case ConditionOperator.LargerOrEqual: + return value.GetAsInt() <= valueComparing; + case ConditionOperator.Smaller: + return value.GetAsInt() > valueComparing; + case ConditionOperator.SmallerOrEqual: + return value.GetAsInt() >= valueComparing; + case ConditionOperator.Equal: + return value.GetAsInt() == valueComparing; + default: + throw new NotImplementedException("Unknown condition operator."); + } + } + + private bool IsSatisfiedBool(bool valueComparing) + { + switch (conditionOperator) + { + case ConditionOperator.Equal: + return value.GetAsBool() == valueComparing; + case ConditionOperator.Larger: + case ConditionOperator.LargerOrEqual: + case ConditionOperator.Smaller: + case ConditionOperator.SmallerOrEqual: + throw new NotImplementedException( + "Condition operator is not supported for bool type." + ); + default: + throw new NotImplementedException("Unknown condition operator."); } } From 69ae1ab2e69277b068bb7a2d5e41f2a9637325e5 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:27:55 +0900 Subject: [PATCH 008/108] Add NotEqual operator --- Goap/Condition.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Goap/Condition.cs b/Goap/Condition.cs index 45826b6..1cad7c1 100644 --- a/Goap/Condition.cs +++ b/Goap/Condition.cs @@ -11,6 +11,7 @@ public enum ConditionOperator Smaller, SmallerOrEqual, Equal, + NotEqual, } /// @@ -51,6 +52,8 @@ private bool IsSatisfiedFloat(float valueComparing) return value.GetAsFloat() >= valueComparing; case ConditionOperator.Equal: return IsEqualApproximately(value.GetAsFloat(), valueComparing); + case ConditionOperator.NotEqual: + return !IsEqualApproximately(value.GetAsFloat(), valueComparing); default: throw new NotImplementedException("Unknown condition operator."); } @@ -70,6 +73,8 @@ private bool IsSatisfiedInt(int valueComparing) return value.GetAsInt() >= valueComparing; case ConditionOperator.Equal: return value.GetAsInt() == valueComparing; + case ConditionOperator.NotEqual: + return value.GetAsInt() != valueComparing; default: throw new NotImplementedException("Unknown condition operator."); } @@ -81,6 +86,8 @@ private bool IsSatisfiedBool(bool valueComparing) { case ConditionOperator.Equal: return value.GetAsBool() == valueComparing; + case ConditionOperator.NotEqual: + return value.GetAsBool() != valueComparing; case ConditionOperator.Larger: case ConditionOperator.LargerOrEqual: case ConditionOperator.Smaller: From 52723187f9d29d8f273a53ed7429a3e328b96b42 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:38:19 +0900 Subject: [PATCH 009/108] dummy State --- Goap/State.cs | 4 ++++ Goap/State.cs.meta | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 Goap/State.cs create mode 100644 Goap/State.cs.meta diff --git a/Goap/State.cs b/Goap/State.cs new file mode 100644 index 0000000..fe667a1 --- /dev/null +++ b/Goap/State.cs @@ -0,0 +1,4 @@ +namespace TsunagiModule.Goap +{ + public abstract class State { } +} diff --git a/Goap/State.cs.meta b/Goap/State.cs.meta new file mode 100644 index 0000000..7d5e541 --- /dev/null +++ b/Goap/State.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8304c669d816bfa48894f5f58923ec1d \ No newline at end of file From b68d93640c14f4e8466dfecc2ac98f1b9543b293 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:41:07 +0900 Subject: [PATCH 010/108] dummy value getter --- Goap/State.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Goap/State.cs b/Goap/State.cs index fe667a1..ccec176 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -1,4 +1,12 @@ +using System; + namespace TsunagiModule.Goap { - public abstract class State { } + public class State + { + public float GetValue(string stateIndex) + { + throw new NotImplementedException(); + } + } } From e6f6e905c06693a6e5e87cfab56a563f4d17fea4 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:42:42 +0900 Subject: [PATCH 011/108] Add Condition class and interface with comparison operators --- Goap/Conditions.meta | 8 ++++++++ Goap/{ => Conditions}/Condition.cs | 15 ++++++++++++--- Goap/{ => Conditions}/Condition.cs.meta | 0 Goap/Conditions/ConditionInterface.cs | 7 +++++++ Goap/Conditions/ConditionInterface.cs.meta | 2 ++ 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 Goap/Conditions.meta rename Goap/{ => Conditions}/Condition.cs (89%) rename Goap/{ => Conditions}/Condition.cs.meta (100%) create mode 100644 Goap/Conditions/ConditionInterface.cs create mode 100644 Goap/Conditions/ConditionInterface.cs.meta diff --git a/Goap/Conditions.meta b/Goap/Conditions.meta new file mode 100644 index 0000000..409f5a2 --- /dev/null +++ b/Goap/Conditions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 534b8e054d2b8e24fa9f772a7ec79866 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/Condition.cs b/Goap/Conditions/Condition.cs similarity index 89% rename from Goap/Condition.cs rename to Goap/Conditions/Condition.cs index 1cad7c1..18f4422 100644 --- a/Goap/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public abstract class Condition + public class Condition : ConditionInterface { public enum ConditionOperator { @@ -19,12 +19,21 @@ public enum ConditionOperator /// private const float EPSILON = 0.0001f; - public string name { get; private set; } + public string stateIndex { get; private set; } public GoapValue value { get; private set; } public ConditionOperator conditionOperator { get; private set; } - public bool IsSatisfied(float valueComparing) + public Condition(string stateIndex, GoapValue value, ConditionOperator conditionOperator) { + this.stateIndex = stateIndex; + this.value = value; + this.conditionOperator = conditionOperator; + } + + public bool IsSatisfied(State state) + { + float valueComparing = state.GetValue(stateIndex); + switch (value.type) { case GoapValue.ValueType.Float: diff --git a/Goap/Condition.cs.meta b/Goap/Conditions/Condition.cs.meta similarity index 100% rename from Goap/Condition.cs.meta rename to Goap/Conditions/Condition.cs.meta diff --git a/Goap/Conditions/ConditionInterface.cs b/Goap/Conditions/ConditionInterface.cs new file mode 100644 index 0000000..e1416f9 --- /dev/null +++ b/Goap/Conditions/ConditionInterface.cs @@ -0,0 +1,7 @@ +namespace TsunagiModule.Goap +{ + public interface ConditionInterface + { + public bool IsSatisfied(State state); + } +} diff --git a/Goap/Conditions/ConditionInterface.cs.meta b/Goap/Conditions/ConditionInterface.cs.meta new file mode 100644 index 0000000..e1f6e9d --- /dev/null +++ b/Goap/Conditions/ConditionInterface.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 48cd3a5fd8565f94b8e162e3704b8098 \ No newline at end of file From c16b1c91e52932587587959c122e5460febba9a3 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:43:21 +0900 Subject: [PATCH 012/108] Add ConditionAnd class to evaluate multiple conditions --- Goap/Conditions/ConditionAnd.cs | 24 ++++++++++++++++++++++++ Goap/Conditions/ConditionAnd.cs.meta | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 Goap/Conditions/ConditionAnd.cs create mode 100644 Goap/Conditions/ConditionAnd.cs.meta diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs new file mode 100644 index 0000000..76601cd --- /dev/null +++ b/Goap/Conditions/ConditionAnd.cs @@ -0,0 +1,24 @@ +namespace TsunagiModule.Goap +{ + public class ConditionAnd : ConditionInterface + { + public Condition[] conditions { get; private set; } + + public ConditionAnd(Condition[] conditions) + { + this.conditions = conditions; + } + + public bool IsSatisfied(State state) + { + foreach (var condition in conditions) + { + if (!condition.IsSatisfied(state)) + { + return false; + } + } + return true; + } + } +} diff --git a/Goap/Conditions/ConditionAnd.cs.meta b/Goap/Conditions/ConditionAnd.cs.meta new file mode 100644 index 0000000..12d2b23 --- /dev/null +++ b/Goap/Conditions/ConditionAnd.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 657fef7846b91c7469be75587d22fdb0 \ No newline at end of file From e74d1e772fd9d5c23ea2379b9a91d84592e8becc Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:44:48 +0900 Subject: [PATCH 013/108] Add ConditionOr class to evaluate conditions with logical OR --- Goap/Conditions/ConditionOr.cs | 24 ++++++++++++++++++++++++ Goap/Conditions/ConditionOr.cs.meta | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 Goap/Conditions/ConditionOr.cs create mode 100644 Goap/Conditions/ConditionOr.cs.meta diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs new file mode 100644 index 0000000..deda5b1 --- /dev/null +++ b/Goap/Conditions/ConditionOr.cs @@ -0,0 +1,24 @@ +namespace TsunagiModule.Goap +{ + public class ConditionOr : ConditionInterface + { + public Condition[] conditions { get; private set; } + + public ConditionOr(Condition[] conditions) + { + this.conditions = conditions; + } + + public bool IsSatisfied(State state) + { + foreach (var condition in conditions) + { + if (condition.IsSatisfied(state)) + { + return true; + } + } + return false; + } + } +} diff --git a/Goap/Conditions/ConditionOr.cs.meta b/Goap/Conditions/ConditionOr.cs.meta new file mode 100644 index 0000000..7a0abd0 --- /dev/null +++ b/Goap/Conditions/ConditionOr.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 93c5b8c5c190ebf4cb1c67977c6b7e31 \ No newline at end of file From 0beb62bd52180784bab2374b7aea77c0d5151884 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:45:05 +0900 Subject: [PATCH 014/108] Refactor foreach loops to use explicit type for conditions in ConditionAnd and ConditionOr classes --- Goap/Conditions/ConditionAnd.cs | 2 +- Goap/Conditions/ConditionOr.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 76601cd..56a987a 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -11,7 +11,7 @@ public ConditionAnd(Condition[] conditions) public bool IsSatisfied(State state) { - foreach (var condition in conditions) + foreach (Condition condition in conditions) { if (!condition.IsSatisfied(state)) { diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index deda5b1..9367d5d 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -11,7 +11,7 @@ public ConditionOr(Condition[] conditions) public bool IsSatisfied(State state) { - foreach (var condition in conditions) + foreach (Condition condition in conditions) { if (condition.IsSatisfied(state)) { From 65b2140fd652f4f6f264b95a2c608c8e1f4cdbd8 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 20:47:02 +0900 Subject: [PATCH 015/108] Add ConditionNot class to negate conditions in the GOAP system --- Goap/Conditions/ConditionNot.cs | 17 +++++++++++++++++ Goap/Conditions/ConditionNot.cs.meta | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 Goap/Conditions/ConditionNot.cs create mode 100644 Goap/Conditions/ConditionNot.cs.meta diff --git a/Goap/Conditions/ConditionNot.cs b/Goap/Conditions/ConditionNot.cs new file mode 100644 index 0000000..d683d87 --- /dev/null +++ b/Goap/Conditions/ConditionNot.cs @@ -0,0 +1,17 @@ +namespace TsunagiModule.Goap +{ + public class ConditionNot : ConditionInterface + { + public Condition condition { get; private set; } + + public ConditionNot(Condition condition) + { + this.condition = condition; + } + + public bool IsSatisfied(State state) + { + return !condition.IsSatisfied(state); + } + } +} diff --git a/Goap/Conditions/ConditionNot.cs.meta b/Goap/Conditions/ConditionNot.cs.meta new file mode 100644 index 0000000..8019dfb --- /dev/null +++ b/Goap/Conditions/ConditionNot.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b5774bdb91bd10142bf0ef0b334aa833 \ No newline at end of file From 42b0e20c65d3f28c0e29e65f8dd6e6db8b968c57 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:14:12 +0900 Subject: [PATCH 016/108] values to independent struct --- Goap/GoapValue.cs | 54 -------------------------- Goap/GoapValue.meta | 8 ++++ Goap/GoapValue/GoapBool.cs | 32 +++++++++++++++ Goap/GoapValue/GoapBool.cs.meta | 2 + Goap/GoapValue/GoapFloat.cs | 32 +++++++++++++++ Goap/GoapValue/GoapFloat.cs.meta | 2 + Goap/GoapValue/GoapInt.cs | 32 +++++++++++++++ Goap/GoapValue/GoapInt.cs.meta | 2 + Goap/GoapValue/GoapValue.cs | 16 ++++++++ Goap/{ => GoapValue}/GoapValue.cs.meta | 0 Goap/GoapValue/ValueType.cs | 9 +++++ Goap/GoapValue/ValueType.cs.meta | 2 + Goap/ValueConverter.cs | 30 -------------- Goap/ValueConverter.cs.meta | 2 - 14 files changed, 137 insertions(+), 86 deletions(-) delete mode 100644 Goap/GoapValue.cs create mode 100644 Goap/GoapValue.meta create mode 100644 Goap/GoapValue/GoapBool.cs create mode 100644 Goap/GoapValue/GoapBool.cs.meta create mode 100644 Goap/GoapValue/GoapFloat.cs create mode 100644 Goap/GoapValue/GoapFloat.cs.meta create mode 100644 Goap/GoapValue/GoapInt.cs create mode 100644 Goap/GoapValue/GoapInt.cs.meta create mode 100644 Goap/GoapValue/GoapValue.cs rename Goap/{ => GoapValue}/GoapValue.cs.meta (100%) create mode 100644 Goap/GoapValue/ValueType.cs create mode 100644 Goap/GoapValue/ValueType.cs.meta delete mode 100644 Goap/ValueConverter.cs delete mode 100644 Goap/ValueConverter.cs.meta diff --git a/Goap/GoapValue.cs b/Goap/GoapValue.cs deleted file mode 100644 index 306c13c..0000000 --- a/Goap/GoapValue.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace TsunagiModule.Goap -{ - /// - /// Value used in Goap system. - /// - /// - /// This is implemented for support integrated control of State Control, being independent of value type. - /// - public struct GoapValue - { - public enum ValueType - { - Int, - Float, - Bool - } - - public float value { get; private set; } - public ValueType type { get; private set; } - - public GoapValue(float value) - { - this.value = value; - type = ValueType.Float; - } - - public GoapValue(int value) - { - this.value = ValueConverter.ToFloat(value); - type = ValueType.Int; - } - - public GoapValue(bool value) - { - this.value = ValueConverter.ToFloat(value); - type = ValueType.Bool; - } - - public bool GetAsBool() - { - return ValueConverter.ToBool(value); - } - - public int GetAsInt() - { - return ValueConverter.ToInt(value); - } - - public float GetAsFloat() - { - return value; - } - } -} diff --git a/Goap/GoapValue.meta b/Goap/GoapValue.meta new file mode 100644 index 0000000..071704c --- /dev/null +++ b/Goap/GoapValue.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 72c7755c0bd64224d8419621fb42a70b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs new file mode 100644 index 0000000..d7e4b59 --- /dev/null +++ b/Goap/GoapValue/GoapBool.cs @@ -0,0 +1,32 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct GoapBool : GoapValue + { + public bool value { get; private set; } + + public GoapBool(bool value) + { + this.value = value; + } + + public ValueType type + { + get { return ValueType.Bool; } + } + + public GoapValue Operate(GoapValue other, Func operationHandler) + { + // type check + if (other.type != ValueType.Bool) + { + throw new InvalidOperationException( + "GoapBool can only operate with another GoapBool." + ); + } + + return operationHandler(other); + } + } +} diff --git a/Goap/GoapValue/GoapBool.cs.meta b/Goap/GoapValue/GoapBool.cs.meta new file mode 100644 index 0000000..2f1f7e6 --- /dev/null +++ b/Goap/GoapValue/GoapBool.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5047d5a2e4c8622429b69efd4d95b062 \ No newline at end of file diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs new file mode 100644 index 0000000..689bebc --- /dev/null +++ b/Goap/GoapValue/GoapFloat.cs @@ -0,0 +1,32 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct GoapFloat : GoapValue + { + public float value { get; private set; } + + public GoapFloat(float value) + { + this.value = value; + } + + public ValueType type + { + get { return ValueType.Float; } + } + + public GoapValue Operate(GoapValue other, Func operationHandler) + { + // type check + if (other.type != ValueType.Float) + { + throw new InvalidOperationException( + "GoapFloat can only operate with another GoapFloat." + ); + } + + return operationHandler(other); + } + } +} diff --git a/Goap/GoapValue/GoapFloat.cs.meta b/Goap/GoapValue/GoapFloat.cs.meta new file mode 100644 index 0000000..5de9980 --- /dev/null +++ b/Goap/GoapValue/GoapFloat.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6c030f54c4d4f804e97f09df8a9ded5c \ No newline at end of file diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs new file mode 100644 index 0000000..f295c11 --- /dev/null +++ b/Goap/GoapValue/GoapInt.cs @@ -0,0 +1,32 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct GoapInt : GoapValue + { + public int value { get; private set; } + + public GoapInt(int value) + { + this.value = value; + } + + public ValueType type + { + get { return ValueType.Int; } + } + + public GoapValue Operate(GoapValue other, Func operationHandler) + { + // type check + if (other.type != ValueType.Int) + { + throw new InvalidOperationException( + "GoapInt can only operate with another GoapInt." + ); + } + + return operationHandler(other); + } + } +} diff --git a/Goap/GoapValue/GoapInt.cs.meta b/Goap/GoapValue/GoapInt.cs.meta new file mode 100644 index 0000000..1a80d97 --- /dev/null +++ b/Goap/GoapValue/GoapInt.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 002bdbf45c3b63e4fa3cc53c4649f8d2 \ No newline at end of file diff --git a/Goap/GoapValue/GoapValue.cs b/Goap/GoapValue/GoapValue.cs new file mode 100644 index 0000000..0cf1459 --- /dev/null +++ b/Goap/GoapValue/GoapValue.cs @@ -0,0 +1,16 @@ +using System; + +namespace TsunagiModule.Goap +{ + /// + /// Value used in Goap system. + /// + /// + /// This is implemented for support integrated control of State Control, being independent of value type. + /// + public interface GoapValue + { + public ValueType type { get; } + public GoapValue Operate(GoapValue other, Func operationHandler); + } +} diff --git a/Goap/GoapValue.cs.meta b/Goap/GoapValue/GoapValue.cs.meta similarity index 100% rename from Goap/GoapValue.cs.meta rename to Goap/GoapValue/GoapValue.cs.meta diff --git a/Goap/GoapValue/ValueType.cs b/Goap/GoapValue/ValueType.cs new file mode 100644 index 0000000..ec3115a --- /dev/null +++ b/Goap/GoapValue/ValueType.cs @@ -0,0 +1,9 @@ +namespace TsunagiModule.Goap +{ + public enum ValueType + { + Int, + Float, + Bool + } +} diff --git a/Goap/GoapValue/ValueType.cs.meta b/Goap/GoapValue/ValueType.cs.meta new file mode 100644 index 0000000..b081cc8 --- /dev/null +++ b/Goap/GoapValue/ValueType.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: abd407e8509d867419d96ee6c474c7be \ No newline at end of file diff --git a/Goap/ValueConverter.cs b/Goap/ValueConverter.cs deleted file mode 100644 index 596905f..0000000 --- a/Goap/ValueConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - /// - /// Convert values for GOAP inner calculation - /// - public static class ValueConverter - { - public static float ToFloat(int value) - { - return value; - } - - public static float ToFloat(bool value) - { - return value ? 1 : 0; - } - - public static int ToInt(float value) - { - return (int)Math.Round(value); - } - - public static bool ToBool(float value) - { - return value >= 0.5f; - } - } -} diff --git a/Goap/ValueConverter.cs.meta b/Goap/ValueConverter.cs.meta deleted file mode 100644 index 8dbdcd9..0000000 --- a/Goap/ValueConverter.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: f4e445cad5b89ec46ab1eae9702144c2 \ No newline at end of file From 7720bb1d13e14b58abe1ea6556cb1293f3aaf588 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:16:17 +0900 Subject: [PATCH 017/108] Implement state vector storage and retrieval in State class --- Goap/State.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Goap/State.cs b/Goap/State.cs index ccec176..fef1fae 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -1,12 +1,25 @@ using System; +using System.Collections.Generic; namespace TsunagiModule.Goap { public class State { - public float GetValue(string stateIndex) + /// + /// Main body of state vector + /// + private Dictionary values { get; set; } + + public GoapValue GetValue(string stateIndex) { - throw new NotImplementedException(); + if (values.TryGetValue(stateIndex, out GoapValue value)) + { + return value; + } + else + { + throw new KeyNotFoundException($"State index '{stateIndex}' not found."); + } } } } From b1dbc4145616a152f69c0146ca15b12bb28c26cb Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:20:24 +0900 Subject: [PATCH 018/108] appropriately get values --- Goap/Conditions/Condition.cs | 14 +++++++------- Goap/GoapValue/GoapBool.cs | 5 +++++ Goap/GoapValue/GoapFloat.cs | 5 +++++ Goap/GoapValue/GoapInt.cs | 5 +++++ Goap/GoapValue/GoapValue.cs | 18 ++++++++++++++++++ 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 18f4422..744888b 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -32,16 +32,16 @@ public Condition(string stateIndex, GoapValue value, ConditionOperator condition public bool IsSatisfied(State state) { - float valueComparing = state.GetValue(stateIndex); + GoapValue valueComparing = state.GetValue(stateIndex); switch (value.type) { - case GoapValue.ValueType.Float: - return IsSatisfiedFloat(valueComparing); - case GoapValue.ValueType.Int: - return IsSatisfiedInt(ValueConverter.ToInt(valueComparing)); - case GoapValue.ValueType.Bool: - return IsSatisfiedBool(ValueConverter.ToBool(valueComparing)); + case ValueType.Float: + return IsSatisfiedFloat(valueComparing.GetAsFloat()); + case ValueType.Int: + return IsSatisfiedInt(valueComparing.GetAsInt()); + case ValueType.Bool: + return IsSatisfiedBool(valueComparing.GetAsBool()); default: throw new NotImplementedException("Unknown GoapValue.ValueType"); } diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs index d7e4b59..ddc7cf5 100644 --- a/Goap/GoapValue/GoapBool.cs +++ b/Goap/GoapValue/GoapBool.cs @@ -28,5 +28,10 @@ public GoapValue Operate(GoapValue other, Func operationHa return operationHandler(other); } + + public bool GetAsBool() + { + return value; + } } } diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs index 689bebc..e75aa19 100644 --- a/Goap/GoapValue/GoapFloat.cs +++ b/Goap/GoapValue/GoapFloat.cs @@ -28,5 +28,10 @@ public GoapValue Operate(GoapValue other, Func operationHa return operationHandler(other); } + + public float GetAsFloat() + { + return value; + } } } diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs index f295c11..c294137 100644 --- a/Goap/GoapValue/GoapInt.cs +++ b/Goap/GoapValue/GoapInt.cs @@ -28,5 +28,10 @@ public GoapValue Operate(GoapValue other, Func operationHa return operationHandler(other); } + + public int GetAsInt() + { + return value; + } } } diff --git a/Goap/GoapValue/GoapValue.cs b/Goap/GoapValue/GoapValue.cs index 0cf1459..fd1cb54 100644 --- a/Goap/GoapValue/GoapValue.cs +++ b/Goap/GoapValue/GoapValue.cs @@ -12,5 +12,23 @@ public interface GoapValue { public ValueType type { get; } public GoapValue Operate(GoapValue other, Func operationHandler); + public float GetAsFloat() + { + throw new NotImplementedException( + "GetAsFloat is not implemented for this GoapValue type." + ); + } + public int GetAsInt() + { + throw new NotImplementedException( + "GetAsInt is not implemented for this GoapValue type." + ); + } + public bool GetAsBool() + { + throw new NotImplementedException( + "GetAsBool is not implemented for this GoapValue type." + ); + } } } From 221991832379169de377f58995cc71d72c0d5b50 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:21:18 +0900 Subject: [PATCH 019/108] rename: GoapValue -> GoapValueInterface --- Goap/Conditions/Condition.cs | 12 ++++++++---- Goap/GoapValue/GoapBool.cs | 7 +++++-- Goap/GoapValue/GoapFloat.cs | 7 +++++-- Goap/GoapValue/GoapInt.cs | 7 +++++-- .../{GoapValue.cs => GoapValueInterface.cs} | 7 +++++-- ...{GoapValue.cs.meta => GoapValueInterface.cs.meta} | 0 Goap/State.cs | 6 +++--- 7 files changed, 31 insertions(+), 15 deletions(-) rename Goap/GoapValue/{GoapValue.cs => GoapValueInterface.cs} (80%) rename Goap/GoapValue/{GoapValue.cs.meta => GoapValueInterface.cs.meta} (100%) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 744888b..96d7d18 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public class Condition : ConditionInterface + public struct Condition : ConditionInterface { public enum ConditionOperator { @@ -20,10 +20,14 @@ public enum ConditionOperator private const float EPSILON = 0.0001f; public string stateIndex { get; private set; } - public GoapValue value { get; private set; } + public GoapValueInterface value { get; private set; } public ConditionOperator conditionOperator { get; private set; } - public Condition(string stateIndex, GoapValue value, ConditionOperator conditionOperator) + public Condition( + string stateIndex, + GoapValueInterface value, + ConditionOperator conditionOperator + ) { this.stateIndex = stateIndex; this.value = value; @@ -32,7 +36,7 @@ public Condition(string stateIndex, GoapValue value, ConditionOperator condition public bool IsSatisfied(State state) { - GoapValue valueComparing = state.GetValue(stateIndex); + GoapValueInterface valueComparing = state.GetValue(stateIndex); switch (value.type) { diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs index ddc7cf5..642a469 100644 --- a/Goap/GoapValue/GoapBool.cs +++ b/Goap/GoapValue/GoapBool.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public struct GoapBool : GoapValue + public struct GoapBool : GoapValueInterface { public bool value { get; private set; } @@ -16,7 +16,10 @@ public ValueType type get { return ValueType.Bool; } } - public GoapValue Operate(GoapValue other, Func operationHandler) + public GoapValueInterface Operate( + GoapValueInterface other, + Func operationHandler + ) { // type check if (other.type != ValueType.Bool) diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs index e75aa19..abc23f7 100644 --- a/Goap/GoapValue/GoapFloat.cs +++ b/Goap/GoapValue/GoapFloat.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public struct GoapFloat : GoapValue + public struct GoapFloat : GoapValueInterface { public float value { get; private set; } @@ -16,7 +16,10 @@ public ValueType type get { return ValueType.Float; } } - public GoapValue Operate(GoapValue other, Func operationHandler) + public GoapValueInterface Operate( + GoapValueInterface other, + Func operationHandler + ) { // type check if (other.type != ValueType.Float) diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs index c294137..32c8d05 100644 --- a/Goap/GoapValue/GoapInt.cs +++ b/Goap/GoapValue/GoapInt.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public struct GoapInt : GoapValue + public struct GoapInt : GoapValueInterface { public int value { get; private set; } @@ -16,7 +16,10 @@ public ValueType type get { return ValueType.Int; } } - public GoapValue Operate(GoapValue other, Func operationHandler) + public GoapValueInterface Operate( + GoapValueInterface other, + Func operationHandler + ) { // type check if (other.type != ValueType.Int) diff --git a/Goap/GoapValue/GoapValue.cs b/Goap/GoapValue/GoapValueInterface.cs similarity index 80% rename from Goap/GoapValue/GoapValue.cs rename to Goap/GoapValue/GoapValueInterface.cs index fd1cb54..bad1829 100644 --- a/Goap/GoapValue/GoapValue.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -8,10 +8,13 @@ namespace TsunagiModule.Goap /// /// This is implemented for support integrated control of State Control, being independent of value type. /// - public interface GoapValue + public interface GoapValueInterface { public ValueType type { get; } - public GoapValue Operate(GoapValue other, Func operationHandler); + public GoapValueInterface Operate( + GoapValueInterface other, + Func operationHandler + ); public float GetAsFloat() { throw new NotImplementedException( diff --git a/Goap/GoapValue/GoapValue.cs.meta b/Goap/GoapValue/GoapValueInterface.cs.meta similarity index 100% rename from Goap/GoapValue/GoapValue.cs.meta rename to Goap/GoapValue/GoapValueInterface.cs.meta diff --git a/Goap/State.cs b/Goap/State.cs index fef1fae..30db7e3 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -8,11 +8,11 @@ public class State /// /// Main body of state vector /// - private Dictionary values { get; set; } + private Dictionary values { get; set; } - public GoapValue GetValue(string stateIndex) + public GoapValueInterface GetValue(string stateIndex) { - if (values.TryGetValue(stateIndex, out GoapValue value)) + if (values.TryGetValue(stateIndex, out GoapValueInterface value)) { return value; } From 3b40d9d48a9ec16a2d74f748a4093948ad3cd615 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:23:43 +0900 Subject: [PATCH 020/108] condition logics to struct --- Goap/Conditions/ConditionAnd.cs | 2 +- Goap/Conditions/ConditionNot.cs | 2 +- Goap/Conditions/ConditionOr.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 56a987a..a9528ea 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -1,6 +1,6 @@ namespace TsunagiModule.Goap { - public class ConditionAnd : ConditionInterface + public struct ConditionAnd : ConditionInterface { public Condition[] conditions { get; private set; } diff --git a/Goap/Conditions/ConditionNot.cs b/Goap/Conditions/ConditionNot.cs index d683d87..c9552bc 100644 --- a/Goap/Conditions/ConditionNot.cs +++ b/Goap/Conditions/ConditionNot.cs @@ -1,6 +1,6 @@ namespace TsunagiModule.Goap { - public class ConditionNot : ConditionInterface + public struct ConditionNot : ConditionInterface { public Condition condition { get; private set; } diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index 9367d5d..dcf5377 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -1,6 +1,6 @@ namespace TsunagiModule.Goap { - public class ConditionOr : ConditionInterface + public struct ConditionOr : ConditionInterface { public Condition[] conditions { get; private set; } From d5fbb3a952ec5afd5873dcc273d85d6e74549547 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:35:11 +0900 Subject: [PATCH 021/108] fix handler type --- Goap/GoapValue/GoapBool.cs | 4 ++-- Goap/GoapValue/GoapFloat.cs | 4 ++-- Goap/GoapValue/GoapInt.cs | 4 ++-- Goap/GoapValue/GoapValueInterface.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs index 642a469..19e29e9 100644 --- a/Goap/GoapValue/GoapBool.cs +++ b/Goap/GoapValue/GoapBool.cs @@ -18,7 +18,7 @@ public ValueType type public GoapValueInterface Operate( GoapValueInterface other, - Func operationHandler + Func operationHandler ) { // type check @@ -29,7 +29,7 @@ Func operationHandler ); } - return operationHandler(other); + return operationHandler(this, other); } public bool GetAsBool() diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs index abc23f7..e7d19da 100644 --- a/Goap/GoapValue/GoapFloat.cs +++ b/Goap/GoapValue/GoapFloat.cs @@ -18,7 +18,7 @@ public ValueType type public GoapValueInterface Operate( GoapValueInterface other, - Func operationHandler + Func operationHandler ) { // type check @@ -29,7 +29,7 @@ Func operationHandler ); } - return operationHandler(other); + return operationHandler(this, other); } public float GetAsFloat() diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs index 32c8d05..2f76210 100644 --- a/Goap/GoapValue/GoapInt.cs +++ b/Goap/GoapValue/GoapInt.cs @@ -18,7 +18,7 @@ public ValueType type public GoapValueInterface Operate( GoapValueInterface other, - Func operationHandler + Func operationHandler ) { // type check @@ -29,7 +29,7 @@ Func operationHandler ); } - return operationHandler(other); + return operationHandler(this, other); } public int GetAsInt() diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Goap/GoapValue/GoapValueInterface.cs index bad1829..dbe08bc 100644 --- a/Goap/GoapValue/GoapValueInterface.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -13,7 +13,7 @@ public interface GoapValueInterface public ValueType type { get; } public GoapValueInterface Operate( GoapValueInterface other, - Func operationHandler + Func operationHandler ); public float GetAsFloat() { From 0831855ab6a154750cf04d5a51e337f05223a7fe Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:36:32 +0900 Subject: [PATCH 022/108] value setting --- Goap/State.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Goap/State.cs b/Goap/State.cs index 30db7e3..3af2d15 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -21,5 +21,10 @@ public GoapValueInterface GetValue(string stateIndex) throw new KeyNotFoundException($"State index '{stateIndex}' not found."); } } + + public void SetValue(string stateIndex, GoapValueInterface value) + { + values[stateIndex] = value; + } } } From 4c74a061f4c7e26590e1e36eab2e3e71cc97cf38 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:46:51 +0900 Subject: [PATCH 023/108] StateDiffSet --- Goap/StateDiff.meta | 8 ++++++++ Goap/StateDiff/StateDiff.cs | 4 ++++ Goap/StateDiff/StateDiff.cs.meta | 2 ++ Goap/StateDiff/StateDiffSet.cs | 14 ++++++++++++++ Goap/StateDiff/StateDiffSet.cs.meta | 2 ++ 5 files changed, 30 insertions(+) create mode 100644 Goap/StateDiff.meta create mode 100644 Goap/StateDiff/StateDiff.cs create mode 100644 Goap/StateDiff/StateDiff.cs.meta create mode 100644 Goap/StateDiff/StateDiffSet.cs create mode 100644 Goap/StateDiff/StateDiffSet.cs.meta diff --git a/Goap/StateDiff.meta b/Goap/StateDiff.meta new file mode 100644 index 0000000..3b48dd8 --- /dev/null +++ b/Goap/StateDiff.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b22791e46cd4ba540b4df40099f6be66 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/StateDiff/StateDiff.cs b/Goap/StateDiff/StateDiff.cs new file mode 100644 index 0000000..eeae4f6 --- /dev/null +++ b/Goap/StateDiff/StateDiff.cs @@ -0,0 +1,4 @@ +namespace TsunagiModule.Goap +{ + public struct StateDiff { } +} diff --git a/Goap/StateDiff/StateDiff.cs.meta b/Goap/StateDiff/StateDiff.cs.meta new file mode 100644 index 0000000..1e6dc8f --- /dev/null +++ b/Goap/StateDiff/StateDiff.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 732262d93b3b05245b2b1eb06e4ca551 \ No newline at end of file diff --git a/Goap/StateDiff/StateDiffSet.cs b/Goap/StateDiff/StateDiffSet.cs new file mode 100644 index 0000000..ab7e72c --- /dev/null +++ b/Goap/StateDiff/StateDiffSet.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace TsunagiModule.Goap +{ + public struct StateDiffSet + { + public List diffs; + + public StateDiffSet(List diffs) + { + this.diffs = diffs; + } + } +} diff --git a/Goap/StateDiff/StateDiffSet.cs.meta b/Goap/StateDiff/StateDiffSet.cs.meta new file mode 100644 index 0000000..256faf5 --- /dev/null +++ b/Goap/StateDiff/StateDiffSet.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 788968bbd4bf8cc42889f60828dbb29b \ No newline at end of file From 218044e975a7133f7c6f8c7905e86c7f905a7aed Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 21:52:55 +0900 Subject: [PATCH 024/108] GoapValue don't operate themselves --- Goap/GoapValue/GoapBool.cs | 16 ---------------- Goap/GoapValue/GoapFloat.cs | 16 ---------------- Goap/GoapValue/GoapInt.cs | 16 ---------------- Goap/GoapValue/GoapValueInterface.cs | 4 ---- 4 files changed, 52 deletions(-) diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs index 19e29e9..bfae9d7 100644 --- a/Goap/GoapValue/GoapBool.cs +++ b/Goap/GoapValue/GoapBool.cs @@ -16,22 +16,6 @@ public ValueType type get { return ValueType.Bool; } } - public GoapValueInterface Operate( - GoapValueInterface other, - Func operationHandler - ) - { - // type check - if (other.type != ValueType.Bool) - { - throw new InvalidOperationException( - "GoapBool can only operate with another GoapBool." - ); - } - - return operationHandler(this, other); - } - public bool GetAsBool() { return value; diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs index e7d19da..291f73f 100644 --- a/Goap/GoapValue/GoapFloat.cs +++ b/Goap/GoapValue/GoapFloat.cs @@ -16,22 +16,6 @@ public ValueType type get { return ValueType.Float; } } - public GoapValueInterface Operate( - GoapValueInterface other, - Func operationHandler - ) - { - // type check - if (other.type != ValueType.Float) - { - throw new InvalidOperationException( - "GoapFloat can only operate with another GoapFloat." - ); - } - - return operationHandler(this, other); - } - public float GetAsFloat() { return value; diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs index 2f76210..15f2cfd 100644 --- a/Goap/GoapValue/GoapInt.cs +++ b/Goap/GoapValue/GoapInt.cs @@ -16,22 +16,6 @@ public ValueType type get { return ValueType.Int; } } - public GoapValueInterface Operate( - GoapValueInterface other, - Func operationHandler - ) - { - // type check - if (other.type != ValueType.Int) - { - throw new InvalidOperationException( - "GoapInt can only operate with another GoapInt." - ); - } - - return operationHandler(this, other); - } - public int GetAsInt() { return value; diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Goap/GoapValue/GoapValueInterface.cs index dbe08bc..349b27a 100644 --- a/Goap/GoapValue/GoapValueInterface.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -11,10 +11,6 @@ namespace TsunagiModule.Goap public interface GoapValueInterface { public ValueType type { get; } - public GoapValueInterface Operate( - GoapValueInterface other, - Func operationHandler - ); public float GetAsFloat() { throw new NotImplementedException( From 2ff820a3b68301d496ff4a2fee46ab59c5a84136 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 22:53:04 +0900 Subject: [PATCH 025/108] state diff --- Goap/StateDiff/StateDiff.cs | 4 ---- Goap/StateDiff/StateDiff.cs.meta | 2 -- Goap/StateDiff/StateDiffInt.cs | 28 +++++++++++++++++++++++ Goap/StateDiff/StateDiffInt.cs.meta | 2 ++ Goap/StateDiff/StateDiffInterface.cs | 8 +++++++ Goap/StateDiff/StateDiffInterface.cs.meta | 2 ++ Goap/StateDiff/StateDiffSet.cs | 12 +--------- 7 files changed, 41 insertions(+), 17 deletions(-) delete mode 100644 Goap/StateDiff/StateDiff.cs delete mode 100644 Goap/StateDiff/StateDiff.cs.meta create mode 100644 Goap/StateDiff/StateDiffInt.cs create mode 100644 Goap/StateDiff/StateDiffInt.cs.meta create mode 100644 Goap/StateDiff/StateDiffInterface.cs create mode 100644 Goap/StateDiff/StateDiffInterface.cs.meta diff --git a/Goap/StateDiff/StateDiff.cs b/Goap/StateDiff/StateDiff.cs deleted file mode 100644 index eeae4f6..0000000 --- a/Goap/StateDiff/StateDiff.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace TsunagiModule.Goap -{ - public struct StateDiff { } -} diff --git a/Goap/StateDiff/StateDiff.cs.meta b/Goap/StateDiff/StateDiff.cs.meta deleted file mode 100644 index 1e6dc8f..0000000 --- a/Goap/StateDiff/StateDiff.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 732262d93b3b05245b2b1eb06e4ca551 \ No newline at end of file diff --git a/Goap/StateDiff/StateDiffInt.cs b/Goap/StateDiff/StateDiffInt.cs new file mode 100644 index 0000000..1877fdb --- /dev/null +++ b/Goap/StateDiff/StateDiffInt.cs @@ -0,0 +1,28 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct StateDiffInt : StateDiffInterface + { + public string stateIndex { get; private set; } + public Func operationHandler { get; private set; } + + public StateDiffInt(string stateIndex, Func operationHandler) + { + this.stateIndex = stateIndex; + this.operationHandler = operationHandler; + } + + public State Operate(State state) + { + // compute value + int valueState = state.GetValue(stateIndex).GetAsInt(); + int newValue = operationHandler(valueState); + + // update state + state.SetValue(stateIndex, new GoapInt(newValue)); + + return state; + } + } +} diff --git a/Goap/StateDiff/StateDiffInt.cs.meta b/Goap/StateDiff/StateDiffInt.cs.meta new file mode 100644 index 0000000..e8a9124 --- /dev/null +++ b/Goap/StateDiff/StateDiffInt.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b196d756c04ec7841956ad88e971131d \ No newline at end of file diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Goap/StateDiff/StateDiffInterface.cs new file mode 100644 index 0000000..2898e7e --- /dev/null +++ b/Goap/StateDiff/StateDiffInterface.cs @@ -0,0 +1,8 @@ +namespace TsunagiModule.Goap +{ + public interface StateDiffInterface + { + public string stateIndex { get; } + public State Operate(State state); + } +} diff --git a/Goap/StateDiff/StateDiffInterface.cs.meta b/Goap/StateDiff/StateDiffInterface.cs.meta new file mode 100644 index 0000000..aa50fd3 --- /dev/null +++ b/Goap/StateDiff/StateDiffInterface.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0f8ac22e296f4f24496ce84a48755804 \ No newline at end of file diff --git a/Goap/StateDiff/StateDiffSet.cs b/Goap/StateDiff/StateDiffSet.cs index ab7e72c..b0561ef 100644 --- a/Goap/StateDiff/StateDiffSet.cs +++ b/Goap/StateDiff/StateDiffSet.cs @@ -1,14 +1,4 @@ -using System.Collections.Generic; - namespace TsunagiModule.Goap { - public struct StateDiffSet - { - public List diffs; - - public StateDiffSet(List diffs) - { - this.diffs = diffs; - } - } + public struct StateDiffSet { } } From ec0890a26d138b33056fc35f44c538ee557ea0c9 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:08:31 +0900 Subject: [PATCH 026/108] State copies itself --- Goap/State.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Goap/State.cs b/Goap/State.cs index 3af2d15..4738d45 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -1,9 +1,8 @@ -using System; using System.Collections.Generic; namespace TsunagiModule.Goap { - public class State + public struct State { /// /// Main body of state vector @@ -26,5 +25,10 @@ public void SetValue(string stateIndex, GoapValueInterface value) { values[stateIndex] = value; } + + public State Copy() + { + return new State { values = new Dictionary(values) }; + } } } From 7861212a9a50a82cdf5934d2a3d734eb389cbb0b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:11:13 +0900 Subject: [PATCH 027/108] Apply State Diff by set --- Goap/StateDiff/StateDiffInt.cs | 4 +--- Goap/StateDiff/StateDiffInterface.cs | 2 +- Goap/StateDiff/StateDiffSet.cs | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Goap/StateDiff/StateDiffInt.cs b/Goap/StateDiff/StateDiffInt.cs index 1877fdb..af3b2df 100644 --- a/Goap/StateDiff/StateDiffInt.cs +++ b/Goap/StateDiff/StateDiffInt.cs @@ -13,7 +13,7 @@ public StateDiffInt(string stateIndex, Func operationHandler) this.operationHandler = operationHandler; } - public State Operate(State state) + public void Operate(State state) { // compute value int valueState = state.GetValue(stateIndex).GetAsInt(); @@ -21,8 +21,6 @@ public State Operate(State state) // update state state.SetValue(stateIndex, new GoapInt(newValue)); - - return state; } } } diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Goap/StateDiff/StateDiffInterface.cs index 2898e7e..d044f57 100644 --- a/Goap/StateDiff/StateDiffInterface.cs +++ b/Goap/StateDiff/StateDiffInterface.cs @@ -3,6 +3,6 @@ namespace TsunagiModule.Goap public interface StateDiffInterface { public string stateIndex { get; } - public State Operate(State state); + public void Operate(State state); } } diff --git a/Goap/StateDiff/StateDiffSet.cs b/Goap/StateDiff/StateDiffSet.cs index b0561ef..a4fb078 100644 --- a/Goap/StateDiff/StateDiffSet.cs +++ b/Goap/StateDiff/StateDiffSet.cs @@ -1,4 +1,20 @@ namespace TsunagiModule.Goap { - public struct StateDiffSet { } + public struct StateDiffSet + { + public StateDiffInterface[] stateDiffes; + + public StateDiffSet(StateDiffInterface[] stateDiffes) + { + this.stateDiffes = stateDiffes; + } + + public void Apply(State state) + { + foreach (StateDiffInterface stateDiff in stateDiffes) + { + stateDiff.Operate(state); + } + } + } } From 312a1a04588f3ebf906ad525c5e613206c9f8b32 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:15:57 +0900 Subject: [PATCH 028/108] data of Action --- Goap/Action.cs | 23 +++++++++++++++++++++++ Goap/Action.cs.meta | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 Goap/Action.cs create mode 100644 Goap/Action.cs.meta diff --git a/Goap/Action.cs b/Goap/Action.cs new file mode 100644 index 0000000..20d98d3 --- /dev/null +++ b/Goap/Action.cs @@ -0,0 +1,23 @@ +namespace TsunagiModule.Goap +{ + public struct Action + { + public string name { get; private set; } + public ConditionInterface condition { get; private set; } + public StateDiffSet stateDiffSet { get; private set; } + public float cost { get; private set; } + + public Action( + string name, + ConditionInterface condition, + StateDiffSet stateDiffSet, + float cost + ) + { + this.name = name; + this.condition = condition; + this.stateDiffSet = stateDiffSet; + this.cost = cost; + } + } +} diff --git a/Goap/Action.cs.meta b/Goap/Action.cs.meta new file mode 100644 index 0000000..6a31887 --- /dev/null +++ b/Goap/Action.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 76c4afcdeb4ace943a41e9f78b871aa5 \ No newline at end of file From d6cd0c4a2e3a0263debf952afc9602eff6364c18 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:16:35 +0900 Subject: [PATCH 029/108] Add Available method to Action struct for condition satisfaction check --- Goap/Action.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Goap/Action.cs b/Goap/Action.cs index 20d98d3..f9ecc54 100644 --- a/Goap/Action.cs +++ b/Goap/Action.cs @@ -19,5 +19,10 @@ float cost this.stateDiffSet = stateDiffSet; this.cost = cost; } + + public bool Available(State state) + { + return condition.IsSatisfied(state); + } } } From fe2085efad0f4ab48d9f8fd860df5f9af54e5a59 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:18:06 +0900 Subject: [PATCH 030/108] rename --- Goap/State.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Goap/State.cs b/Goap/State.cs index 4738d45..80939ca 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -26,7 +26,7 @@ public void SetValue(string stateIndex, GoapValueInterface value) values[stateIndex] = value; } - public State Copy() + public State Clone() { return new State { values = new Dictionary(values) }; } From a30d898eb6a3555859b1567a0daf980922fb1034 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:20:03 +0900 Subject: [PATCH 031/108] simulate action --- Goap/Action.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Goap/Action.cs b/Goap/Action.cs index f9ecc54..bdee505 100644 --- a/Goap/Action.cs +++ b/Goap/Action.cs @@ -24,5 +24,20 @@ public bool Available(State state) { return condition.IsSatisfied(state); } + + public State Simulate(State state, bool overwrite = false) + { + if (overwrite) + { + stateDiffSet.Apply(state); + return state; + } + else + { + State newState = state.Clone(); + stateDiffSet.Apply(newState); + return newState; + } + } } } From 379982ad5b123c080fab55020261af5095d55f9f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:26:30 +0900 Subject: [PATCH 032/108] option for overwriting State --- Goap/Action.cs | 16 ++++++++++------ Goap/StateDiff/StateDiffInt.cs | 19 ++++++++++++++++--- Goap/StateDiff/StateDiffInterface.cs | 2 +- Goap/StateDiff/StateDiffSet.cs | 18 ++++++++++++++++-- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Goap/Action.cs b/Goap/Action.cs index bdee505..cf380d8 100644 --- a/Goap/Action.cs +++ b/Goap/Action.cs @@ -25,19 +25,23 @@ public bool Available(State state) return condition.IsSatisfied(state); } - public State Simulate(State state, bool overwrite = false) + public State Simulate(State state, bool overwrite = true) { + // cloning or not + State stateTarget; if (overwrite) { - stateDiffSet.Apply(state); - return state; + stateTarget = state; } else { - State newState = state.Clone(); - stateDiffSet.Apply(newState); - return newState; + stateTarget = state.Clone(); } + + // apply action + stateTarget = stateDiffSet.Apply(stateTarget, overwrite: true); + + return stateTarget; } } } diff --git a/Goap/StateDiff/StateDiffInt.cs b/Goap/StateDiff/StateDiffInt.cs index af3b2df..ddf0436 100644 --- a/Goap/StateDiff/StateDiffInt.cs +++ b/Goap/StateDiff/StateDiffInt.cs @@ -13,14 +13,27 @@ public StateDiffInt(string stateIndex, Func operationHandler) this.operationHandler = operationHandler; } - public void Operate(State state) + public State Operate(State state, bool overwrite = true) { + // cloning or not + State stateTarget; + if (overwrite) + { + stateTarget = state; + } + else + { + stateTarget = state.Clone(); + } + // compute value - int valueState = state.GetValue(stateIndex).GetAsInt(); + int valueState = stateTarget.GetValue(stateIndex).GetAsInt(); int newValue = operationHandler(valueState); // update state - state.SetValue(stateIndex, new GoapInt(newValue)); + stateTarget.SetValue(stateIndex, new GoapInt(newValue)); + + return stateTarget; } } } diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Goap/StateDiff/StateDiffInterface.cs index d044f57..a4c8fd1 100644 --- a/Goap/StateDiff/StateDiffInterface.cs +++ b/Goap/StateDiff/StateDiffInterface.cs @@ -3,6 +3,6 @@ namespace TsunagiModule.Goap public interface StateDiffInterface { public string stateIndex { get; } - public void Operate(State state); + public State Operate(State state, bool overwrite = true); } } diff --git a/Goap/StateDiff/StateDiffSet.cs b/Goap/StateDiff/StateDiffSet.cs index a4fb078..1cfd3b0 100644 --- a/Goap/StateDiff/StateDiffSet.cs +++ b/Goap/StateDiff/StateDiffSet.cs @@ -9,12 +9,26 @@ public StateDiffSet(StateDiffInterface[] stateDiffes) this.stateDiffes = stateDiffes; } - public void Apply(State state) + public State Apply(State state, bool overwrite = true) { + // cloning or not + State stateTarget; + if (overwrite) + { + stateTarget = state; + } + else + { + stateTarget = state.Clone(); + } + + // apply all operations foreach (StateDiffInterface stateDiff in stateDiffes) { - stateDiff.Operate(state); + stateDiff.Operate(stateTarget, overwrite: true); } + + return stateTarget; } } } From da6cab0ca2698768418ff5bfb47f0a1fe62093ca Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Tue, 22 Apr 2025 23:30:38 +0900 Subject: [PATCH 033/108] setting action --- Goap/GoapSolver.cs | 30 ++++++++++++++++++++++++++++++ Goap/GoapSolver.cs.meta | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 Goap/GoapSolver.cs create mode 100644 Goap/GoapSolver.cs.meta diff --git a/Goap/GoapSolver.cs b/Goap/GoapSolver.cs new file mode 100644 index 0000000..f7b9e01 --- /dev/null +++ b/Goap/GoapSolver.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace TsunagiModule.Goap +{ + public class GoapSolver + { + /// + /// Pool of actions + /// + /// + /// action name -> Action + /// + private Dictionary actionPool = new Dictionary(); + + public void AddAction(Action action) + { + actionPool.Add(action.name, action); + } + + public void RemoveAction(string name) + { + actionPool.Remove(name); + } + + public void ReplaceAction(string name, Action action) + { + actionPool[name] = action; + } + } +} diff --git a/Goap/GoapSolver.cs.meta b/Goap/GoapSolver.cs.meta new file mode 100644 index 0000000..7e5ab3c --- /dev/null +++ b/Goap/GoapSolver.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 196ccfb8bd4148346812874761d8c7c9 \ No newline at end of file From 0b217392b8dcd3b6a8d9cfa2abbe74590b15e9fd Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Wed, 23 Apr 2025 00:27:47 +0900 Subject: [PATCH 034/108] code checker --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8b13789..a050a70 100644 --- a/README.md +++ b/README.md @@ -1 +1,4 @@ +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/b5fe6529e18f4843876976907689c563)](https://app.codacy.com/gh/tsunagi-ai/TsunagiModuleUnity/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) + +[![Maintainability](https://qlty.sh/badges/8bcac4c3-7aeb-4c26-b2e1-6aa8b3cf8843/maintainability.svg)](https://qlty.sh/gh/tsunagi-ai/projects/TsunagiModuleUnity) \ No newline at end of file From ddbb83ebfc155e170c94fd439494439768d1d4f4 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Wed, 23 Apr 2025 00:31:55 +0900 Subject: [PATCH 035/108] move folder --- Goap/GoapSolver.meta | 8 ++++++++ .../GoapSolver_ActionControl.cs} | 2 +- .../GoapSolver_ActionControl.cs.meta} | 0 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Goap/GoapSolver.meta rename Goap/{GoapSolver.cs => GoapSolver/GoapSolver_ActionControl.cs} (94%) rename Goap/{GoapSolver.cs.meta => GoapSolver/GoapSolver_ActionControl.cs.meta} (100%) diff --git a/Goap/GoapSolver.meta b/Goap/GoapSolver.meta new file mode 100644 index 0000000..c33f273 --- /dev/null +++ b/Goap/GoapSolver.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6be95c9a7ca333c4998dae17d848b073 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/GoapSolver.cs b/Goap/GoapSolver/GoapSolver_ActionControl.cs similarity index 94% rename from Goap/GoapSolver.cs rename to Goap/GoapSolver/GoapSolver_ActionControl.cs index f7b9e01..395cf32 100644 --- a/Goap/GoapSolver.cs +++ b/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -2,7 +2,7 @@ namespace TsunagiModule.Goap { - public class GoapSolver + public partial class GoapSolver { /// /// Pool of actions diff --git a/Goap/GoapSolver.cs.meta b/Goap/GoapSolver/GoapSolver_ActionControl.cs.meta similarity index 100% rename from Goap/GoapSolver.cs.meta rename to Goap/GoapSolver/GoapSolver_ActionControl.cs.meta From df03cc70f0e41447eec153c9c56cf511581b8d48 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Wed, 23 Apr 2025 00:51:16 +0900 Subject: [PATCH 036/108] dummy Pathfinding --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 12 ++++++++++++ Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 Goap/GoapSolver/GoapSolver_Pathfinding.cs create mode 100644 Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs new file mode 100644 index 0000000..d6f3dea --- /dev/null +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -0,0 +1,12 @@ +using System; + +namespace TsunagiModule.Goap +{ + public partial class GoapSolver + { + public Action[] Solve(State stateCurrent, Condition goal) + { + throw new NotImplementedException("Solve method not implemented yet."); + } + } +} diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta b/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta new file mode 100644 index 0000000..ad0635e --- /dev/null +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b3884298cf1091944be3fc4122477e39 \ No newline at end of file From 77b2508ffe32a1b41ccbfba5599682082362b5ac Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Wed, 23 Apr 2025 00:56:28 +0900 Subject: [PATCH 037/108] temp; rebase this --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index d6f3dea..e4a3990 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -1,12 +1,20 @@ using System; +using System.Collections.Generic; namespace TsunagiModule.Goap { public partial class GoapSolver { + /// + /// Cost estimation weights for each state index + /// + private Dictionary stateIndexCostWeights = new Dictionary(); + public Action[] Solve(State stateCurrent, Condition goal) { throw new NotImplementedException("Solve method not implemented yet."); } + + private Dictionary ComputeCostWeights() { } } } From 8daac44aeca95c138aba225fad5e1ca0084503bd Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Wed, 23 Apr 2025 09:55:17 +0900 Subject: [PATCH 038/108] Add indices property to State struct for easier access to keys --- Goap/State.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Goap/State.cs b/Goap/State.cs index 80939ca..78ddbd0 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace TsunagiModule.Goap { @@ -9,6 +10,8 @@ public struct State /// private Dictionary values { get; set; } + public string[] indices => values.Keys.ToArray(); + public GoapValueInterface GetValue(string stateIndex) { if (values.TryGetValue(stateIndex, out GoapValueInterface value)) From 34cffd7c4accf48de6ef9ff82f0395f19da8c6c5 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 05:40:15 +0900 Subject: [PATCH 039/108] integrate GoapValue --- Goap/GoapValue/GoapBool.cs | 24 ------------------------ Goap/GoapValue/GoapBool.cs.meta | 2 -- Goap/GoapValue/GoapFloat.cs | 24 ------------------------ Goap/GoapValue/GoapFloat.cs.meta | 2 -- Goap/GoapValue/GoapInt.cs | 24 ------------------------ Goap/GoapValue/GoapInt.cs.meta | 2 -- Goap/GoapValue/GoapValueInterface.cs | 23 +++-------------------- Goap/GoapValue/GoapValueNumeric.cs | 22 ++++++++++++++++++++++ Goap/GoapValue/GoapValueNumeric.cs.meta | 2 ++ Goap/GoapValue/ValueType.cs | 9 --------- Goap/GoapValue/ValueType.cs.meta | 2 -- 11 files changed, 27 insertions(+), 109 deletions(-) delete mode 100644 Goap/GoapValue/GoapBool.cs delete mode 100644 Goap/GoapValue/GoapBool.cs.meta delete mode 100644 Goap/GoapValue/GoapFloat.cs delete mode 100644 Goap/GoapValue/GoapFloat.cs.meta delete mode 100644 Goap/GoapValue/GoapInt.cs delete mode 100644 Goap/GoapValue/GoapInt.cs.meta create mode 100644 Goap/GoapValue/GoapValueNumeric.cs create mode 100644 Goap/GoapValue/GoapValueNumeric.cs.meta delete mode 100644 Goap/GoapValue/ValueType.cs delete mode 100644 Goap/GoapValue/ValueType.cs.meta diff --git a/Goap/GoapValue/GoapBool.cs b/Goap/GoapValue/GoapBool.cs deleted file mode 100644 index bfae9d7..0000000 --- a/Goap/GoapValue/GoapBool.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct GoapBool : GoapValueInterface - { - public bool value { get; private set; } - - public GoapBool(bool value) - { - this.value = value; - } - - public ValueType type - { - get { return ValueType.Bool; } - } - - public bool GetAsBool() - { - return value; - } - } -} diff --git a/Goap/GoapValue/GoapBool.cs.meta b/Goap/GoapValue/GoapBool.cs.meta deleted file mode 100644 index 2f1f7e6..0000000 --- a/Goap/GoapValue/GoapBool.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 5047d5a2e4c8622429b69efd4d95b062 \ No newline at end of file diff --git a/Goap/GoapValue/GoapFloat.cs b/Goap/GoapValue/GoapFloat.cs deleted file mode 100644 index 291f73f..0000000 --- a/Goap/GoapValue/GoapFloat.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct GoapFloat : GoapValueInterface - { - public float value { get; private set; } - - public GoapFloat(float value) - { - this.value = value; - } - - public ValueType type - { - get { return ValueType.Float; } - } - - public float GetAsFloat() - { - return value; - } - } -} diff --git a/Goap/GoapValue/GoapFloat.cs.meta b/Goap/GoapValue/GoapFloat.cs.meta deleted file mode 100644 index 5de9980..0000000 --- a/Goap/GoapValue/GoapFloat.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 6c030f54c4d4f804e97f09df8a9ded5c \ No newline at end of file diff --git a/Goap/GoapValue/GoapInt.cs b/Goap/GoapValue/GoapInt.cs deleted file mode 100644 index 15f2cfd..0000000 --- a/Goap/GoapValue/GoapInt.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct GoapInt : GoapValueInterface - { - public int value { get; private set; } - - public GoapInt(int value) - { - this.value = value; - } - - public ValueType type - { - get { return ValueType.Int; } - } - - public int GetAsInt() - { - return value; - } - } -} diff --git a/Goap/GoapValue/GoapInt.cs.meta b/Goap/GoapValue/GoapInt.cs.meta deleted file mode 100644 index 1a80d97..0000000 --- a/Goap/GoapValue/GoapInt.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 002bdbf45c3b63e4fa3cc53c4649f8d2 \ No newline at end of file diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Goap/GoapValue/GoapValueInterface.cs index 349b27a..60a3fbf 100644 --- a/Goap/GoapValue/GoapValueInterface.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -8,26 +8,9 @@ namespace TsunagiModule.Goap /// /// This is implemented for support integrated control of State Control, being independent of value type. /// - public interface GoapValueInterface + public interface GoapValueInterface { - public ValueType type { get; } - public float GetAsFloat() - { - throw new NotImplementedException( - "GetAsFloat is not implemented for this GoapValue type." - ); - } - public int GetAsInt() - { - throw new NotImplementedException( - "GetAsInt is not implemented for this GoapValue type." - ); - } - public bool GetAsBool() - { - throw new NotImplementedException( - "GetAsBool is not implemented for this GoapValue type." - ); - } + public Type type { get; } + public T value { get; set; } // getter and setter for value } } diff --git a/Goap/GoapValue/GoapValueNumeric.cs b/Goap/GoapValue/GoapValueNumeric.cs new file mode 100644 index 0000000..e6a7a9d --- /dev/null +++ b/Goap/GoapValue/GoapValueNumeric.cs @@ -0,0 +1,22 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct GoapValueNumeric : GoapValueInterface, IFormattable + where T : struct, IConvertible, IComparable, IComparable, IEquatable + { + public Type type => typeof(T); + public T value { get; set; } // getter and setter for value + + public GoapValueNumeric(T value) + { + this.value = value; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + double numericValue = Convert.ToDouble(value, formatProvider); + return numericValue.ToString(format, formatProvider); + } + } +} diff --git a/Goap/GoapValue/GoapValueNumeric.cs.meta b/Goap/GoapValue/GoapValueNumeric.cs.meta new file mode 100644 index 0000000..bb3b451 --- /dev/null +++ b/Goap/GoapValue/GoapValueNumeric.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 15f7b0502366ecd4ea2a77993982f99e \ No newline at end of file diff --git a/Goap/GoapValue/ValueType.cs b/Goap/GoapValue/ValueType.cs deleted file mode 100644 index ec3115a..0000000 --- a/Goap/GoapValue/ValueType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TsunagiModule.Goap -{ - public enum ValueType - { - Int, - Float, - Bool - } -} diff --git a/Goap/GoapValue/ValueType.cs.meta b/Goap/GoapValue/ValueType.cs.meta deleted file mode 100644 index b081cc8..0000000 --- a/Goap/GoapValue/ValueType.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: abd407e8509d867419d96ee6c474c7be \ No newline at end of file From 5d204312261515a5b3549e1f69404532739c5a65 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 06:18:54 +0900 Subject: [PATCH 040/108] numeric addition --- Goap/GoapValue/GoapValueInterface.cs | 8 ++- Goap/State.cs | 9 ++- Goap/StateDiff/StateDiffAddition.cs | 66 +++++++++++++++++++ ...fInt.cs.meta => StateDiffAddition.cs.meta} | 0 Goap/StateDiff/StateDiffInt.cs | 39 ----------- 5 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 Goap/StateDiff/StateDiffAddition.cs rename Goap/StateDiff/{StateDiffInt.cs.meta => StateDiffAddition.cs.meta} (100%) delete mode 100644 Goap/StateDiff/StateDiffInt.cs diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Goap/GoapValue/GoapValueInterface.cs index 60a3fbf..632d7de 100644 --- a/Goap/GoapValue/GoapValueInterface.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -2,15 +2,19 @@ namespace TsunagiModule.Goap { + public interface GoapValueInterface + { + public Type type { get; } + } + /// /// Value used in Goap system. /// /// /// This is implemented for support integrated control of State Control, being independent of value type. /// - public interface GoapValueInterface + public interface GoapValueInterface : GoapValueInterface { - public Type type { get; } public T value { get; set; } // getter and setter for value } } diff --git a/Goap/State.cs b/Goap/State.cs index 78ddbd0..462735f 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; @@ -24,11 +25,17 @@ public GoapValueInterface GetValue(string stateIndex) } } - public void SetValue(string stateIndex, GoapValueInterface value) + public void SetValue(string stateIndex, GoapValueInterface value) { values[stateIndex] = value; } + public void SetNumericValue(string stateIndex, T value) + where T : struct, IConvertible, IComparable, IComparable, IEquatable + { + SetValue(stateIndex, new GoapValueNumeric(value)); + } + public State Clone() { return new State { values = new Dictionary(values) }; diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Goap/StateDiff/StateDiffAddition.cs new file mode 100644 index 0000000..cf25fb8 --- /dev/null +++ b/Goap/StateDiff/StateDiffAddition.cs @@ -0,0 +1,66 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct StateDiffAddition : StateDiffInterface + where T : struct, IConvertible, IComparable, IComparable, IEquatable + { + public string stateIndex { get; private set; } + public T additionValue { get; set; } + + public StateDiffAddition(string stateIndex, T additionValue) + { + this.stateIndex = stateIndex; + this.additionValue = additionValue; + } + + public State Operate(State state, bool overwrite = true) + { + // cloning or not + State stateTarget; + if (overwrite) + { + stateTarget = state; + } + else + { + stateTarget = state.Clone(); + } + + GoapValueInterface targetValueInterface = stateTarget.GetValue(stateIndex); + + // if both are same type... + if (targetValueInterface is GoapValueInterface targetValue) + { + // ...operate addtion + + // compute value + double newValueDouble = + Convert.ToDouble(targetValue.value) + Convert.ToDouble(additionValue); + + // type converting + T newValue; + if (typeof(T) == typeof(int)) + { + // int support + newValue = (T)Convert.ChangeType(Math.Round(newValueDouble), typeof(T)); + } + else + { + newValue = (T)Convert.ChangeType(newValueDouble, typeof(T)); + } + + // update state + stateTarget.SetNumericValue(stateIndex, newValue); // Updated to remove unnecessary new() + return stateTarget; + } + else + { + // ...panic + throw new ArgumentException( + "StateDiffAddition: Type mismatch in addition operation and State value type." + ); + } + } + } +} diff --git a/Goap/StateDiff/StateDiffInt.cs.meta b/Goap/StateDiff/StateDiffAddition.cs.meta similarity index 100% rename from Goap/StateDiff/StateDiffInt.cs.meta rename to Goap/StateDiff/StateDiffAddition.cs.meta diff --git a/Goap/StateDiff/StateDiffInt.cs b/Goap/StateDiff/StateDiffInt.cs deleted file mode 100644 index ddf0436..0000000 --- a/Goap/StateDiff/StateDiffInt.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct StateDiffInt : StateDiffInterface - { - public string stateIndex { get; private set; } - public Func operationHandler { get; private set; } - - public StateDiffInt(string stateIndex, Func operationHandler) - { - this.stateIndex = stateIndex; - this.operationHandler = operationHandler; - } - - public State Operate(State state, bool overwrite = true) - { - // cloning or not - State stateTarget; - if (overwrite) - { - stateTarget = state; - } - else - { - stateTarget = state.Clone(); - } - - // compute value - int valueState = stateTarget.GetValue(stateIndex).GetAsInt(); - int newValue = operationHandler(valueState); - - // update state - stateTarget.SetValue(stateIndex, new GoapInt(newValue)); - - return stateTarget; - } - } -} From 001c1a8bc6e3a6ab076c1a432ef9204f5b032c9f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 06:51:51 +0900 Subject: [PATCH 041/108] equitable value --- Goap/GoapValue/GoapValueEquitable.cs | 16 ++++++++++++++++ Goap/GoapValue/GoapValueEquitable.cs.meta | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 Goap/GoapValue/GoapValueEquitable.cs create mode 100644 Goap/GoapValue/GoapValueEquitable.cs.meta diff --git a/Goap/GoapValue/GoapValueEquitable.cs b/Goap/GoapValue/GoapValueEquitable.cs new file mode 100644 index 0000000..e60fe02 --- /dev/null +++ b/Goap/GoapValue/GoapValueEquitable.cs @@ -0,0 +1,16 @@ +using System; + +namespace TsunagiModule.Goap +{ + public struct GoapValueEquitable : GoapValueInterface + where T : struct, IEquatable + { + public Type type => typeof(T); + public T value { get; set; } // getter and setter for value + + public GoapValueEquitable(T value) + { + this.value = value; + } + } +} diff --git a/Goap/GoapValue/GoapValueEquitable.cs.meta b/Goap/GoapValue/GoapValueEquitable.cs.meta new file mode 100644 index 0000000..4e35e38 --- /dev/null +++ b/Goap/GoapValue/GoapValueEquitable.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 67e1b44ee3d9b5d49b1689e029dd3c54 \ No newline at end of file From 7a3d5b268db8fe7217142e27e1a6b7b2d845f1a9 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 06:52:03 +0900 Subject: [PATCH 042/108] better typing --- Goap/State.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Goap/State.cs b/Goap/State.cs index 462735f..b853507 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -25,7 +25,7 @@ public GoapValueInterface GetValue(string stateIndex) } } - public void SetValue(string stateIndex, GoapValueInterface value) + public void SetValue(string stateIndex, GoapValueInterface value) { values[stateIndex] = value; } From 6f3c17a044c9d13a2fbcbcab1febcc16ab58dbf9 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 06:52:09 +0900 Subject: [PATCH 043/108] mapping operation --- Goap/GoapValue/GoapValueMapping.cs | 61 +++++++++++++++++++++++++ Goap/GoapValue/GoapValueMapping.cs.meta | 2 + 2 files changed, 63 insertions(+) create mode 100644 Goap/GoapValue/GoapValueMapping.cs create mode 100644 Goap/GoapValue/GoapValueMapping.cs.meta diff --git a/Goap/GoapValue/GoapValueMapping.cs b/Goap/GoapValue/GoapValueMapping.cs new file mode 100644 index 0000000..542b963 --- /dev/null +++ b/Goap/GoapValue/GoapValueMapping.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +namespace TsunagiModule.Goap +{ + public struct StateDiffMapping : StateDiffInterface + where T : struct, IEquatable + { + public string stateIndex { get; private set; } + public Dictionary mapping { get; set; } + + public StateDiffMapping(string stateIndex, Dictionary mapping) + { + this.stateIndex = stateIndex; + this.mapping = mapping; + } + + public State Operate(State state, bool overwrite = true) + { + // cloning or not + State stateTarget; + if (overwrite) + { + stateTarget = state; + } + else + { + stateTarget = state.Clone(); + } + + GoapValueInterface targetValueInterface = stateTarget.GetValue(stateIndex); + + // if both are same type... + if (targetValueInterface is GoapValueInterface targetValue) + { + // ...operate mapping + + // if the current valus is in the map... + if (mapping.TryGetValue(targetValue.value, out T newValue)) + { + // ...update state + GoapValueInterface newGoapValue = new GoapValueEquitable(newValue); + stateTarget.SetValue(stateIndex, newGoapValue); + return stateTarget; + } + else + { + // ...do nothing + return stateTarget; + } + } + else + { + // ...panic + throw new ArgumentException( + $"State index '{stateIndex}' is not of type {typeof(T)}." + ); + } + } + } +} diff --git a/Goap/GoapValue/GoapValueMapping.cs.meta b/Goap/GoapValue/GoapValueMapping.cs.meta new file mode 100644 index 0000000..fee775e --- /dev/null +++ b/Goap/GoapValue/GoapValueMapping.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fff0d2ed0ed048045a849a28eb0dc71b \ No newline at end of file From db218695fe45c9525763b6ab72697557b0e399c0 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 06:58:43 +0900 Subject: [PATCH 044/108] integrate all values --- Goap/GoapValue/GoapValue.cs | 22 +++++++ ...lueEquitable.cs.meta => GoapValue.cs.meta} | 0 Goap/GoapValue/GoapValueEquitable.cs | 16 ----- Goap/GoapValue/GoapValueInterface.cs | 11 ---- Goap/GoapValue/GoapValueMapping.cs | 61 ------------------- Goap/GoapValue/GoapValueMapping.cs.meta | 2 - Goap/GoapValue/GoapValueNumeric.cs | 22 ------- Goap/GoapValue/GoapValueNumeric.cs.meta | 2 - 8 files changed, 22 insertions(+), 114 deletions(-) create mode 100644 Goap/GoapValue/GoapValue.cs rename Goap/GoapValue/{GoapValueEquitable.cs.meta => GoapValue.cs.meta} (100%) delete mode 100644 Goap/GoapValue/GoapValueEquitable.cs delete mode 100644 Goap/GoapValue/GoapValueMapping.cs delete mode 100644 Goap/GoapValue/GoapValueMapping.cs.meta delete mode 100644 Goap/GoapValue/GoapValueNumeric.cs delete mode 100644 Goap/GoapValue/GoapValueNumeric.cs.meta diff --git a/Goap/GoapValue/GoapValue.cs b/Goap/GoapValue/GoapValue.cs new file mode 100644 index 0000000..f17e8aa --- /dev/null +++ b/Goap/GoapValue/GoapValue.cs @@ -0,0 +1,22 @@ +using System; + +namespace TsunagiModule.Goap +{ + /// + /// Value used in Goap system. + /// + /// + /// This is implemented for support integrated control of State Control, being independent of value type. + /// + public struct GoapValue : GoapValueInterface + where T : struct + { + public Type type => typeof(T); // getter for type + public T value { get; set; } // getter and setter for value + + public GoapValue(T value) + { + this.value = value; + } + } +} diff --git a/Goap/GoapValue/GoapValueEquitable.cs.meta b/Goap/GoapValue/GoapValue.cs.meta similarity index 100% rename from Goap/GoapValue/GoapValueEquitable.cs.meta rename to Goap/GoapValue/GoapValue.cs.meta diff --git a/Goap/GoapValue/GoapValueEquitable.cs b/Goap/GoapValue/GoapValueEquitable.cs deleted file mode 100644 index e60fe02..0000000 --- a/Goap/GoapValue/GoapValueEquitable.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct GoapValueEquitable : GoapValueInterface - where T : struct, IEquatable - { - public Type type => typeof(T); - public T value { get; set; } // getter and setter for value - - public GoapValueEquitable(T value) - { - this.value = value; - } - } -} diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Goap/GoapValue/GoapValueInterface.cs index 632d7de..0d9929c 100644 --- a/Goap/GoapValue/GoapValueInterface.cs +++ b/Goap/GoapValue/GoapValueInterface.cs @@ -6,15 +6,4 @@ public interface GoapValueInterface { public Type type { get; } } - - /// - /// Value used in Goap system. - /// - /// - /// This is implemented for support integrated control of State Control, being independent of value type. - /// - public interface GoapValueInterface : GoapValueInterface - { - public T value { get; set; } // getter and setter for value - } } diff --git a/Goap/GoapValue/GoapValueMapping.cs b/Goap/GoapValue/GoapValueMapping.cs deleted file mode 100644 index 542b963..0000000 --- a/Goap/GoapValue/GoapValueMapping.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace TsunagiModule.Goap -{ - public struct StateDiffMapping : StateDiffInterface - where T : struct, IEquatable - { - public string stateIndex { get; private set; } - public Dictionary mapping { get; set; } - - public StateDiffMapping(string stateIndex, Dictionary mapping) - { - this.stateIndex = stateIndex; - this.mapping = mapping; - } - - public State Operate(State state, bool overwrite = true) - { - // cloning or not - State stateTarget; - if (overwrite) - { - stateTarget = state; - } - else - { - stateTarget = state.Clone(); - } - - GoapValueInterface targetValueInterface = stateTarget.GetValue(stateIndex); - - // if both are same type... - if (targetValueInterface is GoapValueInterface targetValue) - { - // ...operate mapping - - // if the current valus is in the map... - if (mapping.TryGetValue(targetValue.value, out T newValue)) - { - // ...update state - GoapValueInterface newGoapValue = new GoapValueEquitable(newValue); - stateTarget.SetValue(stateIndex, newGoapValue); - return stateTarget; - } - else - { - // ...do nothing - return stateTarget; - } - } - else - { - // ...panic - throw new ArgumentException( - $"State index '{stateIndex}' is not of type {typeof(T)}." - ); - } - } - } -} diff --git a/Goap/GoapValue/GoapValueMapping.cs.meta b/Goap/GoapValue/GoapValueMapping.cs.meta deleted file mode 100644 index fee775e..0000000 --- a/Goap/GoapValue/GoapValueMapping.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: fff0d2ed0ed048045a849a28eb0dc71b \ No newline at end of file diff --git a/Goap/GoapValue/GoapValueNumeric.cs b/Goap/GoapValue/GoapValueNumeric.cs deleted file mode 100644 index e6a7a9d..0000000 --- a/Goap/GoapValue/GoapValueNumeric.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace TsunagiModule.Goap -{ - public struct GoapValueNumeric : GoapValueInterface, IFormattable - where T : struct, IConvertible, IComparable, IComparable, IEquatable - { - public Type type => typeof(T); - public T value { get; set; } // getter and setter for value - - public GoapValueNumeric(T value) - { - this.value = value; - } - - public string ToString(string format, IFormatProvider formatProvider) - { - double numericValue = Convert.ToDouble(value, formatProvider); - return numericValue.ToString(format, formatProvider); - } - } -} diff --git a/Goap/GoapValue/GoapValueNumeric.cs.meta b/Goap/GoapValue/GoapValueNumeric.cs.meta deleted file mode 100644 index bb3b451..0000000 --- a/Goap/GoapValue/GoapValueNumeric.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 15f7b0502366ecd4ea2a77993982f99e \ No newline at end of file From 345405ffff78a13b9ff9793dd82f13d850941099 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 07:25:30 +0900 Subject: [PATCH 045/108] adapt --- Goap/State.cs | 5 +++-- Goap/StateDiff/StateDiffAddition.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Goap/State.cs b/Goap/State.cs index b853507..e7c4f1c 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -31,9 +31,10 @@ public void SetValue(string stateIndex, GoapValueInterface value) } public void SetNumericValue(string stateIndex, T value) - where T : struct, IConvertible, IComparable, IComparable, IEquatable + where T : struct { - SetValue(stateIndex, new GoapValueNumeric(value)); + // wrapping + SetValue(stateIndex, new GoapValue(value)); } public State Clone() diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Goap/StateDiff/StateDiffAddition.cs index cf25fb8..846c3f0 100644 --- a/Goap/StateDiff/StateDiffAddition.cs +++ b/Goap/StateDiff/StateDiffAddition.cs @@ -30,7 +30,7 @@ public State Operate(State state, bool overwrite = true) GoapValueInterface targetValueInterface = stateTarget.GetValue(stateIndex); // if both are same type... - if (targetValueInterface is GoapValueInterface targetValue) + if (targetValueInterface is GoapValue targetValue) { // ...operate addtion From 9f8c49c1179edae6e5d123829a4329dd8d85601e Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 07:29:40 +0900 Subject: [PATCH 046/108] implement StateDiffMapping struct for state value mapping operations --- Goap/StateDiff/StateDiffMapping.cs | 63 +++++++++++++++++++++++++ Goap/StateDiff/StateDiffMapping.cs.meta | 2 + 2 files changed, 65 insertions(+) create mode 100644 Goap/StateDiff/StateDiffMapping.cs create mode 100644 Goap/StateDiff/StateDiffMapping.cs.meta diff --git a/Goap/StateDiff/StateDiffMapping.cs b/Goap/StateDiff/StateDiffMapping.cs new file mode 100644 index 0000000..573a158 --- /dev/null +++ b/Goap/StateDiff/StateDiffMapping.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; + +namespace TsunagiModule.Goap +{ + public struct StateDiffMapping : StateDiffInterface + where T : struct, IEquatable + { + public string stateIndex { get; private set; } + public Dictionary mapping { get; set; } + + public StateDiffMapping(string stateIndex, Dictionary mapping) + { + this.stateIndex = stateIndex; + this.mapping = mapping; + } + + public State Operate(State state, bool overwrite = true) + { + // cloning or not + State stateTarget; + if (overwrite) + { + stateTarget = state; + } + else + { + stateTarget = state.Clone(); + } + + GoapValueInterface targetValueInterface = stateTarget.GetValue(stateIndex); + + // if both are same type... + if (targetValueInterface is GoapValue targetValue) + { + // ...operate addtion + + T currentValue = targetValue.value; + + // if the current value is in the map... + if (mapping.TryGetValue(currentValue, out T value)) + { + // ...update the state with the mapped value + stateTarget.SetNumericValue(stateIndex, value); + } + // if there isn't... + else + { + // ...do nothing + } + + return stateTarget; + } + else + { + // ...panic + throw new ArgumentException( + "StateDiffMapping: Type mismatch in mapping operation and State value type." + ); + } + } + } +} diff --git a/Goap/StateDiff/StateDiffMapping.cs.meta b/Goap/StateDiff/StateDiffMapping.cs.meta new file mode 100644 index 0000000..a1f8aa0 --- /dev/null +++ b/Goap/StateDiff/StateDiffMapping.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 33fc616dabc4fdf4b9ba1d95d7d1fde1 \ No newline at end of file From fc2500f004e55cf1b58b792c756fcde99d77f41d Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 07:32:49 +0900 Subject: [PATCH 047/108] export diffs --- Goap/StateDiff/StateDiffAddition.cs | 1 + Goap/StateDiff/StateDiffInterface.cs | 1 + Goap/StateDiff/StateDiffMapping.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Goap/StateDiff/StateDiffAddition.cs index 846c3f0..355e9da 100644 --- a/Goap/StateDiff/StateDiffAddition.cs +++ b/Goap/StateDiff/StateDiffAddition.cs @@ -7,6 +7,7 @@ public struct StateDiffAddition : StateDiffInterface { public string stateIndex { get; private set; } public T additionValue { get; set; } + public float diff => Convert.ToSingle(additionValue); public StateDiffAddition(string stateIndex, T additionValue) { diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Goap/StateDiff/StateDiffInterface.cs index a4c8fd1..5ad2a6f 100644 --- a/Goap/StateDiff/StateDiffInterface.cs +++ b/Goap/StateDiff/StateDiffInterface.cs @@ -4,5 +4,6 @@ public interface StateDiffInterface { public string stateIndex { get; } public State Operate(State state, bool overwrite = true); + public float diff { get; } } } diff --git a/Goap/StateDiff/StateDiffMapping.cs b/Goap/StateDiff/StateDiffMapping.cs index 573a158..d6e49a8 100644 --- a/Goap/StateDiff/StateDiffMapping.cs +++ b/Goap/StateDiff/StateDiffMapping.cs @@ -8,6 +8,7 @@ public struct StateDiffMapping : StateDiffInterface { public string stateIndex { get; private set; } public Dictionary mapping { get; set; } + public float diff => 1f; public StateDiffMapping(string stateIndex, Dictionary mapping) { From 5db8ff3c47d2caf12f5982f31602df93e334c678 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 07:37:52 +0900 Subject: [PATCH 048/108] refactor: implement ComputeCostWeights method for cost estimation --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index e4a3990..bbd574c 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -12,9 +12,34 @@ public partial class GoapSolver public Action[] Solve(State stateCurrent, Condition goal) { + // compute cost weights + stateIndexCostWeights = ComputeCostWeights(stateCurrent); + throw new NotImplementedException("Solve method not implemented yet."); } - private Dictionary ComputeCostWeights() { } + private Dictionary ComputeCostWeights(State stateCurrent) + { + // state index -> cost per diff + Dictionary largestCostPerDiff = new Dictionary(); + foreach (string stateIndex in stateCurrent.indices) + { + largestCostPerDiff[stateIndex] = 0f; + } + + foreach (Action action in actionPool.Values) + { + foreach (StateDiffInterface stateDiff in action.stateDiffSet.stateDiffes) + { + // get cost per diff + float costPerDiff = Math.Abs(action.cost / stateDiff.diff); + if (largestCostPerDiff[stateDiff.stateIndex] < costPerDiff) + { + largestCostPerDiff[stateDiff.stateIndex] = costPerDiff; + } + } + } + return largestCostPerDiff; + } } } From ea24debf238f4db6db5068a37ef72a48f3790c81 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 07:46:34 +0900 Subject: [PATCH 049/108] AstarQueue --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index bbd574c..c29f56b 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -3,8 +3,40 @@ namespace TsunagiModule.Goap { + //https://medium.com/@hanxuyang0826/mastering-pathfinding-with-a-star-a-practical-guide-and-c-implementation-f76f1643d8c3 public partial class GoapSolver { + private class AstarQueue : IComparable + { + public State state; + public AstarQueue parent; + public float currentCost; + public float heuristicCost; + public float totalCost => currentCost + heuristicCost; + + public AstarQueue( + State state, + AstarQueue parent, + float currentCost, + float heuristicCost + ) + { + this.state = state; + this.parent = parent; + this.currentCost = currentCost; + this.heuristicCost = heuristicCost; + } + + public int CompareTo(AstarQueue other) + { + if (totalCost < other.totalCost) + return -1; + if (totalCost > other.totalCost) + return 1; + return 0; + } + } + /// /// Cost estimation weights for each state index /// From c15f944fc1ec7578bd562f6ec2843f7c14f7f419 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:11:41 +0900 Subject: [PATCH 050/108] integrated conditioning --- Goap/Conditions/Condition.cs | 133 +++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 96d7d18..8c60336 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -2,7 +2,8 @@ namespace TsunagiModule.Goap { - public struct Condition : ConditionInterface + public struct Condition : ConditionInterface + where T : struct { public enum ConditionOperator { @@ -14,111 +15,119 @@ public enum ConditionOperator NotEqual, } - /// - /// error acceptance value for float comparison. - /// - private const float EPSILON = 0.0001f; - public string stateIndex { get; private set; } - public GoapValueInterface value { get; private set; } - public ConditionOperator conditionOperator { get; private set; } + public T valueComparing { get; set; } + public ConditionOperator conditionOperator { get; set; } - public Condition( - string stateIndex, - GoapValueInterface value, - ConditionOperator conditionOperator - ) + public Condition(string stateIndex, T valueComparing, ConditionOperator conditionOperator) { this.stateIndex = stateIndex; - this.value = value; + this.valueComparing = valueComparing; this.conditionOperator = conditionOperator; } public bool IsSatisfied(State state) { - GoapValueInterface valueComparing = state.GetValue(stateIndex); + GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); - switch (value.type) + // if type check passed... + if (valueGivenInterface is GoapValue valueGiven) { - case ValueType.Float: - return IsSatisfiedFloat(valueComparing.GetAsFloat()); - case ValueType.Int: - return IsSatisfiedInt(valueComparing.GetAsInt()); - case ValueType.Bool: - return IsSatisfiedBool(valueComparing.GetAsBool()); - default: - throw new NotImplementedException("Unknown GoapValue.ValueType"); + // ...compare value + return Compare(valueGiven.value, valueComparing); + } + // if different type... + else + { + // ...panic + throw new InvalidCastException( + $"State index '{stateIndex}' is not of type '{typeof(T)}'." + ); } } - private bool IsSatisfiedFloat(float valueComparing) + private bool Compare(T valueGiven, T valueComparing) { + // delegate to corresponding comparison method switch (conditionOperator) { + case ConditionOperator.Equal: + case ConditionOperator.NotEqual: + // if operation available... + if (valueGiven is IEquatable valueGivenEquatable) + { + // ...compare value + return CompareEquatable(valueGivenEquatable, valueComparing); + } + else + { + // ...panic + throw new InvalidOperationException( + $"Condition operator '{conditionOperator}' is not supported for type '{typeof(T)}'." + ); + } case ConditionOperator.Larger: - return value.GetAsFloat() < valueComparing; case ConditionOperator.LargerOrEqual: - return value.GetAsFloat() <= valueComparing; case ConditionOperator.Smaller: - return value.GetAsFloat() > valueComparing; case ConditionOperator.SmallerOrEqual: - return value.GetAsFloat() >= valueComparing; - case ConditionOperator.Equal: - return IsEqualApproximately(value.GetAsFloat(), valueComparing); - case ConditionOperator.NotEqual: - return !IsEqualApproximately(value.GetAsFloat(), valueComparing); + // if operation available... + if (valueGiven is IComparable valueGivenComparable) + { + // ...compare value + return CompareComparable(valueGivenComparable, valueComparing); + } + else + { + // ...panic + throw new InvalidOperationException( + $"Condition operator '{conditionOperator}' is not supported for type '{typeof(T)}'." + ); + } default: - throw new NotImplementedException("Unknown condition operator."); + // ...panic + throw new NotImplementedException( + $"Condition operator '{conditionOperator}' not implemented yet." + ); } } - private bool IsSatisfiedInt(int valueComparing) + private bool CompareEquatable(IEquatable valueGivenEquatable, T valueComparing) { switch (conditionOperator) { - case ConditionOperator.Larger: - return value.GetAsInt() < valueComparing; - case ConditionOperator.LargerOrEqual: - return value.GetAsInt() <= valueComparing; - case ConditionOperator.Smaller: - return value.GetAsInt() > valueComparing; - case ConditionOperator.SmallerOrEqual: - return value.GetAsInt() >= valueComparing; case ConditionOperator.Equal: - return value.GetAsInt() == valueComparing; + return valueGivenEquatable.Equals(valueComparing); case ConditionOperator.NotEqual: - return value.GetAsInt() != valueComparing; + return !valueGivenEquatable.Equals(valueComparing); default: - throw new NotImplementedException("Unknown condition operator."); + throw new NotImplementedException( + $"Condition operator '{conditionOperator}' not implemented yet." + ); } } - private bool IsSatisfiedBool(bool valueComparing) + private bool CompareComparable(IComparable valueGivenComparable, T valueComparing) { + // https://learn.microsoft.com/en-us/dotnet/api/system.icomparable?view=net-9.0 + // A.CompareTo(B) < 0 means A < B + // A.CompareTo(B) == 0 means A == B + // A.CompareTo(B) > 0 means A > B + switch (conditionOperator) { - case ConditionOperator.Equal: - return value.GetAsBool() == valueComparing; - case ConditionOperator.NotEqual: - return value.GetAsBool() != valueComparing; case ConditionOperator.Larger: + return valueGivenComparable.CompareTo(valueComparing) > 0; case ConditionOperator.LargerOrEqual: + return valueGivenComparable.CompareTo(valueComparing) >= 0; case ConditionOperator.Smaller: + return valueGivenComparable.CompareTo(valueComparing) < 0; case ConditionOperator.SmallerOrEqual: + return valueGivenComparable.CompareTo(valueComparing) <= 0; + default: throw new NotImplementedException( - "Condition operator is not supported for bool type." + $"Condition operator '{conditionOperator}' not implemented yet." ); - default: - throw new NotImplementedException("Unknown condition operator."); } } - - /// - /// Equality check for float values. - /// - private bool IsEqualApproximately(float a, float b) - { - return Math.Abs(a - b) < EPSILON; - } } } From bf4298bd8e535356de65ce18a1e059867e4f0074 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:12:21 +0900 Subject: [PATCH 051/108] fix: logic conditions use ConditionInterface --- Goap/Conditions/ConditionAnd.cs | 6 +++--- Goap/Conditions/ConditionNot.cs | 4 ++-- Goap/Conditions/ConditionOr.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index a9528ea..27cc74a 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -2,16 +2,16 @@ namespace TsunagiModule.Goap { public struct ConditionAnd : ConditionInterface { - public Condition[] conditions { get; private set; } + public ConditionInterface[] conditions { get; private set; } - public ConditionAnd(Condition[] conditions) + public ConditionAnd(ConditionInterface[] conditions) { this.conditions = conditions; } public bool IsSatisfied(State state) { - foreach (Condition condition in conditions) + foreach (ConditionInterface condition in conditions) { if (!condition.IsSatisfied(state)) { diff --git a/Goap/Conditions/ConditionNot.cs b/Goap/Conditions/ConditionNot.cs index c9552bc..c383cb9 100644 --- a/Goap/Conditions/ConditionNot.cs +++ b/Goap/Conditions/ConditionNot.cs @@ -2,9 +2,9 @@ namespace TsunagiModule.Goap { public struct ConditionNot : ConditionInterface { - public Condition condition { get; private set; } + public ConditionInterface condition { get; private set; } - public ConditionNot(Condition condition) + public ConditionNot(ConditionInterface condition) { this.condition = condition; } diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index dcf5377..5340889 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -2,16 +2,16 @@ namespace TsunagiModule.Goap { public struct ConditionOr : ConditionInterface { - public Condition[] conditions { get; private set; } + public ConditionInterface[] conditions { get; private set; } - public ConditionOr(Condition[] conditions) + public ConditionOr(ConditionInterface[] conditions) { this.conditions = conditions; } public bool IsSatisfied(State state) { - foreach (Condition condition in conditions) + foreach (ConditionInterface condition in conditions) { if (condition.IsSatisfied(state)) { From 23fc61428fa330676b6f13596c7424f0cca788b6 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:12:42 +0900 Subject: [PATCH 052/108] fix: update Solve method to use ConditionInterface for goal parameter --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index c29f56b..56b927d 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -42,7 +42,7 @@ public int CompareTo(AstarQueue other) /// private Dictionary stateIndexCostWeights = new Dictionary(); - public Action[] Solve(State stateCurrent, Condition goal) + public Action[] Solve(State stateCurrent, ConditionInterface goal) { // compute cost weights stateIndexCostWeights = ComputeCostWeights(stateCurrent); From 4ab5ca904d825a2dc229f66a72d0094217e58f77 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:22:51 +0900 Subject: [PATCH 053/108] distance estimation from condition --- Goap/Conditions/Condition.cs | 38 +++++++++++++++++++++++++++ Goap/Conditions/ConditionInterface.cs | 1 + 2 files changed, 39 insertions(+) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 8c60336..a708342 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -46,6 +46,44 @@ public bool IsSatisfied(State state) } } + public float EstimateDistance(State state) + { + GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); + // if type check passed... + if (valueGivenInterface is GoapValue valueGiven) + { + // bool + if (valueGiven.value is bool valueGivenBool) + { + return valueGivenBool.Equals(valueComparing) ? 1f : 0f; + } + // numeric + else if (valueGiven.value is IConvertible) + { + // convert to double + double valueGivenDouble = Convert.ToDouble(valueGiven.value); + double valueComparingDouble = Convert.ToDouble(valueComparing); + + // compute distance + return (float)Math.Abs(valueGivenDouble - valueComparingDouble); + } + else + { + throw new NotImplementedException( + $"Condition operator '{conditionOperator}' not implemented yet." + ); + } + } + // if different type... + else + { + // ...panic + throw new InvalidCastException( + $"State index '{stateIndex}' is not of type '{typeof(T)}'." + ); + } + } + private bool Compare(T valueGiven, T valueComparing) { // delegate to corresponding comparison method diff --git a/Goap/Conditions/ConditionInterface.cs b/Goap/Conditions/ConditionInterface.cs index e1416f9..1e44fee 100644 --- a/Goap/Conditions/ConditionInterface.cs +++ b/Goap/Conditions/ConditionInterface.cs @@ -3,5 +3,6 @@ namespace TsunagiModule.Goap public interface ConditionInterface { public bool IsSatisfied(State state); + public float EstimateDistance(State state); } } From 4fb51d61728778e9859dffa5058951ecc3faa5ff Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:26:09 +0900 Subject: [PATCH 054/108] distance estimation for logic conditions NotCondition is deleted because it is crazy --- Goap/Conditions/ConditionAnd.cs | 14 ++++++++++++++ Goap/Conditions/ConditionNot.cs | 17 ----------------- Goap/Conditions/ConditionNot.cs.meta | 2 -- Goap/Conditions/ConditionOr.cs | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 19 deletions(-) delete mode 100644 Goap/Conditions/ConditionNot.cs delete mode 100644 Goap/Conditions/ConditionNot.cs.meta diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 27cc74a..0246abb 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -1,3 +1,5 @@ +using System; + namespace TsunagiModule.Goap { public struct ConditionAnd : ConditionInterface @@ -20,5 +22,17 @@ public bool IsSatisfied(State state) } return true; } + + public float EstimateDistance(State state) + { + // square root of sum of squares + float sum = 0; + foreach (ConditionInterface condition in conditions) + { + float distance = condition.EstimateDistance(state); + sum += distance * distance; + } + return (float)Math.Sqrt(sum); + } } } diff --git a/Goap/Conditions/ConditionNot.cs b/Goap/Conditions/ConditionNot.cs deleted file mode 100644 index c383cb9..0000000 --- a/Goap/Conditions/ConditionNot.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace TsunagiModule.Goap -{ - public struct ConditionNot : ConditionInterface - { - public ConditionInterface condition { get; private set; } - - public ConditionNot(ConditionInterface condition) - { - this.condition = condition; - } - - public bool IsSatisfied(State state) - { - return !condition.IsSatisfied(state); - } - } -} diff --git a/Goap/Conditions/ConditionNot.cs.meta b/Goap/Conditions/ConditionNot.cs.meta deleted file mode 100644 index 8019dfb..0000000 --- a/Goap/Conditions/ConditionNot.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: b5774bdb91bd10142bf0ef0b334aa833 \ No newline at end of file diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index 5340889..f858bac 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -20,5 +20,20 @@ public bool IsSatisfied(State state) } return false; } + + public float EstimateDistance(State state) + { + // shortest distance + float min = float.MaxValue; + foreach (ConditionInterface condition in conditions) + { + float distance = condition.EstimateDistance(state); + if (distance < min) + { + min = distance; + } + } + return min; + } } } From 53bf10449017a8e7a35251a6f80a44a28078a2bd Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:28:28 +0900 Subject: [PATCH 055/108] return 0 if already satisfied --- Goap/Conditions/Condition.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index a708342..c78a322 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -48,6 +48,15 @@ public bool IsSatisfied(State state) public float EstimateDistance(State state) { + // if already satisfied... + if (IsSatisfied(state)) + { + // ... early return + return 0f; + } + + // not satisfied! + GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); // if type check passed... if (valueGivenInterface is GoapValue valueGiven) @@ -55,7 +64,7 @@ public float EstimateDistance(State state) // bool if (valueGiven.value is bool valueGivenBool) { - return valueGivenBool.Equals(valueComparing) ? 1f : 0f; + return 1f; } // numeric else if (valueGiven.value is IConvertible) From 4e4a76046e2b848092a88f787d53d96d1f432a2d Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:30:45 +0900 Subject: [PATCH 056/108] float to double --- Goap/Conditions/Condition.cs | 8 ++++---- Goap/Conditions/ConditionAnd.cs | 8 ++++---- Goap/Conditions/ConditionInterface.cs | 2 +- Goap/Conditions/ConditionOr.cs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index c78a322..6d42199 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -46,13 +46,13 @@ public bool IsSatisfied(State state) } } - public float EstimateDistance(State state) + public double EstimateDistance(State state) { // if already satisfied... if (IsSatisfied(state)) { // ... early return - return 0f; + return 0.0; } // not satisfied! @@ -64,7 +64,7 @@ public float EstimateDistance(State state) // bool if (valueGiven.value is bool valueGivenBool) { - return 1f; + return 1.0; } // numeric else if (valueGiven.value is IConvertible) @@ -74,7 +74,7 @@ public float EstimateDistance(State state) double valueComparingDouble = Convert.ToDouble(valueComparing); // compute distance - return (float)Math.Abs(valueGivenDouble - valueComparingDouble); + return Math.Abs(valueGivenDouble - valueComparingDouble); } else { diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 0246abb..56f72fe 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -23,16 +23,16 @@ public bool IsSatisfied(State state) return true; } - public float EstimateDistance(State state) + public double EstimateDistance(State state) { // square root of sum of squares - float sum = 0; + double sum = 0; foreach (ConditionInterface condition in conditions) { - float distance = condition.EstimateDistance(state); + double distance = condition.EstimateDistance(state); sum += distance * distance; } - return (float)Math.Sqrt(sum); + return Math.Sqrt(sum); } } } diff --git a/Goap/Conditions/ConditionInterface.cs b/Goap/Conditions/ConditionInterface.cs index 1e44fee..29d5b31 100644 --- a/Goap/Conditions/ConditionInterface.cs +++ b/Goap/Conditions/ConditionInterface.cs @@ -3,6 +3,6 @@ namespace TsunagiModule.Goap public interface ConditionInterface { public bool IsSatisfied(State state); - public float EstimateDistance(State state); + public double EstimateDistance(State state); } } diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index f858bac..bd0e4c0 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -21,13 +21,13 @@ public bool IsSatisfied(State state) return false; } - public float EstimateDistance(State state) + public double EstimateDistance(State state) { // shortest distance - float min = float.MaxValue; + double min = double.MaxValue; foreach (ConditionInterface condition in conditions) { - float distance = condition.EstimateDistance(state); + double distance = condition.EstimateDistance(state); if (distance < min) { min = distance; From c294be55e178f4222d338cc91ee2fa3d313c3ea8 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:33:52 +0900 Subject: [PATCH 057/108] all cost to double --- Goap/Action.cs | 4 ++-- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Goap/Action.cs b/Goap/Action.cs index cf380d8..5b320f9 100644 --- a/Goap/Action.cs +++ b/Goap/Action.cs @@ -5,13 +5,13 @@ public struct Action public string name { get; private set; } public ConditionInterface condition { get; private set; } public StateDiffSet stateDiffSet { get; private set; } - public float cost { get; private set; } + public double cost { get; private set; } public Action( string name, ConditionInterface condition, StateDiffSet stateDiffSet, - float cost + double cost ) { this.name = name; diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 56b927d..8367a09 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -10,15 +10,15 @@ private class AstarQueue : IComparable { public State state; public AstarQueue parent; - public float currentCost; - public float heuristicCost; - public float totalCost => currentCost + heuristicCost; + public double currentCost; + public double heuristicCost; + public double totalCost => currentCost + heuristicCost; public AstarQueue( State state, AstarQueue parent, - float currentCost, - float heuristicCost + double currentCost, + double heuristicCost ) { this.state = state; @@ -40,7 +40,7 @@ public int CompareTo(AstarQueue other) /// /// Cost estimation weights for each state index /// - private Dictionary stateIndexCostWeights = new Dictionary(); + private Dictionary stateIndexCostWeights = new Dictionary(); public Action[] Solve(State stateCurrent, ConditionInterface goal) { @@ -50,13 +50,13 @@ public Action[] Solve(State stateCurrent, ConditionInterface goal) throw new NotImplementedException("Solve method not implemented yet."); } - private Dictionary ComputeCostWeights(State stateCurrent) + private Dictionary ComputeCostWeights(State stateCurrent) { // state index -> cost per diff - Dictionary largestCostPerDiff = new Dictionary(); + Dictionary largestCostPerDiff = new Dictionary(); foreach (string stateIndex in stateCurrent.indices) { - largestCostPerDiff[stateIndex] = 0f; + largestCostPerDiff[stateIndex] = 0.0; } foreach (Action action in actionPool.Values) @@ -64,7 +64,7 @@ private Dictionary ComputeCostWeights(State stateCurrent) foreach (StateDiffInterface stateDiff in action.stateDiffSet.stateDiffes) { // get cost per diff - float costPerDiff = Math.Abs(action.cost / stateDiff.diff); + double costPerDiff = Math.Abs(action.cost / stateDiff.diff); if (largestCostPerDiff[stateDiff.stateIndex] < costPerDiff) { largestCostPerDiff[stateDiff.stateIndex] = costPerDiff; From acde42bd9fa735dc551e64fe8124433313068059 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 08:35:18 +0900 Subject: [PATCH 058/108] cost estimation --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 8367a09..63f12a2 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -73,5 +73,10 @@ private Dictionary ComputeCostWeights(State stateCurrent) } return largestCostPerDiff; } + + private double EstimateCost(State state, ConditionInterface goal) + { + return goal.EstimateDistance(state); + } } } From ee97fd37d9c881515352b75ae10e12c8877a697c Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 09:14:50 +0900 Subject: [PATCH 059/108] hashing State --- Goap/GoapValue/GoapValue.cs | 14 ++++++++++++-- Goap/State.cs | 26 +++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Goap/GoapValue/GoapValue.cs b/Goap/GoapValue/GoapValue.cs index f17e8aa..43c7610 100644 --- a/Goap/GoapValue/GoapValue.cs +++ b/Goap/GoapValue/GoapValue.cs @@ -8,8 +8,8 @@ namespace TsunagiModule.Goap /// /// This is implemented for support integrated control of State Control, being independent of value type. /// - public struct GoapValue : GoapValueInterface - where T : struct + public struct GoapValue : GoapValueInterface, IEquatable> + where T : struct, IEquatable { public Type type => typeof(T); // getter for type public T value { get; set; } // getter and setter for value @@ -18,5 +18,15 @@ public GoapValue(T value) { this.value = value; } + + public bool Equals(GoapValue other) + { + return value.Equals(other.value); + } + + public override int GetHashCode() + { + return value.GetHashCode(); + } } } diff --git a/Goap/State.cs b/Goap/State.cs index e7c4f1c..66f2a1d 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -4,7 +4,7 @@ namespace TsunagiModule.Goap { - public struct State + public struct State : IEquatable { /// /// Main body of state vector @@ -37,8 +37,32 @@ public void SetNumericValue(string stateIndex, T value) SetValue(stateIndex, new GoapValue(value)); } + public bool Equals(State other) + { + // HACK: there could be a better data structure + // since length of dictionary tends not to be too large, + // this is fast enough. + return values.SequenceEqual(other.values); + } + + public override int GetHashCode() + { + // HACK: there could be a better data structure + // since length of dictionary tends not to be too large, + // hash collision is not likely to happen. + + int hash = 17; + foreach (var pair in values) + { + hash = hash * 31 + pair.Key.GetHashCode(); + hash = hash * 31 + pair.Value.GetHashCode(); + } + return hash; + } + public State Clone() { + // HACK: there could be a better data structure return new State { values = new Dictionary(values) }; } } From 376002df8c0ebe33db82e8f95d311c7ac904790c Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 09:16:52 +0900 Subject: [PATCH 060/108] setting value --- Goap/State.cs | 4 ++-- Goap/StateDiff/StateDiffAddition.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Goap/State.cs b/Goap/State.cs index 66f2a1d..e742db7 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -30,8 +30,8 @@ public void SetValue(string stateIndex, GoapValueInterface value) values[stateIndex] = value; } - public void SetNumericValue(string stateIndex, T value) - where T : struct + public void SetValue(string stateIndex, T value) + where T : struct, IEquatable { // wrapping SetValue(stateIndex, new GoapValue(value)); diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Goap/StateDiff/StateDiffAddition.cs index 355e9da..d0a5dde 100644 --- a/Goap/StateDiff/StateDiffAddition.cs +++ b/Goap/StateDiff/StateDiffAddition.cs @@ -52,7 +52,7 @@ public State Operate(State state, bool overwrite = true) } // update state - stateTarget.SetNumericValue(stateIndex, newValue); // Updated to remove unnecessary new() + stateTarget.SetValue(stateIndex, newValue); // Updated to remove unnecessary new() return stateTarget; } else From e75aed0ad76e74226c04fee998f139e155a1a32f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 09:20:55 +0900 Subject: [PATCH 061/108] fix type --- Goap/Conditions/Condition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 6d42199..3bfb84d 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -3,7 +3,7 @@ namespace TsunagiModule.Goap { public struct Condition : ConditionInterface - where T : struct + where T : struct, IEquatable { public enum ConditionOperator { From 0b775a213ff9ca456a9c26ba883f36da6d816308 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 09:55:19 +0900 Subject: [PATCH 062/108] solving A* --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 81 ++++++++++++++++++++++- Goap/Utils.meta | 8 +++ Goap/Utils/PriorityQueue.cs | 73 ++++++++++++++++++++ Goap/Utils/PriorityQueue.cs.meta | 2 + 4 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 Goap/Utils.meta create mode 100644 Goap/Utils/PriorityQueue.cs create mode 100644 Goap/Utils/PriorityQueue.cs.meta diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 63f12a2..b84a94f 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using TsunagiModule.Goap.Utils; namespace TsunagiModule.Goap { @@ -10,21 +11,25 @@ private class AstarQueue : IComparable { public State state; public AstarQueue parent; + public Action? action; public double currentCost; public double heuristicCost; public double totalCost => currentCost + heuristicCost; + public int depth => parent == null ? 0 : parent.depth + 1; public AstarQueue( State state, AstarQueue parent, double currentCost, - double heuristicCost + double heuristicCost, + Action? action ) { this.state = state; this.parent = parent; this.currentCost = currentCost; this.heuristicCost = heuristicCost; + this.action = action; } public int CompareTo(AstarQueue other) @@ -42,12 +47,12 @@ public int CompareTo(AstarQueue other) /// private Dictionary stateIndexCostWeights = new Dictionary(); - public Action[] Solve(State stateCurrent, ConditionInterface goal) + public Action[] Solve(State stateCurrent, ConditionInterface goal, int maxLength = 10) { // compute cost weights stateIndexCostWeights = ComputeCostWeights(stateCurrent); - throw new NotImplementedException("Solve method not implemented yet."); + return SolveAstar(stateCurrent, goal, maxLength); } private Dictionary ComputeCostWeights(State stateCurrent) @@ -74,6 +79,76 @@ private Dictionary ComputeCostWeights(State stateCurrent) return largestCostPerDiff; } + private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int maxDepth) + { + PriorityQueue queue = new PriorityQueue(); + HashSet closedSet = new HashSet(); + + // starting point + queue.Enqueue( + new AstarQueue(stateCurrent, null, 0, EstimateCost(stateCurrent, goal), null) + ); + + while (queue.Count > 0) + { + // to next node + AstarQueue current = queue.Dequeue(); + + // if already closed... + if (closedSet.Contains(current.state)) + { + // ...skip + continue; + } + + // if arrived at goal... + if (goal.IsSatisfied(current.state)) + { + // ...return the Action path + List actions = new List(); + while (current.action != null) + { + actions.Add(current.action ?? throw new InvalidOperationException()); + current = current.parent; + } + actions.Reverse(); + return actions.ToArray(); + } + + // close the current node + closedSet.Add(current.state); + + // if not arrived to the max depth... + if (current.depth < maxDepth) + { + // ...find next nodes + foreach (Action action in actionPool.Values) + { + // if the action is available... + if (action.condition.IsSatisfied(stateCurrent)) + { + // ...go to this state + State nextState = action.Simulate(current.state, overwrite: false); + double costCurrent = current.currentCost + action.cost; + double costEstimated = EstimateCost(nextState, goal); + queue.Enqueue( + new AstarQueue( + nextState, + current, + costCurrent, + costEstimated, + action + ) + ); + } + } + } + } + + // Return empty array if no solution is found + return new Action[0]; + } + private double EstimateCost(State state, ConditionInterface goal) { return goal.EstimateDistance(state); diff --git a/Goap/Utils.meta b/Goap/Utils.meta new file mode 100644 index 0000000..f614288 --- /dev/null +++ b/Goap/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 97249b122072eaa48a0c498015dbc899 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap/Utils/PriorityQueue.cs b/Goap/Utils/PriorityQueue.cs new file mode 100644 index 0000000..a434a72 --- /dev/null +++ b/Goap/Utils/PriorityQueue.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; + +namespace TsunagiModule.Goap.Utils +{ + public class PriorityQueue + where T : IComparable + { + private List elements = new List(); + + public int Count => elements.Count; + + public void Enqueue(T item) + { + elements.Add(item); + int childIndex = elements.Count - 1; + while (childIndex > 0) + { + int parentIndex = (childIndex - 1) / 2; + if (elements[childIndex].CompareTo(elements[parentIndex]) >= 0) + break; + + T temp = elements[childIndex]; + elements[childIndex] = elements[parentIndex]; + elements[parentIndex] = temp; + + childIndex = parentIndex; + } + } + + public T Dequeue() + { + if (elements.Count == 0) + throw new InvalidOperationException("The priority queue is empty."); + + T result = elements[0]; + int lastIndex = elements.Count - 1; + elements[0] = elements[lastIndex]; + elements.RemoveAt(lastIndex); + + int parentIndex = 0; + while (true) + { + int leftChildIndex = 2 * parentIndex + 1; + int rightChildIndex = 2 * parentIndex + 2; + int smallestIndex = parentIndex; + + if ( + leftChildIndex < elements.Count + && elements[leftChildIndex].CompareTo(elements[smallestIndex]) < 0 + ) + smallestIndex = leftChildIndex; + + if ( + rightChildIndex < elements.Count + && elements[rightChildIndex].CompareTo(elements[smallestIndex]) < 0 + ) + smallestIndex = rightChildIndex; + + if (smallestIndex == parentIndex) + break; + + T temp = elements[parentIndex]; + elements[parentIndex] = elements[smallestIndex]; + elements[smallestIndex] = temp; + + parentIndex = smallestIndex; + } + + return result; + } + } +} diff --git a/Goap/Utils/PriorityQueue.cs.meta b/Goap/Utils/PriorityQueue.cs.meta new file mode 100644 index 0000000..d903722 --- /dev/null +++ b/Goap/Utils/PriorityQueue.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 101d9294a56e00e43bbfa6afc096e367 \ No newline at end of file From 103e9191eb3506a95477748dcbdeeddf22dbb01e Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 10:06:36 +0900 Subject: [PATCH 063/108] receiving costPerDiff dictionary --- Goap/Conditions/Condition.cs | 25 +++++++++++++++++++---- Goap/Conditions/ConditionAnd.cs | 7 ++++--- Goap/Conditions/ConditionInterface.cs | 4 +++- Goap/Conditions/ConditionOr.cs | 10 +++++---- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 3bfb84d..7d95bed 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace TsunagiModule.Goap { @@ -46,7 +47,7 @@ public bool IsSatisfied(State state) } } - public double EstimateDistance(State state) + public double EstimateCost(State state, Dictionary costPerDiffes = null) { // if already satisfied... if (IsSatisfied(state)) @@ -57,14 +58,28 @@ public double EstimateDistance(State state) // not satisfied! + // get weight + double costPerDiff = 1.0; + // if corresponding weight given... + if ( + (costPerDiffes != null) + && costPerDiffes.TryGetValue(stateIndex, out double weightFound) + ) + { + // ...use it + costPerDiff = weightFound; + } + + // compute distance + double distance = 0.0; GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); // if type check passed... if (valueGivenInterface is GoapValue valueGiven) { // bool - if (valueGiven.value is bool valueGivenBool) + if (valueGiven.value is bool) { - return 1.0; + distance = 1.0; } // numeric else if (valueGiven.value is IConvertible) @@ -74,7 +89,7 @@ public double EstimateDistance(State state) double valueComparingDouble = Convert.ToDouble(valueComparing); // compute distance - return Math.Abs(valueGivenDouble - valueComparingDouble); + distance = Math.Abs(valueGivenDouble - valueComparingDouble); } else { @@ -91,6 +106,8 @@ public double EstimateDistance(State state) $"State index '{stateIndex}' is not of type '{typeof(T)}'." ); } + + return distance * costPerDiff; } private bool Compare(T valueGiven, T valueComparing) diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 56f72fe..5730eaf 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace TsunagiModule.Goap { @@ -23,14 +24,14 @@ public bool IsSatisfied(State state) return true; } - public double EstimateDistance(State state) + public double EstimateCost(State state, Dictionary costPerDiffes = null) { // square root of sum of squares double sum = 0; foreach (ConditionInterface condition in conditions) { - double distance = condition.EstimateDistance(state); - sum += distance * distance; + double cost = condition.EstimateCost(state, costPerDiffes: costPerDiffes); + sum += cost * cost; } return Math.Sqrt(sum); } diff --git a/Goap/Conditions/ConditionInterface.cs b/Goap/Conditions/ConditionInterface.cs index 29d5b31..857ca86 100644 --- a/Goap/Conditions/ConditionInterface.cs +++ b/Goap/Conditions/ConditionInterface.cs @@ -1,8 +1,10 @@ +using System.Collections.Generic; + namespace TsunagiModule.Goap { public interface ConditionInterface { public bool IsSatisfied(State state); - public double EstimateDistance(State state); + public double EstimateCost(State state, Dictionary costPerDiffes = null); } } diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index bd0e4c0..b904323 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace TsunagiModule.Goap { public struct ConditionOr : ConditionInterface @@ -21,16 +23,16 @@ public bool IsSatisfied(State state) return false; } - public double EstimateDistance(State state) + public double EstimateCost(State state, Dictionary costPerDiffes = null) { // shortest distance double min = double.MaxValue; foreach (ConditionInterface condition in conditions) { - double distance = condition.EstimateDistance(state); - if (distance < min) + double cost = condition.EstimateCost(state, costPerDiffes: costPerDiffes); + if (cost < min) { - min = distance; + min = cost; } } return min; diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index b84a94f..5bc134c 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -151,7 +151,7 @@ private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int max private double EstimateCost(State state, ConditionInterface goal) { - return goal.EstimateDistance(state); + return goal.EstimateCost(state); } } } From 3218f897987185b28af9a34968214c3aaf9d1c66 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 10:09:20 +0900 Subject: [PATCH 064/108] Passing cost weights --- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 5bc134c..01ac4fc 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -45,12 +45,12 @@ public int CompareTo(AstarQueue other) /// /// Cost estimation weights for each state index /// - private Dictionary stateIndexCostWeights = new Dictionary(); + private Dictionary costPerDiffes = new Dictionary(); public Action[] Solve(State stateCurrent, ConditionInterface goal, int maxLength = 10) { // compute cost weights - stateIndexCostWeights = ComputeCostWeights(stateCurrent); + costPerDiffes = ComputeCostWeights(stateCurrent); return SolveAstar(stateCurrent, goal, maxLength); } @@ -151,7 +151,7 @@ private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int max private double EstimateCost(State state, ConditionInterface goal) { - return goal.EstimateCost(state); + return goal.EstimateCost(state, costPerDiffes: costPerDiffes); } } } From 90d7e8d352d9251cb0ec6f75a0ee9205f334ab1b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 10:11:51 +0900 Subject: [PATCH 065/108] fix: update state setting method in StateDiffMapping --- Goap/StateDiff/StateDiffMapping.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Goap/StateDiff/StateDiffMapping.cs b/Goap/StateDiff/StateDiffMapping.cs index d6e49a8..79831a2 100644 --- a/Goap/StateDiff/StateDiffMapping.cs +++ b/Goap/StateDiff/StateDiffMapping.cs @@ -42,7 +42,7 @@ public State Operate(State state, bool overwrite = true) if (mapping.TryGetValue(currentValue, out T value)) { // ...update the state with the mapped value - stateTarget.SetNumericValue(stateIndex, value); + stateTarget.SetValue(stateIndex, value); } // if there isn't... else From ddf491493edfa06c5a75934cff05a30851a355e3 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 10:53:13 +0900 Subject: [PATCH 066/108] constructor for State --- Goap/State.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Goap/State.cs b/Goap/State.cs index e742db7..dd74380 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -13,6 +13,11 @@ public struct State : IEquatable public string[] indices => values.Keys.ToArray(); + public State(Dictionary values) + { + this.values = values; + } + public GoapValueInterface GetValue(string stateIndex) { if (values.TryGetValue(stateIndex, out GoapValueInterface value)) From 11a76a2c67e1bded441e40426f2775dacb415e61 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 10:56:53 +0900 Subject: [PATCH 067/108] delete State constructor it couldn't receive multiple types --- Goap/State.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Goap/State.cs b/Goap/State.cs index dd74380..6c3aff7 100644 --- a/Goap/State.cs +++ b/Goap/State.cs @@ -13,9 +13,16 @@ public struct State : IEquatable public string[] indices => values.Keys.ToArray(); - public State(Dictionary values) + public State(Dictionary values = null) { - this.values = values; + if (values == null) + { + this.values = new Dictionary(); + } + else + { + this.values = values; + } } public GoapValueInterface GetValue(string stateIndex) From b470f438e8c53f72be2ecbc2392371da9e584ea9 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:00:32 +0900 Subject: [PATCH 068/108] rename State -> GoapState conflict with Unity namespace --- Goap/Action.cs | 6 +++--- Goap/Conditions/Condition.cs | 4 ++-- Goap/Conditions/ConditionAnd.cs | 4 ++-- Goap/Conditions/ConditionInterface.cs | 7 +++++-- Goap/Conditions/ConditionOr.cs | 4 ++-- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 16 ++++++++-------- Goap/{State.cs => GoapState.cs} | 10 +++++----- Goap/{State.cs.meta => GoapState.cs.meta} | 0 Goap/StateDiff/StateDiffAddition.cs | 4 ++-- Goap/StateDiff/StateDiffInterface.cs | 2 +- Goap/StateDiff/StateDiffMapping.cs | 4 ++-- Goap/StateDiff/StateDiffSet.cs | 4 ++-- 12 files changed, 34 insertions(+), 31 deletions(-) rename Goap/{State.cs => GoapState.cs} (87%) rename Goap/{State.cs.meta => GoapState.cs.meta} (100%) diff --git a/Goap/Action.cs b/Goap/Action.cs index 5b320f9..6b1c647 100644 --- a/Goap/Action.cs +++ b/Goap/Action.cs @@ -20,15 +20,15 @@ double cost this.cost = cost; } - public bool Available(State state) + public bool Available(GoapState state) { return condition.IsSatisfied(state); } - public State Simulate(State state, bool overwrite = true) + public GoapState Simulate(GoapState state, bool overwrite = true) { // cloning or not - State stateTarget; + GoapState stateTarget; if (overwrite) { stateTarget = state; diff --git a/Goap/Conditions/Condition.cs b/Goap/Conditions/Condition.cs index 7d95bed..c391709 100644 --- a/Goap/Conditions/Condition.cs +++ b/Goap/Conditions/Condition.cs @@ -27,7 +27,7 @@ public Condition(string stateIndex, T valueComparing, ConditionOperator conditio this.conditionOperator = conditionOperator; } - public bool IsSatisfied(State state) + public bool IsSatisfied(GoapState state) { GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); @@ -47,7 +47,7 @@ public bool IsSatisfied(State state) } } - public double EstimateCost(State state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // if already satisfied... if (IsSatisfied(state)) diff --git a/Goap/Conditions/ConditionAnd.cs b/Goap/Conditions/ConditionAnd.cs index 5730eaf..a2aa733 100644 --- a/Goap/Conditions/ConditionAnd.cs +++ b/Goap/Conditions/ConditionAnd.cs @@ -12,7 +12,7 @@ public ConditionAnd(ConditionInterface[] conditions) this.conditions = conditions; } - public bool IsSatisfied(State state) + public bool IsSatisfied(GoapState state) { foreach (ConditionInterface condition in conditions) { @@ -24,7 +24,7 @@ public bool IsSatisfied(State state) return true; } - public double EstimateCost(State state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // square root of sum of squares double sum = 0; diff --git a/Goap/Conditions/ConditionInterface.cs b/Goap/Conditions/ConditionInterface.cs index 857ca86..311b0e7 100644 --- a/Goap/Conditions/ConditionInterface.cs +++ b/Goap/Conditions/ConditionInterface.cs @@ -4,7 +4,10 @@ namespace TsunagiModule.Goap { public interface ConditionInterface { - public bool IsSatisfied(State state); - public double EstimateCost(State state, Dictionary costPerDiffes = null); + public bool IsSatisfied(GoapState state); + public double EstimateCost( + GoapState state, + Dictionary costPerDiffes = null + ); } } diff --git a/Goap/Conditions/ConditionOr.cs b/Goap/Conditions/ConditionOr.cs index b904323..1782aef 100644 --- a/Goap/Conditions/ConditionOr.cs +++ b/Goap/Conditions/ConditionOr.cs @@ -11,7 +11,7 @@ public ConditionOr(ConditionInterface[] conditions) this.conditions = conditions; } - public bool IsSatisfied(State state) + public bool IsSatisfied(GoapState state) { foreach (ConditionInterface condition in conditions) { @@ -23,7 +23,7 @@ public bool IsSatisfied(State state) return false; } - public double EstimateCost(State state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // shortest distance double min = double.MaxValue; diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 01ac4fc..1408f4c 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -9,7 +9,7 @@ public partial class GoapSolver { private class AstarQueue : IComparable { - public State state; + public GoapState state; public AstarQueue parent; public Action? action; public double currentCost; @@ -18,7 +18,7 @@ private class AstarQueue : IComparable public int depth => parent == null ? 0 : parent.depth + 1; public AstarQueue( - State state, + GoapState state, AstarQueue parent, double currentCost, double heuristicCost, @@ -47,7 +47,7 @@ public int CompareTo(AstarQueue other) /// private Dictionary costPerDiffes = new Dictionary(); - public Action[] Solve(State stateCurrent, ConditionInterface goal, int maxLength = 10) + public Action[] Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) { // compute cost weights costPerDiffes = ComputeCostWeights(stateCurrent); @@ -55,7 +55,7 @@ public Action[] Solve(State stateCurrent, ConditionInterface goal, int maxLength return SolveAstar(stateCurrent, goal, maxLength); } - private Dictionary ComputeCostWeights(State stateCurrent) + private Dictionary ComputeCostWeights(GoapState stateCurrent) { // state index -> cost per diff Dictionary largestCostPerDiff = new Dictionary(); @@ -79,10 +79,10 @@ private Dictionary ComputeCostWeights(State stateCurrent) return largestCostPerDiff; } - private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int maxDepth) + private Action[] SolveAstar(GoapState stateCurrent, ConditionInterface goal, int maxDepth) { PriorityQueue queue = new PriorityQueue(); - HashSet closedSet = new HashSet(); + HashSet closedSet = new HashSet(); // starting point queue.Enqueue( @@ -128,7 +128,7 @@ private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int max if (action.condition.IsSatisfied(stateCurrent)) { // ...go to this state - State nextState = action.Simulate(current.state, overwrite: false); + GoapState nextState = action.Simulate(current.state, overwrite: false); double costCurrent = current.currentCost + action.cost; double costEstimated = EstimateCost(nextState, goal); queue.Enqueue( @@ -149,7 +149,7 @@ private Action[] SolveAstar(State stateCurrent, ConditionInterface goal, int max return new Action[0]; } - private double EstimateCost(State state, ConditionInterface goal) + private double EstimateCost(GoapState state, ConditionInterface goal) { return goal.EstimateCost(state, costPerDiffes: costPerDiffes); } diff --git a/Goap/State.cs b/Goap/GoapState.cs similarity index 87% rename from Goap/State.cs rename to Goap/GoapState.cs index 6c3aff7..a0f68ad 100644 --- a/Goap/State.cs +++ b/Goap/GoapState.cs @@ -4,7 +4,7 @@ namespace TsunagiModule.Goap { - public struct State : IEquatable + public struct GoapState : IEquatable { /// /// Main body of state vector @@ -13,7 +13,7 @@ public struct State : IEquatable public string[] indices => values.Keys.ToArray(); - public State(Dictionary values = null) + public GoapState(Dictionary values = null) { if (values == null) { @@ -49,7 +49,7 @@ public void SetValue(string stateIndex, T value) SetValue(stateIndex, new GoapValue(value)); } - public bool Equals(State other) + public bool Equals(GoapState other) { // HACK: there could be a better data structure // since length of dictionary tends not to be too large, @@ -72,10 +72,10 @@ public override int GetHashCode() return hash; } - public State Clone() + public GoapState Clone() { // HACK: there could be a better data structure - return new State { values = new Dictionary(values) }; + return new GoapState { values = new Dictionary(values) }; } } } diff --git a/Goap/State.cs.meta b/Goap/GoapState.cs.meta similarity index 100% rename from Goap/State.cs.meta rename to Goap/GoapState.cs.meta diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Goap/StateDiff/StateDiffAddition.cs index d0a5dde..7da6480 100644 --- a/Goap/StateDiff/StateDiffAddition.cs +++ b/Goap/StateDiff/StateDiffAddition.cs @@ -15,10 +15,10 @@ public StateDiffAddition(string stateIndex, T additionValue) this.additionValue = additionValue; } - public State Operate(State state, bool overwrite = true) + public GoapState Operate(GoapState state, bool overwrite = true) { // cloning or not - State stateTarget; + GoapState stateTarget; if (overwrite) { stateTarget = state; diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Goap/StateDiff/StateDiffInterface.cs index 5ad2a6f..ed8fee0 100644 --- a/Goap/StateDiff/StateDiffInterface.cs +++ b/Goap/StateDiff/StateDiffInterface.cs @@ -3,7 +3,7 @@ namespace TsunagiModule.Goap public interface StateDiffInterface { public string stateIndex { get; } - public State Operate(State state, bool overwrite = true); + public GoapState Operate(GoapState state, bool overwrite = true); public float diff { get; } } } diff --git a/Goap/StateDiff/StateDiffMapping.cs b/Goap/StateDiff/StateDiffMapping.cs index 79831a2..12bdbfe 100644 --- a/Goap/StateDiff/StateDiffMapping.cs +++ b/Goap/StateDiff/StateDiffMapping.cs @@ -16,10 +16,10 @@ public StateDiffMapping(string stateIndex, Dictionary mapping) this.mapping = mapping; } - public State Operate(State state, bool overwrite = true) + public GoapState Operate(GoapState state, bool overwrite = true) { // cloning or not - State stateTarget; + GoapState stateTarget; if (overwrite) { stateTarget = state; diff --git a/Goap/StateDiff/StateDiffSet.cs b/Goap/StateDiff/StateDiffSet.cs index 1cfd3b0..92a24a9 100644 --- a/Goap/StateDiff/StateDiffSet.cs +++ b/Goap/StateDiff/StateDiffSet.cs @@ -9,10 +9,10 @@ public StateDiffSet(StateDiffInterface[] stateDiffes) this.stateDiffes = stateDiffes; } - public State Apply(State state, bool overwrite = true) + public GoapState Apply(GoapState state, bool overwrite = true) { // cloning or not - State stateTarget; + GoapState stateTarget; if (overwrite) { stateTarget = state; From 3a1317b666dcc0b7bc2ccda651f27adac7195a5c Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:01:15 +0900 Subject: [PATCH 069/108] rename Action -> GoapAction avoiding conflict --- Goap/{Action.cs => GoapAction.cs} | 4 ++-- Goap/{Action.cs.meta => GoapAction.cs.meta} | 0 Goap/GoapSolver/GoapSolver_ActionControl.cs | 6 +++--- Goap/GoapSolver/GoapSolver_Pathfinding.cs | 24 ++++++++++++++------- 4 files changed, 21 insertions(+), 13 deletions(-) rename Goap/{Action.cs => GoapAction.cs} (95%) rename Goap/{Action.cs.meta => GoapAction.cs.meta} (100%) diff --git a/Goap/Action.cs b/Goap/GoapAction.cs similarity index 95% rename from Goap/Action.cs rename to Goap/GoapAction.cs index 6b1c647..3a48b1e 100644 --- a/Goap/Action.cs +++ b/Goap/GoapAction.cs @@ -1,13 +1,13 @@ namespace TsunagiModule.Goap { - public struct Action + public struct GoapAction { public string name { get; private set; } public ConditionInterface condition { get; private set; } public StateDiffSet stateDiffSet { get; private set; } public double cost { get; private set; } - public Action( + public GoapAction( string name, ConditionInterface condition, StateDiffSet stateDiffSet, diff --git a/Goap/Action.cs.meta b/Goap/GoapAction.cs.meta similarity index 100% rename from Goap/Action.cs.meta rename to Goap/GoapAction.cs.meta diff --git a/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Goap/GoapSolver/GoapSolver_ActionControl.cs index 395cf32..139df27 100644 --- a/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -10,9 +10,9 @@ public partial class GoapSolver /// /// action name -> Action /// - private Dictionary actionPool = new Dictionary(); + private Dictionary actionPool = new Dictionary(); - public void AddAction(Action action) + public void AddAction(GoapAction action) { actionPool.Add(action.name, action); } @@ -22,7 +22,7 @@ public void RemoveAction(string name) actionPool.Remove(name); } - public void ReplaceAction(string name, Action action) + public void ReplaceAction(string name, GoapAction action) { actionPool[name] = action; } diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 1408f4c..e5979d8 100644 --- a/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -11,7 +11,7 @@ private class AstarQueue : IComparable { public GoapState state; public AstarQueue parent; - public Action? action; + public GoapAction? action; public double currentCost; public double heuristicCost; public double totalCost => currentCost + heuristicCost; @@ -22,7 +22,7 @@ public AstarQueue( AstarQueue parent, double currentCost, double heuristicCost, - Action? action + GoapAction? action ) { this.state = state; @@ -47,7 +47,11 @@ public int CompareTo(AstarQueue other) /// private Dictionary costPerDiffes = new Dictionary(); - public Action[] Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) + public GoapAction[] Solve( + GoapState stateCurrent, + ConditionInterface goal, + int maxLength = 10 + ) { // compute cost weights costPerDiffes = ComputeCostWeights(stateCurrent); @@ -64,7 +68,7 @@ private Dictionary ComputeCostWeights(GoapState stateCurrent) largestCostPerDiff[stateIndex] = 0.0; } - foreach (Action action in actionPool.Values) + foreach (GoapAction action in actionPool.Values) { foreach (StateDiffInterface stateDiff in action.stateDiffSet.stateDiffes) { @@ -79,7 +83,11 @@ private Dictionary ComputeCostWeights(GoapState stateCurrent) return largestCostPerDiff; } - private Action[] SolveAstar(GoapState stateCurrent, ConditionInterface goal, int maxDepth) + private GoapAction[] SolveAstar( + GoapState stateCurrent, + ConditionInterface goal, + int maxDepth + ) { PriorityQueue queue = new PriorityQueue(); HashSet closedSet = new HashSet(); @@ -105,7 +113,7 @@ private Action[] SolveAstar(GoapState stateCurrent, ConditionInterface goal, int if (goal.IsSatisfied(current.state)) { // ...return the Action path - List actions = new List(); + List actions = new List(); while (current.action != null) { actions.Add(current.action ?? throw new InvalidOperationException()); @@ -122,7 +130,7 @@ private Action[] SolveAstar(GoapState stateCurrent, ConditionInterface goal, int if (current.depth < maxDepth) { // ...find next nodes - foreach (Action action in actionPool.Values) + foreach (GoapAction action in actionPool.Values) { // if the action is available... if (action.condition.IsSatisfied(stateCurrent)) @@ -146,7 +154,7 @@ private Action[] SolveAstar(GoapState stateCurrent, ConditionInterface goal, int } // Return empty array if no solution is found - return new Action[0]; + return new GoapAction[0]; } private double EstimateCost(GoapState state, ConditionInterface goal) From a62aeee9e1bb7f6770f58b8b28d38670fa2b4233 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:12:51 +0900 Subject: [PATCH 070/108] create scripts directory --- Scripts.meta | 8 ++++++++ Goap.meta => Scripts/Goap.meta | 0 {Goap => Scripts/Goap}/Conditions.meta | 0 {Goap => Scripts/Goap}/Conditions/Condition.cs | 0 {Goap => Scripts/Goap}/Conditions/Condition.cs.meta | 0 {Goap => Scripts/Goap}/Conditions/ConditionAnd.cs | 0 {Goap => Scripts/Goap}/Conditions/ConditionAnd.cs.meta | 0 {Goap => Scripts/Goap}/Conditions/ConditionInterface.cs | 0 .../Goap}/Conditions/ConditionInterface.cs.meta | 0 {Goap => Scripts/Goap}/Conditions/ConditionOr.cs | 0 {Goap => Scripts/Goap}/Conditions/ConditionOr.cs.meta | 0 {Goap => Scripts/Goap}/GoapAction.cs | 0 {Goap => Scripts/Goap}/GoapAction.cs.meta | 0 {Goap => Scripts/Goap}/GoapSolver.meta | 0 .../Goap}/GoapSolver/GoapSolver_ActionControl.cs | 0 .../Goap}/GoapSolver/GoapSolver_ActionControl.cs.meta | 0 .../Goap}/GoapSolver/GoapSolver_Pathfinding.cs | 0 .../Goap}/GoapSolver/GoapSolver_Pathfinding.cs.meta | 0 {Goap => Scripts/Goap}/GoapState.cs | 0 {Goap => Scripts/Goap}/GoapState.cs.meta | 0 {Goap => Scripts/Goap}/GoapValue.meta | 0 {Goap => Scripts/Goap}/GoapValue/GoapValue.cs | 0 {Goap => Scripts/Goap}/GoapValue/GoapValue.cs.meta | 0 {Goap => Scripts/Goap}/GoapValue/GoapValueInterface.cs | 0 .../Goap}/GoapValue/GoapValueInterface.cs.meta | 0 {Goap => Scripts/Goap}/StateDiff.meta | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffAddition.cs | 0 .../Goap}/StateDiff/StateDiffAddition.cs.meta | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffInterface.cs | 0 .../Goap}/StateDiff/StateDiffInterface.cs.meta | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffMapping.cs | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffMapping.cs.meta | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffSet.cs | 0 {Goap => Scripts/Goap}/StateDiff/StateDiffSet.cs.meta | 0 {Goap => Scripts/Goap}/Utils.meta | 0 {Goap => Scripts/Goap}/Utils/PriorityQueue.cs | 0 {Goap => Scripts/Goap}/Utils/PriorityQueue.cs.meta | 0 37 files changed, 8 insertions(+) create mode 100644 Scripts.meta rename Goap.meta => Scripts/Goap.meta (100%) rename {Goap => Scripts/Goap}/Conditions.meta (100%) rename {Goap => Scripts/Goap}/Conditions/Condition.cs (100%) rename {Goap => Scripts/Goap}/Conditions/Condition.cs.meta (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionAnd.cs (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionAnd.cs.meta (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionInterface.cs (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionInterface.cs.meta (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionOr.cs (100%) rename {Goap => Scripts/Goap}/Conditions/ConditionOr.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapAction.cs (100%) rename {Goap => Scripts/Goap}/GoapAction.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapSolver.meta (100%) rename {Goap => Scripts/Goap}/GoapSolver/GoapSolver_ActionControl.cs (100%) rename {Goap => Scripts/Goap}/GoapSolver/GoapSolver_ActionControl.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapSolver/GoapSolver_Pathfinding.cs (100%) rename {Goap => Scripts/Goap}/GoapSolver/GoapSolver_Pathfinding.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapState.cs (100%) rename {Goap => Scripts/Goap}/GoapState.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapValue.meta (100%) rename {Goap => Scripts/Goap}/GoapValue/GoapValue.cs (100%) rename {Goap => Scripts/Goap}/GoapValue/GoapValue.cs.meta (100%) rename {Goap => Scripts/Goap}/GoapValue/GoapValueInterface.cs (100%) rename {Goap => Scripts/Goap}/GoapValue/GoapValueInterface.cs.meta (100%) rename {Goap => Scripts/Goap}/StateDiff.meta (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffAddition.cs (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffAddition.cs.meta (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffInterface.cs (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffInterface.cs.meta (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffMapping.cs (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffMapping.cs.meta (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffSet.cs (100%) rename {Goap => Scripts/Goap}/StateDiff/StateDiffSet.cs.meta (100%) rename {Goap => Scripts/Goap}/Utils.meta (100%) rename {Goap => Scripts/Goap}/Utils/PriorityQueue.cs (100%) rename {Goap => Scripts/Goap}/Utils/PriorityQueue.cs.meta (100%) diff --git a/Scripts.meta b/Scripts.meta new file mode 100644 index 0000000..5a3d76f --- /dev/null +++ b/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c76e76e5b0e590a44875d223b3d70618 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Goap.meta b/Scripts/Goap.meta similarity index 100% rename from Goap.meta rename to Scripts/Goap.meta diff --git a/Goap/Conditions.meta b/Scripts/Goap/Conditions.meta similarity index 100% rename from Goap/Conditions.meta rename to Scripts/Goap/Conditions.meta diff --git a/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs similarity index 100% rename from Goap/Conditions/Condition.cs rename to Scripts/Goap/Conditions/Condition.cs diff --git a/Goap/Conditions/Condition.cs.meta b/Scripts/Goap/Conditions/Condition.cs.meta similarity index 100% rename from Goap/Conditions/Condition.cs.meta rename to Scripts/Goap/Conditions/Condition.cs.meta diff --git a/Goap/Conditions/ConditionAnd.cs b/Scripts/Goap/Conditions/ConditionAnd.cs similarity index 100% rename from Goap/Conditions/ConditionAnd.cs rename to Scripts/Goap/Conditions/ConditionAnd.cs diff --git a/Goap/Conditions/ConditionAnd.cs.meta b/Scripts/Goap/Conditions/ConditionAnd.cs.meta similarity index 100% rename from Goap/Conditions/ConditionAnd.cs.meta rename to Scripts/Goap/Conditions/ConditionAnd.cs.meta diff --git a/Goap/Conditions/ConditionInterface.cs b/Scripts/Goap/Conditions/ConditionInterface.cs similarity index 100% rename from Goap/Conditions/ConditionInterface.cs rename to Scripts/Goap/Conditions/ConditionInterface.cs diff --git a/Goap/Conditions/ConditionInterface.cs.meta b/Scripts/Goap/Conditions/ConditionInterface.cs.meta similarity index 100% rename from Goap/Conditions/ConditionInterface.cs.meta rename to Scripts/Goap/Conditions/ConditionInterface.cs.meta diff --git a/Goap/Conditions/ConditionOr.cs b/Scripts/Goap/Conditions/ConditionOr.cs similarity index 100% rename from Goap/Conditions/ConditionOr.cs rename to Scripts/Goap/Conditions/ConditionOr.cs diff --git a/Goap/Conditions/ConditionOr.cs.meta b/Scripts/Goap/Conditions/ConditionOr.cs.meta similarity index 100% rename from Goap/Conditions/ConditionOr.cs.meta rename to Scripts/Goap/Conditions/ConditionOr.cs.meta diff --git a/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs similarity index 100% rename from Goap/GoapAction.cs rename to Scripts/Goap/GoapAction.cs diff --git a/Goap/GoapAction.cs.meta b/Scripts/Goap/GoapAction.cs.meta similarity index 100% rename from Goap/GoapAction.cs.meta rename to Scripts/Goap/GoapAction.cs.meta diff --git a/Goap/GoapSolver.meta b/Scripts/Goap/GoapSolver.meta similarity index 100% rename from Goap/GoapSolver.meta rename to Scripts/Goap/GoapSolver.meta diff --git a/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs similarity index 100% rename from Goap/GoapSolver/GoapSolver_ActionControl.cs rename to Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs diff --git a/Goap/GoapSolver/GoapSolver_ActionControl.cs.meta b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs.meta similarity index 100% rename from Goap/GoapSolver/GoapSolver_ActionControl.cs.meta rename to Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs.meta diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs similarity index 100% rename from Goap/GoapSolver/GoapSolver_Pathfinding.cs rename to Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs diff --git a/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta similarity index 100% rename from Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta rename to Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs.meta diff --git a/Goap/GoapState.cs b/Scripts/Goap/GoapState.cs similarity index 100% rename from Goap/GoapState.cs rename to Scripts/Goap/GoapState.cs diff --git a/Goap/GoapState.cs.meta b/Scripts/Goap/GoapState.cs.meta similarity index 100% rename from Goap/GoapState.cs.meta rename to Scripts/Goap/GoapState.cs.meta diff --git a/Goap/GoapValue.meta b/Scripts/Goap/GoapValue.meta similarity index 100% rename from Goap/GoapValue.meta rename to Scripts/Goap/GoapValue.meta diff --git a/Goap/GoapValue/GoapValue.cs b/Scripts/Goap/GoapValue/GoapValue.cs similarity index 100% rename from Goap/GoapValue/GoapValue.cs rename to Scripts/Goap/GoapValue/GoapValue.cs diff --git a/Goap/GoapValue/GoapValue.cs.meta b/Scripts/Goap/GoapValue/GoapValue.cs.meta similarity index 100% rename from Goap/GoapValue/GoapValue.cs.meta rename to Scripts/Goap/GoapValue/GoapValue.cs.meta diff --git a/Goap/GoapValue/GoapValueInterface.cs b/Scripts/Goap/GoapValue/GoapValueInterface.cs similarity index 100% rename from Goap/GoapValue/GoapValueInterface.cs rename to Scripts/Goap/GoapValue/GoapValueInterface.cs diff --git a/Goap/GoapValue/GoapValueInterface.cs.meta b/Scripts/Goap/GoapValue/GoapValueInterface.cs.meta similarity index 100% rename from Goap/GoapValue/GoapValueInterface.cs.meta rename to Scripts/Goap/GoapValue/GoapValueInterface.cs.meta diff --git a/Goap/StateDiff.meta b/Scripts/Goap/StateDiff.meta similarity index 100% rename from Goap/StateDiff.meta rename to Scripts/Goap/StateDiff.meta diff --git a/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs similarity index 100% rename from Goap/StateDiff/StateDiffAddition.cs rename to Scripts/Goap/StateDiff/StateDiffAddition.cs diff --git a/Goap/StateDiff/StateDiffAddition.cs.meta b/Scripts/Goap/StateDiff/StateDiffAddition.cs.meta similarity index 100% rename from Goap/StateDiff/StateDiffAddition.cs.meta rename to Scripts/Goap/StateDiff/StateDiffAddition.cs.meta diff --git a/Goap/StateDiff/StateDiffInterface.cs b/Scripts/Goap/StateDiff/StateDiffInterface.cs similarity index 100% rename from Goap/StateDiff/StateDiffInterface.cs rename to Scripts/Goap/StateDiff/StateDiffInterface.cs diff --git a/Goap/StateDiff/StateDiffInterface.cs.meta b/Scripts/Goap/StateDiff/StateDiffInterface.cs.meta similarity index 100% rename from Goap/StateDiff/StateDiffInterface.cs.meta rename to Scripts/Goap/StateDiff/StateDiffInterface.cs.meta diff --git a/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs similarity index 100% rename from Goap/StateDiff/StateDiffMapping.cs rename to Scripts/Goap/StateDiff/StateDiffMapping.cs diff --git a/Goap/StateDiff/StateDiffMapping.cs.meta b/Scripts/Goap/StateDiff/StateDiffMapping.cs.meta similarity index 100% rename from Goap/StateDiff/StateDiffMapping.cs.meta rename to Scripts/Goap/StateDiff/StateDiffMapping.cs.meta diff --git a/Goap/StateDiff/StateDiffSet.cs b/Scripts/Goap/StateDiff/StateDiffSet.cs similarity index 100% rename from Goap/StateDiff/StateDiffSet.cs rename to Scripts/Goap/StateDiff/StateDiffSet.cs diff --git a/Goap/StateDiff/StateDiffSet.cs.meta b/Scripts/Goap/StateDiff/StateDiffSet.cs.meta similarity index 100% rename from Goap/StateDiff/StateDiffSet.cs.meta rename to Scripts/Goap/StateDiff/StateDiffSet.cs.meta diff --git a/Goap/Utils.meta b/Scripts/Goap/Utils.meta similarity index 100% rename from Goap/Utils.meta rename to Scripts/Goap/Utils.meta diff --git a/Goap/Utils/PriorityQueue.cs b/Scripts/Goap/Utils/PriorityQueue.cs similarity index 100% rename from Goap/Utils/PriorityQueue.cs rename to Scripts/Goap/Utils/PriorityQueue.cs diff --git a/Goap/Utils/PriorityQueue.cs.meta b/Scripts/Goap/Utils/PriorityQueue.cs.meta similarity index 100% rename from Goap/Utils/PriorityQueue.cs.meta rename to Scripts/Goap/Utils/PriorityQueue.cs.meta From 462d3ed88ccfdc31b2688ba7443252cc5cdc34bc Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:34:27 +0900 Subject: [PATCH 071/108] condition that always true --- Scripts/Goap/Conditions/NoCondition.cs | 17 +++++++++++++++++ Scripts/Goap/Conditions/NoCondition.cs.meta | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 Scripts/Goap/Conditions/NoCondition.cs create mode 100644 Scripts/Goap/Conditions/NoCondition.cs.meta diff --git a/Scripts/Goap/Conditions/NoCondition.cs b/Scripts/Goap/Conditions/NoCondition.cs new file mode 100644 index 0000000..221fba9 --- /dev/null +++ b/Scripts/Goap/Conditions/NoCondition.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace TsunagiModule.Goap +{ + public struct NoCondition : ConditionInterface + { + public bool IsSatisfied(GoapState state) + { + return true; + } + + public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) + { + return 0; + } + } +} diff --git a/Scripts/Goap/Conditions/NoCondition.cs.meta b/Scripts/Goap/Conditions/NoCondition.cs.meta new file mode 100644 index 0000000..188ccdb --- /dev/null +++ b/Scripts/Goap/Conditions/NoCondition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fa2a2ec7aa07b9542948720f4f4f2e5b \ No newline at end of file From 30eccb7a0f01ccd0b680c668be6e4ba28209e942 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:38:55 +0900 Subject: [PATCH 072/108] accept passing diff list directly --- Scripts/Goap/GoapAction.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Scripts/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs index 3a48b1e..e1c35e7 100644 --- a/Scripts/Goap/GoapAction.cs +++ b/Scripts/Goap/GoapAction.cs @@ -20,6 +20,19 @@ double cost this.cost = cost; } + public GoapAction( + string name, + ConditionInterface condition, + StateDiffInterface[] stateDiffSet, + double cost + ) + { + this.name = name; + this.condition = condition; + this.stateDiffSet = new StateDiffSet(stateDiffSet); + this.cost = cost; + } + public bool Available(GoapState state) { return condition.IsSatisfied(state); From 86578f9ea72f09815071bec5a5d18425477f3765 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:43:08 +0900 Subject: [PATCH 073/108] seperate ConditionOperator --- Scripts/Goap/Conditions/Condition.cs | 10 ---------- Scripts/Goap/Conditions/ConditionOperator.cs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 Scripts/Goap/Conditions/ConditionOperator.cs diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index c391709..e891006 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -6,16 +6,6 @@ namespace TsunagiModule.Goap public struct Condition : ConditionInterface where T : struct, IEquatable { - public enum ConditionOperator - { - Larger, - LargerOrEqual, - Smaller, - SmallerOrEqual, - Equal, - NotEqual, - } - public string stateIndex { get; private set; } public T valueComparing { get; set; } public ConditionOperator conditionOperator { get; set; } diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs b/Scripts/Goap/Conditions/ConditionOperator.cs new file mode 100644 index 0000000..6c3474c --- /dev/null +++ b/Scripts/Goap/Conditions/ConditionOperator.cs @@ -0,0 +1,12 @@ +namespace TsunagiModule.Goap +{ + public enum ConditionOperator + { + Larger, + LargerOrEqual, + Smaller, + SmallerOrEqual, + Equal, + NotEqual, + } +} From 79e0aa78c3129af82e63efd8473adfe8c07ad75c Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 11:47:32 +0900 Subject: [PATCH 074/108] intruitive order --- Scripts/Goap/Conditions/Condition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index e891006..95ec3c3 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -10,7 +10,7 @@ public struct Condition : ConditionInterface public T valueComparing { get; set; } public ConditionOperator conditionOperator { get; set; } - public Condition(string stateIndex, T valueComparing, ConditionOperator conditionOperator) + public Condition(string stateIndex, ConditionOperator conditionOperator, T valueComparing) { this.stateIndex = stateIndex; this.valueComparing = valueComparing; From 50783d7fdfa7abb620baf1e1ddf5e95e4862ade0 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 12:03:27 +0900 Subject: [PATCH 075/108] test code --- .../Goap/Conditions/ConditionOperator.cs.meta | 2 + Tests.asmdef | 21 +++ Tests.asmdef.meta | 7 + Tests.meta | 8 + Tests/GoapTest.cs | 173 ++++++++++++++++++ Tests/GoapTest.cs.meta | 2 + 6 files changed, 213 insertions(+) create mode 100644 Scripts/Goap/Conditions/ConditionOperator.cs.meta create mode 100644 Tests.asmdef create mode 100644 Tests.asmdef.meta create mode 100644 Tests.meta create mode 100644 Tests/GoapTest.cs create mode 100644 Tests/GoapTest.cs.meta diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs.meta b/Scripts/Goap/Conditions/ConditionOperator.cs.meta new file mode 100644 index 0000000..6c6af6c --- /dev/null +++ b/Scripts/Goap/Conditions/ConditionOperator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8698ddb9307e0094bae9fe705539db66 \ No newline at end of file diff --git a/Tests.asmdef b/Tests.asmdef new file mode 100644 index 0000000..aa17583 --- /dev/null +++ b/Tests.asmdef @@ -0,0 +1,21 @@ +{ + "name": "Tests", + "rootNamespace": "", + "references": [ + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Tests.asmdef.meta b/Tests.asmdef.meta new file mode 100644 index 0000000..656e015 --- /dev/null +++ b/Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1c6cce9f195ef6c479540225cff2ec90 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests.meta b/Tests.meta new file mode 100644 index 0000000..3c9590c --- /dev/null +++ b/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c67e08ee36fbe4f41a957c7694801f98 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs new file mode 100644 index 0000000..e06d62e --- /dev/null +++ b/Tests/GoapTest.cs @@ -0,0 +1,173 @@ +using System.Collections.Generic; +using NUnit.Framework; +using TsunagiModule.Goap; + +public class GoapTest +{ + private const string ACTION_1 = "#1 increase int"; + private const string ACTION_2 = "#2 increase float"; + private const string ACTION_3 = "#3 increase double and switch boolean"; + + [Test] + public void GoapTestSimple() + { + Condition goal = new Condition("int", ConditionOperator.LargerOrEqual, 2); + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + + //run + GoapAction[] actions = solver.Solve(state, goal, 10); + + // #1 -> #1 + Assert.AreEqual(actions.Length, 2); + Assert.AreEqual(actions[0].name, ACTION_1); + Assert.AreEqual(actions[1].name, ACTION_1); + } + + [Test] + public void GoapTestPathFinding() + { + Condition goal = new Condition( + "float", + ConditionOperator.LargerOrEqual, + 0.5f + ); + + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + + //run + GoapAction[] actions = solver.Solve(state, goal, 10); + + // #1 -> #1 -> #1 -> #2 + Assert.AreEqual(actions.Length, 4); + Assert.AreEqual(actions[0].name, ACTION_1); + Assert.AreEqual(actions[1].name, ACTION_1); + Assert.AreEqual(actions[2].name, ACTION_1); + Assert.AreEqual(actions[3].name, ACTION_2); + } + + [Test] + public void GoapTestMapping() + { + ConditionInterface goal = new ConditionAnd( + new ConditionInterface[] + { + new Condition("double", ConditionOperator.LargerOrEqual, 0.1), + new Condition("boolean", ConditionOperator.Equal, true) + } + ); + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + + GoapAction[] actions = solver.Solve(state, goal, 10); + + // #3 -> #3 + Assert.AreEqual(actions.Length, 2); + Assert.AreEqual(actions[0].name, ACTION_3); + Assert.AreEqual(actions[1].name, ACTION_3); + } + + [Test] + public void GoapTestBetterCost() + { + const string ACTION_X = "#X int + 100 (cost 100)"; + + Condition goal = new Condition("int", ConditionOperator.Equal, 3); + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + solver.AddAction( + new GoapAction( + ACTION_X, + new NoCondition(), + new StateDiffInterface[] { new StateDiffAddition("int", 100) }, + 100.0 + ) + ); + + //run + GoapAction[] actions = solver.Solve(state, goal, 10); + + // #1 -> #1 -> #1 + Assert.AreEqual(actions.Length, 3); + Assert.AreEqual(actions[0].name, ACTION_1); + Assert.AreEqual(actions[1].name, ACTION_1); + ; + Assert.AreEqual(actions[2].name, ACTION_1); + } + + [Test] + public void GoapTestTooDeep() + { + Condition goal = new Condition("int", ConditionOperator.Equal, 100); + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + + //run + GoapAction[] actions = solver.Solve(state, goal, 10); + + // empty + Assert.AreEqual(actions.Length, 0); + } + + [Test] + public void GoapTestImpossible() + { + Condition goal = new Condition("int", ConditionOperator.Equal, -1); + GoapState state = GenerateState(); + GoapSolver solver = GenerateSolverWithActionPool(); + + //run + GoapAction[] actions = solver.Solve(state, goal, 10); + + // empty + Assert.AreEqual(actions.Length, 0); + } + + private GoapState GenerateState() + { + GoapState state = new GoapState(); + state.SetValue("int", 0); + state.SetValue("float", 0f); + state.SetValue("double", 0); + state.SetValue("boolean", false); + return state; + } + + private GoapSolver GenerateSolverWithActionPool() + { + GoapSolver solver = new GoapSolver(); + solver.AddAction( + new GoapAction( + ACTION_1, + new NoCondition(), + new StateDiffInterface[] { new StateDiffAddition("int", 1) }, + 1.0 + ) + ); + solver.AddAction( + new GoapAction( + ACTION_2, + new Condition("int", ConditionOperator.Larger, 2), + new StateDiffInterface[] { new StateDiffAddition("float", 1f) }, + 2.0 + ) + ); + solver.AddAction( + new GoapAction( + ACTION_3, + new NoCondition(), + new StateDiffInterface[] + { + new StateDiffAddition("double", 1.0), + new StateDiffMapping( + "boolean", + new Dictionary() { { false, true }, { true, false } } + ) + }, + 3.0 + ) + ); + return solver; + } +} diff --git a/Tests/GoapTest.cs.meta b/Tests/GoapTest.cs.meta new file mode 100644 index 0000000..d58b26a --- /dev/null +++ b/Tests/GoapTest.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 54bf1392730ce124d92d928539d9158c \ No newline at end of file From 3390e299364afd51b1087e1b116361bef3153393 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Thu, 24 Apr 2025 12:50:30 +0900 Subject: [PATCH 076/108] test settings --- Tests.asmdef | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests.asmdef b/Tests.asmdef index aa17583..02091dd 100644 --- a/Tests.asmdef +++ b/Tests.asmdef @@ -5,7 +5,9 @@ "UnityEngine.TestRunner", "UnityEditor.TestRunner" ], - "includePlatforms": [], + "includePlatforms": [ + "Editor" + ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, From 33515992e2f98b8e0f6430f1813759b58ccb74ca Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:23:49 +0900 Subject: [PATCH 077/108] rename func conflict with two same-named functions --- Scripts/Goap/GoapState.cs | 2 +- Scripts/Goap/StateDiff/StateDiffAddition.cs | 2 +- Scripts/Goap/StateDiff/StateDiffMapping.cs | 2 +- Tests/GoapTest.cs | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Scripts/Goap/GoapState.cs b/Scripts/Goap/GoapState.cs index a0f68ad..6283650 100644 --- a/Scripts/Goap/GoapState.cs +++ b/Scripts/Goap/GoapState.cs @@ -42,7 +42,7 @@ public void SetValue(string stateIndex, GoapValueInterface value) values[stateIndex] = value; } - public void SetValue(string stateIndex, T value) + public void SetRawValue(string stateIndex, T value) where T : struct, IEquatable { // wrapping diff --git a/Scripts/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs index 7da6480..7079951 100644 --- a/Scripts/Goap/StateDiff/StateDiffAddition.cs +++ b/Scripts/Goap/StateDiff/StateDiffAddition.cs @@ -52,7 +52,7 @@ public GoapState Operate(GoapState state, bool overwrite = true) } // update state - stateTarget.SetValue(stateIndex, newValue); // Updated to remove unnecessary new() + stateTarget.SetRawValue(stateIndex, newValue); return stateTarget; } else diff --git a/Scripts/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs index 12bdbfe..d42bad2 100644 --- a/Scripts/Goap/StateDiff/StateDiffMapping.cs +++ b/Scripts/Goap/StateDiff/StateDiffMapping.cs @@ -42,7 +42,7 @@ public GoapState Operate(GoapState state, bool overwrite = true) if (mapping.TryGetValue(currentValue, out T value)) { // ...update the state with the mapped value - stateTarget.SetValue(stateIndex, value); + stateTarget.SetRawValue(stateIndex, value); } // if there isn't... else diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index e06d62e..7de1b73 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -127,10 +127,10 @@ public void GoapTestImpossible() private GoapState GenerateState() { GoapState state = new GoapState(); - state.SetValue("int", 0); - state.SetValue("float", 0f); - state.SetValue("double", 0); - state.SetValue("boolean", false); + state.SetRawValue("int", 0); + state.SetRawValue("float", 0f); + state.SetRawValue("double", 0); + state.SetRawValue("boolean", false); return state; } From 97293e6d7509104a80c06b7db00646cf47d2bd8e Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:30:06 +0900 Subject: [PATCH 078/108] null guard --- Scripts/Goap/GoapState.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Scripts/Goap/GoapState.cs b/Scripts/Goap/GoapState.cs index 6283650..70c692a 100644 --- a/Scripts/Goap/GoapState.cs +++ b/Scripts/Goap/GoapState.cs @@ -13,20 +13,15 @@ public struct GoapState : IEquatable public string[] indices => values.Keys.ToArray(); - public GoapState(Dictionary values = null) + public GoapState(Dictionary values) { - if (values == null) - { - this.values = new Dictionary(); - } - else - { - this.values = values; - } + this.values = values; } public GoapValueInterface GetValue(string stateIndex) { + GuardValueNull(); + if (values.TryGetValue(stateIndex, out GoapValueInterface value)) { return value; @@ -39,6 +34,8 @@ public GoapValueInterface GetValue(string stateIndex) public void SetValue(string stateIndex, GoapValueInterface value) { + GuardValueNull(); + values[stateIndex] = value; } @@ -51,6 +48,8 @@ public void SetRawValue(string stateIndex, T value) public bool Equals(GoapState other) { + GuardValueNull(); + // HACK: there could be a better data structure // since length of dictionary tends not to be too large, // this is fast enough. @@ -77,5 +76,13 @@ public GoapState Clone() // HACK: there could be a better data structure return new GoapState { values = new Dictionary(values) }; } + + private void GuardValueNull() + { + if (values == null) + { + values = new Dictionary(); + } + } } } From b40cb3a7f779edbaf6f23444dcd00d67c23cf158 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:33:09 +0900 Subject: [PATCH 079/108] fix type --- Tests/GoapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index 7de1b73..8a0c3a2 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -129,7 +129,7 @@ private GoapState GenerateState() GoapState state = new GoapState(); state.SetRawValue("int", 0); state.SetRawValue("float", 0f); - state.SetRawValue("double", 0); + state.SetRawValue("double", 0.0); state.SetRawValue("boolean", false); return state; } From a9835a84ec379b9f9b9b0e9755db0c70f146c69a Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:38:52 +0900 Subject: [PATCH 080/108] fix goal --- Tests/GoapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index 8a0c3a2..450416e 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -54,7 +54,7 @@ public void GoapTestMapping() new ConditionInterface[] { new Condition("double", ConditionOperator.LargerOrEqual, 0.1), - new Condition("boolean", ConditionOperator.Equal, true) + new Condition("boolean", ConditionOperator.Equal, false) } ); GoapState state = GenerateState(); From 2cafb497cce5c9cc7b12ad67c8a8d8a840418218 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:47:56 +0900 Subject: [PATCH 081/108] GoapResult --- Scripts/Goap/GoapSolver/GoapResult.cs | 17 ++++++++++++ Scripts/Goap/GoapSolver/GoapResult.cs.meta | 2 ++ .../Goap/GoapSolver/GoapSolver_Pathfinding.cs | 27 ++++++++++--------- 3 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 Scripts/Goap/GoapSolver/GoapResult.cs create mode 100644 Scripts/Goap/GoapSolver/GoapResult.cs.meta diff --git a/Scripts/Goap/GoapSolver/GoapResult.cs b/Scripts/Goap/GoapSolver/GoapResult.cs new file mode 100644 index 0000000..87f70b5 --- /dev/null +++ b/Scripts/Goap/GoapSolver/GoapResult.cs @@ -0,0 +1,17 @@ +namespace TsunagiModule.Goap +{ + public struct GoapResult + { + public GoapAction[] actions; + public double cost; + public int length => actions.Length; + public bool success; + + public GoapResult(GoapAction[] actions, double cost, bool success) + { + this.actions = actions; + this.cost = cost; + this.success = success; + } + } +} diff --git a/Scripts/Goap/GoapSolver/GoapResult.cs.meta b/Scripts/Goap/GoapSolver/GoapResult.cs.meta new file mode 100644 index 0000000..65c6a73 --- /dev/null +++ b/Scripts/Goap/GoapSolver/GoapResult.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 50aecb45af83a2c43a6638fd3ca3fd3f \ No newline at end of file diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index e5979d8..a0b97de 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -47,11 +47,7 @@ public int CompareTo(AstarQueue other) /// private Dictionary costPerDiffes = new Dictionary(); - public GoapAction[] Solve( - GoapState stateCurrent, - ConditionInterface goal, - int maxLength = 10 - ) + public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) { // compute cost weights costPerDiffes = ComputeCostWeights(stateCurrent); @@ -83,11 +79,7 @@ private Dictionary ComputeCostWeights(GoapState stateCurrent) return largestCostPerDiff; } - private GoapAction[] SolveAstar( - GoapState stateCurrent, - ConditionInterface goal, - int maxDepth - ) + private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, int maxDepth) { PriorityQueue queue = new PriorityQueue(); HashSet closedSet = new HashSet(); @@ -113,6 +105,8 @@ int maxDepth if (goal.IsSatisfied(current.state)) { // ...return the Action path + + // action list List actions = new List(); while (current.action != null) { @@ -120,7 +114,15 @@ int maxDepth current = current.parent; } actions.Reverse(); - return actions.ToArray(); + + // create GoapResult + GoapResult result = new GoapResult( + actions.ToArray(), + current.currentCost, + true + ); + + return result; } // close the current node @@ -153,8 +155,7 @@ int maxDepth } } - // Return empty array if no solution is found - return new GoapAction[0]; + return new GoapResult(null, -1, false); } private double EstimateCost(GoapState state, ConditionInterface goal) From b1101f9559983265ddd9aba7aa3c279f42a2369f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:51:24 +0900 Subject: [PATCH 082/108] use GoapResult --- Tests/GoapTest.cs | 55 +++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index 450416e..0d890b0 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -16,12 +16,13 @@ public void GoapTestSimple() GoapSolver solver = GenerateSolverWithActionPool(); //run - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 - Assert.AreEqual(actions.Length, 2); - Assert.AreEqual(actions[0].name, ACTION_1); - Assert.AreEqual(actions[1].name, ACTION_1); + Assert.AreEqual(result.success, true); + Assert.AreEqual(result.length, 2); + Assert.AreEqual(result.actions[0].name, ACTION_1); + Assert.AreEqual(result.actions[1].name, ACTION_1); } [Test] @@ -37,14 +38,15 @@ public void GoapTestPathFinding() GoapSolver solver = GenerateSolverWithActionPool(); //run - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 -> #1 -> #2 - Assert.AreEqual(actions.Length, 4); - Assert.AreEqual(actions[0].name, ACTION_1); - Assert.AreEqual(actions[1].name, ACTION_1); - Assert.AreEqual(actions[2].name, ACTION_1); - Assert.AreEqual(actions[3].name, ACTION_2); + Assert.AreEqual(result.success, true); + Assert.AreEqual(result.length, 4); + Assert.AreEqual(result.actions[0].name, ACTION_1); + Assert.AreEqual(result.actions[1].name, ACTION_1); + Assert.AreEqual(result.actions[2].name, ACTION_1); + Assert.AreEqual(result.actions[3].name, ACTION_2); } [Test] @@ -60,12 +62,13 @@ public void GoapTestMapping() GoapState state = GenerateState(); GoapSolver solver = GenerateSolverWithActionPool(); - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); // #3 -> #3 - Assert.AreEqual(actions.Length, 2); - Assert.AreEqual(actions[0].name, ACTION_3); - Assert.AreEqual(actions[1].name, ACTION_3); + Assert.AreEqual(result.success, true); + Assert.AreEqual(result.length, 2); + Assert.AreEqual(result.actions[0].name, ACTION_3); + Assert.AreEqual(result.actions[1].name, ACTION_3); } [Test] @@ -86,14 +89,14 @@ public void GoapTestBetterCost() ); //run - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 -> #1 - Assert.AreEqual(actions.Length, 3); - Assert.AreEqual(actions[0].name, ACTION_1); - Assert.AreEqual(actions[1].name, ACTION_1); - ; - Assert.AreEqual(actions[2].name, ACTION_1); + Assert.AreEqual(result.success, true); + Assert.AreEqual(result.length, 3); + Assert.AreEqual(result.actions[0].name, ACTION_1); + Assert.AreEqual(result.actions[1].name, ACTION_1); + Assert.AreEqual(result.actions[2].name, ACTION_1); } [Test] @@ -104,10 +107,10 @@ public void GoapTestTooDeep() GoapSolver solver = GenerateSolverWithActionPool(); //run - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); - // empty - Assert.AreEqual(actions.Length, 0); + // error + Assert.AreEqual(result.success, false); } [Test] @@ -118,10 +121,10 @@ public void GoapTestImpossible() GoapSolver solver = GenerateSolverWithActionPool(); //run - GoapAction[] actions = solver.Solve(state, goal, 10); + GoapResult result = solver.Solve(state, goal, 10); - // empty - Assert.AreEqual(actions.Length, 0); + // error + Assert.AreEqual(result.success, false); } private GoapState GenerateState() From febb3a06c4288664d58319323daaf12fc758ca32 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:54:47 +0900 Subject: [PATCH 083/108] swap params of AreEqual --- Tests/GoapTest.cs | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index 0d890b0..e15cf3b 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -19,10 +19,10 @@ public void GoapTestSimple() GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 - Assert.AreEqual(result.success, true); - Assert.AreEqual(result.length, 2); - Assert.AreEqual(result.actions[0].name, ACTION_1); - Assert.AreEqual(result.actions[1].name, ACTION_1); + Assert.AreEqual(true, result.success); + Assert.AreEqual(2, result.length); + Assert.AreEqual(ACTION_1, result.actions[0].name); + Assert.AreEqual(ACTION_1, result.actions[1].name); } [Test] @@ -41,12 +41,12 @@ public void GoapTestPathFinding() GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 -> #1 -> #2 - Assert.AreEqual(result.success, true); - Assert.AreEqual(result.length, 4); - Assert.AreEqual(result.actions[0].name, ACTION_1); - Assert.AreEqual(result.actions[1].name, ACTION_1); - Assert.AreEqual(result.actions[2].name, ACTION_1); - Assert.AreEqual(result.actions[3].name, ACTION_2); + Assert.AreEqual(true, result.success); + Assert.AreEqual(4, result.length); + Assert.AreEqual(ACTION_1, result.actions[0].name); + Assert.AreEqual(ACTION_1, result.actions[1].name); + Assert.AreEqual(ACTION_1, result.actions[2].name); + Assert.AreEqual(ACTION_2, result.actions[3].name); } [Test] @@ -65,10 +65,10 @@ public void GoapTestMapping() GoapResult result = solver.Solve(state, goal, 10); // #3 -> #3 - Assert.AreEqual(result.success, true); - Assert.AreEqual(result.length, 2); - Assert.AreEqual(result.actions[0].name, ACTION_3); - Assert.AreEqual(result.actions[1].name, ACTION_3); + Assert.AreEqual(true, result.success); + Assert.AreEqual(2, result.length); + Assert.AreEqual(ACTION_3, result.actions[0].name); + Assert.AreEqual(ACTION_3, result.actions[1].name); } [Test] @@ -92,11 +92,11 @@ public void GoapTestBetterCost() GoapResult result = solver.Solve(state, goal, 10); // #1 -> #1 -> #1 - Assert.AreEqual(result.success, true); - Assert.AreEqual(result.length, 3); - Assert.AreEqual(result.actions[0].name, ACTION_1); - Assert.AreEqual(result.actions[1].name, ACTION_1); - Assert.AreEqual(result.actions[2].name, ACTION_1); + Assert.AreEqual(true, result.success); + Assert.AreEqual(3, result.length); + Assert.AreEqual(ACTION_1, result.actions[0].name); + Assert.AreEqual(ACTION_1, result.actions[1].name); + Assert.AreEqual(ACTION_1, result.actions[2].name); } [Test] @@ -110,7 +110,7 @@ public void GoapTestTooDeep() GoapResult result = solver.Solve(state, goal, 10); // error - Assert.AreEqual(result.success, false); + Assert.AreEqual(false, result.success); } [Test] @@ -124,7 +124,7 @@ public void GoapTestImpossible() GoapResult result = solver.Solve(state, goal, 10); // error - Assert.AreEqual(result.success, false); + Assert.AreEqual(false, result.success); } private GoapState GenerateState() From 2596438faae9d56c3649b37f92ca9d4c521cf249 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 12:57:20 +0900 Subject: [PATCH 084/108] fix: check condition of the simulated state --- Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index a0b97de..3056a46 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -135,7 +135,7 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i foreach (GoapAction action in actionPool.Values) { // if the action is available... - if (action.condition.IsSatisfied(stateCurrent)) + if (action.condition.IsSatisfied(current.state)) { // ...go to this state GoapState nextState = action.Simulate(current.state, overwrite: false); From 5d2da788368ad67ef4a217d4734ca31f9c38b280 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:17:08 +0900 Subject: [PATCH 085/108] copilot documentation --- Scripts/Goap/Conditions/Condition.cs | 66 +++++++++++++++++ Scripts/Goap/Conditions/ConditionAnd.cs | 21 ++++++ Scripts/Goap/Conditions/ConditionInterface.cs | 15 ++++ Scripts/Goap/Conditions/ConditionOperator.cs | 26 +++++++ Scripts/Goap/Conditions/ConditionOr.cs | 24 +++++- Scripts/Goap/Conditions/NoCondition.cs | 14 ++++ Scripts/Goap/GoapAction.cs | 45 +++++++++++- Scripts/Goap/GoapSolver/GoapResult.cs | 24 ++++++ .../GoapSolver/GoapSolver_ActionControl.cs | 20 ++++- .../Goap/GoapSolver/GoapSolver_Pathfinding.cs | 73 ++++++++++++++++++- Scripts/Goap/GoapState.cs | 45 +++++++++++- Scripts/Goap/GoapValue/GoapValue.cs | 28 ++++++- Scripts/Goap/GoapValue/GoapValueInterface.cs | 6 ++ Scripts/Goap/StateDiff/StateDiffAddition.cs | 27 +++++++ Scripts/Goap/StateDiff/StateDiffInterface.cs | 17 +++++ Scripts/Goap/StateDiff/StateDiffMapping.cs | 27 +++++++ Scripts/Goap/StateDiff/StateDiffSet.cs | 16 ++++ Scripts/Goap/Utils/PriorityQueue.cs | 19 +++++ Tests/GoapTest.cs | 40 ++++++++++ 19 files changed, 544 insertions(+), 9 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index 95ec3c3..8b6eb0b 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -3,13 +3,44 @@ namespace TsunagiModule.Goap { + /// + /// Basic conditioning in the GOAP system. + /// Compares a state value with a target value using a specified operator. + /// + /// + /// Available conditioning method is listed in the enum. + /// + /// + /// This means > 5 + /// + /// new Condition("correspondingindex", ConditionOperator.Greater, 5) + /// + /// + /// This must be the same type as the value in the State. public struct Condition : ConditionInterface where T : struct, IEquatable { + /// + /// The index of the state value to compare. + /// public string stateIndex { get; private set; } + + /// + /// The target value to compare against. + /// public T valueComparing { get; set; } + + /// + /// The operator used for the comparison. + /// public ConditionOperator conditionOperator { get; set; } + /// + /// Initializes a new instance of the struct. + /// + /// The index of the state value to compare. + /// The operator used for the comparison. + /// The target value to compare against. public Condition(string stateIndex, ConditionOperator conditionOperator, T valueComparing) { this.stateIndex = stateIndex; @@ -17,6 +48,12 @@ public Condition(string stateIndex, ConditionOperator conditionOperator, T value this.conditionOperator = conditionOperator; } + /// + /// Determines whether the condition is satisfied given the current state. + /// + /// The current GOAP state. + /// True if the condition is satisfied; otherwise, false. + /// Thrown when the state value type does not match the expected type. public bool IsSatisfied(GoapState state) { GoapValueInterface valueGivenInterface = state.GetValue(stateIndex); @@ -37,6 +74,13 @@ public bool IsSatisfied(GoapState state) } } + /// + /// Estimates the cost of satisfying the condition given the current state. + /// + /// The current GOAP state. + /// Optional dictionary of costs per state difference. + /// The estimated cost of satisfying the condition. + /// Thrown when the state value type does not match the expected type. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // if already satisfied... @@ -100,6 +144,14 @@ public double EstimateCost(GoapState state, Dictionary costPerDi return distance * costPerDiff; } + /// + /// Compares the given value with the target value using the specified operator. + /// + /// The value to compare. + /// The target value to compare against. + /// True if the comparison is successful; otherwise, false. + /// Thrown when the operator is not supported for the value type. + /// Thrown when the operator is not implemented. private bool Compare(T valueGiven, T valueComparing) { // delegate to corresponding comparison method @@ -145,6 +197,13 @@ private bool Compare(T valueGiven, T valueComparing) } } + /// + /// Compares two values using equatable operators. + /// + /// The value to compare. + /// The target value to compare against. + /// True if the comparison is successful; otherwise, false. + /// Thrown when the operator is not implemented. private bool CompareEquatable(IEquatable valueGivenEquatable, T valueComparing) { switch (conditionOperator) @@ -160,6 +219,13 @@ private bool CompareEquatable(IEquatable valueGivenEquatable, T valueComparin } } + /// + /// Compares two values using comparable operators. + /// + /// The value to compare. + /// The target value to compare against. + /// True if the comparison is successful; otherwise, false. + /// Thrown when the operator is not implemented. private bool CompareComparable(IComparable valueGivenComparable, T valueComparing) { // https://learn.microsoft.com/en-us/dotnet/api/system.icomparable?view=net-9.0 diff --git a/Scripts/Goap/Conditions/ConditionAnd.cs b/Scripts/Goap/Conditions/ConditionAnd.cs index a2aa733..296efc8 100644 --- a/Scripts/Goap/Conditions/ConditionAnd.cs +++ b/Scripts/Goap/Conditions/ConditionAnd.cs @@ -3,15 +3,30 @@ namespace TsunagiModule.Goap { + /// + /// Represents a logical AND condition composed of multiple sub-conditions. + /// public struct ConditionAnd : ConditionInterface { + /// + /// The array of sub-conditions that make up this AND condition. + /// public ConditionInterface[] conditions { get; private set; } + /// + /// Initializes a new instance of the struct. + /// + /// The array of sub-conditions. public ConditionAnd(ConditionInterface[] conditions) { this.conditions = conditions; } + /// + /// Determines whether all sub-conditions are satisfied given the current state. + /// + /// The current GOAP state. + /// True if all sub-conditions are satisfied; otherwise, false. public bool IsSatisfied(GoapState state) { foreach (ConditionInterface condition in conditions) @@ -24,6 +39,12 @@ public bool IsSatisfied(GoapState state) return true; } + /// + /// Estimates the cost of satisfying this AND condition given the current state. + /// + /// The current GOAP state. + /// Optional dictionary of costs per state difference. + /// The estimated cost of satisfying the condition. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // square root of sum of squares diff --git a/Scripts/Goap/Conditions/ConditionInterface.cs b/Scripts/Goap/Conditions/ConditionInterface.cs index 311b0e7..b781f9f 100644 --- a/Scripts/Goap/Conditions/ConditionInterface.cs +++ b/Scripts/Goap/Conditions/ConditionInterface.cs @@ -2,9 +2,24 @@ namespace TsunagiModule.Goap { + /// + /// Defines an interface for conditions in the GOAP system. + /// public interface ConditionInterface { + /// + /// Determines whether the condition is satisfied given the current state. + /// + /// The current GOAP state. + /// True if the condition is satisfied; otherwise, false. public bool IsSatisfied(GoapState state); + + /// + /// Estimates the cost of satisfying the condition given the current state. + /// + /// The current GOAP state. + /// Optional dictionary of costs per state difference. + /// The estimated cost of satisfying the condition. public double EstimateCost( GoapState state, Dictionary costPerDiffes = null diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs b/Scripts/Goap/Conditions/ConditionOperator.cs index 6c3474c..936e208 100644 --- a/Scripts/Goap/Conditions/ConditionOperator.cs +++ b/Scripts/Goap/Conditions/ConditionOperator.cs @@ -1,12 +1,38 @@ namespace TsunagiModule.Goap { + /// + /// Defines the operators used in GOAP conditions for comparison. + /// public enum ConditionOperator { + /// + /// Represents a comparison where the left-hand side is larger than the right-hand side. + /// Larger, + + /// + /// Represents a comparison where the left-hand side is larger than or equal to the right-hand side. + /// LargerOrEqual, + + /// + /// Represents a comparison where the left-hand side is smaller than the right-hand side. + /// Smaller, + + /// + /// Represents a comparison where the left-hand side is smaller than or equal to the right-hand side. + /// SmallerOrEqual, + + /// + /// Represents a comparison where the left-hand side is equal to the right-hand side. + /// Equal, + + /// + /// Represents a comparison where the left-hand side is not equal to the right-hand side. + /// NotEqual, } } diff --git a/Scripts/Goap/Conditions/ConditionOr.cs b/Scripts/Goap/Conditions/ConditionOr.cs index 1782aef..5d4e5fe 100644 --- a/Scripts/Goap/Conditions/ConditionOr.cs +++ b/Scripts/Goap/Conditions/ConditionOr.cs @@ -2,15 +2,30 @@ namespace TsunagiModule.Goap { + /// + /// Represents a logical OR condition composed of multiple sub-conditions. + /// public struct ConditionOr : ConditionInterface { + /// + /// The array of sub-conditions that make up this OR condition. + /// public ConditionInterface[] conditions { get; private set; } + /// + /// Initializes a new instance of the struct. + /// + /// The array of sub-conditions. public ConditionOr(ConditionInterface[] conditions) { this.conditions = conditions; } + /// + /// Determines whether any of the sub-conditions are satisfied given the current state. + /// + /// The current state. + /// True if any sub-condition is satisfied; otherwise, false. public bool IsSatisfied(GoapState state) { foreach (ConditionInterface condition in conditions) @@ -23,10 +38,17 @@ public bool IsSatisfied(GoapState state) return false; } + /// + /// Estimates the cost of satisfying this OR condition given the current state. + /// + /// The current state. + /// Optional dictionary of costs per state difference. + /// The estimated cost of satisfying the condition. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { // shortest distance - double min = double.MaxValue; + double min = // shortest distance + double.MaxValue; foreach (ConditionInterface condition in conditions) { double cost = condition.EstimateCost(state, costPerDiffes: costPerDiffes); diff --git a/Scripts/Goap/Conditions/NoCondition.cs b/Scripts/Goap/Conditions/NoCondition.cs index 221fba9..3f66958 100644 --- a/Scripts/Goap/Conditions/NoCondition.cs +++ b/Scripts/Goap/Conditions/NoCondition.cs @@ -2,13 +2,27 @@ namespace TsunagiModule.Goap { + /// + /// Represents a condition that is always satisfied and has no cost. + /// public struct NoCondition : ConditionInterface { + /// + /// Determines whether the condition is satisfied. + /// + /// The current GOAP state. + /// Always returns true. public bool IsSatisfied(GoapState state) { return true; } + /// + /// Estimates the cost of satisfying the condition. + /// + /// The current GOAP state. + /// Optional dictionary of costs per state difference. + /// Always returns 0. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { return 0; diff --git a/Scripts/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs index e1c35e7..e64d835 100644 --- a/Scripts/Goap/GoapAction.cs +++ b/Scripts/Goap/GoapAction.cs @@ -1,12 +1,37 @@ namespace TsunagiModule.Goap { + /// + /// Represents an action in the GOAP (Goal-Oriented Action Planning) system. + /// public struct GoapAction { + /// + /// The name of the action. + /// public string name { get; private set; } + + /// + /// The condition that must be satisfied for the action to be available. + /// public ConditionInterface condition { get; private set; } + + /// + /// The set of state differences that this action applies. + /// public StateDiffSet stateDiffSet { get; private set; } + + /// + /// The cost of performing this action. + /// public double cost { get; private set; } + /// + /// Initializes a new instance of the struct. + /// + /// The name of the action. + /// The condition for the action. + /// The state differences applied by the action. + /// The cost of the action. public GoapAction( string name, ConditionInterface condition, @@ -20,6 +45,13 @@ double cost this.cost = cost; } + /// + /// Initializes a new instance of the struct with an array of state differences. + /// + /// The name of the action. + /// The condition for the action. + /// The array of state differences applied by the action. + /// The cost of the action. public GoapAction( string name, ConditionInterface condition, @@ -33,14 +65,24 @@ double cost this.cost = cost; } + /// + /// Determines whether the action is available given the current state. + /// + /// The current state. + /// True if the action is available; otherwise, false. public bool Available(GoapState state) { return condition.IsSatisfied(state); } + /// + /// Simulates the application of the action on the given state. + /// + /// The current state. + /// Whether to overwrite the current state or clone it. + /// The resulting state after applying the action. public GoapState Simulate(GoapState state, bool overwrite = true) { - // cloning or not GoapState stateTarget; if (overwrite) { @@ -51,7 +93,6 @@ public GoapState Simulate(GoapState state, bool overwrite = true) stateTarget = state.Clone(); } - // apply action stateTarget = stateDiffSet.Apply(stateTarget, overwrite: true); return stateTarget; diff --git a/Scripts/Goap/GoapSolver/GoapResult.cs b/Scripts/Goap/GoapSolver/GoapResult.cs index 87f70b5..b3c7ffc 100644 --- a/Scripts/Goap/GoapSolver/GoapResult.cs +++ b/Scripts/Goap/GoapSolver/GoapResult.cs @@ -1,12 +1,36 @@ namespace TsunagiModule.Goap { + /// + /// Represents the result of solving a GOAP (Goal-Oriented Action Planning) problem. + /// public struct GoapResult { + /// + /// The sequence of actions that lead to the goal. + /// public GoapAction[] actions; + + /// + /// The total cost of the actions. + /// public double cost; + + /// + /// Gets the number of actions in the result. + /// public int length => actions.Length; + + /// + /// Indicates whether the goal was successfully achieved. + /// public bool success; + /// + /// Initializes a new instance of the struct. + /// + /// The sequence of actions that lead to the goal. + /// The total cost of the actions. + /// Indicates whether the goal was successfully achieved. public GoapResult(GoapAction[] actions, double cost, bool success) { this.actions = actions; diff --git a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs index 139df27..2d3404f 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -2,26 +2,42 @@ namespace TsunagiModule.Goap { + /// + /// Provides methods to manage the pool of actions in the GOAP solver. + /// public partial class GoapSolver { /// - /// Pool of actions + /// Pool of actions. /// /// - /// action name -> Action + /// Maps action names to their corresponding instances. /// private Dictionary actionPool = new Dictionary(); + /// + /// Adds a new action to the action pool. + /// + /// The action to add. public void AddAction(GoapAction action) { actionPool.Add(action.name, action); } + /// + /// Removes an action from the action pool by its name. + /// + /// The name of the action to remove. public void RemoveAction(string name) { actionPool.Remove(name); } + /// + /// Replaces an existing action in the action pool with a new one. + /// + /// The name of the action to replace. + /// The new action to replace the existing one. public void ReplaceAction(string name, GoapAction action) { actionPool[name] = action; diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 3056a46..c1fc328 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -5,18 +5,59 @@ namespace TsunagiModule.Goap { //https://medium.com/@hanxuyang0826/mastering-pathfinding-with-a-star-a-practical-guide-and-c-implementation-f76f1643d8c3 + /// + /// Implements the A* pathfinding algorithm for the GOAP solver. + /// public partial class GoapSolver { + /// + /// Represents a node in the A* search queue. + /// private class AstarQueue : IComparable { + /// + /// The current state of the node. + /// public GoapState state; + + /// + /// The parent node in the search path. + /// public AstarQueue parent; + + /// + /// The action that led to this state. + /// public GoapAction? action; + + /// + /// The cost incurred to reach this state. + /// public double currentCost; + + /// + /// The estimated cost to reach the goal from this state. + /// public double heuristicCost; + + /// + /// Gets the total cost (current cost + heuristic cost) for this node. + /// public double totalCost => currentCost + heuristicCost; + + /// + /// Gets the depth of this node in the search tree. + /// public int depth => parent == null ? 0 : parent.depth + 1; + /// + /// Initializes a new instance of the class. + /// + /// The current state of the node. + /// The parent node in the search path. + /// The cost incurred to reach this state. + /// The estimated cost to reach the goal from this state. + /// The action that led to this state. public AstarQueue( GoapState state, AstarQueue parent, @@ -32,6 +73,11 @@ public AstarQueue( this.action = action; } + /// + /// Compares this node with another node based on their total costs. + /// + /// The other node to compare with. + /// A negative value if this node has a lower total cost, a positive value if it has a higher total cost, or zero if the costs are equal. public int CompareTo(AstarQueue other) { if (totalCost < other.totalCost) @@ -43,10 +89,17 @@ public int CompareTo(AstarQueue other) } /// - /// Cost estimation weights for each state index + /// Cost estimation weights for each state index. /// private Dictionary costPerDiffes = new Dictionary(); + /// + /// Solves the GOAP problem using the A* algorithm. + /// + /// The current state. + /// The goal condition to satisfy. + /// The maximum depth of the search tree. + /// The result of the GOAP problem-solving process. public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) { // compute cost weights @@ -55,6 +108,11 @@ public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int max return SolveAstar(stateCurrent, goal, maxLength); } + /// + /// Computes the cost weights for each state index based on the current state. + /// + /// The current state. + /// A dictionary mapping state indices to their cost weights. private Dictionary ComputeCostWeights(GoapState stateCurrent) { // state index -> cost per diff @@ -79,6 +137,13 @@ private Dictionary ComputeCostWeights(GoapState stateCurrent) return largestCostPerDiff; } + /// + /// Performs the A* search to solve the GOAP problem. + /// + /// The current state. + /// The goal condition to satisfy. + /// The maximum depth of the search tree. + /// The result of the A* search process. private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, int maxDepth) { PriorityQueue queue = new PriorityQueue(); @@ -158,6 +223,12 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i return new GoapResult(null, -1, false); } + /// + /// Estimates the cost to satisfy the goal condition from the given state. + /// + /// The current state. + /// The goal condition to satisfy. + /// The estimated cost to satisfy the goal condition. private double EstimateCost(GoapState state, ConditionInterface goal) { return goal.EstimateCost(state, costPerDiffes: costPerDiffes); diff --git a/Scripts/Goap/GoapState.cs b/Scripts/Goap/GoapState.cs index 70c692a..a758e5f 100644 --- a/Scripts/Goap/GoapState.cs +++ b/Scripts/Goap/GoapState.cs @@ -4,20 +4,36 @@ namespace TsunagiModule.Goap { + /// + /// Represents the state in the GOAP (Goal-Oriented Action Planning) system. + /// public struct GoapState : IEquatable { /// - /// Main body of state vector + /// The main body of the state vector, storing state values by their indices. /// private Dictionary values { get; set; } + /// + /// Gets the indices of all state values. + /// public string[] indices => values.Keys.ToArray(); + /// + /// Initializes a new instance of the struct. + /// + /// The dictionary of state values. public GoapState(Dictionary values) { this.values = values; } + /// + /// Retrieves the value associated with the specified state index. + /// + /// The index of the state value to retrieve. + /// The value associated with the specified state index. + /// Thrown when the specified state index is not found. public GoapValueInterface GetValue(string stateIndex) { GuardValueNull(); @@ -32,6 +48,11 @@ public GoapValueInterface GetValue(string stateIndex) } } + /// + /// Sets the value for the specified state index. + /// + /// The index of the state value to set. + /// The value to set. public void SetValue(string stateIndex, GoapValueInterface value) { GuardValueNull(); @@ -39,6 +60,12 @@ public void SetValue(string stateIndex, GoapValueInterface value) values[stateIndex] = value; } + /// + /// Sets the raw value for the specified state index. + /// + /// The type of the value. + /// The index of the state value to set. + /// The raw value to set. public void SetRawValue(string stateIndex, T value) where T : struct, IEquatable { @@ -46,6 +73,11 @@ public void SetRawValue(string stateIndex, T value) SetValue(stateIndex, new GoapValue(value)); } + /// + /// Determines whether the current state is equal to another state. + /// + /// The other state to compare with. + /// True if the states are equal; otherwise, false. public bool Equals(GoapState other) { GuardValueNull(); @@ -56,6 +88,10 @@ public bool Equals(GoapState other) return values.SequenceEqual(other.values); } + /// + /// Returns the hash code for the current state. + /// + /// The hash code for the current state. public override int GetHashCode() { // HACK: there could be a better data structure @@ -71,12 +107,19 @@ public override int GetHashCode() return hash; } + /// + /// Creates a clone of the current state. + /// + /// A new instance of that is a clone of the current state. public GoapState Clone() { // HACK: there could be a better data structure return new GoapState { values = new Dictionary(values) }; } + /// + /// Ensures that the state values dictionary is not null. + /// private void GuardValueNull() { if (values == null) diff --git a/Scripts/Goap/GoapValue/GoapValue.cs b/Scripts/Goap/GoapValue/GoapValue.cs index 43c7610..cf8703e 100644 --- a/Scripts/Goap/GoapValue/GoapValue.cs +++ b/Scripts/Goap/GoapValue/GoapValue.cs @@ -8,22 +8,46 @@ namespace TsunagiModule.Goap /// /// This is implemented for support integrated control of State Control, being independent of value type. /// + /// + /// Represents a value used in the GOAP system, supporting integrated control of state values independent of their type. + /// + /// The type of the value. Must be a struct and implement . public struct GoapValue : GoapValueInterface, IEquatable> where T : struct, IEquatable { - public Type type => typeof(T); // getter for type - public T value { get; set; } // getter and setter for value + /// + /// Gets the type of the GOAP value. + /// + public Type type => typeof(T); + + /// + /// Gets or sets the value. + /// + public T value { get; set; } + /// + /// Initializes a new instance of the struct. + /// + /// The value to initialize with. public GoapValue(T value) { this.value = value; } + /// + /// Determines whether the current value is equal to another value of the same type. + /// + /// The other value to compare with. + /// True if the values are equal; otherwise, false. public bool Equals(GoapValue other) { return value.Equals(other.value); } + /// + /// Returns the hash code for the current value. + /// + /// The hash code for the current value. public override int GetHashCode() { return value.GetHashCode(); diff --git a/Scripts/Goap/GoapValue/GoapValueInterface.cs b/Scripts/Goap/GoapValue/GoapValueInterface.cs index 0d9929c..fcddb6d 100644 --- a/Scripts/Goap/GoapValue/GoapValueInterface.cs +++ b/Scripts/Goap/GoapValue/GoapValueInterface.cs @@ -2,8 +2,14 @@ namespace TsunagiModule.Goap { + /// + /// Defines an interface for GOAP value types. + /// public interface GoapValueInterface { + /// + /// Gets the type of the GOAP value. + /// public Type type { get; } } } diff --git a/Scripts/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs index 7079951..6e551c9 100644 --- a/Scripts/Goap/StateDiff/StateDiffAddition.cs +++ b/Scripts/Goap/StateDiff/StateDiffAddition.cs @@ -2,19 +2,46 @@ namespace TsunagiModule.Goap { + /// + /// Represents an addition operation for state differences in the GOAP system. + /// + /// The type of the state values. Must be a struct and implement , , and . public struct StateDiffAddition : StateDiffInterface where T : struct, IConvertible, IComparable, IComparable, IEquatable { + /// + /// The index of the state to which this addition applies. + /// public string stateIndex { get; private set; } + + /// + /// The value to be added to the state. + /// public T additionValue { get; set; } + + /// + /// Gets the difference value associated with this addition operation. + /// public float diff => Convert.ToSingle(additionValue); + /// + /// Initializes a new instance of the struct. + /// + /// The index of the state to which this addition applies. + /// The value to be added to the state. public StateDiffAddition(string stateIndex, T additionValue) { this.stateIndex = stateIndex; this.additionValue = additionValue; } + /// + /// Applies the addition operation to the given GOAP state. + /// + /// The GOAP state to apply the addition to. + /// Whether to overwrite the current state or clone it. + /// The resulting state after applying the addition. + /// Thrown when there is a type mismatch between the addition operation and the state value type. public GoapState Operate(GoapState state, bool overwrite = true) { // cloning or not diff --git a/Scripts/Goap/StateDiff/StateDiffInterface.cs b/Scripts/Goap/StateDiff/StateDiffInterface.cs index ed8fee0..8f12ce7 100644 --- a/Scripts/Goap/StateDiff/StateDiffInterface.cs +++ b/Scripts/Goap/StateDiff/StateDiffInterface.cs @@ -1,9 +1,26 @@ namespace TsunagiModule.Goap { + /// + /// Defines an interface for state difference operations in the GOAP system. + /// public interface StateDiffInterface { + /// + /// Gets the index of the state to which this difference applies. + /// public string stateIndex { get; } + + /// + /// Applies the state difference operation to the given GOAP state. + /// + /// The GOAP state to apply the difference to. + /// Whether to overwrite the current state or clone it. + /// The resulting state after applying the difference. public GoapState Operate(GoapState state, bool overwrite = true); + + /// + /// Gets the difference value associated with this operation. + /// public float diff { get; } } } diff --git a/Scripts/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs index d42bad2..4a63ca4 100644 --- a/Scripts/Goap/StateDiff/StateDiffMapping.cs +++ b/Scripts/Goap/StateDiff/StateDiffMapping.cs @@ -3,19 +3,46 @@ namespace TsunagiModule.Goap { + /// + /// Represents a mapping operation for state differences in the GOAP system. + /// + /// The type of the state values. Must be a struct and implement . public struct StateDiffMapping : StateDiffInterface where T : struct, IEquatable { + /// + /// The index of the state to which this mapping applies. + /// public string stateIndex { get; private set; } + + /// + /// The dictionary defining the mapping of state values. + /// public Dictionary mapping { get; set; } + + /// + /// Gets the difference value for this mapping operation. + /// public float diff => 1f; + /// + /// Initializes a new instance of the struct. + /// + /// The index of the state to which this mapping applies. + /// The dictionary defining the mapping of state values. public StateDiffMapping(string stateIndex, Dictionary mapping) { this.stateIndex = stateIndex; this.mapping = mapping; } + /// + /// Applies the mapping operation to the given GOAP state. + /// + /// The GOAP state to apply the mapping to. + /// Whether to overwrite the current state or clone it. + /// The resulting state after applying the mapping. + /// Thrown when there is a type mismatch between the mapping operation and the state value type. public GoapState Operate(GoapState state, bool overwrite = true) { // cloning or not diff --git a/Scripts/Goap/StateDiff/StateDiffSet.cs b/Scripts/Goap/StateDiff/StateDiffSet.cs index 92a24a9..5c675d9 100644 --- a/Scripts/Goap/StateDiff/StateDiffSet.cs +++ b/Scripts/Goap/StateDiff/StateDiffSet.cs @@ -1,14 +1,30 @@ namespace TsunagiModule.Goap { + /// + /// Represents a collection of state differences and provides functionality to apply them to a GOAP state. + /// public struct StateDiffSet { + /// + /// The array of state differences in this set. + /// public StateDiffInterface[] stateDiffes; + /// + /// Initializes a new instance of the struct. + /// + /// The array of state differences. public StateDiffSet(StateDiffInterface[] stateDiffes) { this.stateDiffes = stateDiffes; } + /// + /// Applies all state differences in this set to the given GOAP state. + /// + /// The GOAP state to apply the differences to. + /// Whether to overwrite the current state or clone it. + /// The resulting state after applying the differences. public GoapState Apply(GoapState state, bool overwrite = true) { // cloning or not diff --git a/Scripts/Goap/Utils/PriorityQueue.cs b/Scripts/Goap/Utils/PriorityQueue.cs index a434a72..9fd3563 100644 --- a/Scripts/Goap/Utils/PriorityQueue.cs +++ b/Scripts/Goap/Utils/PriorityQueue.cs @@ -3,13 +3,27 @@ namespace TsunagiModule.Goap.Utils { + /// + /// Represents a generic priority queue that organizes elements based on their priority. + /// + /// The type of elements in the priority queue. Must implement . public class PriorityQueue where T : IComparable { + /// + /// The list of elements in the priority queue. + /// private List elements = new List(); + /// + /// Gets the number of elements in the priority queue. + /// public int Count => elements.Count; + /// + /// Adds an element to the priority queue. + /// + /// The element to add. public void Enqueue(T item) { elements.Add(item); @@ -28,6 +42,11 @@ public void Enqueue(T item) } } + /// + /// Removes and returns the element with the highest priority from the priority queue. + /// + /// The element with the highest priority. + /// Thrown when the priority queue is empty. public T Dequeue() { if (elements.Count == 0) diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index e15cf3b..5feaf69 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -2,12 +2,29 @@ using NUnit.Framework; using TsunagiModule.Goap; +/// +/// Contains unit tests for the GOAP (Goal-Oriented Action Planning) system. +/// public class GoapTest { + /// + /// Constant representing the name of the first action. + /// private const string ACTION_1 = "#1 increase int"; + + /// + /// Constant representing the name of the second action. + /// private const string ACTION_2 = "#2 increase float"; + + /// + /// Constant representing the name of the third action. + /// private const string ACTION_3 = "#3 increase double and switch boolean"; + /// + /// Tests a simple GOAP scenario. + /// [Test] public void GoapTestSimple() { @@ -25,6 +42,9 @@ public void GoapTestSimple() Assert.AreEqual(ACTION_1, result.actions[1].name); } + /// + /// Tests the pathfinding capabilities of the GOAP solver. + /// [Test] public void GoapTestPathFinding() { @@ -49,6 +69,9 @@ public void GoapTestPathFinding() Assert.AreEqual(ACTION_2, result.actions[3].name); } + /// + /// Tests the mapping functionality of the GOAP solver. + /// [Test] public void GoapTestMapping() { @@ -71,6 +94,9 @@ public void GoapTestMapping() Assert.AreEqual(ACTION_3, result.actions[1].name); } + /// + /// Tests the solver's ability to choose actions with better costs. + /// [Test] public void GoapTestBetterCost() { @@ -99,6 +125,9 @@ public void GoapTestBetterCost() Assert.AreEqual(ACTION_1, result.actions[2].name); } + /// + /// Tests the solver's behavior when the goal depth is too deep. + /// [Test] public void GoapTestTooDeep() { @@ -113,6 +142,9 @@ public void GoapTestTooDeep() Assert.AreEqual(false, result.success); } + /// + /// Tests the solver's behavior when the goal is impossible to achieve. + /// [Test] public void GoapTestImpossible() { @@ -127,6 +159,10 @@ public void GoapTestImpossible() Assert.AreEqual(false, result.success); } + /// + /// Generates a sample GOAP state for testing. + /// + /// A sample GOAP state. private GoapState GenerateState() { GoapState state = new GoapState(); @@ -137,6 +173,10 @@ private GoapState GenerateState() return state; } + /// + /// Generates a GOAP solver with a predefined action pool for testing. + /// + /// A GOAP solver with predefined actions. private GoapSolver GenerateSolverWithActionPool() { GoapSolver solver = new GoapSolver(); From 4127d34052b72e2d94819dd31539fb962ea4414d Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:17:37 +0900 Subject: [PATCH 086/108] larger -> greater --- Scripts/Goap/Conditions/Condition.cs | 8 ++++---- Scripts/Goap/Conditions/ConditionOperator.cs | 4 ++-- Tests/GoapTest.cs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index 8b6eb0b..d2ee42a 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -172,8 +172,8 @@ private bool Compare(T valueGiven, T valueComparing) $"Condition operator '{conditionOperator}' is not supported for type '{typeof(T)}'." ); } - case ConditionOperator.Larger: - case ConditionOperator.LargerOrEqual: + case ConditionOperator.Greater: + case ConditionOperator.GreaterOrEqual: case ConditionOperator.Smaller: case ConditionOperator.SmallerOrEqual: // if operation available... @@ -235,9 +235,9 @@ private bool CompareComparable(IComparable valueGivenComparable, T valueCompa switch (conditionOperator) { - case ConditionOperator.Larger: + case ConditionOperator.Greater: return valueGivenComparable.CompareTo(valueComparing) > 0; - case ConditionOperator.LargerOrEqual: + case ConditionOperator.GreaterOrEqual: return valueGivenComparable.CompareTo(valueComparing) >= 0; case ConditionOperator.Smaller: return valueGivenComparable.CompareTo(valueComparing) < 0; diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs b/Scripts/Goap/Conditions/ConditionOperator.cs index 936e208..b5a0a20 100644 --- a/Scripts/Goap/Conditions/ConditionOperator.cs +++ b/Scripts/Goap/Conditions/ConditionOperator.cs @@ -8,12 +8,12 @@ public enum ConditionOperator /// /// Represents a comparison where the left-hand side is larger than the right-hand side. /// - Larger, + Greater, /// /// Represents a comparison where the left-hand side is larger than or equal to the right-hand side. /// - LargerOrEqual, + GreaterOrEqual, /// /// Represents a comparison where the left-hand side is smaller than the right-hand side. diff --git a/Tests/GoapTest.cs b/Tests/GoapTest.cs index 5feaf69..5cab34a 100644 --- a/Tests/GoapTest.cs +++ b/Tests/GoapTest.cs @@ -28,7 +28,7 @@ public class GoapTest [Test] public void GoapTestSimple() { - Condition goal = new Condition("int", ConditionOperator.LargerOrEqual, 2); + Condition goal = new Condition("int", ConditionOperator.GreaterOrEqual, 2); GoapState state = GenerateState(); GoapSolver solver = GenerateSolverWithActionPool(); @@ -50,7 +50,7 @@ public void GoapTestPathFinding() { Condition goal = new Condition( "float", - ConditionOperator.LargerOrEqual, + ConditionOperator.GreaterOrEqual, 0.5f ); @@ -78,7 +78,7 @@ public void GoapTestMapping() ConditionInterface goal = new ConditionAnd( new ConditionInterface[] { - new Condition("double", ConditionOperator.LargerOrEqual, 0.1), + new Condition("double", ConditionOperator.GreaterOrEqual, 0.1), new Condition("boolean", ConditionOperator.Equal, false) } ); @@ -191,7 +191,7 @@ private GoapSolver GenerateSolverWithActionPool() solver.AddAction( new GoapAction( ACTION_2, - new Condition("int", ConditionOperator.Larger, 2), + new Condition("int", ConditionOperator.Greater, 2), new StateDiffInterface[] { new StateDiffAddition("float", 1f) }, 2.0 ) From b5c442cca2f714150e1249f16ae804d878bd65e8 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:18:00 +0900 Subject: [PATCH 087/108] smaller -> less --- Scripts/Goap/Conditions/Condition.cs | 8 ++++---- Scripts/Goap/Conditions/ConditionOperator.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index d2ee42a..437fbf0 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -174,8 +174,8 @@ private bool Compare(T valueGiven, T valueComparing) } case ConditionOperator.Greater: case ConditionOperator.GreaterOrEqual: - case ConditionOperator.Smaller: - case ConditionOperator.SmallerOrEqual: + case ConditionOperator.Less: + case ConditionOperator.LessOrEqual: // if operation available... if (valueGiven is IComparable valueGivenComparable) { @@ -239,9 +239,9 @@ private bool CompareComparable(IComparable valueGivenComparable, T valueCompa return valueGivenComparable.CompareTo(valueComparing) > 0; case ConditionOperator.GreaterOrEqual: return valueGivenComparable.CompareTo(valueComparing) >= 0; - case ConditionOperator.Smaller: + case ConditionOperator.Less: return valueGivenComparable.CompareTo(valueComparing) < 0; - case ConditionOperator.SmallerOrEqual: + case ConditionOperator.LessOrEqual: return valueGivenComparable.CompareTo(valueComparing) <= 0; default: throw new NotImplementedException( diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs b/Scripts/Goap/Conditions/ConditionOperator.cs index b5a0a20..085ae2c 100644 --- a/Scripts/Goap/Conditions/ConditionOperator.cs +++ b/Scripts/Goap/Conditions/ConditionOperator.cs @@ -18,12 +18,12 @@ public enum ConditionOperator /// /// Represents a comparison where the left-hand side is smaller than the right-hand side. /// - Smaller, + Less, /// /// Represents a comparison where the left-hand side is smaller than or equal to the right-hand side. /// - SmallerOrEqual, + LessOrEqual, /// /// Represents a comparison where the left-hand side is equal to the right-hand side. From 6cebaa25d753a0a7a0ffd867c7a060f63cd13c9b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:26:49 +0900 Subject: [PATCH 088/108] assume unknown struct diff as 1 --- Scripts/Goap/Conditions/Condition.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index 437fbf0..643bfaa 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -125,11 +125,10 @@ public double EstimateCost(GoapState state, Dictionary costPerDi // compute distance distance = Math.Abs(valueGivenDouble - valueComparingDouble); } + // unknown else { - throw new NotImplementedException( - $"Condition operator '{conditionOperator}' not implemented yet." - ); + distance = 1.0; } } // if different type... From 19545f6c02b36848f502887446c80e83341b48aa Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:35:07 +0900 Subject: [PATCH 089/108] comments for Condition --- Scripts/Goap/Conditions/Condition.cs | 28 +++++++++++++------ Scripts/Goap/Conditions/ConditionAnd.cs | 3 ++ Scripts/Goap/Conditions/ConditionInterface.cs | 6 +++- Scripts/Goap/Conditions/ConditionOperator.cs | 14 +++++----- Scripts/Goap/Conditions/ConditionOr.cs | 3 ++ Scripts/Goap/Conditions/NoCondition.cs | 5 +++- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index 643bfaa..96b8c84 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -11,7 +11,7 @@ namespace TsunagiModule.Goap /// Available conditioning method is listed in the enum. /// /// - /// This means > 5 + /// This means (> 5) /// /// new Condition("correspondingindex", ConditionOperator.Greater, 5) /// @@ -26,12 +26,12 @@ public struct Condition : ConditionInterface public string stateIndex { get; private set; } /// - /// The target value to compare against. + /// The value to compare against. /// public T valueComparing { get; set; } /// - /// The operator used for the comparison. + /// Conditioning method /// public ConditionOperator conditionOperator { get; set; } @@ -39,8 +39,8 @@ public struct Condition : ConditionInterface /// Initializes a new instance of the struct. /// /// The index of the state value to compare. - /// The operator used for the comparison. - /// The target value to compare against. + /// Conditioning method + /// Value to compare against. public Condition(string stateIndex, ConditionOperator conditionOperator, T valueComparing) { this.stateIndex = stateIndex; @@ -51,7 +51,7 @@ public Condition(string stateIndex, ConditionOperator conditionOperator, T value /// /// Determines whether the condition is satisfied given the current state. /// - /// The current GOAP state. + /// The current GOAP state. This class will read the state value associated with the stateIndex. /// True if the condition is satisfied; otherwise, false. /// Thrown when the state value type does not match the expected type. public bool IsSatisfied(GoapState state) @@ -77,8 +77,13 @@ public bool IsSatisfied(GoapState state) /// /// Estimates the cost of satisfying the condition given the current state. /// - /// The current GOAP state. - /// Optional dictionary of costs per state difference. + /// + /// If boolean, the diff is set as 1.0. + /// If numerical (), the diff is the absolute difference of them. + /// If others, the diff assumed to be 1.0 if not equal. + /// + /// The current GOAP state. This class will read the state value associated with the stateIndex. + /// Optional dictionary of costs per state difference. If null, this will assume as 1.0 /// The estimated cost of satisfying the condition. /// Thrown when the state value type does not match the expected type. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) @@ -146,6 +151,9 @@ public double EstimateCost(GoapState state, Dictionary costPerDi /// /// Compares the given value with the target value using the specified operator. /// + /// + /// This function routes to corresponding comparison method. + /// /// The value to compare. /// The target value to compare against. /// True if the comparison is successful; otherwise, false. @@ -188,8 +196,10 @@ private bool Compare(T valueGiven, T valueComparing) $"Condition operator '{conditionOperator}' is not supported for type '{typeof(T)}'." ); } + + // unknown default: - // ...panic + // panic throw new NotImplementedException( $"Condition operator '{conditionOperator}' not implemented yet." ); diff --git a/Scripts/Goap/Conditions/ConditionAnd.cs b/Scripts/Goap/Conditions/ConditionAnd.cs index 296efc8..92000c7 100644 --- a/Scripts/Goap/Conditions/ConditionAnd.cs +++ b/Scripts/Goap/Conditions/ConditionAnd.cs @@ -42,6 +42,9 @@ public bool IsSatisfied(GoapState state) /// /// Estimates the cost of satisfying this AND condition given the current state. /// + /// + /// This method uses the square root of the sum of squares to estimate the cost of the sub-conditions + /// /// The current GOAP state. /// Optional dictionary of costs per state difference. /// The estimated cost of satisfying the condition. diff --git a/Scripts/Goap/Conditions/ConditionInterface.cs b/Scripts/Goap/Conditions/ConditionInterface.cs index b781f9f..9df45be 100644 --- a/Scripts/Goap/Conditions/ConditionInterface.cs +++ b/Scripts/Goap/Conditions/ConditionInterface.cs @@ -3,8 +3,12 @@ namespace TsunagiModule.Goap { /// - /// Defines an interface for conditions in the GOAP system. + /// interface for conditions in the GOAP system. /// + /// + /// If you want basical conditioning, see . + /// If you DONT want to use the default condition, see . + /// public interface ConditionInterface { /// diff --git a/Scripts/Goap/Conditions/ConditionOperator.cs b/Scripts/Goap/Conditions/ConditionOperator.cs index 085ae2c..7d5dec2 100644 --- a/Scripts/Goap/Conditions/ConditionOperator.cs +++ b/Scripts/Goap/Conditions/ConditionOperator.cs @@ -1,37 +1,37 @@ namespace TsunagiModule.Goap { /// - /// Defines the operators used in GOAP conditions for comparison. + /// Defines the comparing method in /// public enum ConditionOperator { /// - /// Represents a comparison where the left-hand side is larger than the right-hand side. + /// StateValue > ConditionValue /// Greater, /// - /// Represents a comparison where the left-hand side is larger than or equal to the right-hand side. + /// StateValue >= ConditionValue /// GreaterOrEqual, /// - /// Represents a comparison where the left-hand side is smaller than the right-hand side. + /// StateValue < ConditionValue /// Less, /// - /// Represents a comparison where the left-hand side is smaller than or equal to the right-hand side. + /// StateValue <= ConditionValue /// LessOrEqual, /// - /// Represents a comparison where the left-hand side is equal to the right-hand side. + /// StateValue == ConditionValue /// Equal, /// - /// Represents a comparison where the left-hand side is not equal to the right-hand side. + /// StateValue != ConditionValue /// NotEqual, } diff --git a/Scripts/Goap/Conditions/ConditionOr.cs b/Scripts/Goap/Conditions/ConditionOr.cs index 5d4e5fe..2d5584e 100644 --- a/Scripts/Goap/Conditions/ConditionOr.cs +++ b/Scripts/Goap/Conditions/ConditionOr.cs @@ -41,6 +41,9 @@ public bool IsSatisfied(GoapState state) /// /// Estimates the cost of satisfying this OR condition given the current state. /// + /// + /// This method uses the minimum cost of the sub-conditions to estimate the cost of satisfying the OR condition. + /// /// The current state. /// Optional dictionary of costs per state difference. /// The estimated cost of satisfying the condition. diff --git a/Scripts/Goap/Conditions/NoCondition.cs b/Scripts/Goap/Conditions/NoCondition.cs index 3f66958..2e8aade 100644 --- a/Scripts/Goap/Conditions/NoCondition.cs +++ b/Scripts/Goap/Conditions/NoCondition.cs @@ -3,7 +3,7 @@ namespace TsunagiModule.Goap { /// - /// Represents a condition that is always satisfied and has no cost. + /// Represents a condition that is always satisfied /// public struct NoCondition : ConditionInterface { @@ -20,6 +20,9 @@ public bool IsSatisfied(GoapState state) /// /// Estimates the cost of satisfying the condition. /// + /// + /// this is always 0 because the condition is always satisfied. + /// /// The current GOAP state. /// Optional dictionary of costs per state difference. /// Always returns 0. From 922caee1c9ca2f3fd6c19615ef13cc2a4d69f32f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:46:13 +0900 Subject: [PATCH 090/108] comment GoapSolver --- Scripts/Goap/GoapSolver/GoapResult.cs | 5 +- .../GoapSolver/GoapSolver_ActionControl.cs | 3 - .../Goap/GoapSolver/GoapSolver_Pathfinding.cs | 57 ++++++------------- 3 files changed, 20 insertions(+), 45 deletions(-) diff --git a/Scripts/Goap/GoapSolver/GoapResult.cs b/Scripts/Goap/GoapSolver/GoapResult.cs index b3c7ffc..c71d46e 100644 --- a/Scripts/Goap/GoapSolver/GoapResult.cs +++ b/Scripts/Goap/GoapSolver/GoapResult.cs @@ -1,8 +1,11 @@ namespace TsunagiModule.Goap { /// - /// Represents the result of solving a GOAP (Goal-Oriented Action Planning) problem. + /// The result returned by the /// + /// + /// You may want to check to check if the goal was achieved or not, before accessing + /// public struct GoapResult { /// diff --git a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs index 2d3404f..2f098ec 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -2,9 +2,6 @@ namespace TsunagiModule.Goap { - /// - /// Provides methods to manage the pool of actions in the GOAP solver. - /// public partial class GoapSolver { /// diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index c1fc328..c26d16c 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -4,7 +4,6 @@ namespace TsunagiModule.Goap { - //https://medium.com/@hanxuyang0826/mastering-pathfinding-with-a-star-a-practical-guide-and-c-implementation-f76f1643d8c3 /// /// Implements the A* pathfinding algorithm for the GOAP solver. /// @@ -16,68 +15,36 @@ public partial class GoapSolver private class AstarQueue : IComparable { /// - /// The current state of the node. + /// simulated State /// public GoapState state; /// - /// The parent node in the search path. + /// previous queue /// public AstarQueue parent; - - /// - /// The action that led to this state. - /// public GoapAction? action; - - /// - /// The cost incurred to reach this state. - /// public double currentCost; - - /// - /// The estimated cost to reach the goal from this state. - /// - public double heuristicCost; - - /// - /// Gets the total cost (current cost + heuristic cost) for this node. - /// - public double totalCost => currentCost + heuristicCost; - - /// - /// Gets the depth of this node in the search tree. - /// + public double estimatedCost; + public double totalCost => currentCost + estimatedCost; public int depth => parent == null ? 0 : parent.depth + 1; - /// - /// Initializes a new instance of the class. - /// - /// The current state of the node. - /// The parent node in the search path. - /// The cost incurred to reach this state. - /// The estimated cost to reach the goal from this state. - /// The action that led to this state. public AstarQueue( GoapState state, AstarQueue parent, double currentCost, - double heuristicCost, + double estimatedCost, GoapAction? action ) { this.state = state; this.parent = parent; this.currentCost = currentCost; - this.heuristicCost = heuristicCost; + this.estimatedCost = estimatedCost; this.action = action; } - /// - /// Compares this node with another node based on their total costs. - /// - /// The other node to compare with. - /// A negative value if this node has a lower total cost, a positive value if it has a higher total cost, or zero if the costs are equal. + // for sorting public int CompareTo(AstarQueue other) { if (totalCost < other.totalCost) @@ -90,6 +57,7 @@ public int CompareTo(AstarQueue other) /// /// Cost estimation weights for each state index. + /// This is simply multipied with the diff from the goal of State /// private Dictionary costPerDiffes = new Dictionary(); @@ -98,7 +66,8 @@ public int CompareTo(AstarQueue other) /// /// The current state. /// The goal condition to satisfy. - /// The maximum depth of the search tree. + /// The maximum depth of the search tree. + /// If the search exceeds this depth, it will terminate early. /// The result of the GOAP problem-solving process. public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) { @@ -111,6 +80,9 @@ public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int max /// /// Computes the cost weights for each state index based on the current state. /// + /// + /// This assume that the weight should be the largest costPerDiff about each State values + /// /// The current state. /// A dictionary mapping state indices to their cost weights. private Dictionary ComputeCostWeights(GoapState stateCurrent) @@ -144,6 +116,9 @@ private Dictionary ComputeCostWeights(GoapState stateCurrent) /// The goal condition to satisfy. /// The maximum depth of the search tree. /// The result of the A* search process. + /// + /// Implementation of A* Pathfinding + /// private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, int maxDepth) { PriorityQueue queue = new PriorityQueue(); From 0108ea4506f58e510e19bf02a60b05898d652a44 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 13:55:48 +0900 Subject: [PATCH 091/108] comment GOAPValue --- Scripts/Goap/GoapValue/GoapValue.cs | 17 ++++++++++++----- Scripts/Goap/GoapValue/GoapValueInterface.cs | 4 ++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Scripts/Goap/GoapValue/GoapValue.cs b/Scripts/Goap/GoapValue/GoapValue.cs index cf8703e..2ec52aa 100644 --- a/Scripts/Goap/GoapValue/GoapValue.cs +++ b/Scripts/Goap/GoapValue/GoapValue.cs @@ -6,12 +6,16 @@ namespace TsunagiModule.Goap /// Value used in Goap system. /// /// - /// This is implemented for support integrated control of State Control, being independent of value type. + /// This is implemented for State supporting as much type as possible.
+ /// You can use:
+ /// - + /// - + /// - + /// - + ///
is recommended instead of for accurate pathfinding. ///
- /// - /// Represents a value used in the GOAP system, supporting integrated control of state values independent of their type. - /// - /// The type of the value. Must be a struct and implement . + /// The type of the value. Must be a struct and implement . + /// public struct GoapValue : GoapValueInterface, IEquatable> where T : struct, IEquatable { @@ -47,6 +51,9 @@ public bool Equals(GoapValue other) /// /// Returns the hash code for the current value. /// + /// + /// the GetHashCode() of the original value should be implemented + /// /// The hash code for the current value. public override int GetHashCode() { diff --git a/Scripts/Goap/GoapValue/GoapValueInterface.cs b/Scripts/Goap/GoapValue/GoapValueInterface.cs index fcddb6d..2a8bbbe 100644 --- a/Scripts/Goap/GoapValue/GoapValueInterface.cs +++ b/Scripts/Goap/GoapValue/GoapValueInterface.cs @@ -5,6 +5,10 @@ namespace TsunagiModule.Goap /// /// Defines an interface for GOAP value types. /// + /// + /// You may want to use for real instance. + /// This is implemented for State supporting as much type as possible. + /// public interface GoapValueInterface { /// From 5463a8b0be45230ed79e237dcbbb1e30c2d5ad73 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 14:00:28 +0900 Subject: [PATCH 092/108] comment StateDiff --- Scripts/Goap/StateDiff/StateDiffAddition.cs | 6 ++++++ Scripts/Goap/StateDiff/StateDiffMapping.cs | 4 ++++ Scripts/Goap/Utils/PriorityQueue.cs | 2 ++ 3 files changed, 12 insertions(+) diff --git a/Scripts/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs index 6e551c9..3e084fe 100644 --- a/Scripts/Goap/StateDiff/StateDiffAddition.cs +++ b/Scripts/Goap/StateDiff/StateDiffAddition.cs @@ -5,6 +5,12 @@ namespace TsunagiModule.Goap /// /// Represents an addition operation for state differences in the GOAP system. /// + /// + /// This is example of adding 3 at index0 + /// + /// new StateDiffAddition("index0", 1) + /// + /// /// The type of the state values. Must be a struct and implement , , and . public struct StateDiffAddition : StateDiffInterface where T : struct, IConvertible, IComparable, IComparable, IEquatable diff --git a/Scripts/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs index 4a63ca4..955ddb4 100644 --- a/Scripts/Goap/StateDiff/StateDiffMapping.cs +++ b/Scripts/Goap/StateDiff/StateDiffMapping.cs @@ -6,6 +6,10 @@ namespace TsunagiModule.Goap /// /// Represents a mapping operation for state differences in the GOAP system. /// + /// + /// the State value is replaced according to the given mapping dictionary. + /// The unwritten state value in the dictionary will not be changed. + /// /// The type of the state values. Must be a struct and implement . public struct StateDiffMapping : StateDiffInterface where T : struct, IEquatable diff --git a/Scripts/Goap/Utils/PriorityQueue.cs b/Scripts/Goap/Utils/PriorityQueue.cs index 9fd3563..212058c 100644 --- a/Scripts/Goap/Utils/PriorityQueue.cs +++ b/Scripts/Goap/Utils/PriorityQueue.cs @@ -1,3 +1,4 @@ +// This entire file is written by GitHub Copilot using System; using System.Collections.Generic; @@ -7,6 +8,7 @@ namespace TsunagiModule.Goap.Utils /// Represents a generic priority queue that organizes elements based on their priority. /// /// The type of elements in the priority queue. Must implement . + /// public class PriorityQueue where T : IComparable { From ecb88ef7068fe6bdf8027a6e6c4935e18be54b92 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 14:04:00 +0900 Subject: [PATCH 093/108] comments --- Scripts/Goap/GoapAction.cs | 6 +++++- Scripts/Goap/GoapState.cs | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Scripts/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs index e64d835..4e71e13 100644 --- a/Scripts/Goap/GoapAction.cs +++ b/Scripts/Goap/GoapAction.cs @@ -1,8 +1,12 @@ namespace TsunagiModule.Goap { /// - /// Represents an action in the GOAP (Goal-Oriented Action Planning) system. + /// Represents an action in the GOAP system. /// + /// + /// This contains the condition and effect of the action. + /// The will conduct a pathfinding according to these conditions and effects. + /// public struct GoapAction { /// diff --git a/Scripts/Goap/GoapState.cs b/Scripts/Goap/GoapState.cs index a758e5f..9114317 100644 --- a/Scripts/Goap/GoapState.cs +++ b/Scripts/Goap/GoapState.cs @@ -7,6 +7,9 @@ namespace TsunagiModule.Goap /// /// Represents the state in the GOAP (Goal-Oriented Action Planning) system. /// + /// + /// You need to write what is happening in the game world into this . + /// public struct GoapState : IEquatable { /// From 6c114679713434040395cc08b00fa8e9cdcd540f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 15:43:56 +0900 Subject: [PATCH 094/108] state diff as double --- Scripts/Goap/StateDiff/StateDiffAddition.cs | 2 +- Scripts/Goap/StateDiff/StateDiffInterface.cs | 2 +- Scripts/Goap/StateDiff/StateDiffMapping.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Scripts/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs index 3e084fe..2b58dcf 100644 --- a/Scripts/Goap/StateDiff/StateDiffAddition.cs +++ b/Scripts/Goap/StateDiff/StateDiffAddition.cs @@ -28,7 +28,7 @@ public struct StateDiffAddition : StateDiffInterface /// /// Gets the difference value associated with this addition operation. /// - public float diff => Convert.ToSingle(additionValue); + public double diff => Convert.ToDouble(additionValue); /// /// Initializes a new instance of the struct. diff --git a/Scripts/Goap/StateDiff/StateDiffInterface.cs b/Scripts/Goap/StateDiff/StateDiffInterface.cs index 8f12ce7..d3fcc73 100644 --- a/Scripts/Goap/StateDiff/StateDiffInterface.cs +++ b/Scripts/Goap/StateDiff/StateDiffInterface.cs @@ -21,6 +21,6 @@ public interface StateDiffInterface /// /// Gets the difference value associated with this operation. /// - public float diff { get; } + public double diff { get; } } } diff --git a/Scripts/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs index 955ddb4..f3f8df6 100644 --- a/Scripts/Goap/StateDiff/StateDiffMapping.cs +++ b/Scripts/Goap/StateDiff/StateDiffMapping.cs @@ -27,7 +27,7 @@ public struct StateDiffMapping : StateDiffInterface /// /// Gets the difference value for this mapping operation. /// - public float diff => 1f; + public double diff => 1.0; /// /// Initializes a new instance of the struct. From bc14e250938674f7dccccdc725118fe0a4457c36 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 15:53:59 +0900 Subject: [PATCH 095/108] rename IsAvailable --- Scripts/Goap/GoapAction.cs | 2 +- Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs index 4e71e13..d5caaee 100644 --- a/Scripts/Goap/GoapAction.cs +++ b/Scripts/Goap/GoapAction.cs @@ -74,7 +74,7 @@ double cost /// /// The current state. /// True if the action is available; otherwise, false. - public bool Available(GoapState state) + public bool IsAvailable(GoapState state) { return condition.IsSatisfied(state); } diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index c26d16c..6032a0e 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -175,7 +175,7 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i foreach (GoapAction action in actionPool.Values) { // if the action is available... - if (action.condition.IsSatisfied(current.state)) + if (action.IsAvailable(current.state)) { // ...go to this state GoapState nextState = action.Simulate(current.state, overwrite: false); From 1669892cc8619ac9cc26322a359029b3c14f4f17 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 15:54:09 +0900 Subject: [PATCH 096/108] add remarks --- Scripts/Goap/StateDiff/StateDiffInterface.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Scripts/Goap/StateDiff/StateDiffInterface.cs b/Scripts/Goap/StateDiff/StateDiffInterface.cs index d3fcc73..bb283dc 100644 --- a/Scripts/Goap/StateDiff/StateDiffInterface.cs +++ b/Scripts/Goap/StateDiff/StateDiffInterface.cs @@ -21,6 +21,9 @@ public interface StateDiffInterface /// /// Gets the difference value associated with this operation. /// + /// + /// This is used to estimate cost weight by + /// public double diff { get; } } } From bde2a705f601cf77e858a8e716d09f2e9571cf9f Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:03:54 +0900 Subject: [PATCH 097/108] class diagram --- Docs/GOAP_ClassDiagram.png | Bin 0 -> 138494 bytes Docs/GOAP_ClassDiagram.puml | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Docs/GOAP_ClassDiagram.png create mode 100644 Docs/GOAP_ClassDiagram.puml diff --git a/Docs/GOAP_ClassDiagram.png b/Docs/GOAP_ClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..c9348884108135a41594d2b4fe5f26c9f1a226ce GIT binary patch literal 138494 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%0D=YyUiJrH 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 literal 0 HcmV?d00001 diff --git a/Docs/GOAP_ClassDiagram.puml b/Docs/GOAP_ClassDiagram.puml new file mode 100644 index 0000000..d6a9fdd --- /dev/null +++ b/Docs/GOAP_ClassDiagram.puml @@ -0,0 +1,63 @@ +@startuml + +interface GoapValueInterface { + +Type type +} + +class GoapState { + -Dictionary values + +GoapValueInterface GetValue(string stateIndex) + +void SetValue(string stateIndex, GoapValueInterface value) + +GoapState Clone() +} +GoapState o-- GoapValueInterface : consists of > + +interface ConditionInterface { + +bool IsSatisfied(GoapState state) + +double EstimateCost(GoapState state, Dictionary costPerDiffes) +} +ConditionInterface ..> GoapState : reads > + +interface StateDiffInterface { + +string stateIndex + +GoapState Operate(GoapState state, bool overwrite) + +double diff +} +StateDiffInterface --> GoapState : operates on > + +class StateDiffSet { + -StateDiffInterface[] stateDiffs + +GoapState Apply(GoapState state, bool overwrite) +} +StateDiffSet o-- StateDiffInterface : delegates to > + +class GoapAction { + +string name + +ConditionInterface condition + +StateDiffSet stateDiffSet + +double cost + +bool IsAvailable(GoapState state) + +GoapState Simulate(GoapState state, bool overwrite) +} +GoapAction *-- ConditionInterface : delegates to > +GoapAction *-- StateDiffSet : delegates to > + +class GoapResult { + +GoapAction[] actions + +double cost + +int length + +bool success +} +GoapResult o-- GoapAction : has > + +class GoapSolver { + -GoapAction[] actionPool + +GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength) + +void AddAction(GoapAction action) +} +GoapSolver --> GoapResult : Generates > +GoapSolver --> GoapState : uses > +GoapSolver --> ConditionInterface : uses > +GoapSolver o-- GoapAction : stores > + +@enduml \ No newline at end of file From 89f3e563242820fc88242b9ae4633b1dac5c5f89 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:05:40 +0900 Subject: [PATCH 098/108] meta files --- Docs.meta | 8 +++ Docs/GOAP_ClassDiagram.png.meta | 117 +++++++++++++++++++++++++++++++ Docs/GOAP_ClassDiagram.puml.meta | 7 ++ 3 files changed, 132 insertions(+) create mode 100644 Docs.meta create mode 100644 Docs/GOAP_ClassDiagram.png.meta create mode 100644 Docs/GOAP_ClassDiagram.puml.meta diff --git a/Docs.meta b/Docs.meta new file mode 100644 index 0000000..66cd592 --- /dev/null +++ b/Docs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b04b84ea3c92a340a9d48a92d141ae3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Docs/GOAP_ClassDiagram.png.meta b/Docs/GOAP_ClassDiagram.png.meta new file mode 100644 index 0000000..5a38049 --- /dev/null +++ b/Docs/GOAP_ClassDiagram.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 92ca0d3f08666f3418e4b9b5316c70ec +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Docs/GOAP_ClassDiagram.puml.meta b/Docs/GOAP_ClassDiagram.puml.meta new file mode 100644 index 0000000..572965c --- /dev/null +++ b/Docs/GOAP_ClassDiagram.puml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b2df6d4814bb41b4f9c06cc2d27bab46 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 5c8dc49f5e4a0fcc3c45ca75b6800cbac7b057d2 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:08:16 +0900 Subject: [PATCH 099/108] refac --- Scripts/Goap/Conditions/ConditionOr.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Scripts/Goap/Conditions/ConditionOr.cs b/Scripts/Goap/Conditions/ConditionOr.cs index 2d5584e..e5601f8 100644 --- a/Scripts/Goap/Conditions/ConditionOr.cs +++ b/Scripts/Goap/Conditions/ConditionOr.cs @@ -49,9 +49,7 @@ public bool IsSatisfied(GoapState state) /// The estimated cost of satisfying the condition. public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) { - // shortest distance - double min = // shortest distance - double.MaxValue; + double min = double.MaxValue; foreach (ConditionInterface condition in conditions) { double cost = condition.EstimateCost(state, costPerDiffes: costPerDiffes); From d8880eab6345b725ea949e51c4d8e37522ef6898 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:21:36 +0900 Subject: [PATCH 100/108] quit optional overwrite params --- Scripts/Goap/GoapAction.cs | 4 ++-- Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- Scripts/Goap/StateDiff/StateDiffAddition.cs | 2 +- Scripts/Goap/StateDiff/StateDiffInterface.cs | 2 +- Scripts/Goap/StateDiff/StateDiffMapping.cs | 2 +- Scripts/Goap/StateDiff/StateDiffSet.cs | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Scripts/Goap/GoapAction.cs b/Scripts/Goap/GoapAction.cs index d5caaee..98e4de4 100644 --- a/Scripts/Goap/GoapAction.cs +++ b/Scripts/Goap/GoapAction.cs @@ -85,7 +85,7 @@ public bool IsAvailable(GoapState state) /// The current state. /// Whether to overwrite the current state or clone it. /// The resulting state after applying the action. - public GoapState Simulate(GoapState state, bool overwrite = true) + public GoapState Simulate(GoapState state, bool overwrite) { GoapState stateTarget; if (overwrite) @@ -97,7 +97,7 @@ public GoapState Simulate(GoapState state, bool overwrite = true) stateTarget = state.Clone(); } - stateTarget = stateDiffSet.Apply(stateTarget, overwrite: true); + stateTarget = stateDiffSet.Apply(stateTarget, overwrite); return stateTarget; } diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 6032a0e..10da156 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -178,7 +178,7 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i if (action.IsAvailable(current.state)) { // ...go to this state - GoapState nextState = action.Simulate(current.state, overwrite: false); + GoapState nextState = action.Simulate(current.state, false); double costCurrent = current.currentCost + action.cost; double costEstimated = EstimateCost(nextState, goal); queue.Enqueue( diff --git a/Scripts/Goap/StateDiff/StateDiffAddition.cs b/Scripts/Goap/StateDiff/StateDiffAddition.cs index 2b58dcf..004ac16 100644 --- a/Scripts/Goap/StateDiff/StateDiffAddition.cs +++ b/Scripts/Goap/StateDiff/StateDiffAddition.cs @@ -48,7 +48,7 @@ public StateDiffAddition(string stateIndex, T additionValue) /// Whether to overwrite the current state or clone it. /// The resulting state after applying the addition. /// Thrown when there is a type mismatch between the addition operation and the state value type. - public GoapState Operate(GoapState state, bool overwrite = true) + public GoapState Operate(GoapState state, bool overwrite) { // cloning or not GoapState stateTarget; diff --git a/Scripts/Goap/StateDiff/StateDiffInterface.cs b/Scripts/Goap/StateDiff/StateDiffInterface.cs index bb283dc..fd3086f 100644 --- a/Scripts/Goap/StateDiff/StateDiffInterface.cs +++ b/Scripts/Goap/StateDiff/StateDiffInterface.cs @@ -16,7 +16,7 @@ public interface StateDiffInterface /// The GOAP state to apply the difference to. /// Whether to overwrite the current state or clone it. /// The resulting state after applying the difference. - public GoapState Operate(GoapState state, bool overwrite = true); + public GoapState Operate(GoapState state, bool overwrite); /// /// Gets the difference value associated with this operation. diff --git a/Scripts/Goap/StateDiff/StateDiffMapping.cs b/Scripts/Goap/StateDiff/StateDiffMapping.cs index f3f8df6..7244dc8 100644 --- a/Scripts/Goap/StateDiff/StateDiffMapping.cs +++ b/Scripts/Goap/StateDiff/StateDiffMapping.cs @@ -47,7 +47,7 @@ public StateDiffMapping(string stateIndex, Dictionary mapping) /// Whether to overwrite the current state or clone it. /// The resulting state after applying the mapping. /// Thrown when there is a type mismatch between the mapping operation and the state value type. - public GoapState Operate(GoapState state, bool overwrite = true) + public GoapState Operate(GoapState state, bool overwrite) { // cloning or not GoapState stateTarget; diff --git a/Scripts/Goap/StateDiff/StateDiffSet.cs b/Scripts/Goap/StateDiff/StateDiffSet.cs index 5c675d9..4ba9731 100644 --- a/Scripts/Goap/StateDiff/StateDiffSet.cs +++ b/Scripts/Goap/StateDiff/StateDiffSet.cs @@ -25,7 +25,7 @@ public StateDiffSet(StateDiffInterface[] stateDiffes) /// The GOAP state to apply the differences to. /// Whether to overwrite the current state or clone it. /// The resulting state after applying the differences. - public GoapState Apply(GoapState state, bool overwrite = true) + public GoapState Apply(GoapState state, bool overwrite) { // cloning or not GoapState stateTarget; @@ -41,7 +41,7 @@ public GoapState Apply(GoapState state, bool overwrite = true) // apply all operations foreach (StateDiffInterface stateDiff in stateDiffes) { - stateDiff.Operate(stateTarget, overwrite: true); + stateDiff.Operate(stateTarget, true); } return stateTarget; From a4a1d3964476b5564218d05bdb1c9f4f0db55fca Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:25:04 +0900 Subject: [PATCH 101/108] refac curly braces SonarCSharp_S121 --- Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs | 6 ++++++ Scripts/Goap/Utils/PriorityQueue.cs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 10da156..e8c8846 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -48,9 +48,15 @@ public AstarQueue( public int CompareTo(AstarQueue other) { if (totalCost < other.totalCost) + { return -1; + } + if (totalCost > other.totalCost) + { return 1; + } + return 0; } } diff --git a/Scripts/Goap/Utils/PriorityQueue.cs b/Scripts/Goap/Utils/PriorityQueue.cs index 212058c..9cc3e36 100644 --- a/Scripts/Goap/Utils/PriorityQueue.cs +++ b/Scripts/Goap/Utils/PriorityQueue.cs @@ -34,7 +34,9 @@ public void Enqueue(T item) { int parentIndex = (childIndex - 1) / 2; if (elements[childIndex].CompareTo(elements[parentIndex]) >= 0) + { break; + } T temp = elements[childIndex]; elements[childIndex] = elements[parentIndex]; @@ -52,7 +54,9 @@ public void Enqueue(T item) public T Dequeue() { if (elements.Count == 0) + { throw new InvalidOperationException("The priority queue is empty."); + } T result = elements[0]; int lastIndex = elements.Count - 1; @@ -70,16 +74,22 @@ public T Dequeue() leftChildIndex < elements.Count && elements[leftChildIndex].CompareTo(elements[smallestIndex]) < 0 ) + { smallestIndex = leftChildIndex; + } if ( rightChildIndex < elements.Count && elements[rightChildIndex].CompareTo(elements[smallestIndex]) < 0 ) + { smallestIndex = rightChildIndex; + } if (smallestIndex == parentIndex) + { break; + } T temp = elements[parentIndex]; elements[parentIndex] = elements[smallestIndex]; From f691475cfbecc2d08cb378420e4d49875861d560 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:28:02 +0900 Subject: [PATCH 102/108] add ClearActionPool method to manage action pool --- Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs index 2f098ec..099bc35 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -39,5 +39,13 @@ public void ReplaceAction(string name, GoapAction action) { actionPool[name] = action; } + + /// + /// Clear all actions in the action pool. + /// + public void ClearActionPool() + { + actionPool.Clear(); + } } } From f7250ccac99e96fdf29d79f43db58f2bdbc153f5 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:28:58 +0900 Subject: [PATCH 103/108] set readonly for constructor-only-fields SonarCSharp_S2933 --- Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs | 3 ++- Scripts/Goap/Utils/PriorityQueue.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs index 099bc35..44c47b1 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -10,7 +10,8 @@ public partial class GoapSolver /// /// Maps action names to their corresponding instances. /// - private Dictionary actionPool = new Dictionary(); + public readonly Dictionary actionPool = + new Dictionary(); /// /// Adds a new action to the action pool. diff --git a/Scripts/Goap/Utils/PriorityQueue.cs b/Scripts/Goap/Utils/PriorityQueue.cs index 9cc3e36..51a0461 100644 --- a/Scripts/Goap/Utils/PriorityQueue.cs +++ b/Scripts/Goap/Utils/PriorityQueue.cs @@ -15,7 +15,7 @@ public class PriorityQueue /// /// The list of elements in the priority queue. /// - private List elements = new List(); + private readonly List elements = new List(); /// /// Gets the number of elements in the priority queue. From 2ad5cec54ce273ba28bd1c2c6f45f12f7ebb0454 Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:32:26 +0900 Subject: [PATCH 104/108] fix optional parameters for costPerDiffes --- Scripts/Goap/Conditions/Condition.cs | 2 +- Scripts/Goap/Conditions/ConditionAnd.cs | 2 +- Scripts/Goap/Conditions/ConditionInterface.cs | 5 +---- Scripts/Goap/Conditions/ConditionOr.cs | 2 +- Scripts/Goap/Conditions/NoCondition.cs | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Scripts/Goap/Conditions/Condition.cs b/Scripts/Goap/Conditions/Condition.cs index 96b8c84..50fd19b 100644 --- a/Scripts/Goap/Conditions/Condition.cs +++ b/Scripts/Goap/Conditions/Condition.cs @@ -86,7 +86,7 @@ public bool IsSatisfied(GoapState state) /// Optional dictionary of costs per state difference. If null, this will assume as 1.0 /// The estimated cost of satisfying the condition. /// Thrown when the state value type does not match the expected type. - public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes) { // if already satisfied... if (IsSatisfied(state)) diff --git a/Scripts/Goap/Conditions/ConditionAnd.cs b/Scripts/Goap/Conditions/ConditionAnd.cs index 92000c7..f6828f7 100644 --- a/Scripts/Goap/Conditions/ConditionAnd.cs +++ b/Scripts/Goap/Conditions/ConditionAnd.cs @@ -48,7 +48,7 @@ public bool IsSatisfied(GoapState state) /// The current GOAP state. /// Optional dictionary of costs per state difference. /// The estimated cost of satisfying the condition. - public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes) { // square root of sum of squares double sum = 0; diff --git a/Scripts/Goap/Conditions/ConditionInterface.cs b/Scripts/Goap/Conditions/ConditionInterface.cs index 9df45be..401e2f7 100644 --- a/Scripts/Goap/Conditions/ConditionInterface.cs +++ b/Scripts/Goap/Conditions/ConditionInterface.cs @@ -24,9 +24,6 @@ public interface ConditionInterface /// The current GOAP state. /// Optional dictionary of costs per state difference. /// The estimated cost of satisfying the condition. - public double EstimateCost( - GoapState state, - Dictionary costPerDiffes = null - ); + public double EstimateCost(GoapState state, Dictionary costPerDiffes); } } diff --git a/Scripts/Goap/Conditions/ConditionOr.cs b/Scripts/Goap/Conditions/ConditionOr.cs index e5601f8..e4a6d1d 100644 --- a/Scripts/Goap/Conditions/ConditionOr.cs +++ b/Scripts/Goap/Conditions/ConditionOr.cs @@ -47,7 +47,7 @@ public bool IsSatisfied(GoapState state) /// The current state. /// Optional dictionary of costs per state difference. /// The estimated cost of satisfying the condition. - public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes) { double min = double.MaxValue; foreach (ConditionInterface condition in conditions) diff --git a/Scripts/Goap/Conditions/NoCondition.cs b/Scripts/Goap/Conditions/NoCondition.cs index 2e8aade..da5bf4d 100644 --- a/Scripts/Goap/Conditions/NoCondition.cs +++ b/Scripts/Goap/Conditions/NoCondition.cs @@ -26,7 +26,7 @@ public bool IsSatisfied(GoapState state) /// The current GOAP state. /// Optional dictionary of costs per state difference. /// Always returns 0. - public double EstimateCost(GoapState state, Dictionary costPerDiffes = null) + public double EstimateCost(GoapState state, Dictionary costPerDiffes) { return 0; } From f6d3be1eaf89615bdee09e221b751e0ea2a7b0fb Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:33:19 +0900 Subject: [PATCH 105/108] quit optional parameters SonarCSharp_S2360 --- Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index e8c8846..9ba86a4 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -75,7 +75,7 @@ public int CompareTo(AstarQueue other) /// The maximum depth of the search tree. /// If the search exceeds this depth, it will terminate early. /// The result of the GOAP problem-solving process. - public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength = 10) + public GoapResult Solve(GoapState stateCurrent, ConditionInterface goal, int maxLength) { // compute cost weights costPerDiffes = ComputeCostWeights(stateCurrent); From f6510cae32469990ef0b46634bc71271149a065d Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:35:15 +0900 Subject: [PATCH 106/108] fix protection https://app.codacy.com/gh/tsunagi-ai/TsunagiModuleUnity/issues/131317239960 --- Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs index 44c47b1..cc78592 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_ActionControl.cs @@ -10,7 +10,7 @@ public partial class GoapSolver /// /// Maps action names to their corresponding instances. /// - public readonly Dictionary actionPool = + private readonly Dictionary actionPool = new Dictionary(); /// From 86d14ce0209636e55cd1c39cf292081d833f656b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:38:54 +0900 Subject: [PATCH 107/108] refac: seperating result creation https://app.codacy.com/gh/tsunagi-ai/TsunagiModuleUnity/issues/131317239960 --- .../Goap/GoapSolver/GoapSolver_Pathfinding.cs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index 9ba86a4..a69a005 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -150,25 +150,8 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i // if arrived at goal... if (goal.IsSatisfied(current.state)) { - // ...return the Action path - - // action list - List actions = new List(); - while (current.action != null) - { - actions.Add(current.action ?? throw new InvalidOperationException()); - current = current.parent; - } - actions.Reverse(); - - // create GoapResult - GoapResult result = new GoapResult( - actions.ToArray(), - current.currentCost, - true - ); - - return result; + // ...early return result + return CreateSuccessResult(current); } // close the current node @@ -201,6 +184,26 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i } } + return CreateFailureResult(); + } + + private GoapResult CreateSuccessResult(AstarQueue latestQueue) + { + // action list + List actions = new List(); + while (latestQueue.action != null) + { + actions.Add(latestQueue.action ?? throw new InvalidOperationException()); + latestQueue = latestQueue.parent; + } + actions.Reverse(); + + // create GoapResult + return new GoapResult(actions.ToArray(), latestQueue.currentCost, true); + } + + private GoapResult CreateFailureResult() + { return new GoapResult(null, -1, false); } From 0d6fb138ec3ba8919c78aa4b7d730d5d6211d94b Mon Sep 17 00:00:00 2001 From: konbraphat51 Date: Fri, 25 Apr 2025 16:41:35 +0900 Subject: [PATCH 108/108] seperate enquing https://app.codacy.com/gh/tsunagi-ai/TsunagiModuleUnity/issues/131317239960 --- .../Goap/GoapSolver/GoapSolver_Pathfinding.cs | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs index a69a005..5866fbc 100644 --- a/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs +++ b/Scripts/Goap/GoapSolver/GoapSolver_Pathfinding.cs @@ -160,33 +160,37 @@ private GoapResult SolveAstar(GoapState stateCurrent, ConditionInterface goal, i // if not arrived to the max depth... if (current.depth < maxDepth) { - // ...find next nodes - foreach (GoapAction action in actionPool.Values) - { - // if the action is available... - if (action.IsAvailable(current.state)) - { - // ...go to this state - GoapState nextState = action.Simulate(current.state, false); - double costCurrent = current.currentCost + action.cost; - double costEstimated = EstimateCost(nextState, goal); - queue.Enqueue( - new AstarQueue( - nextState, - current, - costCurrent, - costEstimated, - action - ) - ); - } - } + // ...enqueue next nodes + EnqueueNextActions(queue, current, goal); } } return CreateFailureResult(); } + private void EnqueueNextActions( + PriorityQueue queue, + AstarQueue current, + ConditionInterface goal + ) + { + // ...find next nodes + foreach (GoapAction action in actionPool.Values) + { + // if the action is available... + if (action.IsAvailable(current.state)) + { + // ...go to this state + GoapState nextState = action.Simulate(current.state, false); + double costCurrent = current.currentCost + action.cost; + double costEstimated = EstimateCost(nextState, goal); + queue.Enqueue( + new AstarQueue(nextState, current, costCurrent, costEstimated, action) + ); + } + } + } + private GoapResult CreateSuccessResult(AstarQueue latestQueue) { // action list