Spaces:
Sleeping
Sleeping
Commit ·
09f65bb
1
Parent(s): 65aa2ad
feat(memory): store + replay food_cells (paint under agents)
Browse filesCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
proteus/grid/scenario.py
CHANGED
|
@@ -237,6 +237,13 @@ class Scenario(ABC):
|
|
| 237 |
"""
|
| 238 |
return []
|
| 239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
def record_focal_move(self, action: str) -> None:
|
| 241 |
"""Record the focal's last committed move (no-op by default).
|
| 242 |
|
|
|
|
| 237 |
"""
|
| 238 |
return []
|
| 239 |
|
| 240 |
+
def food_cells(self) -> list[tuple[int, int]]:
|
| 241 |
+
"""Static 1x1 food cells of the built world, for replay/observation.
|
| 242 |
+
|
| 243 |
+
Default: none. Scenarios with food override this (see ``memory_gen``).
|
| 244 |
+
"""
|
| 245 |
+
return []
|
| 246 |
+
|
| 247 |
def record_focal_move(self, action: str) -> None:
|
| 248 |
"""Record the focal's last committed move (no-op by default).
|
| 249 |
|
proteus/runtime/memory.py
CHANGED
|
@@ -70,6 +70,8 @@ class MemoryCheckpoint(BaseModel):
|
|
| 70 |
Populated for scenarios whose observation is prose (no ASCII grid), so the
|
| 71 |
web replay can paint walls it cannot recover from ``frame_ascii``. Empty for
|
| 72 |
grid scenarios (their walls are already in each ``frame_ascii``)."""
|
|
|
|
|
|
|
| 73 |
|
| 74 |
|
| 75 |
def _safe(name: str) -> str:
|
|
@@ -164,6 +166,7 @@ def memory_frames(
|
|
| 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 |
|
|
@@ -182,6 +185,9 @@ def memory_frames(
|
|
| 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})
|
|
|
|
| 70 |
Populated for scenarios whose observation is prose (no ASCII grid), so the
|
| 71 |
web replay can paint walls it cannot recover from ``frame_ascii``. Empty for
|
| 72 |
grid scenarios (their walls are already in each ``frame_ascii``)."""
|
| 73 |
+
food_cells: list[tuple[int, int]] = Field(default_factory=list)
|
| 74 |
+
"""Static 1x1 food cells of the episode world (same rationale as wall_rects)."""
|
| 75 |
|
| 76 |
|
| 77 |
def _safe(name: str) -> str:
|
|
|
|
| 166 |
focal_idx = sym2idx.get("A", 1)
|
| 167 |
predator_idx = sym2idx.get("B", 2)
|
| 168 |
wall_idx = sym2idx.get("#", 3)
|
| 169 |
+
food_idx = sym2idx.get("F", 14)
|
| 170 |
w, h = grid_size
|
| 171 |
out: list[dict] = []
|
| 172 |
|
|
|
|
| 185 |
for y in range(max(0, ry0), min(h, ry1 + 1)):
|
| 186 |
for x in range(max(0, rx0), min(w, rx1 + 1)):
|
| 187 |
grid[y][x] = wall_idx
|
| 188 |
+
for (fx, fy) in checkpoint.food_cells:
|
| 189 |
+
if 0 <= fx < w and 0 <= fy < h:
|
| 190 |
+
grid[fy][fx] = food_idx
|
| 191 |
paint(grid, mt.predator_pos[0], mt.predator_pos[1], 5, predator_idx)
|
| 192 |
paint(grid, mt.focal_pos[0], mt.focal_pos[1], 3, focal_idx)
|
| 193 |
out.append({"turn_idx": mt.turn_idx, "action": mt.action, "grid": grid})
|
proteus/runtime/memory_gen.py
CHANGED
|
@@ -121,4 +121,5 @@ def generate_memory(
|
|
| 121 |
transparent_prompt=brief,
|
| 122 |
persona_weight_id=persona.persona_weight_id if persona else None,
|
| 123 |
wall_rects=list(scenario.wall_rects()),
|
|
|
|
| 124 |
)
|
|
|
|
| 121 |
transparent_prompt=brief,
|
| 122 |
persona_weight_id=persona.persona_weight_id if persona else None,
|
| 123 |
wall_rects=list(scenario.wall_rects()),
|
| 124 |
+
food_cells=list(scenario.food_cells()),
|
| 125 |
)
|
tests/runtime/test_memory_frames.py
CHANGED
|
@@ -34,3 +34,27 @@ def test_prose_frame_paints_positions_and_walls():
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_prose_frame_paints_food_cells():
|
| 40 |
+
ck = _ck(
|
| 41 |
+
[MemoryTurn(turn_idx=1, frame_ascii="Open field 8x8. You are A ...",
|
| 42 |
+
action="up", focal_pos=(0, 0), predator_pos=(6, 6))],
|
| 43 |
+
wall_rects=[(5, 1, 5, 1)], # a clear cell (not under focal/predator/food)
|
| 44 |
+
)
|
| 45 |
+
ck.food_cells = [(4, 1), (1, 4)]
|
| 46 |
+
g = memory_frames(ck, legend=_GRID_LEGEND, grid_size=(8, 8))[0]["grid"]
|
| 47 |
+
assert g[1][4] == 14 and g[4][1] == 14 # food (idx 14) painted
|
| 48 |
+
assert g[1][5] == 3 # wall still there
|
| 49 |
+
assert g[0][0] == 1 # focal still on top
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def test_checkpoint_food_cells_roundtrips():
|
| 53 |
+
from proteus.runtime.memory import MemoryCheckpoint
|
| 54 |
+
ck = MemoryCheckpoint(
|
| 55 |
+
model="m", scenario="pack_evade", difficulty="easy", seed=1,
|
| 56 |
+
created_at="t", outcome="survived", transparent_prompt="x",
|
| 57 |
+
food_cells=[(3, 4)],
|
| 58 |
+
)
|
| 59 |
+
ck2 = MemoryCheckpoint.model_validate_json(ck.model_dump_json())
|
| 60 |
+
assert ck2.food_cells == [(3, 4)]
|