Kirino 的游戏宝藏
← Back to issue 022
Game observation

Domain Language and Generated Backtracking: How Jose Antonio Escribano Open-Sourced a Guerrilla-Lineage HTN as a Game AI Planner That Does Not Parse at Runtime

If the default grammar of contemporary game AI is still “hang nodes on a Behavior Tree, write booleans on a blackboard, then paste an NPC’s high-level intent into atmosphere with…

Saved articles ↗

If the default grammar of contemporary game AI is still “hang nodes on a Behavior Tree, write booleans on a blackboard, then paste an NPC’s high-level intent into atmosphere with a chain of ifs inside `Update()`,” then **htn_planner**, released publicly by Jose Antonio Escribano (GitHub: urosidoki) in late September 2026, almost nails another lineage back onto engineering ground in public: Hierarchical Task Networks (HTN) need not be a runtime-interpreted scripting dialect—they can be a compile pipeline of **domain DSL → AST → IR → generated C**, where the runtime executes the generated planner directly and does not parse domain source during gameplay.

That sentence can sound like a compiler slogan. What carries weight is the boundary the author himself locks in public statements: this is an **independent implementation** informed by the design and production experience of the HTN planner Guerrilla used on *Horizon Zero Dawn* and *Horizon Forbidden West*. It is **not** Guerrilla’s planner, and it is **not** a release of their source code. Lineage may be credited; copyright and bloodline must stay distinct.

The image that runs through this piece is **backtracking** itself. It is not abstract search from an AI paper, but the decision tree a debugger will expand on the production floor: how to retreat when a precondition fails, how method branches rebind, how deferred decomposition expands again against an updated world state—and how all of that lands in a versioned C ABI rather than in next frame’s temporary boolean.

GameFromScratch hero image: htn_planner in a Horizon context

1. Default logic: high-level decisions equal Behavior Trees, blackboard scripts, runtime interpreters

The industry default is clean—and therefore lazy.

Once an NPC needs cross-second intent—“patrol, then alert, then call support after spotting a target”—teams usually open a familiar menu: hang a Behavior Tree, stuff `enemyVisible` / `lowHealth` onto a blackboard, encode priority as a tree UI with decorators and selectors, and, when they need to look “a bit smarter,” drop in GOAP or a homemade state machine. Players and developers learn to read NPCs in the same vocabulary—“the tree is tuned,” “the blackboard is clean,” “this frame looks like thinking.” That pact turns “high-level decision = interpret a graph at runtime” into forgivable industry grammar.

The problem is not Behavior Trees as such. Trees excel at arranging executable reactions into visualizable control flow. Where they strain is when decisions must combinatorially search **world-state predicates**, systematically retreat from failed preconditions, and separate “a local plan that is honest now” from “follow-on tasks that should only expand later”—exactly the work the HTN lineage has long claimed.

Guerrilla’s Decima engine pushed that lineage onto the AAA public stage. On 25 November 2024, Principal AI Programmer Tim Johan Verweij’s talk *HTN Planning in Decima* (Goldsmiths / AI and Games Conference) stated the internals hard: Prolog-like backtracking over preconditions; visualizing that flow; showing how it is realized in **generated C++**; and touching in-game debugging of HTN decompositions. In other words, HTN on an AAA floor was never the romance of “yet another script interpreter,” but the production institution of “generated code + debuggable decomposition.”

htn_planner stands on the public side of the same lineage. What it refuses is not Behavior Trees, but a quieter default—the belief that NPC high-level intent can forever be sustained by runtime parsing and blackboard atmosphere, without being written as a system that compiles, hot-reloads, and version-checks an ABI.

2. Breaking the script: from “interpret the domain” to “generate a native planner”

In Jose’s public LinkedIn post (about 2026-09-27), the r/gamedev relay (u/Thhaki, 2026-09-30), and the GameFromScratch write-up (2026-09-30), the engineering claim is blunt: behavior domains are written in a Lisp-style DSL and compiled **Domain source → AST → IR → generated C**; the generated execution path is independent of an interpreter; a versioned C ABI connects generated domains to the host runtime, with optional C++ integration helpers.

The repository README breaks the same pipeline into verifiable steps: lexer and source ranges → compiler-owned AST → validation and linking (declarations, includes, overrides, references) → IR → `HTNCCodeGenerator` emits C source and static metadata → the client builds that C into the game or a domain module. AST and IR are build-time representations; generated runtime execution **neither retains nor depends on** them. This is not “AOT” as slogan, but a pipeline you can walk with `HTNTranslator --check` and the code-generation command.

What does a domain look like? A declarative S-expression DSL, not a general-purpose Lisp. A minimal guard-NPC method can say: when `guard_on_duty` holds, enqueue primitive tasks such as `!move_to` and `!scan_area` into the plan; the engine assigns meaning to those tasks and decides when they start, complete, or fail. Method branches are tried in source order; condition bodies combine fact queries and axioms with `and` / `or` / `alt` / `not`; `!` marks primitives that enter the plan immediately, bare compound tasks decompose now, and `&` emits **deferred decomposition**—execute earlier actions first, then expand the follow-on method only after the world state has been updated.

Deferred decomposition is the sharpest, most production-facing cut in the thesis. It admits that a complete plan need not lie about the future at t0. You can `!move_to` first and leave `&after_arrival` until after arrival, re-decomposing against a target that is still visible—or already lost—“then.” The host owns scheduling, cancellation, and the later decomposition request; `&` is not a timer and does not quietly spawn a background thread. The planner separates what can be honestly promised now from what is only honest when deferred—much harsher, and cleaner, than pretending inside `Update()` that you already planned the whole path with temporary booleans.

Guerrilla HTN Planning in Decima page OG: Horizon-lineage production context

3. Mechanisms crush rhetoric: nested backtracking, C ABI, hot reload, 209 tests and eight SDK variants

Without a mechanism list, the compile narrative slides into empty talk. Public materials give nails you can check against the repo.

**Nested backtracking.** Hierarchical backtracking across methods and axioms covers nested calls and alternative bindings; `alt` retains alternatives, while `or` discards remaining paths after the first success—not a copy difference, but an institutional difference in whether failure can retreat and rebind. The Decima talk stresses Prolog-like precondition backtracking; htn_planner writes the same class of problem as explicit policy on the generated execution path, with translator options for backtracking capacity and runtime mode selection.

**Versioned C ABI.** Generated domains attach to the host through a C ABI; dynamically loaded domain modules carry compatibility checks. Upgrades often require regenerating domains and rebuilding host and runtime bridge **together**—the README warns repeatedly that the public API, domain language, and generated-code ABI still evolve. That sounds like hassle; it is honesty: once a planner enters the “generated code” lane, ABI is your shader-variant contract, not a script string you can pretend is forever stable.

**Visual debugger.** Instrumented builds can capture generated execution without changing planner logic: methods, branches, conditions, axioms, tasks, results, source ranges, and scoped bindings. The data model is independent of ImGui; an engine can render captured nodes in its own editor. HTNDemo offers Domain Runner, a generated-event debugger, and a small NPC simulation—the demo loads world-state data for inspection, while domain definitions are already compiled into native generated planners. It is almost an open-source echo of Verweij’s public promise of in-game decomposition debugging.

**Hot reload.** In the reference integration for dynamic domain modules, the **host application** controls compilation, loading, and reload orchestration. Domain iteration becomes a production loop like shader hot-reload: edit predicates and method alternatives → translate/generate → host reloads → see the new decomposition tree immediately. Jose states he integrated the SDK into his own engine and tested planning and domain reloading there.

**Validation numbers.** At the public release node, the author reports **v2.0.2 passing 209 tests**, plus external consumer validation across eight Windows SDK variants (including checks that generated domain DLLs resolve required runtime exports). The repo kept moving afterward: the CHANGELOG shows 2.1–2.3 adding a domain-language tutorial, Linux x64 (Ubuntu 24.04 / GCC 14 / Clang 18), native negative literals, and more; at the time of writing, mainline VERSION points at a 2.4.0 in preparation. This article pins “209 + eight variants” to that announced release node, rather than inventing new slogan numbers for later commits that did not restate the same tally in the same post.

The license is MIT. Contributor notes are clear: the generated planner, compiler pipeline, and public release are led and maintained by Jose Antonio Escribano; the **shared 2023 foundation** retained in core, parsing, world-state, and integration layers was co-authored with Sandra Alvarez (Sandruski)—whose repository `Sandruski/htn-planner` is a related earlier work on an interpretive domain-language path. Lineage may be mentioned; the repositories must not be conflated.

GitHub social preview: urosidoki/htn_planner

4. Reflux: what indies should steal is not the “Horizon halo,” but “planning as a compiled system”

The most useful ending of the open-source story is not another AAA keyword, but reflux of production ethics.

For indie and small teams, what stings about htn_planner is not that you must throw away Behavior Trees tomorrow, but that it translates a claim long locked in studio-internal lectures into cloneable engineering objects: **world-state predicates and method alternatives are first-class**; failure is searchable backtracking, not a temporary flag overwritten next frame; deferred decomposition forces you to admit time rewrites preconditions; hot reload forces domain edits to be as verifiable as shader edits; ABI version checks force you to treat “generated artifacts” as contracts, not one-shot exports.

You need not adopt this SDK. You can still extract a smaller institution from the same ethics: write “enemy visible / ammo low / cover reachable” as an explicit predicate table, not booleans scattered across six components; write “approach / call help / retreat” as replaceable method branches, not one immovable megatree; make “decide after arrival” an explicit second planning stage, not a sleeping coroutine pretending the plan still exists. Planning as a compiled system does not demand every studio invent an IR; it demands stopping the substitution of `Update()` atmosphere for auditable decision structure.

Closing: independence and AI, redefined inside generable backtracking

Place this release back on the 2026 production floor and what it really strikes is not Behavior Trees’ reputation, but a quieter AI inertia—the belief that high-level intent can forever live on interpreters and blackboard temporaries, that debugging why an NPC “did that” can only be log-spam luck, that indie teams and AAA are separated by untranslatable black-box experience.

Jose’s counterexample is clumsy and deeply engineering: Lisp-flavored DSL, AST/IR, generated C, versioned ABI, nested backtracking, deferred decomposition, visual debugging, host-controlled hot reload, and—at the public release node—tests plus an SDK consumer matrix; plus the repeated declaration that it is informed by Guerrilla experience yet is not Guerrilla source. Clumsy, because it forces behavior into a translatable domain; expensive, because it spends real system design, not just another frame of ifs.

So “indie” and “game AI” can be redefined here. Indie need not mean forever hand-authored Behavior Trees; it can mean still asking, when resources are tighter, which decisions die the moment they can only be sustained by atmosphere. Game AI need not mean stacking feature nodes; it can mean being willing to prepare a path that compiles, debugs, and hot-reloads for that minimal decision set—even if the path looks like turning NPCs into a small language.

The executable checklist is short, and harshly honest:

  • Write your “world-state predicate” list first: which ten facts, once vague, make NPC high-level intent inexplicable; they hold veto power when you optimize or add features.
  • Encode at least one set of mutually exclusive intents as method alternatives (branch + condition + task list), not as overlapping blackboard booleans; on failure you should be able to say which layer retreated.
  • Find one place that “must wait until the world changes before deciding,” and build it as an explicit second planning stage (deferred decomposition or equivalent active replan); forbid sleeping frames that impersonate a plan.
  • If you introduce any domain/script hot reload, let the **host** own compile and load orchestration, and run one deliberate ABI/version mismatch drill—if hot reload cannot fail cleanly, it is not yet a system.
  • When you read Verweij’s *HTN Planning in Decima* and htn_planner’s domain-language guide, compare against “generation + debuggable decomposition,” not against the Horizon art halo.

The late-September public posts and coverage already locked the audit standard: not “does it look smart in a demo,” but “can high-level decisions be compiled, backtracked, and expanded again against an updated world.” After that, the indie floor only has a colder question left—are your NPCs planning, or performing planning for one temporary frame.

小光

Sources and further reading

Jose Antonio Escribano, public LinkedIn release note (about 2026-09-27): I’ve released HTN Planner…

u/Thhaki, r/gamedev relay (2026-09-30): New Open-Source Enemy/NPC AI planner informed by… Guerrilla…

GameFromScratch (2026-09-30): htn_planner – AAA Inspired Game AI Framework

Repository and README: urosidoki/htn_planner

Domain language guide (including deferred `&`): docs/DOMAIN_LANGUAGE.md

Tim Johan Verweij, Guerrilla: HTN Planning in Decima (2024-11-25; Goldsmiths / AI and Games Conference 2024)

Related earlier work (interpretive path; do not conflate): Sandruski/htn-planner

Further: Exploring HTN Planners through Example; The AI of Horizon Zero Dawn

Sign in to keep your reading history.

View your likes and saved articles across devices.

Sign in