Remember what torchlight showed: persist seen cells as map memory - #39
Merged
Conversation
The light reveal (1.2.1) was deliberately ephemeral: the player view recomputed what the party sees by its own light on every build, so the moment the party walked on — or the torch guttered out — every cell it had glimpsed but never stepped on fell dark again. A front end's automap could therefore only remember the physically walked footprint, and a mapped room vanished behind the party (osr-web issue #23). The client cannot fix this itself; the projection is its only source of geometry. Persist sight as its own state, distinct from exploration: - `DungeonState.seen` holds the remembered cells beside `explored`, same `"{dungeon}:{level}"` key shape. `mark_seen` is an idempotent bulk append, new cells in sorted (x, y) order so serialization is deterministic across runs. - `GameSession.execute` folds the current light reveal into `seen` after every accepted command (`_persist_sight`) — the one hook that covers every reveal-changing action uniformly: entering, moving, stairs, doors, lighting, placement, discovery, battle-flight relocation. Rejected commands change no state, so they never reach it. - The projection unions walked, remembered-seen, and currently lit cells; the live reveal stays so lighting a torch reveals immediately, even when a light effect lands on the ledger without a command. - `_light_reveal` and its radius helpers move from views.py to exploration.py, beside `_sight_passes` — they are game logic now, not projection. Seen is memory of sight, never exploration: movement cost still reads the walked footprint (a lit-but-unwalked cell costs the full 30), drop-pile visibility stays on explored cells, an undiscovered secret door's sealed alcove is never recorded, and the referee view is unchanged. The field is additive with a default — no schema bump, no migration; an older save loads with empty memory and starts remembering. Adds TestSeenPersistence (walk-away persistence, save/load round-trip, pre-`seen` save loads clean, sealed-alcove no-leak) and mark_seen idempotence/ordering coverage; the leak property test's invariant becomes explored-or-seen, and the cross-level test now expects level 1's memory to survive the descent while the live reveal still never bleeds across levels. Claude-Session: https://claude.ai/code/session_01Gu9KVvucLb7NoquyNjmxxv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The light reveal (1.2.1) was deliberately ephemeral: the player view recomputed what the party sees by its own light on every build, so the moment the party walked on — or the torch guttered out — every cell it had glimpsed but never stepped on fell dark again. A front end's automap could therefore only remember the physically walked footprint, and a mapped room vanished behind the party (osr-web #23). The client cannot fix this itself; the projection is its only source of geometry.
What changes
DungeonState.seenholds the remembered cells besideexplored, same"{dungeon}:{level}"key shape.mark_seenis an idempotent bulk append, new cells in sorted(x, y)order so serialization is deterministic across runs.GameSession.executefolds the current light reveal intoseenafter every accepted command (_persist_sight) — the one hook that covers every reveal-changing action uniformly: entering, moving, stairs, doors, lighting, placement, discovery, battle-flight relocation. Rejected commands change no state, so they never reach it._light_revealand its radius helpers move fromviews.pytoexploration.py, beside_sight_passes— they are game logic now, not projection.What deliberately does not change
Seen is memory of sight, never exploration: movement cost still reads the walked footprint (a lit-but-unwalked cell costs the full 30 thirds), drop-pile visibility stays on explored cells (piles only ever form where the party has stood), an undiscovered secret door's sealed alcove is never recorded, and the referee view is untouched. The field is additive with a default — no schema bump, no migration per the versioning policy; an older save loads with empty memory and starts remembering.
Tests
TestSeenPersistence: the issue's walk-away scenario (torch extinguished so live sight can't mask a persistence failure — fails onmain), save/load round-trip, a pre-seensave document loads clean, and the sealed alcove stays unrecorded.mark_seenidempotence and deterministic ordering; the overlay round-trip test now carries a populatedseen.explored ∪ seen(its old form was already violated in principle by the 1.2.1 ephemeral reveal whenever the fuzz lit a torch); the cross-level test now expects level 1's memory to survive the descent while the live reveal still never bleeds across levels.test_seeing_a_cell_by_light_does_not_change_movement_costpasses unchanged — the guardrail that motivated keepingseenout ofexplored.Suite: 1657 passed, 58 skipped (baseline 1652).
ruff formatandruff checkclean. Verified end to end through osr-web against this branch: the automap keeps every cell seen on a walk after the party returns, and the memory survives save/restore over the wire.https://claude.ai/code/session_01Gu9KVvucLb7NoquyNjmxxv