-
Notifications
You must be signed in to change notification settings - Fork 0
03 11 Config Expressions
This page explains expression in Sprint Boost.
Expressions let Sprint Boost turn raw live values into more useful values.
A simple way to think about them:
-
game_inforeads what the game is doing right now -
expressionreshapes that data into something easier to display, save, or reuse
Expressions are especially helpful when the game stores data in a way that is not immediately player-friendly.
Expressions are useful when you want to:
- combine multiple raw values into one final value
- decide whether a game is in a certain state
- build text or filenames from live values
- define logic once and reuse it in several places
Common examples:
- build a full score from separate score digits
- create a reusable
in_gameorgame_overflag - build an image path like
track_mud.pngfrom a label value - create one derived score and use it in both
play_statsand display text
Expressions are relevant for:
boost_mode = "enhanced"
Like game_info and play_stats, expressions are part of the enhanced runtime flow.
Sprint Boost supports three expression namespaces:
expression.intexpression.boolexpression.string
Each one is meant for a different kind of result.
Use expression.int when you want a number.
Common uses:
- score
- timer
- combined counters
- math based on one or more
game_infovalues
Example:
[expression.int.score]
sum = [
{ game_info = "score_lo" },
{ mul = [{ game_info = "score_hi" }, 1000] }
]This is a good fit when a game stores one value in pieces.
Use expression.bool when you want true/false logic.
Common uses:
- whether a run has started
- whether player 1 is active
- whether a special state is currently active
- whether another config feature should be allowed to update or save
Example:
[expression.bool.has_started]
gt = [{ expression.int = "score" }, 0]This is useful when you want a simple yes/no rule that can be reused elsewhere.
Use expression.string when you want text.
Common uses:
- state labels
- image names or paths
- text fragments used in overlays
- readable values built from labels and strings
Example:
[expression.string.terrain_art]
concat = ["{gv.assets_images}/track_", { gil = "condition" }, ".png"]This is useful when a live label should help choose art or text dynamically.
Expressions can pull input from a few different places.
Use this when the starting point is a numeric live value read from game memory.
{ game_info = "score" }This is the most common expression input.
Use this when you want the text label for a game_info value instead of its number.
{ gil = "state" }That is useful for readable text or filename building.
Use this when part of the expression should come from a global path or shared folder value.
{ global_variable = "assets_images" }This is most useful in expression.string definitions.
Expressions can build on other expressions.
Pitfall is a good real-world example of this.
In Pitfall, Sprint Boost first turns each raw score digit into a usable number, then combines those digit expressions into one final score:
[expression.int.score_ones]
if = [
{ eq = [{ game_info = "score_ones_raw" }, 0] },
0,
{ div = [{ sub = [{ game_info = "score_ones_raw" }, 135] }, 8] }
]
[expression.int.score_tens]
if = [
{ eq = [{ game_info = "score_tens_raw" }, 0] },
0,
{ div = [{ sub = [{ game_info = "score_tens_raw" }, 135] }, 8] }
]
[expression.int.score_hundreds]
if = [
{ eq = [{ game_info = "score_hundreds_raw" }, 0] },
0,
{ div = [{ sub = [{ game_info = "score_hundreds_raw" }, 135] }, 8] }
]
[expression.int.score_thousands]
if = [
{ eq = [{ game_info = "score_thousands_raw" }, 0] },
0,
{ div = [{ sub = [{ game_info = "score_thousands_raw" }, 135] }, 8] }
]
[expression.int.score_ten_thousands]
if = [
{ eq = [{ game_info = "score_ten_thousands_raw" }, 0] },
0,
{ div = [{ sub = [{ game_info = "score_ten_thousands_raw" }, 135] }, 8] }
]
[expression.int.derived_score]
sum = [
{ expression.int = "score_ones" },
{ mul = [{ expression.int = "score_tens" }, 10] },
{ mul = [{ expression.int = "score_hundreds" }, 100] },
{ mul = [{ expression.int = "score_thousands" }, 1000] },
{ mul = [{ expression.int = "score_ten_thousands" }, 10000] }
]In plain language, this means:
- several smaller expressions clean up the individual digits
- a final expression reuses those smaller expressions
- the result is one
derived_scorevalue that other config sections can use
This is what makes expressions reusable. You can define small building blocks once and then combine them into something more useful.
Type matters when one expression refers to another.
That means:
-
expression.intcan only reference an int expression -
expression.boolcan only reference a bool expression -
expression.stringcan only reference a string expression
This keeps the config predictable and avoids mixing numbers, logic, and text in confusing ways.
Not every operator fits every expression type.
This is the simplest way to think about it:
- number expressions use math-style operators
- boolean expressions use yes/no logic and comparisons
- string expressions use text-building operators
Use these when the result should be a number:
sumsubmuldivmodminmaxbit_andbit_orbit_xorlshiftrshiftclampifcase
These are the main tools for:
- score math
- packed-value cleanup
- range limiting
- combining several raw values into one final number
Use these when the result should be true or false:
andornoteqneqltltegtgteifcase
Practical guidance:
-
eqandneqare the most flexible comparison operators -
lt,lte,gt, andgteare for numeric comparisons -
and,or, andnotare for combining simpler true/false rules into one reusable gate
Use these when the result should be text:
concatformatifcase
Practical guidance:
-
concatis the simple choice for joining pieces of text together -
formatis useful when you want to build a string from a template with named parts
Expressions can also be simple direct references instead of operator blocks.
Examples:
{ game_info = "score" }
{ gil = "state" }
{ global_variable = "assets_images" }
{ expression.int = "derived_score" }These are not operators by themselves. They are direct inputs that your expressions can build on.
Most users only need a small set of expression patterns.
Use when several values should become one total.
[expression.int.total_score]
sum = [
{ game_info = "score_lo" },
{ mul = [{ game_info = "score_hi" }, 1000] }
]Use when one value represents a place value or multiplier.
{ mul = [{ game_info = "score_hi" }, 1000] }Use when a raw value needs to be shifted into a usable range.
{ sub = [{ game_info = "raw_digit" }, 135] }Use when a raw value needs to be scaled down.
{ div = [{ sub = [{ game_info = "raw_digit" }, 135] }, 8] }Use these when the goal is a true/false answer.
[expression.bool.active_p1]
eq = [{ game_info = "active_player" }, 1]Use if when one expression should behave differently depending on a condition.
[expression.int.safe_digit]
if = [
{ eq = [{ game_info = "raw_digit" }, 0] },
0,
{ div = [{ sub = [{ game_info = "raw_digit" }, 135] }, 8] }
]This is a common way to clean up unusual raw values.
Use when you want to join text and live values together.
[expression.string.banner_text]
concat = ["State: ", { gil = "state" }]A good beginner workflow is:
- Use
game_infoto read the raw values you know. - Use
expressionto turn those raw values into something useful. - Use the expression result in display or
play_stats.
That keeps each part of the config easier to understand.
game_info and expressions usually work together.
Helpful mental model:
-
game_infois the raw input layer - expressions are the cleanup and reusable logic layer
If a game already stores a value in a clean form, you may not need an expression.
If a game stores a value in pieces, packed formats, or game-specific codes, expressions are often the cleanest next step.
Expressions are often the best source for play_stats when the thing you want to track is not directly available from raw memory.
Example:
[play_stats.players.p1.tracked.score]
expression = { int = "score" }
mode = "final"This means:
- let expressions define what "score" really is
- let
play_statstrack and save that value
Expressions can also be used in play_stats conditions.
Example:
[[play_stats.players.p1.save_conditions]]
expression = { bool = "has_started" }
op = "eq"
value = trueThat lets you save only when a reusable condition is true.
Session triggers can also watch expression.int or expression.bool values when those are the best signal for an end-of-run or state change.
Expressions can be shown directly in display text and substitution-enabled paths.
Sprint Boost exposes expression values as:
-
{ei.<key>}for int expressions -
{eb.<key>}for bool expressions -
{es.<key>}for string expressions
Example text:
[[display.layouts.hud.elements]]
type = "text"
text = "Score: {ei.score}"
x = 20
y = 20
width = 280
height = 40
color = "#FFFFFF"
align = "left"
size = 24Example image path:
[[display.layouts.hud.elements]]
type = "image"
source = "{es.terrain_art}"
fallback = "{gv.assets_images}/track_unknown.png"
x = 20
y = 80
width = 140
height = 48
scaling = "fit"
alpha = 1.0This is a good way to keep display layouts simple. Instead of repeating logic inside the layout, you can define the logic once in expression and reuse the result.
If an expression cannot produce a valid value at a given moment, Sprint Boost simply leaves that expression out for that poll.
In plain language:
- the expression is not treated as permanently broken
- it is just unavailable for that moment
- when the needed input becomes valid again, the expression can appear again
This is another reason to build expressions in small, understandable steps.
- Start with one derived value, not ten.
- Give expression keys clear names like
score,has_started, orstate_label. - Keep raw memory reading in
game_info. - Use expressions for cleanup, combination, and reusable logic.
- If the same math or condition is needed in two places, move it into an expression.
- Prefer simple, readable expressions over clever ones.
[game_info.values.score_lo]
address = "0x0208"
decode = "u8_lo"
[game_info.values.score_hi]
address = "0x0207"
decode = "u8_lo"
[expression.int.score]
sum = [
{ game_info = "score_lo" },
{ mul = [{ game_info = "score_hi" }, 100] }
]
[expression.bool.has_started]
gt = [{ expression.int = "score" }, 0]
[play_stats.players.p1.tracked.score]
expression = { int = "score" }
mode = "final"This is a good starter pattern because it shows the full chain:
- read raw values
- build a better value
- use that value in another feature
Continue to: