irregular6612 Claude Opus 4.8 (1M context) commited on
Commit
6a4d305
·
1 Parent(s): ae092aa

feat(memory): memory_frames() reconstructs per-turn color grids

Browse files

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

proteus/runtime/memory.py CHANGED
@@ -127,3 +127,62 @@ def render_memory_block(checkpoint: MemoryCheckpoint) -> str:
127
  parts.append(mt.frame_ascii)
128
  parts.append(f" you chose: {mt.action}")
129
  return "\n".join(parts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  parts.append(mt.frame_ascii)
128
  parts.append(f" you chose: {mt.action}")
129
  return "\n".join(parts)
130
+
131
+
132
+ def _invert_legend(legend: dict[int, str]) -> dict[str, int]:
133
+ return {sym: idx for idx, sym in legend.items()}
134
+
135
+
136
+ def _ascii_to_grid(frame: str, sym2idx: dict[str, int]) -> list[list[int]] | None:
137
+ """Parse *frame* as a rectangular grid of legend symbols, else None."""
138
+ lines = frame.split("\n")
139
+ if len(lines) < 2 or len({len(ln) for ln in lines}) != 1 or not lines[0]:
140
+ return None
141
+ grid: list[list[int]] = []
142
+ for ln in lines:
143
+ row: list[int] = []
144
+ for ch in ln:
145
+ if ch not in sym2idx:
146
+ return None
147
+ row.append(sym2idx[ch])
148
+ grid.append(row)
149
+ return grid
150
+
151
+
152
+ def memory_frames(
153
+ checkpoint: MemoryCheckpoint, *, legend: dict[int, str], grid_size: tuple[int, int]
154
+ ) -> list[dict]:
155
+ """Reconstruct a renderable color grid per memory turn.
156
+
157
+ Returns ``[{turn_idx, action, grid}]`` where ``grid`` is a ``height x width``
158
+ list of palette indices. Grid-style ``frame_ascii`` is inverted via *legend*;
159
+ prose frames are rebuilt from walls + recorded focal/predator positions.
160
+ Pure: no engine, no IO.
161
+ """
162
+ sym2idx = _invert_legend(legend)
163
+ bg = sym2idx.get(".", 5)
164
+ focal_idx = sym2idx.get("A", 1)
165
+ predator_idx = sym2idx.get("B", 2)
166
+ wall_idx = sym2idx.get("#", 3)
167
+ w, h = grid_size
168
+ out: list[dict] = []
169
+
170
+ def paint(grid, x0, y0, n, idx):
171
+ for j in range(n):
172
+ for i in range(n):
173
+ x, y = x0 + i, y0 + j
174
+ if 0 <= x < w and 0 <= y < h:
175
+ grid[y][x] = idx
176
+
177
+ for mt in checkpoint.memory_turns:
178
+ grid = _ascii_to_grid(mt.frame_ascii, sym2idx)
179
+ if grid is None:
180
+ grid = [[bg] * w for _ in range(h)]
181
+ for (rx0, ry0, rx1, ry1) in checkpoint.wall_rects:
182
+ for y in range(max(0, ry0), min(h, ry1 + 1)):
183
+ for x in range(max(0, rx0), min(w, rx1 + 1)):
184
+ grid[y][x] = wall_idx
185
+ paint(grid, mt.predator_pos[0], mt.predator_pos[1], 5, predator_idx)
186
+ paint(grid, mt.focal_pos[0], mt.focal_pos[1], 3, focal_idx)
187
+ out.append({"turn_idx": mt.turn_idx, "action": mt.action, "grid": grid})
188
+ return out
tests/runtime/test_memory_frames.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Pure reconstruction of per-turn color grids from a MemoryCheckpoint."""
2
+ from __future__ import annotations
3
+
4
+ from proteus.runtime.memory import MemoryCheckpoint, MemoryTurn, memory_frames
5
+
6
+ _GRID_LEGEND = {5: ".", 1: "A", 2: "B", 3: "#"}
7
+
8
+
9
+ def _ck(turns, scenario="x", wall_rects=None):
10
+ return MemoryCheckpoint(
11
+ model="m", scenario=scenario, difficulty="easy", seed=1,
12
+ created_at="t", outcome="survived", transparent_prompt="p",
13
+ memory_turns=turns, wall_rects=wall_rects or [],
14
+ )
15
+
16
+
17
+ def test_grid_frame_inverts_ascii_via_legend():
18
+ ck = _ck([MemoryTurn(turn_idx=1, frame_ascii="A.\n.#", action="right",
19
+ focal_pos=(0, 0), predator_pos=(1, 1))])
20
+ frames = memory_frames(ck, legend=_GRID_LEGEND, grid_size=(2, 2))
21
+ assert frames[0]["action"] == "right"
22
+ assert frames[0]["grid"] == [[1, 5], [5, 3]] # A . / . #
23
+
24
+
25
+ def test_prose_frame_paints_positions_and_walls():
26
+ ck = _ck(
27
+ [MemoryTurn(turn_idx=1, frame_ascii="Open field 8x8. You are A ...",
28
+ action="up", focal_pos=(0, 0), predator_pos=(5, 5))],
29
+ wall_rects=[(2, 2, 3, 3)],
30
+ )
31
+ g = memory_frames(ck, legend=_GRID_LEGEND, grid_size=(8, 8))[0]["grid"]
32
+ assert g[0][0] == 1 and g[2][2] == 1 # focal 3x3 at (0,0)
33
+ assert g[5][5] == 2 and g[7][7] == 2 # predator 5x5 at (5,5) clipped in-bounds
34
+ assert g[2][2] != 3 # focal painted over its cells
35
+ assert g[3][3] == 3 # wall cell visible
36
+ assert g[0][4] == 5 # background elsewhere