Skip to content

Restricting places

There is no placeFilter field on a recipe. "Ones only", "ones and tens", "everything except ones" — all of it is expressed through rules you attach to the recipe.

This page is the practical catalogue of those expressions. It assumes you already know what place, formula, and digitBefore mean; if not, read Decimal places first.

The basic place restriction

Attach a rule with role: constraint, scope: step, usage: constraint, and leave the count fields null — counts do not apply to constraints.

IntentExpression
Ones onlyplace = 1
Tens onlyplace = 10
Hundreds onlyplace = 100
Ones and tensplace IN (1, 10)
Tens and hundredsplace IN (10, 100)
Everything except onesplace NOT IN (1)

These are evaluated inside the same classifier the feasibility analysis calls, so they prune candidates with the full power a dedicated structural filter would have had. The only difference is that an out-of-place candidate is generated and then rejected, rather than never generated — one extra memoized rule evaluation per distinct state.

The generator's operand alphabet comes from sumMax alone (every power of ten up to the ceiling), so a place constraint is how you narrow it. See Sum range.

A bare place constraint silently kills every composite step

place is null on a multi-place composite step such as +47 — a composite step does not have one affected place. A constraint like place = 1 therefore rejects every composite step the recipe could otherwise produce, with no error and no diagnostic. The recipe simply generates atomic steps only, or reports infeasible.

If your recipe uses place channels, do not restrict combinations with a bare place comparison. Use a per-place digit test instead:

digitAt(current_value, 1) != 0

See Multi-place combo steps.

Restricting to a specific operand set

Values like 10, 20, 30, 50 are atomic — exactly one non-zero digit — so they are already in the generator's candidate alphabet. Restricting a recipe to them needs no special mechanism, just a narrower expression:

place = 10 AND formula IN (1, 2, 3, 5)

Since formula = abs(current_value) / place, this matches the operands ±10, ±20, ±30, ±50.

formula uses abs, so both signs are admitted by default. Pin the sign explicitly when you want only additions:

current_value > 0 AND place = 10 AND formula IN (1, 2, 3, 5)

An expression this specific usually belongs on the pattern rule itself — the target or filler rule the recipe is built around — rather than as a separate global constraint.

Conditioning on the running sum

digitBefore is the digit of prev_sum at the place current_value affects. It is the key input for pattern classification, and it lets a rule describe the position a technique applies from:

current_value > 0 AND place = 10 AND formula = 8 AND digitBefore IN (2, 3, 4)

Read: "add 80, but only when the tens digit is currently 2, 3, or 4."

A rule like this needs a sumMax with enough headroom for the resulting carry. digitBefore >= 2 at the tens place implies prev_sum >= 20, and +80 needs room up to roughly prev_sum + 80. A recipe whose sumMax is too tight for its own rules reports infeasible — correctly, not spuriously.

Pinning the opening value

Place and sum-range rules constrain transitions. They say nothing about prev_sum at step 0 unless a rule explicitly references step_index.

This matters more than it sounds. A recipe pinning place = 10 can still open on a value whose ones digit is non-zero, because nothing in place = 10 constrains where the exercise starts. To pin the opening digit at a given place:

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

digitAt(value, place) returns the digit of abs(value) at the given decimal place — useful for inspecting a place other than the one current_value itself affects. See Digit functions and the function reference.

When opening rules matter

Every exercise the API generates opens from an empty abacus (prev_sum = 0 at step 0), so a step_index = 0 guard is often already satisfied. It becomes load-bearing when a caller supplies an explicit opening value as an authoring probe, since such a value is seated as prev_sum for step 0 without passing through the classifier — no pattern rule and no constraint applies to it.

Quick reference

WantExpressionAttach as
Steps only in the onesplace = 1constraint role, step scope, usage: constraint
Steps only ±10, ±20, ±30, ±50place = 10 AND formula IN (1, 2, 3, 5)pattern role, on the target or filler rule
Only additions of that setcurrent_value > 0 AND place = 10 AND formula IN (1, 2, 3, 5)pattern role
Tens digit conditioned on the sumplace = 10 AND formula = 8 AND digitBefore IN (2, 3, 4)pattern role, step scope
Pin the opening digitIF step_index = 0 THEN digitAt(prev_sum, 1) = 0constraint role, step scope
Restrict a channel combinationdigitAt(current_value, 1) != 0constraint role, step scope, place: 0

A note on conflicting restrictions

A rule combination that admits nothing — two mutually exclusive place constraints on the same recipe, say — is not detected when you save the recipe. Feasibility is computed by the rule engine at generation time, not by the editor.

The practical workflow is to generate a preview batch after editing and check that it still produces results. An empty or degraded batch is the signal; Troubleshooting covers reading the failure.

See also

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