Spaces:
Running
Running
| """Structured-fog text representation (text alternative to the PNG | |
| minimap), for the text-vs-vision comparison. | |
| `compute_unexplored_regions` + `format_structured_fog` are ported | |
| **verbatim** from the training repo | |
| (`scripts/run_one_game_rust_lmstudio.py`, ~L920โ1010 โ the v2-ablation | |
| representation validated against Qwen3.5-9B, Finding 7 Test 4). Kept | |
| byte-faithful so the text channel is identical to training's. Do not | |
| hand-edit; re-port if upstream changes. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any | |
| def compute_unexplored_regions( | |
| explored_cells, playable_bounds, max_regions: int = 8, | |
| ) -> list[dict[str, int]]: | |
| """4-connected flood-fill of the playable rect, treating cells in | |
| `explored_cells` as 'seen' and everything else as shroud. Returns a | |
| list of {x_lo, x_hi, y_lo, y_hi, cells} bounding boxes for each | |
| contiguous unexplored component, sorted by cell count descending and | |
| capped at `max_regions`.""" | |
| bx, by, bw, bh = playable_bounds | |
| explored = set((int(c[0]), int(c[1])) for c in explored_cells) | |
| visited: set[tuple[int, int]] = set() | |
| components: list[dict[str, int]] = [] | |
| for y0 in range(by, by + bh): | |
| for x0 in range(bx, bx + bw): | |
| if (x0, y0) in explored or (x0, y0) in visited: | |
| continue | |
| stack = [(x0, y0)] | |
| cells: list[tuple[int, int]] = [] | |
| while stack: | |
| cx, cy = stack.pop() | |
| if (cx, cy) in visited or (cx, cy) in explored: | |
| continue | |
| if cx < bx or cx >= bx + bw or cy < by or cy >= by + bh: | |
| continue | |
| visited.add((cx, cy)) | |
| cells.append((cx, cy)) | |
| stack.extend([(cx + 1, cy), (cx - 1, cy), | |
| (cx, cy + 1), (cx, cy - 1)]) | |
| if cells: | |
| xs = [c[0] for c in cells] | |
| ys = [c[1] for c in cells] | |
| components.append({ | |
| "x_lo": min(xs), "x_hi": max(xs), | |
| "y_lo": min(ys), "y_hi": max(ys), | |
| "cells": len(cells), | |
| }) | |
| components.sort(key=lambda r: -r["cells"]) | |
| return components[:max_regions] | |
| def format_structured_fog(obs: dict[str, Any], playable_bounds) -> str: | |
| """Render the engine's `explored_cells` as a structured-text fog | |
| summary (the text channel that substitutes for the PNG minimap).""" | |
| ec = obs.get("explored_cells") or [] | |
| if not ec: | |
| bx, by, bw, bh = playable_bounds | |
| return ( | |
| "Unexplored regions (largest first, computed from engine fog state):\n" | |
| f" - x โ [{bx}, {bx+bw-1}], y โ [{by}, {by+bh-1}] " | |
| f"({bw*bh} cells, entire playable map)" | |
| ) | |
| regions = compute_unexplored_regions(ec, playable_bounds, max_regions=6) | |
| if not regions: | |
| return "Unexplored regions: none โ entire playable map is explored." | |
| lines = ["Unexplored regions (largest first, computed from engine fog state):"] | |
| for r in regions: | |
| lines.append( | |
| f" - x โ [{r['x_lo']}, {r['x_hi']}], " | |
| f"y โ [{r['y_lo']}, {r['y_hi']}] ({r['cells']} cells)" | |
| ) | |
| return "\n".join(lines) | |