Skip to content

Fix 'rooms explored' statistic undercounting and minimap not appearing on new maps#356

Merged
dmccoystephenson merged 4 commits into
mainfrom
copilot/fix-rooms-explored-statistic
Apr 23, 2026
Merged

Fix 'rooms explored' statistic undercounting and minimap not appearing on new maps#356
dmccoystephenson merged 4 commits into
mainfrom
copilot/fix-rooms-explored-statistic

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

incrementRoomsExplored() was never called for rooms pre-loaded by RoomPreloader because _loadOrGenerateRoom only incremented on the generateNewRoom branch — which is skipped when the room is already in memory. Result: the stat stalled in single digits regardless of how far the player explored.

Additionally, the minimap would not appear consistently on new maps due to a silent error in MapImageGenerator.

Approach

Rooms explored stat fix

Introduce a "freshly generated" flag on Map that decouples when a room is created from when the player enters it.

  • Map._freshlyGeneratedRooms — thread-safe set (under existing _lock) marking rooms created fresh (not loaded from disk)
  • Map.generateNewRoom() — adds (x, y) to the set when actually generating (double-checked, inside lock)
  • Map.consumeIsNewRoom(x, y) -> bool — atomically checks and clears the flag; returns True exactly once per newly generated room

_loadOrGenerateRoom now calls consumeIsNewRoom instead of branching on whether generateNewRoom was called inline:

def _loadOrGenerateRoom(self, x, y, updateStats=True):
    wasCached = self.map.hasRoom(x, y)
    room = self.map.getRoom(x, y)
    if room == -1:
        room = self.map.generateNewRoom(x, y)
    if updateStats and self.map.consumeIsNewRoom(x, y):
        self.status.set("New area discovered")
        self.stats.incrementScore()
        self.stats.incrementRoomsExplored()
    elif updateStats and not wasCached:
        self.status.set("Area loaded")
    return room

This handles all cases uniformly:

  • Room generated inline → flagged → consumed on player entry → increments ✓
  • Room pre-loaded by RoomPreloader → flagged → consumed on player entry → increments ✓
  • Room loaded from disk → never flagged → no increment ✓
  • Re-entry → flag already consumed → no increment ✓
  • Pre-loaded but never entered → flag never consumed, no increment ✓

Other stat-related fixes:

  • initialize(): replaced unconditional incrementRoomsExplored() with consumeIsNewRoom(0, 0) to prevent double-counting when the player returns to the spawn room
  • Living-entity cross-room moves call _loadOrGenerateRoom(..., updateStats=False) so they don't consume the flag or affect stats

Minimap fix

MapImageGenerator.getRoomImages() and clearRoomImages() both called os.listdir() on the roompngs directory without checking if it existed. There is a race between the _doSave() background thread (which triggers updateMapImage() after each room-change save) and the main thread's draw() call (which creates the roompngs directory lazily via saveCurrentRoomAsPNG()). When the background thread won the race, os.listdir() raised FileNotFoundError, which was caught silently — preventing mapImage.png from ever being written and leaving the minimap invisible.

  • MapImageGenerator.getRoomImages(): returns [] immediately if the roompngs directory does not exist
  • MapImageGenerator.clearRoomImages(): exits early (no-op) if the roompngs directory does not exist

This ensures generate() never throws on a fresh game, allowing mapImage.png to be written (initially blank) on the first map update call, so the minimap widget appears immediately and fills in as rooms are visited.

…y generated rooms

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/fddd4ef0-c05f-48cb-8f68-8ea7779e130b

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix 'rooms explored' statistic not updating correctly Fix 'rooms explored' statistic undercounting due to RoomPreloader bypass Apr 23, 2026
Copilot AI requested a review from dmccoystephenson April 23, 2026 06:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes undercounting of the “rooms explored” statistic when rooms are generated ahead-of-time by RoomPreloader, by tracking “freshly generated” rooms in Map and consuming that flag when the player actually enters the room.

Changes:

  • Added Map._freshlyGeneratedRooms plus consumeIsNewRoom(x, y) to atomically mark/consume newly generated rooms.
  • Updated WorldScreen room-loading logic to increment stats based on consumeIsNewRoom, including suppressing stat updates for non-player transitions.
  • Added unit tests covering the new-room flag behavior and the “rooms explored” increment rules (including preloaded rooms).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/world/map.py Tracks freshly generated room coordinates and exposes consumeIsNewRoom() for atomic “first entry” detection.
src/screen/worldScreen.py Uses consumeIsNewRoom() to drive stat increments consistently (including preloaded rooms) and adds updateStats control for non-player transitions.
tests/world/test_roomsExplored.py New tests for end-to-end “rooms explored” increment semantics (new, re-entry, preloaded, disk-loaded).
tests/world/test_map.py Adds focused tests for consumeIsNewRoom() behavior (true once, false thereafter, not set for addRoom).
CHANGELOG.md Documents the bug root cause, fix approach, and adds a Learning Log entry.

Comment thread CHANGELOG.md Outdated
increment `rooms explored`; this handles both the pre-loaded (RoomPreloader background
thread) and direct-generate paths uniformly. Pass `updateStats=False` when calling
`_loadOrGenerateRoom` for non-player transitions (e.g., living-entity cross-room moves)
so the flag is not consumed and stats are not affected.
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Learning Log entry added here is marked [not yet integrated]. If this behavior is now part of the expected project conventions, please also integrate it into .github/copilot-instructions.md (and flip the tag to [integrated]), or clarify why it should remain unintegrated.

Suggested change
so the flag is not consumed and stats are not affected.
so the flag is not consumed and stats are not affected. This remains not yet integrated
because it is a localized implementation detail for `Map`/`WorldScreen` room-generation
and stats behavior, not a repository-wide contributor convention for
`.github/copilot-instructions.md`.

Copilot uses AI. Check for mistakes.
Comment thread tests/world/test_roomsExplored.py Outdated
Comment on lines +11 to +12
import pytest

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest is imported but never used in this test module. Please remove the unused import to keep the test file clean (and to avoid autoflake/linters flagging it).

Suggested change
import pytest

Copilot uses AI. Check for mistakes.
Comment thread tests/world/test_roomsExplored.py Outdated
Comment on lines +109 to +111
import json
import os

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json and os are imported inside this test but never used. Please remove these unused imports (autoflake will likely strip them anyway).

Suggested change
import json
import os

Copilot uses AI. Check for mistakes.
Comment thread tests/world/test_roomsExplored.py Outdated
try:
gameMap.generateNewRoom(0, 0)
preloader.preloadNearbyRooms(0, 0, gameMap)
preloader.shutdown(wait=True)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test calls preloader.shutdown(wait=True) both inside the try block and again in finally. The first call is redundant; consider keeping only the finally shutdown (or using a single shutdown after preloadNearbyRooms) to avoid double work and to make the intent clearer.

Suggested change
preloader.shutdown(wait=True)

Copilot uses AI. Check for mistakes.
Comment thread tests/world/test_map.py Outdated
Comment on lines +128 to +129
from lib.graphik.src.graphik import Graphik
from world.room import Room
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These local imports duplicate the module-level imports at the top of the file (Graphik, Room). Please remove the redundant imports here to keep the tests consistent and avoid unnecessary clutter.

Suggested change
from lib.graphik.src.graphik import Graphik
from world.room import Room

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes.


Instructions

1. Discover all comments

  • Read every review thread, inline comment, and general PR comment
  • Include comments from: Copilot, human reviewers, and any other bots
  • Do not skip comments marked as suggestions — apply them if they make sense

2. For each comment, do the following:

  • Understand what is being requested or flagged
  • Locate the relevant file(s) and line(s)
  • Apply the fix directly in code
  • If a Copilot suggestion diff is provided, apply it (or an equivalent fix)
  • If the comment is ambiguous, use your best judgment and reply in the PR thread or review comment explaining what you did; only add a code comment when explicitly requested or when future maintainers would benefit from it
  • After addressing a comment, reply to the thread summarising what you did and mark it as resolved if your environment supports it

3. Types of issues to handle:

  • Code quality / style issues (naming, formatting, readability)
  • Logic bugs or edge cases flagged by reviewers
  • Security concerns (e.g. input validation, secrets, injection risks)
  • Performance suggestions
  • Missing or incomplete error handling
  • Test coverage gaps
  • Documentation / comment improvements
  • Dependency or import issues

4. Do NOT:

  • Dismiss or ignore any comment without acting on it — note that "acting" can also mean replying to the thread with a rationale (e.g., won't fix, already addressed elsewhere) and leaving the code unchanged when appropriate
  • Leave TODO comments as a substitute for fixing the issue
  • Break existing tests while addressing comments
  • Change functionality beyond the scope of what the comment asks for

After making changes

  • Ensure all existing tests still pass
  • Add or update tests if a comment explicitly asks for them, or if a bug fix warrants it
  • Double-check that every open thread has been addressed, replied to, and resolved

Context

  • This PR may contain Copilot-generated review comments — treat these with the same priority as human comments
  • If two comments conflict with each other, prefer the human reviewer's intent and document the conflict and resolution in the PR conversation or commit message, not in a code comment
  • Commit changes in logical groups if there are many unrelated fixes

…own, clarify changelog

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/58f28541-b329-46c7-9ba1-ca24ed3c0973

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes....

All review threads addressed in 2cc8208:

  • test_roomsExplored.py: Removed unused pytest import (line 11), removed unused json/os imports inside test_loaded_from_disk_room_does_not_increment, and removed the redundant preloader.shutdown(wait=True) inside the try block (kept only in finally).
  • test_map.py: Removed the redundant local Graphik/Room imports in test_consume_is_new_room_false_for_added_room — they were already imported at the module level.
  • CHANGELOG.md: Added a clarification sentence to the [not yet integrated] Learning Log entry explaining why it's not being integrated into copilot-instructions.md (localized implementation detail, not a repository-wide contributor convention).

All 436 tests still pass.

@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot The minimap is not showing up consistently anymore, especially with new maps. Resolve.

…nimap on new maps

Agent-Logs-Url: https://github.com/Preponderous-Software/roam/sessions/15f3d783-a181-4c68-995b-346849d6e690

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

@copilot The minimap is not showing up consistently anymore, especially with new maps. Resolve.

Fixed in 735811f.

Root cause: MapImageGenerator.getRoomImages() and clearRoomImages() both called os.listdir() on the roompngs directory without first checking if it exists. There's a race between the _doSave() background thread (which triggers updateMapImage() after each room-change save) and the main thread's draw() call (which lazily creates the roompngs directory via saveCurrentRoomAsPNG()). When _doSave() won the race and the directory didn't exist yet, os.listdir() raised FileNotFoundError, which was caught silently by _doUpdateMapImage()'s try/except — so mapImage.png was never written and the minimap stayed invisible. This was most noticeable on new maps where the directory had never been created before.

Fix: Added os.path.isdir() guards to both methods — getRoomImages() returns [] and clearRoomImages() exits early when the directory is absent. This allows generate() to succeed on the first map update call regardless of race timing, writing an initially blank mapImage.png that fills in as rooms are visited.

Copilot AI changed the title Fix 'rooms explored' statistic undercounting due to RoomPreloader bypass Fix 'rooms explored' statistic undercounting and minimap not appearing on new maps Apr 23, 2026
@dmccoystephenson dmccoystephenson marked this pull request as ready for review April 23, 2026 21:13
@dmccoystephenson dmccoystephenson merged commit d1092ca into main Apr 23, 2026
1 check passed
@dmccoystephenson dmccoystephenson deleted the copilot/fix-rooms-explored-statistic branch April 23, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix 'rooms explored' statistic not updating correctly

3 participants