Git Product home page Git Product logo

mountain-goap's People

Contributors

benhawkinsicon avatar caesuric avatar giollord avatar janwerder avatar thomasbartanen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

mountain-goap's Issues

Multi-goal

Multiple goals should be supported, right?

When I have this happiness-increaser based example it runs until it hits 25 as expected:

public static void RunBasicExample()
{
    var ag = new GoapAgent(
        name: "Happiness Agent",
        state: new()
        {
            { "happiness", 0 },
            { "complete", false }
        },
        goals: new()
        {
            new Goal(
                name: "Acquire Max Happiness",
                desiredState: new()
                {
                    { "happiness", 25 }
                })
        },
        sensors: new()
        {
            new Sensor(CheckComplete)
        },
        actions: new()
        {
            new MountainGoap.Action(
                name: "Increase Happiness",
                executor: IncreaseHappiness,
                arithmeticPostconditions: new()
                {
                    { "happiness", 1 }
                })                    
        });

    while ((bool)ag.State["complete"] == false)
        ag.Step(StepMode.AllActions);
}

        private static void CheckComplete(GoapAgent agent)
        {
            if ((int)agent.State["happiness"] >= 25)
                agent.State["complete"] = true;
        }

        private static ExecutionStatus IncreaseHappiness(GoapAgent agent, MountainGoap.Action action)
        {
            Console.WriteLine("Happiness is at {0} - Increasing!", agent.State["happiness"]);
            return ExecutionStatus.Succeeded;
        }

But that's pretty contrived as it's basically a goap version of a while-loop. Whenever I add an additional goal + action, it gets an IndexOutOfBounds exception related to A*- this setup should be solvable though, right?

 public static void RunAdvancedExample()
 {
     var ag = new GoapAgent(
         name: "Phase 1",
         state: new()
         {
             { "happiness", 0 },
             { "complete", false },
             { "anotherThingDone", false }
         },
         goals: new()
         {
             new Goal(
                 name: "Acquire Max Happiness",
                 desiredState: new()
                 {
                     { "happiness", 25 }                            
                 }),
             new Goal(
                 name: "Do the other thing",
                 desiredState: new()
                 {
                     { "anotherThingDone", true }
                 })
         },
         sensors: new()
         {
             new Sensor(CheckComplete)
         },
         actions: new()
         {
             new MountainGoap.Action(
                 name: "Increase Happiness",
                 executor: IncreaseHappiness,
                 arithmeticPostconditions: new()
                 {
                     { "happiness", 1 }
                 }),
             new MountainGoap.Action(
                 name: "Do the other thing",
                 executor: DoAnotherThing,
                 postconditions: new()
                 {
                     { "anotherThingDone", true }
                 })
         });

     while ((bool)ag.State["complete"] == false)
         ag.Step(StepMode.AllActions);

    // not shown: update to CheckComplete to also consider anotherThingDone == true
 }

        private static ExecutionStatus DoAnotherThing(GoapAgent agent, MountainGoap.Action action)
        {
            Console.WriteLine("Doing the other thing");
            return ExecutionStatus.Succeeded;
        }

Tried:

  • Adding weights to goals
  • remove StepMode.AllActions on ag.Step()
  • Instead of additional Goal, adding { "anotherThingDone", true } to the single goal along with happiness == 25

Is it because they are booleans? Is this even supported? What am I missing?

Is there a way to access parameters of actions back in the chain?

Hi,
I want to implement target selection feature.
I would like my agent to consider various factors while determining it's target. Distance, strength and things I might want to add in the future. Also I want the agent to be able to reconsider the target after it has been selected.

In the approach that I came up with the agent would consider the cost of each branch of actions for a tree of possibilities.

For example I could have 2 actions: move and attack and two possible targets.
For each possible target I would like the agent to consider the cost of moving towards them via permutation selectors in move action.
And then in attack action I would like agent to consider the cost based on the strength of the target that it decided to move towards in the previous move action. But there is a problem. In attack action I don't know how to access the target parameter from the move action or if it's even possible.

Is it possible? Or if not then could we make it possible?
Or maybe there is some better approach that I didn't think of for target selection?

Thanks!

Create a new Thread every time Update() ?

   /// <summary>Executes an asynchronous step of agent work.</summary>
    private void StepAsync()
    {
      if (!this.IsBusy && !this.IsPlanning)
      {
        this.IsPlanning = true;
        new Thread((ThreadStart) (() => Planner.Plan(this, this.CostMaximum, this.StepMaximum))).Start();
      }
      else
      {
        if (this.IsPlanning)
          return;
        this.Execute();
      }
    }

I'm not good at C# Unity, but I wonder if this code is bad for performance. Creating a new thread every time Update() is called sounds like a very bad idea. Could you explain this to me?

Action executor runs once every 3 steps

Hello,

First of all thanks for sharing your hard work @caesuric. Out of all the alternatives I've looked up this one seemed to have the most intuitive API for me. I think it's great and I hope to see it grow!

So regarding my problem.
I'm trying to implement mountain goap in my real time diablo like game in Unity.
I want my agent to follow the player once he enters agent's vision. I've got goals, sensors, actions and state setup and I'm executing agent.Step() on every update as documentation suggests.

And It works but there is one issue. The executor is fired once every 3 steps which results in choppy inaccurate movement because my movement executor is meant to be run on every update/step.

Have you encountered this issue before? I'd appreciate any help. Thanks!

Extreme Goal Sequence Issues

I noticed this when trying out extreme goals, and was able to quickly reproduce by modifying the ExtremeHappinessIncrementer.cs example. It appears as though actions whose postconditions are non-arithmetic will always satisfy an ExtremeGoal's MeetsGoal, which leads to infinite looping if any actions are non-arithmetic.

In my case, I need to create a sequence leading to an arithmetic action, where normal postconditions are necessary to plan for the arithmetic action's preconditions.

Below is a minimal modification where I make "Seek Greater Happiness" have a dummy postcondition, and it is chosen continuously over the arithmetic action.

        internal static void Run() {
            _ = new DefaultLogger();
            Agent agent = new(
                name: "Happiness Agent",
                state: new() {
                    { "happiness", 0 },
                },
                goals: new() {
                    new ExtremeGoal(
                        name: "Maximize Happiness",
                        desiredState: new() {
                            { "happiness", true }
                        })
                },
                actions: new() {
                    new(
                        name: "Seek Happiness",
                        executor: SeekHappinessAction,
                        arithmeticPostconditions: new() {
                            {
                                "happiness",
                                1
                            }
                        }
                    ),
                    new(
                        name: "Seek Greater Happiness",
                        executor: SeekGreaterHappinessAction,
                        postconditions: new()
                        {
                            { "anything", true }
                        }
                    ),
                }
            );
            while (agent.State["happiness"] is int happiness && happiness != 10) {
                agent.Step();
                Console.WriteLine($"NEW HAPPINESS IS {agent.State["happiness"]}");
            }
        }

And i've tracked the execution to this (seemingly incorrect) evaluation of MeetsGoal. I am wondering if it is because this method only checks isLowerThan / isHigherThan when it should be isLowerThanOrEqualTo since no arithmetic difference means no progress towards the extreme goal is made.

            // ActionAStar.cs (line 47)
            while (frontier.Count > 0) {
                var current = frontier.Dequeue();
                // this is always true for non-arithmetic postconditions
                if (MeetsGoal(current, start)) { 
                    FinalPoint = current;
                    break;
                }
                foreach (var next in graph.Neighbors(current)) {
                    float newCost = CostSoFar[current] + next.Cost(current.State);
                    if (newCost > costMaximum) continue;
                    if (!CostSoFar.ContainsKey(next) || newCost < CostSoFar[next]) {
                        CostSoFar[next] = newCost;
                        float priority = newCost + Heuristic(next, goal, current);
                        frontier.Enqueue(next, priority);
                        CameFrom[next] = current;
                        Agent.TriggerOnEvaluatedActionNode(next, CameFrom);
                    }
                }
            }

Though I might just be missing something

Car demo not working properly

To reproduce: Build and run Examples.dll with the car argument.

Problem: The agent is supposed to get in their car and drive 50 miles efficiently. The agent will instead walk 50 miles. The agent seems to stop evaluating action sequences once it finds this simple action sequence that solves their issue.

This can be "fixed" by switching both the walk and drive actions from having postconditions of distanceTraveled 50 to having arithmeticPostconditions of distanceTraveled 25, but it should work in its current state.

Reproducing a minimal example with arithmeticPostconditions

Hi,
I'm trying a minimal example with the arithmeticPostconditions, but I'm getting an infinite loop.

_ = new AgentLogger(false, "npc.log");

this.goals = new List<BaseGoal>(){
new Goal(
    name: "Goal1",
    desiredState: new() {
        { "i", new ComparisonValuePair {
            Value = 100,
            Operator = ComparisonOperator.GreaterThan
        } }
    },
    weight: 1f
),
};

this.actions = new List<MountainGoap.Action>(){
new MountainGoap.Action(
    name: "Action1",
    executor: (Agent agent, MountainGoap.Action action) => {
        return ExecutionStatus.Succeeded;
    },
    arithmeticPostconditions: new Dictionary<string, object> {
        { "i", 10 }
    },
    cost: 0.5f
),
};

this.agent = new Agent(
    goals: this.goals,
    actions: this.actions,
    state: new() {
        { "i", 0 }
    }
);

Afterwards I'm executing the Step function once, but viewing the log I can see an infinite loop.

2023-12-13 21:19:23.174 +01:00 [INF] Agent Agent a37d195d-ddf0-43ee-b5f5-2ce034fabe8a is working.
2023-12-13 21:19:23.201 +01:00 [INF] Agent Agent a37d195d-ddf0-43ee-b5f5-2ce034fabe8a started planning.
2023-12-13 21:19:23.211 +01:00 [INF] Evaluating node Action1 with 0 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 1 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 2 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 3 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 4 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 5 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 6 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 7 nodes leading to it.
2023-12-13 21:19:23.212 +01:00 [INF] Evaluating node Action1 with 8 nodes leading to it.
2023-12-13 21:19:23.213 +01:00 [INF] Evaluating node Action1 with 9 nodes leading to it.
2023-12-13 21:19:23.213 +01:00 [INF] Evaluating node Action1 with 10 nodes leading to it.
2023-12-13 21:19:23.213 +01:00 [INF] Evaluating node Action1 with 11 nodes leading to it.
2023-12-13 21:19:23.213 +01:00 [INF] Evaluating node Action1 with 12 nodes leading to it.
2023-12-13 21:19:23.213 +01:00 [INF] Evaluating node Action1 with 13 nodes leading to it.
[...]

I'm most likely missing something, can you give me a hint?

"Parallelized" actions question

I am experimenting with agents that understand how to use multiple managed resources to execute on goals.

I would like to avoid resource-scoped agents for now. There are occasionally times when the agent needs to perform actions in parallel (simultaneously run health check on 3 subsystems). I have identified a pretty ugly solution which allows marking actions as parallel, and enables the plan to execute these actions in quick succession, while waiting for them all to finish.

The code below uses execution status to indicate work is only done on the first call to the executor while tracking the lifecycle of the action and still letting the plan execute on neighboring parallel nodes.

I don't understand fully how the planner utilizes ExecutionStatus.NotYetExecuted, but I am assuming this would have unwanted consquences. The current behavior works but I am wondering your thoughts on best/worse ways of achieving this.

public static ExecutionStatus MockExecutor(Agent agent, Action action, string lifecycleKey, int seconds=1, bool parallel=false, double failRate=0)
{
    /// execute first time
    if (!agent.State.ContainsKey(lifecycleKey))
    {
        agent.State[lifecycleKey] = DateTime.Now;
        return ExecutionStatus.Executing;
    }

    DateTime startTime = (DateTime)agent.State[lifecycleKey]!;
    if ((DateTime.Now - startTime).TotalSeconds > seconds)
    {
        /// clean up
        agent.State.Remove(lifecycleKey);
        return MaybeFail(failRate) ? ExecutionStatus.Failed : ExecutionStatus.Succeeded;
    }
    
    /// indicate not executing
    return parallel ? ExecutionStatus.NotYetExecuted : ExecutionStatus.Executing;
}

Add stateMutator callback option to actions

PROBLEM: It is not possible for an action to modify state during planning in a known way that is dependent upon factors such as action parameters.

SOLUTION: Implement stateMutator callback option. stateMutator will take the state and the action and will be expected to make modifications to state based on the action parameters. stateMutators will be called not only after an action successfully runs, but also during the planning process, to dynamically introduce state changes based on permutations.

EXAMPLE: Say I have a card game. I want to maximize damage dealt to the opponent. I have the opponent's "hp" in state. However, my playCard action that can play any card in my hand doesn't know how much damage a given card will do. With a stateMutator, I can make a lambda function that takes the "cardPlayed" parameter and gets card.damage (or something similar) then decrements opponent hp by that value.

Cover shooter mechanics implementation

This isn't a bug or problem with the library, I'm just curious about how a specific situation might be implemented. I figured this would be the best place to ask for now.
I want my enemies to take cover from time to time, and shoot out of it. I figure my the goal would be minimizing the player's health, but I'm not sure how to make sure the enemy will consider their own safety. Maybe the goal would also maximize the enemy's safety, but I don't think that would work out. Should I try and bake in danger as part of an action's cost and rely on the agent re-planning? Any advice here would be helpful, thank you,

Make natively compatible with Unity

It seems that this uses C#10 features, which are incompatible with Unity without workarounds (I assume, but I can't find any that would work for me).
The most recent version of Unity (2023.2) through version 2021.2 use C#9. So unless you find an absolutely necessary reason for using C#10, I would recommend using 9 (Or, if you DO know a workaround, could you put that in the readme?). Of course I understand this is meant to work with more than just Unity, so that may not be your focus.

I'm in the process of removing things like global usings (and replacing them in each file) in order to use it within Unity. I can do a pull request when I'm done if you want.

Multi-threaded usage with Unity

Hi,

I'm using this library for an AI in a Unity game. Unity is supposed to use only one thread by default, but using this library the planning is done in another thread.

When I debug, I get the same error every time:
InvalidOperationException: Collection was modified; enumeration operation may not execute.
System.Collections.Generic.Dictionary2+Enumerator[TKey,TValue].MoveNext () (at <a40b0ad4a868437393ad434631fb6ff1>:0) System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement] (System.Collections.Generic.IEnumerable1[T] source, System.Func2[T,TResult] keySelector, System.Func2[T,TResult] elementSelector, System.Collections.Generic.IEqualityComparer1[T] comparer) (at <53701cec7cfb4b5ba84d9e7813b871a8>:0) System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement] (System.Collections.Generic.IEnumerable1[T] source, System.Func2[T,TResult] keySelector, System.Func2[T,TResult] elementSelector) (at <53701cec7cfb4b5ba84d9e7813b871a8>:0)
MountainGoap.CopyDictionaryExtensionMethod.Copy (System.Collections.Generic.Dictionary2[TKey,TValue] dictionary) (at Assets/mountain-goap-0.10.0/MountainGoap/Internals/CopyDictionaryExtensionMethod.cs:19) MountainGoap.ActionNode..ctor (MountainGoap.Action action, System.Collections.Generic.Dictionary2[TKey,TValue] state, System.Collections.Generic.Dictionary`2[TKey,TValue] parameters) (at Assets/mountain-goap-0.10.0/MountainGoap/Internals/ActionNode.cs:21)
MountainGoap.Planner.Plan (MountainGoap.Agent agent, System.Single costMaximum) (at Assets/mountain-goap-0.10.0/MountainGoap/Internals/Planner.cs:26)
MountainGoap.Agent.b__62_0 () (at Assets/mountain-goap-0.10.0/MountainGoap/Agent.cs:187)
System.Threading.ThreadHelper.ThreadStart_Context (System.Object state) (at :0)
System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) (at :0)
System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) (at :0)
System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state) (at :0)
System.Threading.ThreadHelper.ThreadStart () (at :0)
UnityEngine.<>c:b__0_0(Object, UnhandledExceptionEventArgs)

It seems that the error claims that I change the state of an agent in the middle of an enumeration.
This is probably done by changing the state in another thread.
I have only one agent in the system, and the state is changed only in the sensors (that I add to the agent in the constructor), and using the build-in mechanism for postconditions.
There aome keys (strings) for the state that I access both in the sensors and the pos-conditions, maybe this is the problem.
From the documentation it seems that the sensors should change the agent's state, so I assume a sensor should not run in parallel to an Enumaration

I can understand why if the executor will change the agent's state that might cause the problem, but I made sure that the executor only reads the state, and never changes it.

What might be the problem?

External influences that require a replanning

Hi,

My agent is exposed to external influences that happen between the steps. These influences can be so drastic, that it requires a replanning to fulfill it's goals. So my question would be either one:

  • How can I safely manipulate the agents state so that it properly replans?
  • How can I manually retrigger the planning before each step?

I've prepared an example below that illustrates my problem:

 [Fact]
 public void ExternalManipulationTest()
 {

     List<BaseGoal> goals = new List<BaseGoal>(){
         new ComparativeGoal(
             name: "Goal1",
             desiredState: new() {
                 { "x", new ComparisonValuePair {
                     Value = 100,
                     Operator = ComparisonOperator.GreaterThan
                 } },
                 { "y", new ComparisonValuePair {
                     Value = 50,
                     Operator = ComparisonOperator.LessThan
                 }}
             },
             weight: 1f
         ),
     };

     List<MountainGoap.Action> actions = new List<MountainGoap.Action>(){
         new MountainGoap.Action(
             name: "Action1",
             executor: (Agent agent, MountainGoap.Action action) => {
                 return ExecutionStatus.Succeeded;
             },
             arithmeticPostconditions: new Dictionary<string, object> {
                 { "x", 50 },
                 { "y", 15 }
             },
             cost: 0.5f
         ),
          new MountainGoap.Action(
             name: "Action2",
             executor: (Agent agent, MountainGoap.Action action) => {
                 return ExecutionStatus.Succeeded;
             },
             arithmeticPostconditions: new Dictionary<string, object> {
                 { "y", -30 }
             },
             cost: 0.5f
         )
     };

     Agent agent = new Agent(
         goals: goals,
         actions: actions,
         state: new() {
             { "x", 0 },
             { "y", 0 }
         }
     );

     agent.Step(StepMode.OneAction);
     // The agent should have chosen Action1, because it gets it closer to the goal
     Assert.Equal(50, agent.State["x"]);

     //Now we'll manually change the state to make Action2 more attractive
     agent.State["y"] = 40;
     Assert.Equal(50, agent.State["x"]);
     Assert.Equal(40, agent.State["y"]);

     //The agent must now choose Action2, because it would otherwise violate the y condition in the goal
     agent.Step(StepMode.OneAction);
     Assert.Equal(10, agent.State["y"]);
     //But instead it has chosen Action1 again
 }

Can't initialize state entry with null value.

Hi,

I have a Target state that I would like to be empty initially.
Only when something enters agents vision the target should be populated.
So I tried to initiate it with null value but it results in the following error:

NullReferenceException: Object reference not set to an instance of an object
MountainGoap.ActionNode.StateMatches (MountainGoap.ActionNode otherNode) (at Assets/mountain-goap-0.6.5/MountainGoap/Internals/ActionNode.cs:82)

It's not that big of a deal. I had to resort to initiating the entry with an empty object like this:
{ AIStates.Target, new PositionTracker() }
And it works but it would be convinient if I had the ability to just pass null.

If anything the more serious problem might be that the error is not clear as to what is wrong, I had to dig up a little to figure out that it's about state not accepting null.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.