James Liu

C++ game AI contribution

Behavior Tree Framework

Deterministic random selectors and sequences for a shared C++ behavior-tree system, with per-instance seeds and persistent execution state.

C++ initialization code from the behavior tree framework
Focus

Deterministic game AI & debugging

Tools & methods

C++ · Behavior Trees · Mersenne Twister · Cornell University Game Library

Project

C++ game AI contribution

Random behavior becomes reproducible per agent.

For the Cornell University Game Library’s shared C++ behavior-tree framework, I added per-instance seeds and randomized selector / sequence behavior.

Deterministic game AIRandom choices you can reproduce
  1. Per agentTreeInstance seedExplicit or automatically generated
  2. GeneratorMersenne TwisterTracked random state
  3. Tree nodesRandom branchesSelectors + sequences
  4. DebuggingRepeat the traceSame tree, actions, and seed
Implementation details

The shared framework already separated a behavior tree definition from each agent's TreeInstance. My contribution extended that instance state with explicit random seeds and added creation paths that can accept a known seed or generate one automatically.

Random selectors and random sequences use a per-instance Mersenne Twister instead of fresh untracked randomness. A bug can therefore be reproduced with the same tree, action map, and seed.

A shuffled branch order survives across update steps.

A running random sequence retains its child order across frames, keeping long-running actions consistent.

Persistent node stateShuffle once. Keep the order while running.
  1. First entryShuffle childrenStore the order in NodeData
  2. Current frameTick an actionUse currentChildIndex
  3. Still runningKeep its placeRetain order + execution state
  4. Next frameResume the actionContinue the stored sequence
Continue until success, failure, or reset
Technical details & source excerpt

A random sequence may remain running for many frames. Its shuffled child order is stored in that node's NodeData rather than recreated on every step, so the sequence advances consistently until success, failure, or reset.

  • TreeManager exposes seeded and automatically seeded instances
  • Random selector and sequence nodes share the instance generator state
  • Seed progression supports repeatable traces without freezing every choice
Persistent random sequenceCUNode.h · C++
if ((*data)[treeIndex].shuffledChildren.empty())
{
    for (int i = 0; i < children.size(); i++)
    {
        (*data)[treeIndex].shuffledChildren.push_back(i);
    }

    const unsigned int seed = (*data)[0].seed;
    std::mt19937 generator(seed);
    std::shuffle(
        (*data)[treeIndex].shuffledChildren.begin(),
        (*data)[treeIndex].shuffledChildren.end(),
        generator
    );
}

const int childIndex =
    (*data)[treeIndex].shuffledChildren[
        (*data)[treeIndex].currentChildIndex
    ];
State childState = getChild(childIndex)->execute(data);

The shuffled order is generated once from the tree instance seed and stored in per-node state, so a running sequence does not reshuffle between frames.

Small demos make tree behavior visible.

JSON trees and dog-behavior scenarios exercise decorators and random branches. I also documented the boundary between shared tree logic and per-agent state.

Implementation details

I added multiple JSON tree definitions and dog-behavior scenarios to exercise decorators, random branches, and random sequences. I also expanded class and method documentation around Node, TreeInstance, and TreeManager so the boundary between shared logic and per-agent state is explicit.

This was a team repository. The case study focuses on the seeded-randomness implementation, demos, debugging fixes, and documentation visible in my authored commits rather than claiming ownership of the entire framework.

Next projectRay Tracing Hardware Study