Skip to content

feat(sim): model Number Of Players over the scenario roster - #132

Merged
e54-bot merged 3 commits into
mainfrom
feat/bastion-value-constructs
Oct 28, 2026
Merged

e54-bot merged 3 commits into
mainfrom
feat/bastion-value-constructs

Conversation

@e54-bot

@e54-bot e54-bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Number Of Players(team) is modeled: it counts the scenario players on the resolved team — All Teams counting every declared player — over the same scenario roster All Players enumerates, which no action changes. The team argument resolves to a catalog Team member id the way All Players' does; an evaluated non-member is not_modeled.

This was the last Bastion-used general value still returning not_modeled. Every construct the issue lists was already modeled on main (PRs #61 and #62); this PR completes the remaining gap and the issue's verification bookkeeping.

Modeled / not modeled

Modeled by this PR:

  • Number Of Players — All Teams counts every scenario player; Team 1/Team 2 count the declared team; a player without a team counts only for All Teams; the roster is read-only.

Already modeled on main (verified against the current source): If-Then-Else, Compare (all six operators, arrays/null included), Not, Or, Divide, Modulo, Min, Max, Raise To Power, Round To Integer, Sine From Radians, First Of, Last Of, Count Of, Empty Array, Array Contains, Index Of Array Value, Array Slice, Append To Array, Remove From Array, Value In Array, Sorted Array, Filtered Array, Is True For All, Is True For Any, Current Array Element, Current Array Index, Evaluate Once, plus Set/Modify * Variable At Index and the full Modify operator set.

Left NotModeled (all out of scope):

  • Mapped Array — explicitly excluded by "Map-free forms only".
  • All Living Players, Players In Slot — roster reads outside the issue's list (tracked under athena#105 work).

Bastion progression

  • wright/Bastion fixture: moved past value numberOfPlayers (was the tick-0 stop, rule 41). Next stop: value mappedArray, tick 1, rule 20 [Hero/Ana] Display sleep HUD, action 3 — out of scope per "Map-free forms only".
  • Reference bastion.ow + completed scenario: unchanged, stops at action wait under a 90% simulation rate, tick 91, rule 218 initialize effects, action 2 — unrelated slow-motion wait behavior, not a value construct.

Verification

  • cargo fmt --check — clean.
  • cargo clippy --all-targets -p athena-sim -- -D warnings — clean.
  • cargo test -p athena-sim --test number_of_players — 5 tests pass: team counts, empty roster/team, player without a team (boundary), evaluated member-id argument, non-member argument.
  • cargo test -p athena-sim — full suite passes.
  • cargo check --locked --target wasm32-unknown-unknown -p athena-sim — clean.
  • Bastion workload re-runs above confirm both stops.

Docs: docs/simulator-interface.md (modeled-values list), docs/scenario-format.md (modeled reads) and docs/findings.md updated.

Unverified

  • No client capture exists for Number Of Players or the other general/array values; all are recorded as modeled-not-measured in docs/findings.md per the capture policy.
  • Client behavior of All Teams covering unteamed players is unmeasured; the model follows All Players' roster treatment.

Number Of Players(team) counts the scenario players on the resolved
team — All Teams counting every declared player — the same roster All
Players enumerates and no action changes. This was the remaining
Bastion-used value construct tracked here; every construct the issue
lists was already modeled, and the wright/Bastion workload now moves
past numberOfPlayers and stops at value mappedArray (tick 1, rule 20,
action 3), which "Map-free forms only" excludes.

Fixes #56
Divide, Modulo and Raise To Power could produce a NaN or infinity that
serialized as `{"type":"number","value":null}` in the trace: the scalar
arm of `vector::arithmetic`, the `binary` value calls and the `Modify`
operators returned `Val::Number` without the finite check the vector
arms already enforced. Route every scalar numeric result through
`produced_number` so the run stops `not_modeled` naming the construct,
as the documented rule requires.
@e54-bot

e54-bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Review findings addressed in 6dfaac0 (fix(sim): stop scalar arithmetic on a non-finite result).

Fixed: scalar arithmetic producing non-finite values that serialized as {"type":"number","value":null}. produced_number is now pub(crate) and every scalar numeric result routes through it: the scalar arm of vector::arithmetic (vector.rs), the binary closure covering modulo/raiseToPower/min/max (call.rs), and the Modulo/Min/Max/RaiseToPower arms of apply_modify (modify.rs). Each reported failing input — Divide(1, 0), Modulo(1, 0), Raise To Power(10, 400), and the Divide/Modulo/Raise To Power Modify forms — now stops not_modeled naming the construct. A regression test (scalar_arithmetic_non_finite_stops_instead_of_serializing_null) covers all six, and the non-finite-number edge is recorded in the findings.md athena#56 bullet.

Gates: cargo fmt --check, cargo clippy --all-targets -p athena-sim -D warnings, cargo test -p athena-sim --all-targets (39 targets, 0 failures), cargo check --locked --target wasm32-unknown-unknown -p athena-sim — all clean.

Ready for follow-up review.

Round To Integer wrapped its ceil/floor/round result in `Val::Number`
raw, and the `.ow` number literal arm of `eval` returned `Val::Number`
unchecked — `f64::from_str` saturates an overflowing digit run — so a
non-finite `f64` reached store sinks and serialized
`{"type":"number","value":null}` in the trace. Route the
`roundToInteger` result through `produced_number` and finite-check the
literal at evaluation, sealing the `Set`/`Append`/`For`-start literal
sinks issue #56's constructs reach; both now stop `not_modeled` naming
the construct.

Non-finite producers outside issue #56's constructs — the `For` `End`
step write, `Set Max Health`, the `Damage`/`Heal` applied values,
`Speed Of`, the match clock's `elapsed` accumulation,
`Set Ability Cooldown`, `Set Ultimate Charge` and the `hero_settings`
percent multipliers — can still serialize `null`; recorded as a known
gap in docs/findings.md pending a structural guard.
@e54-bot

e54-bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Round-2 review findings addressed in 046393d (fix(sim): stop non-finite literals and Round To Integer results).

Fixed (the sites inside issue #56's constructs):

  • Finding 1, crates/athena-sim/src/call.rs:394 — roundToInteger now routes its ceil/floor/round result through produced_number; a non-finite operand reaching it (e.g. a variable a still-open producer left inf) stops not_modeled (roundToInteger produced a non-finite number) instead of serializing {"type":"number","value":null}.
  • Finding 2, crates/athena-sim/src/eval.rs:262 — eval's Value::Number arm now finite-checks the parsed literal (f64::from_str saturates an overflowing digit run to inf), stopping not_modeled (number literal produced a non-finite number) at the one open .ow input path. This seals every direct-store sink the literal reaches through Model general and array value constructs used by Bastion #56's constructs — Set/Modify variable writes, Append To Array/array elements, At Index writes, and the For start argument. Tests: a_non_finite_number_literal_stops_at_evaluation and round_to_integer_stops_on_a_non_finite_operand in crates/athena-sim/tests/values.rs.

Deliberately left (outside issue #56's constructs — recorded as a known gap in docs/findings.md under athena#56; non-finite produced values there can still serialize null and need a structural guard, to be tracked separately):

  • crates/athena-sim/src/step.rs:418 and step.rs:448 — For/For Player Variable End step's updated variable write
  • crates/athena-sim/src/state_writes.rs:137 — Set Max Health's max * percent / 100.0 write
  • crates/athena-sim/src/actions.rs:97 (Damage) and actions.rs:161 (Heal) — the applied values bound into eventDamage/eventHealing
  • crates/athena-sim/src/player.rs:448 — Speed Of
  • crates/athena-sim/src/clock.rs:64-65 + call.rs:175 (+ sched.rs:389) — elapsed accumulation behind Total Time Elapsed, the timeline time, and Has Status' until
  • crates/athena-sim/src/hero_settings.rs:235 — leaf_modifier's number() settings boundary, and the Set Ability Cooldown/Set Ultimate Charge/Set Max Health sinks for non-finite produced (non-literal) values

Gates: cargo fmt --all -- --check, cargo clippy --locked --all-targets -p athena-sim -D warnings, cargo test --locked --all-targets (0 failures), cargo check --locked --target wasm32-unknown-unknown -p athena-sim — all clean.

Ready for follow-up review.

@e54-bot
e54-bot merged commit 6c2ee1c into main Oct 28, 2026
3 checks passed
@e54-bot
e54-bot deleted the feat/bastion-value-constructs branch October 28, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants