Why a fixed action list fits
A character's next move is a bounded decision: attack, drink a potion, flee, ask for a truce, taunt. Nothing is generated, so the game loop receives one string it can switch on.
Writing that as if-else means every state variable needs a threshold someone guessed. A probability per action lets you see how decisive the pick was, which is useful for animation, for difficulty tuning, or for spotting states where the character dithers.
Question shape
tsimport { choice } from '@system-one-ai/core';
export const actionQuestion = {
next: choice('What should this character do next?', {
attack: 'Close distance and attack the nearest enemy',
potion: 'Drink a healing potion before acting',
flee: 'Retreat toward the nearest exit',
truce: 'Attempt to negotiate a pause',
taunt: 'Provoke the enemies to draw them out',
}),
};
Send the state the character actually has: health, enemies and their distance, inventory, terrain, and whether an ally is nearby.
Illustrative code, not tested. The thresholds are placeholders.
tsexport async function nextAction(client, state: {
healthPct: number; enemies: number; nearestEnemyM: number;
potions: number; allyNearby: boolean; terrain: string;
}) {
const result = await client.evaluate({ state, questions: actionQuestion });
const pick = result.answers.next.choice;
const confidence = result.answers.next.confidence;
// Placeholder: let a scripted behaviour take over when the model is unsure
return confidence >= 0.5 ? pick : 'hold_position';
}
What to keep in code
- The action list itself. Options are game design, not model output.
- Hard constraints. Cooldowns, "no potions while silenced", and anything the rules engine already enforces should short-circuit before the call.
- The fallback. When confidence is low, a scripted behaviour is often better than a coin flip.
Latency and cost
The demo on this site shows sample data, not a live run; measure latency in your own game before calling this per frame. A typical call is one decision per turn, not per frame.
Every 4KB is one block, and anything smaller still counts as one block. Cost = blocks × questions × the model rate. A short character state fits in one block.
New accounts start with 100 credits, and credits never expire. See pricing.
Run the character sample (sign-in required) to see the same question on sample data.