Skip to content

Add test suite for memory reader, state builder, and pathfinding - #1

Open
ch3ronsa wants to merge 1 commit into
NousResearch:mainfrom
ch3ronsa:tests/memory-reader-and-state-builder
Open

Add test suite for memory reader, state builder, and pathfinding#1
ch3ronsa wants to merge 1 commit into
NousResearch:mainfrom
ch3ronsa:tests/memory-reader-and-state-builder

Conversation

@ch3ronsa

@ch3ronsa ch3ronsa commented Mar 5, 2026

Copy link
Copy Markdown

Summary

Adds the first test suite for the project as mentioned in the README's contribution areas ("Tests for memory readers and state builders").

79 tests across 3 test files, all passing. No ROM or emulator required — tests use a mock emulator with a 64KB RAM buffer.

What's covered

test_memory_reader.py (36 tests)

  • Gen-1 encoding table: uppercase, lowercase, digits, space, terminator
  • Name tables: all 151 species present, common moves/items/maps, 8 badges
  • RedBlueMemoryReader (mock emulator):
    • read_player: name, rival, money (BCD), badges, position, facing
    • read_party: single Pokemon, empty party, corrupted count (cap at 6)
    • read_bag: items with quantities, empty bag, terminator handling
    • read_battle: not in battle, wild encounter with enemy data
    • read_dialog: inactive, active via text_box_id, active via joy_ignore bit
    • read_map_info: known map (Pallet Town), unknown map ID
    • read_flags: has_pokedex, has_oaks_parcel, pokedex counts
    • _decode_status: OK, poison, paralysis, sleep, multiple status

test_state_builder.py (19 tests)

  • build_game_state: all sections present, metadata with/without frame_count, error handling (RuntimeError, NotImplementedError), partial failure isolation
  • build_state_summary: player info, party, location, bag, battle (active/inactive), dialog, flags, error sections, empty state

test_pathfinding.py (24 tests)

  • manhattan: same point, horizontal, vertical, diagonal, negative coords
  • neighbors: no collision map (4 neighbors), collision blocking, direction correctness
  • find_path (A*): same start/goal, straight paths, diagonal, collision map routing, no path, max iterations, wall avoidance with path verification
  • directions_to_actions: conversion, empty list
  • navigate: walk actions, same position
  • path_length: reachable, unreachable, same position

Test plan

  • pytest tests/ -v — 79 passed in 0.52s
  • No external dependencies required (mock-based)
  • Compatible with existing pyproject.toml pytest config

79 tests covering:
- RedBlueMemoryReader: player, party, bag, battle, dialog, map,
  flags, status decoding, Gen-1 encoding table, name tables
- State builder: build_game_state sections, error handling,
  partial failures, build_state_summary output
- Pathfinding: A* search, collision maps, wall avoidance,
  manhattan distance, neighbors, navigate, path_length

All tests use mock emulator (no ROM required).
@ch3ronsa

ch3ronsa commented Mar 5, 2026

Copy link
Copy Markdown
Author

On ROM-based integration tests:

These tests are intentionally mock-based because Pokemon ROMs are copyrighted and can't be included in the repo or CI. The mock emulator simulates a 64KB RAM buffer with the same read_u8/read_u16/read_range interface as the real Emulator class, so the memory reader logic (address offsets, BCD decoding, Gen-1 text encoding, party struct parsing) is fully exercised.

That said, integration tests with a real ROM would be valuable for verifying end-to-end behavior. A possible approach:

ROM_PATH = Path(os.environ.get("POKEMON_ROM_PATH", ""))

@pytest.mark.skipif(not ROM_PATH.exists(), reason="ROM not available (set POKEMON_ROM_PATH)")
def test_real_emulator_reads_player():
    emu = PyBoyEmulator(str(ROM_PATH))
    reader = RedBlueMemoryReader(emu)
    # advance a few frames, then verify state reads don't crash
    ...

Users with a ROM could run: POKEMON_ROM_PATH=./red.gb pytest tests/ -v

Happy to add this as a follow-up if the project wants to support it.

teknium1 added a commit that referenced this pull request Jun 2, 2026
Adds RAM-based walkability so the agent navigates with ground truth instead
of guessing from pixels (the #1 cause of getting lost).

- collision.py: reads wTileMap (0xC3A0) + wCurMapTileset (0xD367), maps the
  on-screen 20x18 tile grid to the game's 10x9 walkable block grid, and
  classifies each block via the authoritative per-tileset collision lists
  from pokered data/tilesets/collision_tile_ids.asm (all 24 tilesets).
  Player is locked to cell E5; sampling uses the correct +1 row offset
  (player standing tile is wTileMap (8,9)). Verified 40/40 move predictions
  against the live emulator across a walking path.
- overlay.py: render_grid_overlay() draws a labelled A1..J9 grid; optional
  walkable= arg tints cells green (walkable) / red (blocked) for an
  unambiguous spatial view (great for the agent and the stream).
- server.py: /state now carries a 'collision' block (grid + ASCII) for Red;
  new GET /map/ascii (text) and GET /screenshot/grid (annotated PNG).
- Tests: test_collision.py (pure-data geometry + tileset tables).

Experiment finding: an ASCII collision map is the most reliable + cheapest
navigation input for the agent; the tinted grid image is the best stream
visual; raw-pixel guessing is the weakest. Wire both in accordingly.
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.

1 participant