# Hard attack decisions, normal momentum and Start-only credits — 2026-10-28-r5

jdc asked to test attacking AI as well as r4's defensive coverage. After the
attack improved in paired scenarios, Hard's crowd meter should behave like
Normal. He also requires credits to stay on screen until Start is pressed.

## Behavior

Hard forwards provide puck-relative support in the attacking zone instead of
only skating toward a random zone point. The far winger moves toward the slot,
the near winger offers a return outlet, and the center supports from inside.
An occupied preferred point causes the forward to drop into a return lane.
Both teams benefit, including AI teammates. Humans retain their own controls.

The CPU now evaluates free teammates in better shooting positions when the
native logic considers a pass. It rejects boxed/empty/goalie/unavailable or
animation-locked receivers, marked receivers and obstructed pass corridors.
If no qualifying improvement exists, the original random receiver selection
runs. Native offside, pass-direction checks, actual target selection, passing
and reception physics still execute. The selected candidate guides the pass
direction; native dopass can select a different teammate in that direction.

A wide, poor-angle shot is deferred when a qualifying improved receiver exists;
the normal caller then considers its pass or keeps skating. Other shooting
choices and execution stay native. Breakaways retain their established path.
This is a bounded decision change, not a replacement for the entire engine.

Hard now uses **the same dynamic crowd momentum as Normal**: shots and checks
move the meter, it decays with game-clock seconds, and current momentum scales
each team's ratings within ±10%. The previous permanent human -10% / CPU +10%
handicap is removed, including its real rating effect. Easy keeps its existing
fixed advantage and guaranteed eligible human crits. Hard's new attacking and
r4 defensive positioning changes stay Hard-only. Normal/Easy retain their
native attacking decision paths. Shot physics, crit power, blue-line bonus,
goalie reach, controller ownership and season save format are unchanged.

Credits now wait **indefinitely for Start**. A/B/C and directions do not skip
the screen. Both ordinary controller ports are tested. Start already held
through the preceding picture is not a fresh press on this card.

## Native implementation

`experimental/ai_attack.s` is 1,028 bytes at **2D8800** (ends 2D8C04), clear of
r4 AI defence (2D8400) and the private velocity driver (2D9000). No new RAM:
geometry, scoring and loop state use a temporary stack frame. Installer
`ai_attack.py` asserts original instructions and free bytes.

| Hook | Entry / purpose |
|---|---|
| D1DE, D3BE | tail-jump to support (+0), retaining EvadePC E594 and native skateto 103C8 |
| E45E | JSR passpick (+4), replacing only random teammate-index generation |
| E292 | jump to shotgate (+8); native chkpk/BNE prologue is replayed on fallback |

Support requires Hard, a live friendly carrier, forward role 3/4/5 and carrier
attack depth 112..292. The carrier himself, controlled/unavailable/locked
actors, stopped/replay/offside/penalty-shot/shootout scenes are excluded.
Targets in attack coordinates: far winger X ±44, depth carrier+24 clamped
184..236; near winger X ±90, depth carrier-40 clamped 128..198; center X ±16
opposite the carrier side, depth carrier-12 clamped 172..218. A defender within
28 in each axis moves the target 28 back (and a winger 24 farther outside).

Receiver search additionally requires the actor to own the puck and not be on
a breakaway. Candidate depth 112..252, |X| <=140, Manhattan pass length 32..200.
Position quality is `2*abs(X)+abs(depth-224)`; require at least 24 improvement
over the carrier, then add length/4 to rank eligible choices. Opponents within
26 of a receiver reject him. Segment projection and cross product reject an
opponent in a conservative corridor (12*Manhattan-length cross-product bound).
The native checks remain after this search. The poor-angle veto requires
|puckX| >88 and attack depth 152..<264, plus a qualifying receiver; common
central shots avoid the extra receiver scan.

`crowd.s` changes the fixed-meter branch to Easy only. Entry offsets stay
unchanged and the code remains below its tiles at 2E0800. `credits.s` reuses
its former D066 countdown word as temporary active-card marker 4352, saves and
restores its previous value on the stack, and checks the native Start edge.
No new persistent marker is reserved. `test_core.py` sends an explicit native
Start once at boot when this card is detected; dedicated credits tests disable
that helper. The helper is confined to the first 900 frontend frames and stops
after the card closes, so it cannot pause a long gameplay test later.

Build controls for exact historical reproduction:

```sh
python3 tools/experimental/build_test.py base.bin r4.bin \
  --version 2026-10-28-r4 --no-ai-attack --legacy-hard-crowd --legacy-credits
```

Those flags reproduce the exact released r4. The default build includes r5.
A second current build matches byte-for-byte. The guarded r4 diff is **2,373
bytes**, confined to the listed hooks, attack/credits/crowd modules and
version/checksum. Original-v5.1W IPS reconstruction is verified.

## Measurements and limits

Private evidence: `work/ai-attack-20261028/`.

- The first prototype kept r4's crowd handicap unchanged to isolate the AI.
  In eight staged native pass situations, actual passes toward the free slot
  increased **3/8 → 8/8**; receptions **2/8 → 3/8**. It was not publicly released.
- The final Normal-style momentum build retains **3/8 → 8/8** passes, with
  **2/8 → 4/8** receptions. Both teams, both ends and both carrier sides.
- Eight support comparisons measure frames 40..100 after setup where the
  forward is in |X|<=90, depth160..236 and at least26 from the designated idle
  marker. Count rises **87/488 → 471/488**. This specifically measures escape
  from that marker in the controlled setup, not universal open-space detection.
- Nine initial setup frames settle captured CPU/input state. The next 200
  observation frames run native skating, decisions, collisions and puck physics,
  without position/velocity resets. Idle human receivers are not perfect
  receivers, and several intended passes still miss. No guaranteed scoring,
  win-rate or overall difficulty increase is claimed from these small samples.
- **76 paired native target/receiver/shot cases** cover both teams/ends/wings,
  central/marked/blocked/boxed/poor receivers, guard exclusions and registers.
- **12 native crowd profiles / 84 observations** verify Normal=Hard momentum,
  percentage caps, rating scaling, decay/reset and unchanged Easy policy for
  no humans, either human team and both human teams.
- Native credits wait **800 frames** after appearing, reject A/B/C/Right, then
  exit on Start within one frame on each ordinary port. A fresh process is used
  per port. This tests more than13 seconds with no timeout.
- Full shared **24-check** gate is required for this exact ROM/source, including
  r4 defence, four-pad keeper, 34 OT/shootout results, 14 lifecycle games,
  settings locks, menus/HUD and a complete Hard CPU game with native saved stats.
  See TEST_PLAN.md and current QA_STATUS.json. Scripted input; no new manual
  hardware playtest is claimed.

## Backup / delivery

Verified r4 ROM, source and site backup with MANIFEST.json:
`~/.config/retroarch/roms/claude/work/ai-attack-20261028/backup-r4.zip`, SHA256
`c63bedc746cd0b4e84264bb6a766ac31b7964b9ac68d20399264f5fb7f9874a1`.
It stays private under the standing single-current-download policy.

r5 SHA256 `f2b62c979f44da6635faff9ee5cefc28fc0e684da1e7c8ba55c3362a0338b0b7`;
CRC32 `59269517`. New version replaces r4 in canonical ROMs, Downloads, public
ROM+IPS and Bodin's same playlist item. Fresh progress; controller remaps kept.

## Research credits

Native assignments, passer stack contract, roles and existing shot aiming were
checked against v5.1W instructions and [abdulahmad / NHL94Genesis](https://github.com/abdulahmad/NHL94Genesis/tree/82487a1db9b9117ae23462f675a541d81486bf33/src),
particularly checks94.asm, input94.asm, assign94.asm and equals.inc. The native
engine already adjusts CPU shot aim; this patch changes support/choices rather
than claiming to add that feature. See CREDITS.md for the original game and
base-mod/community authors. New bounded routines and tests are this project's
addition.
