Compare commits
1 Commits
ba444a2002
...
b0f96fac28
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f96fac28 |
67
TODO.md
67
TODO.md
@@ -70,6 +70,73 @@ of Completed Steps.
|
||||
reach only mean something against known walls and a known passage number. All
|
||||
27 mutations tried against the new tests were caught.
|
||||
|
||||
- 2026-08-09 Trap unit-test coverage (`test/traps-coverage`, closes #14):
|
||||
`trapHandlers` had eight entries and **zero** direct tests, on the one
|
||||
subsystem besides combat that can kill the hero outright. New
|
||||
`game/traps_test.go` (17 tests, 13 subtests) covers all eight arms of
|
||||
`move.c be_trapped`, the prologue every trap runs through, and the
|
||||
`rust_armor` tail `T_RUST` calls. Package coverage 56.2% -> 57.9% measured on
|
||||
`main` at `bf820e3`, the branch point, before the sticks tests landed. Every
|
||||
expected value is transcribed from `origin/c-master` and quoted in the file.
|
||||
**No divergence from C was found.**
|
||||
|
||||
The issue body's trap list was wrong and the correction is the first thing
|
||||
worth recording: there is no separate "poison dart" trap — `T_DART` **is**
|
||||
the poisoned dart, its death message being "a poisoned dart killed you" —
|
||||
and the list omitted `T_MYST`, the mystery trap, whose arm is an eleven-way
|
||||
`rnd(11)` message switch. `rogue.h` 192-200 is the authority
|
||||
(`T_DOOR`/`T_ARROW`/`T_SLEEP`/`T_BEAR`/`T_TELEP`/`T_DART`/`T_RUST`/`T_MYST`,
|
||||
`NTRAPS` 8) and the Go `TrapKind` iota matches it index-for-index.
|
||||
|
||||
Three C details the tests are built around. (1) `BEARTIME` and `SLEEPTIME`
|
||||
are `spread(3)` and `spread(5)` (`rogue.h` 108-109), and `spread` is
|
||||
`nm - nm/20 + rnd(nm/10)`; for both, `nm/10` is 0 and C's `rnd` short
|
||||
circuits a zero range without touching the generator, so each is an exact
|
||||
constant that costs **no** random number — and the tests assert the no-draw
|
||||
half as well as the value, because a stray draw desynchronises the
|
||||
seed-compatible stream. (2) `T_ARROW` swings at `s_lvl - 1` and `T_DART` at
|
||||
`s_lvl + 1`: opposite signs, which is exactly the kind of detail a
|
||||
transliterating port drops. (3) The strength loss is gated on
|
||||
`!ISWEARING(R_SUSTSTR) && !save(VS_POISON)`, and the `&&` is load-bearing —
|
||||
with the ring on, C never rolls the save, so the arm must spend two random
|
||||
numbers and not three.
|
||||
|
||||
Two shapes worth keeping, both forced by mutation results rather than
|
||||
foresight. Damage dice are checked by a **sweep**, not one shot: `rnd(n)` is
|
||||
"raw value % n", so a single draw agrees between a d6 and a d5 five times in
|
||||
six and leaves the generator identical either way — the first draft's
|
||||
single-trial arrow test passed with `roll(1,6)` mutated to `roll(1,5)`.
|
||||
Likewise the swing arguments are pinned by a 200-trial boundary sweep at a
|
||||
mid-range to-hit target: a forced hit and a forced miss cannot see a wrong
|
||||
`at_lvl` or a dropped `op_arm`, because both arms are reachable at any level
|
||||
and swing spends one `rnd(20)` regardless.
|
||||
|
||||
Mutation-proved, 31 mutations, each reverted. Two were **not** caught on the
|
||||
first pass and the tests were strengthened until they were, which is the
|
||||
useful part of the record. (a) Deleting `new_level()` from `T_DOOR` left the
|
||||
suite green: `be_trapped`'s own prologue stamps the trap glyph into the cell
|
||||
the hero fell through, so "the map changed" is true even with no new level
|
||||
dug. The test now counts differing cells — exactly one can change that way —
|
||||
and also requires the staircase to move and the hero to be re-placed. (b)
|
||||
The `roll(1,6)` case above. One line is recorded as **not isolable**:
|
||||
`T_TELEP`'s `mvaddch(tc, TRAP)`, whose C comment claims `look()` will not
|
||||
redraw the vacated square. It is in fact redundant in both C and this port —
|
||||
the prologue has already set `p_ch` to `TRAP`, `teleport()` opens by drawing
|
||||
`floor_at()` (which returns `chat(hero)`) over the departing square, and
|
||||
`leave_room` only blanks squares showing `FLOOR` — so no reachable
|
||||
configuration makes deleting it observable. That is stated in the test
|
||||
rather than papered over. The other 29 each failed their own test and only
|
||||
their own; two also moved `TestAutoSaveOnSignalRacesTurnLoop`, which drives
|
||||
real turns and is legitimately sensitive to `BEARTIME` and to armor rusting.
|
||||
|
||||
Deliberately uncovered: the two death messages, "an arrow killed you" and "a
|
||||
poisoned dart killed you". Each is printed immediately before `death()`,
|
||||
which reaches `myExit` and `os.Exit`, so provoking either would take the
|
||||
test binary with it; the hero is pinned with `fortify()` and the damage
|
||||
rolls are checked by replaying C's arithmetic instead of by letting HP reach
|
||||
zero. `Next Step` deliberately not rotated: #14 was an out-of-band gap found
|
||||
while surveying, not part of the rings/sticks/wizard step.
|
||||
|
||||
- 2026-08-09 Ring unit-test coverage (`test/rings-coverage`, closes #5): the
|
||||
first third of the standing coverage step. `game/rings.go` had **zero** tests
|
||||
— not one of the 32 in the suite touched wear, removal, hand choice, or the
|
||||
|
||||
1023
game/traps_test.go
Normal file
1023
game/traps_test.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user