Skip to content

Commit

Permalink
Updated code and tests to mirror C# 0.3.1 version.
Browse files Browse the repository at this point in the history
Updated Readme.md.
  • Loading branch information
Safebox36 committed Sep 9, 2024
1 parent 7e82c50 commit 1c4d474
Show file tree
Hide file tree
Showing 35 changed files with 1,148 additions and 792 deletions.
62 changes: 61 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
* Easy to extend.
* Uses a Factory interface internally to create and free tables/queues/stacks/objects, allowing for pooling, or other memory management schemes.
* Decomposition logging, for debugging.
* Minimal MWSE dependency, allowing for easy porting to OpenMW (official port to come).
* Minimal dependency, allowing for easy importing to MWSE, OpenMW, LÖVE, Defold, Roblox, etc.
* 148 unit tests.

## Concepts

Expand Down Expand Up @@ -185,6 +186,65 @@ Get B
Get C
Done
```
## Partial Planning

We can easily integrate the concept of partial planning into our domains. We call it a Pause Plan, and it must be within a sequence to be valid. It allows the planner to only plan up to a certain step, then continue from once the partial plan has been completed.

```lua
:Sequence("A")
:Action("1")
--...
:End()
:PausePlan()
:Action("2")
--...
:End()
:End()
```

## Sub-Domains

### Splicing

We can define sub-domains and splice them together to form new domains, but they must share the same context type to be compatible. This is useful for sharing sub-domains between larger domains and to make larger domains more legible.

```lua
local subDomain = DomainBuilder:new(MyContext, "SubDomain")
:Select("B")
--...
:End()
:Build();

local myDomain = DomainBuilder:new(MyContext, "MyDomain")
:Select("A")
--...
:End()
:Splice(subDomain)
:Select("C")
--...
:End()
:Build();
```

### Slots

We can define slots in our domains, and mark them with unique ids. This allow us to hook up sub-domains at run-time. This can be useful for smart objects that extend the behaviour of an agent.

```lua
local subDomain = DomainBuilder:new(MyContext, "SubDomain")
.Select("B")
//...
.End()
.Build();

local myDomain = DomainBuilder:new(MyContext, "MyDomain")
.Slot(1)
.Build();

myDomain.TrySetSlotDomain(1, subDomain);
//...
myDomain.ClearSlot(1);
```

## Extensions

Expand Down
10 changes: 5 additions & 5 deletions sb_htn/BaseDomainBuilder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ end
---@param task ICompoundTask
---@return BaseDomainBuilder
function BaseDomainBuilder:CompoundTask(name, task)
assert(task ~= nil, "task")
assert(task, "task")
assert(self:Pointer():isInstanceOf(ICompoundTask),
"Pointer is not a compound task type. Did you forget an End() after a Primitive Task Action was defined?")
task.Name = name
self._domain.AddTask(self:Pointer(), task)
self._domain:AddTask(self:Pointer(), task)
table.insert(self._pointers, task)
return self
end
Expand All @@ -90,7 +90,7 @@ function BaseDomainBuilder:PrimitiveTask(name, P)
"Pointer is not a compound task type. Did you forget an End() after a Primitive Task Action was defined?")
local parent = P:new()
parent.Name = name
self._domain.AddTask(self:Pointer(), parent)
self._domain:AddTask(self:Pointer(), parent)
table.insert(self._pointers, parent)

return self
Expand All @@ -109,7 +109,7 @@ function BaseDomainBuilder:PausePlanTask()
"Pointer is not a decompose-all compound task type, like a Sequence. Maybe you tried to Pause Plan a Selector, or forget an End() after a Primitive Task Action was defined?")
local parent = PausePlanTask:new()
parent.Name = "Pause Plan"
self._domain.AddTask(self:Pointer(), parent)
self._domain:AddTask(self:Pointer(), parent)

return self
end
Expand Down Expand Up @@ -208,7 +208,7 @@ end
---@return BaseDomainBuilder
function BaseDomainBuilder:Splice(domain)
assert(self:Pointer():isInstanceOf(ICompoundTask), "Pointer is not a compound task type. Did you forget an End()?")
self._domain.AddTask(self:Pointer(), domain.Root)
self._domain:AddTask(self:Pointer(), domain.Root)

return self
end
Expand Down
3 changes: 1 addition & 2 deletions sb_htn/Conditions/FuncCondition.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ function FuncCondition:IsValid(ctx)
assert(ctx:isInstanceOf(self.T), "Unexpected context type!")
local result = self.Func and self.Func(ctx) or false
if (ctx.LogDecomposition) then
print(string.format("FuncCondition.IsValid:%s\n\t- %i", result and "True" or "False",
ctx.CurrentDecompositionDepth + 1))
log("%i - FuncCondition.IsValid:%s", ctx.CurrentDecompositionDepth + 1, result and "True" or "False")
end
return result
end
Expand Down
2 changes: 2 additions & 0 deletions sb_htn/Contexts/BaseContext.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ function BaseContext:initialize()
self.CurrentDecompositionDepth = 0
---@type IFactory
self.Factory = nil
---@type IPlannerState
self.PlannerState = nil
---@type table<integer>
self.MethodTraversalRecord = {}
---@type table<integer>
Expand Down
4 changes: 3 additions & 1 deletion sb_htn/Contexts/IContext.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ function IContext:initialize()

---@type IFactory
self.Factory = nil
---@type IPlannerState
self.PlannerState = nil

--- The Method Traversal Record is used while decomposing a domain and
--- records the valid decomposition indices as we go through our
Expand Down Expand Up @@ -74,7 +76,7 @@ function IContext:initialize()
---@type boolean
self.LogDecomposition = nil

---@type Queue PartialPlanEntry
---@type Queue<PartialPlanEntry>
self.PartialPlanQueue = nil

---@type boolean
Expand Down
Loading

0 comments on commit 1c4d474

Please sign in to comment.