Skip to content

Sum range

Every exercise generated from a recipe walks a running total from step to step. sumMax is the highest value that total may ever reach.

sumMax

text
0 <= every running sum <= recipe.sumMax

The ceiling is inclusive: sumMax: 19 permits a running sum of exactly 19. The floor is a hard 0, and it is not a recipe field — a soroban has no negative state, so there is nothing to configure.

PropertyValue
Typeinteger
Range11999
Default on create99
Applies toevery running sum, including the value each step lands on

The upper bound of 1999 exists because sumMax + 1 sizes the generator's feasibility analysis arrays; it is a memory bound, not a pedagogical one.

sumMax also determines the generator's candidate operand alphabet: every power of ten up to sumMax. A recipe with sumMax: 99 can produce steps in the ones and the tens; sumMax: 9 can only produce steps in the ones. You do not configure that alphabet directly — you narrow it with rules, as Restricting places describes.

Why it is a recipe field, not a rule

It is fair to ask why sumMax is not simply a constraint rule such as current_sum <= 99.

The generator proves feasibility before it searches. That analysis is a dynamic program indexed by (layer, sum), and it allocates its working arrays before any rule is evaluated — it has to know the width of the sum axis up front. A rule cannot size an allocation; it can only narrow a domain that already exists.

So the division of labour is:

  • sumMax establishes the domain. Structural, authored on the recipe.
  • A rule referencing current_sum narrows that domain further. It can never widen it.

Writing current_sum <= 500 on a recipe with sumMax: 99 does nothing. Writing current_sum <= 19 on a recipe with sumMax: 99 genuinely tightens the ceiling — with one important caveat, below.

Confining sums to a narrower window

Suppose you want every running sum inside 0..19. The obvious move is to set sumMax: 19 and be done, and for most recipes that is exactly right.

But when you want a window narrower than the recipe's sumMax — or a floor above the structural 0 — you reach for a step-scope constraint rule. The naive version is not enough:

current_sum >= 0 AND current_sum <= 19

This is incomplete. current_sum is prev_sum + current_value: it describes the state after the step is applied. It says nothing about where the exercise started.

current_sum alone does not bound the opening value

current_sum constrains transitions only. With just the current_sum bound above, exercises were measured opening on start values well outside the window — 16 of 40 generated exercises opened above 19 (on values such as 26 and 22), with the first step dropping into range from outside it. Every individual transition satisfied the rule; the opening was never a transition.

The fix is to bound the previous sum as well:

current_sum >= 0 AND current_sum <= 19 AND prev_sum <= 19

prev_sum <= 19 is not redundant with current_sum <= 19. The two clauses close different holes:

ClauseRejects
current_sum <= 19a step that would land above the window
prev_sum <= 19a step taken from a position already above the window — including the very first step, whose prev_sum is the opening value

With both clauses in place the same measurement produced 0 of 40 exercises opening outside 0..19.

This is the concrete technique for a recipe teaching carries into the tens, where sums must stay in 0..19 — a range no fixed digit-count setting could ever express. Either sumMax: 19, or a looser sumMax plus the rule above, works directly.

Prefer sumMax when the window starts at 0

If the window you want is 0..sumMax, set sumMax and skip the rule — the ceiling is already enforced structurally and prunes the search more cheaply. Reach for the constraint rule when you need a floor above 0, or a ceiling below the recipe's sumMax for a subset of situations.

A sum-range rule constrains transitions. If the opening value needs its own shape — a clean tens digit, a zero ones digit — guard on step_index:

IF step_index = 0 THEN digitAt(prev_sum, 1) = 0

See Restricting places for the full treatment of opening-value pinning, and Variables for how prev_sum, current_value, and current_sum relate.

Sum rules and place channels

A rule attached to a place-channel (place: 1, 10, 100, 1000) cannot reference current_sum, and compilation rejects it if it does. Inside a channel, current_sum would mean "prev_sum plus that one channel's digit" — an intermediate state a composite step never actually passes through, because it commits every channel together.

Whole-step sum conditions belong on an ordinary global attachment (place: 0) with usage: constraint. Written that way, a sum rule works unchanged on recipes that produce composite steps. See Multi-place combo steps.

See also

Reference and cookbook for mental-math rule and recipe authoring.