showMap diverges from C's standout handling — decide after PR #38 lands the corrected analysis #39
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
STATUS: the original body of this issue was WRONG and has been replaced.
The analysis below is the corrected one, verified twice in review (PR #38,
two rounds) and now landed in the tree at
13caec4. The original text — whichclaimed C's standout latches on at the first secret square and never turns
off — was mine, written from a subagent's summary rather than from the C, and
is preserved in the comment history rather than here. Do not work from the
earlier comments' description of the mechanism.
The divergence
C's
show_map(wizard.c) tests two different things either side of the draw:The Go port (
game/wizard.go) testsisRealon both sides, so it turnsstandout off correctly. The port is better than C here — which, under a
faithfulness contract, is itself the divergence.
What C actually does
There are exactly three sites that clear
F_REAL, and the only whole-wordwrite to
p_flagsanywhere in the C isnew_level.c:39 = F_REAL— so there isno fourth route to a zero flag word:
p_flagspassages.c:281putpass()— setsF_PASS, then clearsF_REAL0x80, non-zeropassages.c:309door(), secret arm — clearsF_REAL, sets nothing0new_level.c:79trap loop —*sp &= ~F_REAL; *sp |= rnd(NTRAPS)0one time in eight (T_DOORis00,NTRAPSis 8)be_trapped(move.c:276) is what ORsF_SEENlater, so the trap case onlyyields zero while the trap is unsprung; likewise
door()'s case only whilethe secret door is unfound.
Consequence: C's
wstandenddoes fire — at secret doors and unsprungtrapdoors. What C gets wrong is leaking standout forward from a secret
passage (or a non-trapdoor trap) until the row-major scan reaches one of those
zero-word squares. The result is intermittent bands of reverse video, not a
permanently reversed map.
The Go counterparts match C at all three sites (
game/passages.go:265-271,:277-297,game/newlevel.go:53-55), so these squares exist in bothlanguages.
The decision
Option A — keep the port's correct behaviour and document the deliberate
divergence — subject to a final ruling now that the corrected analysis has
landed. Rationale: the faithfulness contract protects what a player can
observe (seed compatibility, message text, RNG call order, save format), and
this touches none of them. It is display-only, wizard-only, and unambiguously
a C typo rather than a C decision. Reproducing intermittent reversed bands in
a debug-only command buys nothing.
MEMORY.mdpermits this — "behavior must not change ... unless a TODO stepsays so" — so deviations are contemplated; they must be deliberate and
recorded.
Definition of done
showMapgiving C's asymmetric test verbatim, the three-sitemechanism above, the intermittent-bands consequence, and the decision not
to reproduce it.
ARCHITECTURE.md§9 entry (§9 is where deliberate departures live).square — replacing the deliberately-agnostic assertion PR #38 left in
place. That assertion was correct while this question was open: a reviewer
confirmed empirically that rewriting the Go to the C-faithful
*g.Level.FlagsAt(y, x) == 0leaves the entire suite green, so the currenttest pins only the intersection of both behaviours.
make checkgreen;TODO.mdCompleted Steps entry; do not rotate"Next Step".
(closes #N).Implementation requirements
analysis has been wrong once already and the write-up is the deliverable —
documenting a mechanism incorrectly would defeat the entire point.
git show origin/c-master:<file>; do NOT check out or modifyc-master/modern-rogue.GOLANGCI_LINT_CACHEto a fresh empty private dir AND retryon
parallel golangci-lint is running; a private cache does not prevent thelock collision. Treat any result naming paths outside your worktree as void.
maketargets only. Do NOT modify.golangci.yml. No Dockerfile/CI/script/.game/testdata/.Decision: Option A — keep the port's correct behaviour, document the
divergence. sneak has delegated repo-local calls, so I am ruling rather than
parking this. Unassigning him.
I filed this assigned to sneak out of reflex because it touches the
faithfulness contract. On reflection that was wrong: unlike #29 (which changes
the canonical config shared by every repo) this is entirely local to rgoue,
and
MEMORY.mdalready contemplates deviations — "behavior must not change... unless a TODO step says so". The contract asks for deviations to be
deliberate and recorded, not for them never to happen. So the decision is
in scope; only the recording is mandatory.
Why A over B, restated as a rule rather than a preference: the
faithfulness contract exists to protect what a player can observe — seed
compatibility, message text, RNG call order, save format. This touches none of
those. It is display-only, wizard-only, and unambiguously a C typo rather than
a C decision (
!(real & F_REAL)against!realin adjacent lines). Faithfullyreproducing a rendering bug in a debug command buys nothing and costs
readability for the exact person the command exists to help.
The part that actually matters is the recording. An undocumented
improvement is indistinguishable from an unnoticed porting error the next time
somebody diffs the port against C — which is precisely how
'+'(#11) stayedhidden until it was found by accident. So this must not land as "the Go code
happens to be right"; it lands as "the Go code is deliberately right, here is
C's bug, here is why we did not copy it".
Scope for whoever implements this (small, and NOT part of PR #38 — that
PR's tests deliberately pin neither behaviour, which was the right call while
the question was open):
showMapgiving C's asymmetric test verbatim, the mechanism(
new_level.cseedsp_flags = F_REAL; squares losing the bit keepF_PASSor a non-zerornd(NTRAPS), sorealis rarely zero), the visibleconsequence in C (standout latches on at the first secret square), and the
decision not to reproduce it.
ARCHITECTURE.md§9-style entry, since §9 is where deliberatedepartures live.
square — replacing PR #38's deliberately-agnostic assertion that stops at
the first secret square.
make checkgreen;TODO.mdCompleted Steps entry; do not rotate"Next Step".
Verify the analysis before writing any of it. Every step above is
second-hand from PR #38's author and is currently being checked by that PR's
reviewer. If the reviewer's adjudication contradicts any of it, this decision
is void and should be re-opened rather than implemented from this comment. On
this repo, briefs have been wrong twelve times, several of them mine.
Filing the implementation as its own issue once PR #38's review lands and the
analysis is confirmed.
DECISION: showMap is more correct than C — reproduce C's broken standout, or keep the fix?to DECIDED: keep showMap's correct standout; document the deliberate divergence from CVOID — my ruling above is withdrawn. The analysis it rested on is wrong.
I wrote that the decision was conditional: "if the reviewer's adjudication
contradicts any of it, this decision is void and should be re-opened rather
than implemented from this comment." PR #38's review contradicted it. The
condition has fired, so I am withdrawing rather than quietly patching.
What is still true: the divergence is real. C's
show_maptests!(real & F_REAL)before the draw and!realafter;game/wizard.go:129-138tests
isRealboth times. The port does not reproduce C's asymmetry.What is false in my issue body and my ruling — both of which I wrote from
PR #38's author's account without checking:
The enumeration of squares that lose
F_REALnames two sites. There arethree:
passages.c putpass()— setsF_PASS, then clearsF_REAL→0x80.Correct as stated.
passages.c door(), the secret-door arm — omitted entirely from myanalysis. It clears
F_REALon a square whose flags are exactlyF_REAL, leavingp_flags == 0. It is the more common case:rnd(10)+1 < level && rnd(5) == 0againstputpass'srnd(40) == 0.game/passages.go:285-296reproduces it, so such squares exist in bothlanguages.
new_level.ctrap loop —*sp |= rnd(NTRAPS)yields 0..7 andT_DOORis
00, so an unseen trapdoor square is alsop_flags == 0. My "anon-zero
rnd(NTRAPS)" is wrong one time in eight.So the consequence I described is wrong. I said standout latches on at the
first secret square and never turns off, "rendering the remainder of the
wizard map reversed". It does not. C leaks forward from a secret passage (or
a non-trapdoor trap) and clears again at the next secret door or unseen
trapdoor — intermittent bands, not a permanently reversed map.
Does the conclusion survive? I think Option A still wins, and for a
stronger reason than before — but I am not re-ruling from this comment,
because that is the mistake I just made.
The corrected severity cuts both ways. It makes C's bug less damaging, which
weakens "the wizard map is unusable, fix it". But it also makes reproducing
the bug even less valuable: faithfully rendering intermittent reversed bands
in a debug-only command buys nothing at all. What it does change is the
ARCHITECTURE.mdtext, which under Option A is the entire deliverable — andwriting that from a false description of C would defeat the purpose of
recording it.
Re-ruling once PR #38's rework lands with the corrected analysis in the
tree, so the decision is made against text that has been through review
rather than against a summary. Nothing depends on the timing: PR #38's tests
deliberately pin neither behaviour, and that was independently confirmed —
rewriting the Go to the C-faithful
*g.Level.FlagsAt(y, x) == 0leaves theentire suite green.
Leaving this open and unassigned. Retitling to drop "DECIDED".
Two lessons I am recording rather than absorbing silently. First: I filed this
issue quoting C line-by-line as though I had read it, when I had read a
subagent's summary of it — the issue reads far more authoritative than my
evidence warranted. Second: stating the void condition is what made this
recoverable. It cost one sentence and saved a wrong decision from being
implemented as fact.
DECIDED: keep showMap's correct standout; document the deliberate divergence from Cto showMap diverges from C's standout handling — decide after PR #38 lands the corrected analysis