Strategies¶
Who is this page for?
Integrators and strategy authors who decide how many points an event is
worth. This is the conceptual + how-to view. The interpreter internals
(AST, limits, sandbox) are in The DSL Strategy Engine; the per-block editor
reference lives under docs/dsl/.
What a strategy is¶
A strategy is the scoring brain bound to a game (and optionally overridden per task). When an event arrives, GAME resolves the effective strategy and asks it for two things:
points- the integer reward, andcaseName- the human-readable label for why that amount was chosen (e.g.variable_basic_points,PeakPerformerBonus). ThecaseNameflows into responses, analytics, and audit so a decision is always explainable.
Strategies come in two families:
Family |
Behavior |
|---|---|
Deterministic |
Fixed, rule-based scoring. Same event → same points, always. |
Adaptive |
Scoring reacts to context: the user’s history, the task’s distribution, comparison to the global average, spatial state, etc. |
Both families are deterministic given identical inputs - adaptivity means the inputs include behavioral/contextual analytics, not that the output is random. That is what makes GAME reproducible (see Overview).
Choosing a strategy: strategyId¶
Every game stores a strategyId; tasks inherit it unless they set their
own. Two id shapes exist:
Shape |
Meaning |
|---|---|
|
A code-defined strategy registered in the engine (table below). |
|
A DSL strategy authored in the dashboard and stored in the database. |
If no strategy is set, the built-in default applies.
Built-in strategies¶
These ship with the engine. Each registers a stable public id (persisted
on games and returned by the API) via @register_strategy - the id is
guaranteed stable across class renames:
|
Version |
Purpose |
|---|---|---|
|
0.0.2 |
The baseline. Awards configurable basic/bonus points; the safe default for any game. |
|
0.0.1 |
Rewards steady, sustained participation rather than bursts. |
|
0.0.2 |
Tuned for the SOCIO-BEE citizen-science scenario. |
|
1.0.0 |
Tuned for the GREENCROWD platform. |
|
0.0.1 |
Tuned for the GREENGAGE scenario. |
|
0.0.1 |
Spatial strategy using the Getis–Ord \(G_i^*\) hot-spot statistic to shift incentives toward under-served areas. |
Discover them at runtime:
GET /api/v1/strategies # list available strategies
GET /api/v1/strategies/{id} # one strategy's metadata
GET /api/v1/strategies/{id}/schema # its configurable variables
GET /api/v1/strategies/{id}/graph # a rendered logic graph
Strategy variables (e.g. variable_basic_points) are the knobs;
game/task params supply their values, so the same strategy behaves
differently per game without code changes.
Extending the engine in code
New built-ins are plain classes that subclass BaseStrategy, implement
the scoring method, and decorate themselves with
@register_strategy(id="..."). Third-party packages can even contribute
strategies through the game.strategies entry-point group - no fork
required. See The DSL Strategy Engine and Codebase Reference.
Custom strategies (the DSL)¶
The dashboard’s Strategy Editor lets game designers build strategies
visually (Blockly) with no Python. A strategy is a tree of blocks; when
a scoring event arrives the engine walks the tree top-to-bottom and emits the
first assign_points it reaches inside a matching rule.
The custom-strategy lifecycle¶
Custom strategies are first-class, versioned resources:
Operation |
Endpoint |
|---|---|
Create a draft |
|
List / get |
|
Update (creates a new draft of a published strategy) |
|
Publish a version |
|
Archive |
|
List versions |
|
Roll back to a version |
|
Where is it used? |
|
Import / export |
|
Simulate (no persistence) |
|
Versioning guarantees¶
Saving a published strategy creates a
v+1draft rather than overwriting. Production keeps running the published version until you explicitly publish the new one.If a published version misbehaves, rollback restores a prior version. Rollback also rewrites the
strategyIdon every game/task that pointed at the archived version, so the cascade reaches all consumers.
This means you can experiment freely - nothing in production changes until you press Publish.
Simulate before you ship¶
Every strategy can be simulated: scoring runs and you get the full
node-by-node trace, but no production data is touched - no UserPoints,
no wallet movement.
Two ways to simulate:
Per-event - send
"isSimulated": trueto the points endpoint (Integrating with GAME).Per-strategy - call
POST /strategies/custom/{id}/simulate(the editor’s Test button) to dry-run a candidate strategy against sample input and inspect exactly which rule would fire and why.
There is also a per-user simulated view,
GET /games/{gameId}/users/{externalUserId}/points/simulated (OAuth2-only,
bound to the caller’s own subject), returning a simulationHash for
integrity plus per-task projected points.
Use simulation liberally - it is the safe way to validate a scoring change.
Safety limits¶
Published DSL strategies run inside a sandbox with hard ceilings so a runaway rule cannot impact your tenant or others:
Limit |
Default |
Env var |
|---|---|---|
Wall-clock per call |
500 ms |
|
AST nodes visited |
1000 |
|
Recursion depth |
32 |
|
Hitting a limit rejects the event with a clear error code rather than degrading the service. The full execution model - how the interpreter yields cooperatively so the timeout can actually cancel it - is in The DSL Strategy Engine.
Worked example: adaptive engagement¶
A common pattern (from the citizen-science deployments) layers cases by the user’s measurement count and performance:
Case |
Condition |
|
|---|---|---|
First/second measurement |
No prior history |
|
Slower than global avg |
|
|
Faster than global avg |
|
|
Beats own history and global |
|
|
The full decision tree and points table is in the repository’s
strategies.md; expressing it in the editor is a matter of nesting
gd_rule blocks with gd_compare conditions and gd_assign_points
leaves.
Observability¶
Every production run of a custom strategy emits Prometheus metrics
(dsl_execution_duration_seconds, dsl_execution_nodes_total,
dsl_execution_errors_total) and, on failure or by sampling, persists a
StrategyExecutionLog row you can inspect later. Aggregations are available
at GET /strategies/custom/{id}/metrics and an A/B view at
GET /strategies/custom/compare. See Observability.