-
Notifications
You must be signed in to change notification settings - Fork 0
Cpp Fluent API
The C++ wrapper provides a typed, readable layer over the C ABI.
Main header:
#include "tensor_planner.hpp"Namespace:
tpUse it when you want:
- real C++ object types instead of integer type IDs,
- fluent action definitions,
- exceptions instead of manual status checks,
- plan-step arguments mapped back to typed object pointers.
#include "tensor_planner.hpp"
#include <string>
struct Character { std::string name; };
struct Location { std::string name; };
int main() {
Character player{"player"};
Location home{"home"};
Location forest{"forest"};
tp::Planner planner;
auto at = planner.predicate<Character, Location>("at");
auto connected = planner.predicate<Location, Location>("connected");
auto energy = planner.function<Character>("energy");
auto move = planner.action("move")
.param<Character>("who")
.param<Location>("from")
.param<Location>("to")
.require(at("who", "from"))
.require(connected("from", "to"))
.require(energy("who") >= 1.0f)
.removes(at("who", "from"))
.adds(at("who", "to"))
.decreases(energy("who"), 1.0f)
.commit();
auto state = planner.state()
.object(player)
.object(home)
.object(forest)
.fact(at(player, home))
.edge(connected, home, forest)
.value(energy(player), 2.0f)
.goal(at(player, forest));
tp::SolveResult result = planner.solve(state);
for (const tp::PlanStep &step : result.steps()) {
if (step.is(move)) {
Character *who = step.arg<Character>("who");
Location *from = step.arg<Location>("from");
Location *to = step.arg<Location>("to");
}
}
}Defaults are small and useful for small tasks:
tp::Planner planner(tp::Limits{
.max_objects = 32,
.max_facts = 256,
.max_goals = 8,
.max_candidates = 512,
.max_expansions = 20000,
.max_plan_length = 64,
});Increase max_candidates, max_expansions, and max_plan_length for wider
domains.
Create typed predicates with template arguments:
auto at = planner.predicate<Character, Location>("at");
auto has = planner.predicate<Item>("has");
auto recipe = planner.predicate<Item, Item>("recipe2_a");Use parameter names when defining action schemas:
at("agent", "from")Use real objects when defining state facts and goals:
at(player, home)The wrapper validates arity and object types.
Action definitions are fluent:
auto gather = planner.action("gather_free")
.param<Agent>("agent")
.param<Item>("item")
.param<Location>("where")
.require(at("agent", "where"))
.require(gather_at("item", "where"))
.require(gather_bare_hands("item"))
.adds(has("item"))
.commit();Use .removes(...) for delete effects and .adds(...) for add effects.
Numeric functions use typed terms too:
auto energy = planner.function<Character>("energy");
planner.action("act")
.param<Character>("who")
.require(energy("who") >= 1.0f)
.decreases(energy("who"), 1.0f)
.commit();Supported numeric comparisons are <, <=, ==, >=, and >. Numeric effects
use .sets(...), .increases(...), and .decreases(...).
Register objects, then add facts and goals:
auto state = planner.state()
.object(agent)
.object(home)
.object(forest)
.object(flint)
.fact(at(agent, home))
.edge(connected, home, forest)
.value(energy(agent), 2.0f)
.fact(gather_at(flint, forest))
.goal(has(flint));Use .edge(predicate, a, b) for undirected binary relations. It adds both facts:
state.edge(connected, home, forest);
// equivalent to:
state.fact(connected(home, forest));
state.fact(connected(forest, home));Use .fact(...) directly for one-way relations.
Objects are borrowed. Keep them alive while using the solve result.
tp::SolveResult result = planner.solve(state);
if (result.solved()) {
for (const tp::PlanStep &step : result.steps()) {
if (step.is(gather)) {
Agent *agent = step.arg<Agent>("agent");
Item *item = step.arg<Item>("item");
Location *where = step.arg<Location>("where");
}
}
}Useful result data:
result.solved()result.status()result.expansions()result.generated()result.steps()
The same API shape works when a domain has many actions and many possible valid sequences. Add reusable action schemas, put current facts into the state, and solve for the goal instead of branching through all combinations manually.