fix(attract): use remainder-driven item phases

Raw 1000:061c-063b divides the idle tick by 144, copies the remainder from
CX:BX into AX:DX, and only then divides by 8. Readable C and Rust instead used
an unbounded quotient equivalent to tick/1152, making DAT801-809 advance far
too slowly and suggesting a nonexistent negative long-idle index.

Use `(tick % 144) / 8` to repeat item states 0,1..9,8..1 every 144 callbacks.
Remove the disproven long-idle status crops and restore the ten-minute simulator
ceiling. Extend the C and Rust phase tests across the forward, reverse, and wrap
boundaries.

Test Plan:
- raw instruction review at `1000:061c-063b` -- remainder transfer confirmed
- `cargo test --workspace --all-targets --all-features` -- 125 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed with zero incomplete or unclassified units
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
This commit is contained in:
2026-08-23 20:50:05 +02:00
parent 5b8351ca1f
commit dd0299c30b
8 changed files with 45 additions and 41 deletions
+8 -30
View File
@@ -150,24 +150,15 @@ impl AttractAnimation {
}
fn item_index(self) -> Option<usize> {
let phase = self.tick / 1_152;
let phase = (self.tick % 144) / 8;
let item = if phase < 10 {
phase
} else {
18_u32.saturating_sub(phase)
18 - phase
};
(item != 0).then(|| usize::try_from(item - 1).unwrap_or(0))
}
fn wrapped_status_item_index(self) -> Option<usize> {
match self.tick / 1_152 {
19 => Some(2),
20 => Some(1),
21 => Some(0),
_ => None,
}
}
fn marquee_source_x(self) -> Option<f32> {
(self.score_counter != 0).then(|| {
let phase = self.score_counter % 49;
@@ -620,16 +611,6 @@ impl App {
300.0,
WHITE,
);
} else if let Some(status) = self.attract.wrapped_status_item_index() {
draw_texture_region(
&self.assets.media[status],
123.0,
300.0,
68.0,
61.0,
0.0,
0.0,
);
}
self.draw_intro_marquee();
}
@@ -1419,15 +1400,12 @@ mod tests {
assert!(!at(800).record_strip_active(0));
assert_eq!(at(4).chase_index(), Some(0));
assert_eq!(at(192).chase_index(), Some(3));
assert_eq!(at(1_151).item_index(), None);
assert_eq!(at(1_152).item_index(), Some(0));
assert_eq!(at(10_368).item_index(), Some(8));
assert_eq!(at(11_520).item_index(), Some(7));
assert_eq!(at(20_736).item_index(), None);
assert_eq!(at(21_888).wrapped_status_item_index(), Some(2));
assert_eq!(at(23_040).wrapped_status_item_index(), Some(1));
assert_eq!(at(24_192).wrapped_status_item_index(), Some(0));
assert_eq!(at(25_344).wrapped_status_item_index(), None);
assert_eq!(at(7).item_index(), None);
assert_eq!(at(8).item_index(), Some(0));
assert_eq!(at(72).item_index(), Some(8));
assert_eq!(at(80).item_index(), Some(7));
assert_eq!(at(136).item_index(), Some(0));
assert_eq!(at(144).item_index(), None);
}
#[test]