-
Notifications
You must be signed in to change notification settings - Fork 0
CSharp and Unity
Tensor Planner includes a Unity-first C# wrapper over the native C ABI.
Main source file:
dev.nick.tensor-planner/Runtime/TensorPlanner.cs
The .NET project links the same file:
csharp/TensorPlanner/TensorPlanner.csproj
Unity package:
{
"name": "dev.nick.tensor-planner",
"displayName": "Tensor Planner",
"version": "0.1.0",
"unity": "2019.4"
}using TensorPlanner;
sealed class Character { public Character(string name) { Name = name; } public string Name { get; } }
sealed class Location { public Location(string name) { Name = name; } public string Name { get; } }
Character player = new Character("player");
Location home = new Location("home");
Location forest = new Location("forest");
using (Planner planner = new Planner()) {
Predicate at = planner.Predicate<Character, Location>("at");
Predicate connected = planner.Predicate<Location, Location>("connected");
NumericFunction energy = planner.Function<Character>("energy");
PlannerAction move = planner.Action("move")
.Param<Character>("who")
.Param<Location>("from")
.Param<Location>("to")
.Require(at.Create("who", "from"))
.Require(connected.Create("from", "to"))
.Require(energy.Create("who").GreaterOrEqual(1.0f))
.Removes(at.Create("who", "from"))
.Adds(at.Create("who", "to"))
.Decreases(energy.Create("who"), 1.0f)
.Commit();
StateBuilder state = planner.State()
.Object(player)
.Object(home)
.Object(forest)
.Fact(at.Create(player, home))
.Edge(connected, home, forest)
.Value(energy.Create(player), 2.0f)
.Goal(at.Create(player, forest));
SolveResult result = planner.Solve(state);
foreach (PlanStep step in result.Steps) {
if (step.Is(move)) {
Character who = step.Arg<Character>("who");
Location to = step.Arg<Location>("to");
}
}
}Main types:
PlannerLimitsPredicateNumericFunctionPlannerActionActionBuilderStateBuilderSolveResultPlanStep
Common methods:
Predicate at = planner.Predicate<Character, Location>("at");
NumericFunction energy = planner.Function<Character>("energy");
PlannerAction move = planner.Action("move")
.Param<Character>("who")
.Param<Location>("from")
.Param<Location>("to")
.Require(at.Create("who", "from"))
.Require(energy.Create("who").GreaterOrEqual(1.0f))
.Adds(at.Create("who", "to"))
.Decreases(energy.Create("who"), 1.0f)
.Commit();
StateBuilder state = planner.State()
.Object(player)
.Fact(at.Create(player, home))
.Edge(connected, home, forest)
.Value(energy.Create(player), 2.0f)
.Goal(at.Create(player, forest));
SolveResult result = planner.Solve(state);
SolveResult asyncResult = await planner.SolveAsync(state, cancellationToken);Predicate.Create(...) is the preferred factory. Predicate.Call(...) remains
as a compatibility alias.
Use StateBuilder.Edge(predicate, a, b) for undirected binary relations. It adds
both directed facts:
state.Edge(connected, home, forest);
// equivalent to:
state.Fact(connected.Create(home, forest));
state.Fact(connected.Create(forest, home));Use Fact(...) directly for one-way relations.
Numeric functions use NumericFunction and NumericTerm objects:
NumericFunction energy = planner.Function<Character>("energy");
planner.Action("act")
.Param<Character>("who")
.Require(energy.Create("who").GreaterOrEqual(1.0f))
.Decreases(energy.Create("who"), 1.0f)
.Commit();
StateBuilder state = planner.State()
.Object(player)
.Value(energy.Create(player), 2.0f);Supported comparisons are LessThan, LessOrEqual, EqualTo,
GreaterOrEqual, and GreaterThan. Numeric effects use Sets, Increases,
and Decreases.
The wrapper exposes async solve methods for tools and Unity code that should not block the caller while the native solver runs:
SolveResult result = await planner.SolveAsync(state);
SolveResult resultWithCancellation = await planner.SolveAsync(state, cancellationToken);SolveAsync queues the existing synchronous native solve work to the .NET
ThreadPool. It does not change the native planner algorithm.
Cancellation is cooperative:
- a pre-canceled token cancels before work starts,
- cancellation is checked before entering the native solve and after it returns,
- cancellation cannot interrupt an in-progress
tp_solver_solvecall because the C API currently has no native cancellation hook.
Only one solve operation may run on a Planner at a time. Starting another
Solve or SolveAsync while one is active throws InvalidOperationException.
Disposing a planner while a solve is active also throws InvalidOperationException.
Unity note: do not access UnityEngine APIs from work running on the background
thread. Read the result after await on the Unity main thread before touching
scene objects, components, transforms, or assets.
In Unity projects, gameplay code produces current facts, Tensor Planner finds a valid action sequence, and Unity code executes that sequence on the main thread.
The wrapper uses:
DllImport("tensor_planner")For Unity, native plugins should be placed under:
dev.nick.tensor-planner/Runtime/Plugins/x86_64/
Linux:
Runtime/Plugins/x86_64/libtensor_planner.so
Windows:
Runtime/Plugins/x86_64/tensor_planner.dll
The build.sh and build.ps1 scripts stage these automatically for selected
target OS values.
dotnet build csharp/TensorPlanner/TensorPlanner.csproj -c ReleaseRun smoke test:
dotnet run --project csharp/TensorPlanner.Smoke/TensorPlanner.Smoke.csproj -c ReleaseBuild Unity package staging:
sh ./build.sh -release -target unity -os linux -o ./distpwsh ./build.ps1 -release -target unity -os windows -o ./distOutput:
dist/unity/dev.nick.tensor-planner/
-
PlannerimplementsIDisposable; useusingor callDispose(). -
StateBuilderkeeps managed references to registered objects. - Keep the state/result alive while reading object references from plan steps.
- Do not unload or move the native plugin while planner objects exist.