[ChatGPT] Hierarchical Task Network (HTN)
Tags: chatgpt, gamedev, csharp
Published: 2022-12-21 05:38:21

Continuing a [previous post on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7010816654171410432/).

Example of **[ChatGPT](https://chat.openai.com/chat)** implementation of **[HTN](https://en.wikipedia.org/wiki/Hierarchical_task_network)** in C#. 

I had to ask for it to complete the answer, which was truncated at first.

**Prompts:**
1. "_show a C# implementation of hierarchical task network_"
2. "_the answer is incomplete_"

**Full response:**
```csharp
using System;
using System.Collections.Generic;

/*
* This code defines a domain in which the goal is to build a wall given some bricks and mortar. 
* It also defines a set of tasks and methods that can be used to achieve this goal, and an HTN 
* planner that uses these tasks and methods to find a plan to solve the problem.
*/

namespace HTNExample
{
    // Define the domain and problem
    class WorldState
    {
        public bool HasBrick { get; set; }
        public bool HasMortar { get; set; }
        public bool HasWall { get; set; }
    }

    class Problem
    {
        public WorldState InitialState { get; set; }
        public WorldState GoalState { get; set; }
    }

    // Define the tasks and methods
    abstract class Task
    {
        public abstract bool CanExecute(WorldState state);
        public abstract void Execute(WorldState state);
    }

    class BuildWallTask : Task
    {
        public override bool CanExecute(WorldState state)
        {
            return state.HasBrick && state.HasMortar;
        }

        public override void Execute(WorldState state)
        {
            state.HasWall = true;
        }
    }

    class GetBrickTask : Task
    {
        public override bool CanExecute(WorldState state)
        {
            return !state.HasBrick;
        }

        public override void Execute(WorldState state)
        {
            state.HasBrick = true;
        }
    }

    class GetMortarTask : Task
    {
        public override bool CanExecute(WorldState state)
        {
            return !state.HasMortar;
        }

        public override void Execute(WorldState state)
        {
            state.HasMortar = true;
        }
    }

    class Method : Task
    {
        public List<Task> Subtasks { get; set; }

        public Method()
        {
            Subtasks = new List<Task>();
        }

        public override bool CanExecute(WorldState state)
        {
            foreach (var subtask in Subtasks)
            {
                if (!subtask.CanExecute(state))
                {
                    return false;
                }
            }
            return true;
        }

        public override void Execute(WorldState state)
        {
            foreach (var subtask in Subtasks)
            {
                subtask.Execute(state);
            }
        }
    }

    class GetBrickAndMortarMethod : Method
    {
        public GetBrickAndMortarMethod()
        {
            Subtasks.Add(new GetBrickTask());
            Subtasks.Add(new GetMortarTask());
        }
    }

    // Define the HTN planner
    class HTNPlanner
    {
        public static List<Task> Plan(Problem problem)
        {
            var plan = new List<Task>();
            var state = problem.InitialState;
            while (!state.HasWall)
            {
                if (state.HasBrick && state.HasMortar)
                {
                    plan.Add(new BuildWallTask());
                }
                else
                {
                    plan.Add(new GetBrickAndMortarMethod());
                }
                state = problem.InitialState;
                foreach (var task in plan)
                {
                    task.Execute(state);
                }
            }
            return plan;
        }
    }

    // Test the HTN planner
    class Program
    {
        static void Main(string[] args)
        {
            var problem = new Problem
            {
                InitialState = new WorldState { HasBrick = false, HasMortar = false, HasWall = false },
                GoalState = new WorldState { HasBrick = true, HasMortar = true, HasWall = true }
            };
            var plan = HTNPlanner.Plan(problem);
            foreach (var task in plan)
            {
                Console.WriteLine(task.GetType().Name);
            }
        }
    }
}
```

I also asked for a **second example**, which I tampered with a bit, but the core answer remains:

```csharp
using System;
using System.Collections.Generic;

/*
* A Hierarchical Task Network (HTN) is a planning representation that can be used to solve problems that 
* involve tasks that have hierarchical structure and require ordering and decomposition. Here's one way 
* you could implement an HTN in C#:
* 
* Define the domain and problem: Start by defining the domain in which you want to solve problems 
* using an HTN. This typically involves identifying the tasks that can be performed, the objects 
* and concepts that are relevant to the domain, and the relationships between them. You should also 
* define the problem you want to solve, which should include the initial state, the goal state, and the 
* available actions that can be used to transform the initial state into the goal state.
* 
* Implement the HTN planner: Next, you'll need to implement the HTN planner itself. This typically 
* involves defining the HTN methods and operators that can be used to decompose high-level tasks 
* into sequences of lower-level tasks. You'll also need to implement the planning algorithm that 
* selects and executes these methods and operators in order to achieve the goal state.
* 
* Test the HTN planner: Once you've implemented the HTN planner, you should test it to ensure that it's 
* working correctly. This typically involves defining a set of test cases that cover a range of different 
* domains and problems, and verifying that the planner is able to find a solution for each of these test cases.
* 
* This example defines a domain with three locations (A, B, C), two object types (Ball, Box), 
* and three tasks (GoToLocation, PickUpObject, PutDownObject). The HTN planner tries to find a plan to 
* transform the initial state into the goal state by decomposing the GoToLocation task into subtasks.
*/
namespace HTNExample1 {
    // Define the domain and problem
    public class Domain {
        public enum TaskType {
            TransportObjectToLocation,
            GoToLocation,
            PickUpObject,
            PutDownObject
        }

        public enum Location {
            A,
            B,
            C
        }

        public enum ObjectType {
            Ball,
            Box
        }

        public class State {
            public Location AgentLocation;
            public Dictionary<ObjectType, Location> ObjectLocations;
            public bool HasObject;
            public ObjectType ObjectType;
        }

        public class Problem {
            public State InitialState;
            public State GoalState;
        }
    }

    // Implement the HTN planner
    public class HTNPlanner {
        private Domain domain;
        private Domain.Problem problem;
        public List<Domain.TaskType> plan;

        public HTNPlanner(Domain domain, Domain.Problem problem) {
            this.domain = domain;
            this.problem = problem;
            this.plan = new List<Domain.TaskType>();
        }

        public bool Plan() {
            // Check if the initial state is already the goal state
            if (problem.InitialState.Equals(problem.GoalState)) {
                return true;
            }

            // Define the top-level task and try to decompose it
            Domain.TaskType topLevelTask = Domain.TaskType.TransportObjectToLocation;
            return DecomposeTask(topLevelTask, problem.InitialState, problem.GoalState);
        }

        private bool DecomposeTask(Domain.TaskType task, Domain.State state, Domain.State goal) {
            // Check if the given state is already the goal state
            if (state.Equals(goal)) {
                return true;
            }

            switch (task) {
                case Domain.TaskType.TransportObjectToLocation:
                    // Decompose the GoToLocation task into subtasks
                    Domain.TaskType[] subtasks = {
                        Domain.TaskType.PickUpObject,
                        Domain.TaskType.GoToLocation,
                        Domain.TaskType.PutDownObject 
                    };

                    foreach (Domain.TaskType subtask in subtasks) {
                        if (DecomposeTask(subtask, state, goal)) {
                            plan.Add(task);
                            return true;
                        }
                    }
                    break;
                case Domain.TaskType.GoToLocation:
                    if (state.AgentLocation != goal.AgentLocation) {
                        state.AgentLocation = goal.AgentLocation;
                        return true;
                    }
                    else {
                        return false;
                    }
                case Domain.TaskType.PickUpObject:
                    // Check if the agent can pick up the object in the current state
                    if (state.HasObject || state.ObjectLocations[state.ObjectType] != state.AgentLocation) {
                        return false;
                    }

                    // Update the state and add the task to the plan
                    state.HasObject = true;
                    plan.Add(task);
                    return true;
                case Domain.TaskType.PutDownObject:
                    // Check if the agent has the object in the current state
                    if (!state.HasObject) {
                        return false;
                    }

                    // Update the state and add the task to the plan
                    // Update the state and add the task to the plan
                    state.HasObject = false;
                    state.ObjectLocations[state.ObjectType] = state.AgentLocation;
                    plan.Add(task);
                    return true;
                default:
                    throw new ArgumentException("Invalid task type");
            }

            return false;
        }
    }

    // Test the HTN planner
    class Program
    {
        static void Main(string[] args)
        {
            // Define the domain
            Domain domain = new Domain();

            // Define the initial and goal states
            Domain.State initialState = new Domain.State {
                AgentLocation = Domain.Location.A,
                ObjectLocations = new Dictionary<Domain.ObjectType, Domain.Location>
                {
                    { Domain.ObjectType.Ball, Domain.Location.A },
                    { Domain.ObjectType.Box, Domain.Location.C }
                },
                HasObject = false,
                ObjectType = Domain.ObjectType.Ball
            };
            Domain.State goalState = new Domain.State {
                AgentLocation = Domain.Location.B,
                ObjectLocations = new Dictionary<Domain.ObjectType, Domain.Location>
                {
                    { Domain.ObjectType.Ball, Domain.Location.B },
                    { Domain.ObjectType.Box, Domain.Location.C }
                },
                HasObject = false,
                ObjectType = Domain.ObjectType.Ball
            };
            Domain.Problem problem = new Domain.Problem {
                InitialState = initialState,
                GoalState = goalState
            };

            // Create an HTN planner and try to find a plan
            HTNPlanner planner = new HTNPlanner(domain, problem);
            if (planner.Plan()) {
                Console.WriteLine("Found a plan:");
                foreach (Domain.TaskType task in planner.plan) {
                    Console.WriteLine(task);
                }
            }
            else {
                Console.WriteLine("No plan found");
            }
        }
    }
}

```