Bots
Configurable, deterministic fillers that keep leaderboards alive, with composable building blocks, a visual state-machine editor, and a publish path to the marketplace.
Bots in Kraty are configurable and deterministic. You compose a behavior from small building blocks in the portal; players see the bot scores in their leaderboards alongside humans. There is no setup on your side; bots scale with the leaderboards they are attached to.
Building blocks
| Block | What it does |
|---|---|
idle | Holds the bot at its current score. Useful as a parked / initial state. |
wait | Spends a fixed duration before transitions evaluate. |
progress_linear | Climbs toward a target score at a constant rate. |
progress_burst | Slow start, fast burst, plateau. |
track_position | Mirrors a leaderboard rank with an offset. |
match_player_pace | Keeps pace with the requesting player's score. |
complete_at | Finishes at a specific score or time. |
complete | Terminal state. Freezes the bot at its current score. |
quit | Drops the bot; its slot opens for fill. |
Composers
A bot's behavior field is one of three shapes:
single: one block. The whole bot is that one behavior.sequence: a list of blocks run one after another.state_machine: named states with conditional transitions between them. The most expressive shape.
{
"behavior": {
"type": "state_machine",
"initial": "stalk",
"states": {
"stalk": {
"block": "track_position",
"params": { "rank": 5, "offset": -1 },
"transitions": [
{ "when": { "expr": "elapsedSeconds > 600" }, "to": "burst" }
]
},
"burst": {
"block": "progress_burst",
"params": { "targetScore": 500 },
"transitions": [
{ "when": { "expr": "elapsedSeconds > 660" }, "to": "stalk" }
]
}
}
}
}Visual editor
The bot editor's Diagram section is an interactive canvas: drag states, connect them by dragging from one node's handle to another, and edit a state's block, params, or transition expression in the side inspector. Single-block and sequence shapes have a Convert to state machine button that promotes them so they can be edited visually too.
The diagram and the raw JSON above it stay in sync as you edit. The Save button validates the full definition before persisting; see Validation below.
Random-range targets
progress_linear, progress_burst, and complete_at all accept
their targetScore as either a fixed number or a random range. In
the inspector, click Use random range on a target-score field
and you get min / max inputs:
"params": { "targetScore": { "min": 80, "max": 120 } }Each bot rolls one value inside that range, deterministically from its seed. The value stays the same for the bot's entire window; the randomness shapes which bots aim where, not how their score flickers moment to moment.
How many bots spawn
Bot count is global per leaderboard, not per bot type. You set the how many on the leaderboard, and the which types on the bot bindings (each binding just picks a bot and gives it a weight, its selection probability).
On the leaderboard (Bot count, for global / grouped / segmented boards):
-
Fixed: a plain number, e.g.
20bots on the board. -
Random range:
{ "min": 15, "max": 30 }, rolled once per window (deterministic, so reads don't flicker). -
Scaled to the rank gap: for segmented boards, let the per-segment count track how wide the segment's rank band is. A
linearcurve at slope0.5puts ~5 bots in an11–20band (gap 10) and ~25 in a50–100band (gap 50), clamped to amin/max:"botCount": { "scale": { "input": { "kind": "segment_rank_gap", "segmentKey": "level_band" }, "fn": { "type": "linear", "parameters": { "slope": 0.5 } }, "min": 1, "max": 12 } }Any curve works (
linear,tiered,exponential), so you can shape the bot population per segment however you like.
Which types fill those slots is drawn from your bot bindings,
weighted by each binding's weight. A binding with weight 3 is three
times as likely to be picked for each slot as one with weight 1.
Lobby fill, strategy & caps
For lobby-matched events, bots backfill empty seats. Each empty seat draws a bot type at random, weighted by each binding's weight, so a heavier archetype shows up more often, and the same type can fill more than one seat by chance, just like a real matchmade lobby.
Fill strategy controls when/how the lobby fills and starts:
- Wait a timeout, then fill: hold the lobby open for real players up to a timeout, then backfill with bots and start. (Classic.)
- Fill immediately after first join: one player joins → fill the rest with bots and start instantly. Great for solo-vs-bots.
- Drip a bot every random interval: add a bot every random interval (min–max seconds) until full or the window ends, for a natural "players trickling in" feel.
- Wait until full of real players: only start early when humans reach capacity; bots fill in only at the window deadline.
Bot fill behaviour controls how many bots backfill the lobby:
- Don't add bots: leave empty seats open; the lobby starts short-handed.
- Fill remaining seats with bots: top the lobby up to capacity, optionally capped by a Max bot count.
- Add a random amount of bots: roll a bot count in a
min … maxrange (defaults to0 … lobby size) and add that many, instead of filling every seat. Rolled once per lobby (deterministic by lobby id, so reads don't flicker), great for a natural, varied lobby size that isn't always full.
Caps:
- Capacity (size): total seats (players + bots).
- Reserved player seats: always kept open for real players, so
bots fill at most
capacity − reserved. Minimum 1 (a lobby is never all bots); default 1. Applies to both fill behaviours. - Max bot count (optional, Fill remaining seats only): a hard cap on bot count; blank fills the rest of the seats.
- No repeated bots: draw each type at most once, capping bots at the number of bot bindings you've defined. Handy when each bot is a distinct named "character." Overrides Max bot count when stricter.
Tags
Give bots free-form tags (Basics tab), e.g. aggressive,
rubberband, tutorial, to organize them and find them faster.
Tags show up alongside the name in every bot picker (event bindings,
shared-board bot pools), so they're searchable when you have a large
roster.
Validation
Saving a bot runs it through several time samples before persisting. A bot that would error at runtime fails to save instead; the issue list shows up in the editor's Validation section.
A bot must pass validation before it can be published to the marketplace.
Identities (bots and players)
Identity pools power two things: the display name + avatar a bot shows on the leaderboard, and the stable synthetic identity every player gets (a privacy-preserving fake name + avatar drawn from your game's default pool; see Player tools).
Pools are generative: instead of a fixed roster you provide three lists, and the engine combines them:
- First names and Last names are paired Discord-style, so
Crazy×Panda→ "Crazy Panda". A handful of each yields a big space (10 first × 10 last = 100 distinct names). Leave last names empty for single-word identities. - Avatars are assigned at random alongside the name. An avatar
entry is any reference your game resolves: a full image URL, an
asset key (
hero_blue), or a numeric id. For a game with a fixed set of built-in avatars, add a numeric range (e.g.0–9) so each identity gets an index into that set. Leave empty for no avatar.
Edit these lists on a pool's detail page under Bots → Identity pools: first/last names are chip inputs (type + Enter), avatars have a URL/key/id field plus an "add range" helper, and a live Preview shows sample combinations. Clone a pool across games with Copy to game….
The built-in System pool (the fallback) ships ~9,200 name
combinations (adjective × animal) and a default avatar id 0–9.
Per-bot picker (Identity tab in the bot editor)
Three choices:
- Inherit: fall back to the game's default pool (see below). This is the default for new bots.
- System: use the platform's built-in generative pool
(
Crazy Panda,Swift Falcon, …). Useful when the bot doesn't need themed identities. - <Pool name>: a specific game-scoped pool you've curated.
Game default (Settings → Default bot identity pool)
The fallback used by every bot whose picker is set to Inherit. Same options minus Inherit: either System or a specific game pool. Empty means bots that inherit land on System. This default pool also seeds each player's synthetic identity.
Resolution order at spawn time
For every bot the engine instantiates:
- Bot's
definition.identityPoolRefif set (a pool UUID, or'system'). - Game settings'
defaultBotIdentityPoolId, if set. - Platform System pool.
Identity picks within a window never repeat; once every combination is used, overflow bots get a numeric suffix. A pool with a few dozen first × last combinations comfortably covers any realistic bot count.
Sharing across studios
Bots that work well are worth sharing. See Bot marketplace for how to publish a validated bot, browse other studios' bots, and copy one into your own game.
Build a "shadow rival" bot end-to-end
A bot that tracks the current rank-2 player with a small offset: the classic "always 5 points behind you" effect that keeps a leaderboard feeling competitive even on slow nights.
Open Bots under your game and click + New bot. Pick a key
(e.g. shadow_rival) and a display name.
In the editor's Diagram tab, click Convert to state machine. A blank canvas appears.
Drag a track_position block onto the canvas. In the
inspector pane set:
rankto2offsetto-5(negative means stay below the target)fallbacktolast_available(clamp to the lowest known rank when nobody is there yet)
Click Save. The bot's validation pass runs three time-sample evaluations behind the scenes; the Validation section confirms it produces a score for each.
Click Simulate at the bottom of the editor. The plot shows the trajectory the bot would draw across a synthetic 60-minute window, useful for sanity-checking your offset before binding it to an event.
Open any event, add a Bot binding row pointing at shadow_rival,
and set the leaderboard's Bot count to at least 1 (or, for a
lobby event, choose Fill remaining seats with bots and leave
Max bot count blank so seats fill). Save the event.
Start an attempt from your game client. The bot is instantiated
on the leaderboard within seconds and visible under
kraty.leaderboards.read(...) with kind: 'bot'.
Publish a bot to the marketplace and copy it back
From the bot editor, scroll to the Marketplace section and click Publish to marketplace. The bot enters a review queue; it is not visible to other studios until a Kraty platform admin approves it. Bots that fail validation cannot reach this stage.
Once approved, other studios see the bot at the top-level
Marketplace tab. From any game they own, they open the bot's
detail page and click Copy to my studio. They pick a target
game + an optional new key / name, and Kraty inserts an
independent copy into their workspace.
The copy is independent: they can tune parameters, retire it,
even republish a derivative without affecting your original. The
copy carries a copiedFromBotId so your bot row tracks how often
it has been forked.