Merge test/wizard-coverage (wizard commands verified against C)
This commit was merged in pull request #38.
This commit is contained in:
67
TODO.md
67
TODO.md
@@ -29,12 +29,67 @@ Refactor ground rules:
|
||||
|
||||
# Next Step
|
||||
|
||||
Broaden unit test coverage where playtesting finds thin spots — wizard commands
|
||||
(#7). Rings and sticks, the first two thirds of this step, are done; see the top
|
||||
of Completed Steps.
|
||||
Tag a release once a full game (Amulet retrieval and score entry) completes
|
||||
without defects. Promoted from Future Steps now that the coverage step above it
|
||||
is finished.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-09 Wizard commands under test (`test/wizard-coverage`, closes #7): the
|
||||
last of the three thin spots, so the coverage step is now closed rather than
|
||||
narrowed. `game/wizard.go`'s eight functions had no tests of their own, and
|
||||
the file is not purely a debug surface — `set_know` writes the per-game
|
||||
discovered tables that name items in ordinary play, and `teleport` is what the
|
||||
teleport ring calls every fiftieth turn. Package coverage 60.6% -> 62.4%.
|
||||
Everything expected was transcribed from `wizard.c`, `command.c` (the
|
||||
`CTRL('I')` kit), `extern.c` (`a_class[]`), `weapons.c` (`init_dam[]`) and
|
||||
`rogue.h`; 30 mutations were tried and all 30 were caught.
|
||||
|
||||
Two findings came out of the reading. (1) **A wizard-created cursed weapon
|
||||
is not cursed, in C or here.** `create_obj` sets `ISCURSED` and then calls
|
||||
`init_weapon`, which _assigns_ `weap->o_flags = iwp->iw_flags` and so
|
||||
overwrites the bit it just set; only the `o_hplus` penalty survives, and the
|
||||
"cursed" weapon can still be dropped and unwielded. The port reproduces this
|
||||
exactly. The test asserts the whole flag word comes back as the `init_dam[]`
|
||||
row's value whatever blessing was answered, and deleting the `ISCURSED` line
|
||||
from the port leaves every weapon test green — which is the evidence that
|
||||
the line is dead for weapons. The armor arm has no such clobber and does
|
||||
keep the curse. (2) **`show_map`'s standout is asymmetric in C and symmetric
|
||||
here.** C tests `!(real & F_REAL)` before drawing and `!real` — the whole
|
||||
flag word — after. `new_level` seeds every square with `p_flags = F_REAL`,
|
||||
and exactly three sites clear that bit. `passages.c putpass` sets `F_PASS`
|
||||
first, so its secret passage is left at `0x80`. `passages.c door`'s
|
||||
secret-door arm clears it on a room-wall exit whose flags are still exactly
|
||||
`F_REAL` (`rooms.c` writes no `p_flags` at all), leaving `p_flags == 0`; its
|
||||
per-square gate is `rnd(5) == 0` against `putpass`'s `rnd(40) == 0`, and
|
||||
`game/passages.go`'s `door` reproduces it. `new_level`'s trap loop then ORs
|
||||
in `rnd(NTRAPS)`, which is `abs((int) RN) % 8` and so yields `0..7`, and
|
||||
`T_DOOR` is `00` — an unsprung trapdoor square is also exactly zero
|
||||
(`be_trapped` is what later ORs `F_SEEN` into it). So C _does_ turn standout
|
||||
off again, at secret doors and unsprung trapdoors; what it gets wrong is
|
||||
leaking the attribute forward from a secret passage or a non-trapdoor trap
|
||||
until it reaches one of those. Intermittent bands of reverse video, not a
|
||||
permanently reversed map. `game/wizard.go` tests `isReal` both times and
|
||||
highlights the one square. That is a display-only difference in a
|
||||
wizard-only command and was reported on the issue rather than changed here;
|
||||
the test asserts the map characters unconditionally but the standout
|
||||
attribute only up to the first secret square, so it pins nothing that C
|
||||
contradicts.
|
||||
|
||||
Two things the tests had to be built around. The `insist` arm of `whatis` is
|
||||
a loop whose only exits are picking a matching item and `n_objs == 0`, so a
|
||||
script that runs dry hangs instead of failing — every sequence that can
|
||||
re-prompt ends in an abort tail, the `n_objs == 0` exit is reached the way a
|
||||
player reaches it (`*` for a list with nothing appropriate in the pack)
|
||||
rather than by poking the counter, and the one mutation that deletes that
|
||||
exit is the only one of the 30 that fails by timeout instead of fast,
|
||||
necessarily so. And `show_map` does **not** mark squares seen — it writes
|
||||
into `hw` and touches no `PLACE` at all — so the issue's wording for it
|
||||
could not be tested as written; the loop bounds are asserted instead by
|
||||
planting a marker in the rows C's loop excludes, since those rows are blank
|
||||
on a real level and copying blanks over blanks would have made the bound
|
||||
unfalsifiable.
|
||||
|
||||
- 2026-08-09 Wands and staffs under test (`test/sticks-coverage`, closes #6):
|
||||
the second of the three thin spots the Next Step names. `game/sticks.go` was
|
||||
the largest under-tested file in the repo — 534 lines, 23 functions, one test
|
||||
@@ -724,13 +779,11 @@ of Completed Steps.
|
||||
|
||||
# Future Steps
|
||||
|
||||
1. Tag a release once a full game (Amulet retrieval and score entry) completes
|
||||
without defects.
|
||||
2. Full-terminal-size support (deferred by explicit decision 2026-07-06):
|
||||
1. Full-terminal-size support (deferred by explicit decision 2026-07-06):
|
||||
per-game dungeon dimensions instead of the 80x24 constants; open design
|
||||
questions are resize policy, gameplay tuning at larger sizes, and a --classic
|
||||
80x24 mode.
|
||||
3. Note: this repo is exempt from the standard policy scaffold. A minimal dev
|
||||
2. Note: this repo is exempt from the standard policy scaffold. A minimal dev
|
||||
Makefile (fmt/fmt-check/lint/test/check targets) exists per sneak's
|
||||
2026-07-07 request, but do not add a Dockerfile, CI config, or
|
||||
REPO_POLICIES.md.
|
||||
|
||||
Reference in New Issue
Block a user