search challenge: triangle marker on a crate's back face (rendered in vision, in digest), report tool with distance verification, smooth turn(duration), --free/--search autonomous modes, look_at crates; verified with gpt-5.6-luna

This commit is contained in:
opencode
2026-08-08 21:58:57 +03:00
parent 315762363e
commit daa0371519
9 changed files with 444 additions and 34 deletions
+57
View File
@@ -8,6 +8,7 @@ from __future__ import annotations
import math
from dataclasses import dataclass
from typing import Any
ROOM_SIZE = 16.0
ROOM_HEIGHT = 3.0
@@ -124,6 +125,62 @@ class Room:
]
# Pending audio events, drained by the hear sensor.
self._audio: list[dict] = []
# Search challenge: a small triangle painted on the BACK side of one
# of the crates (the face pointing away from the spawn corner). The
# agent must find it by exploring and looking with vision.
self.marker: dict[str, Any] = self._place_marker()
# ---------- search marker ----------
def _place_marker(self) -> dict[str, Any]:
"""Put the triangle on a random crate, on the face most hidden from
the spawn corner (1.5, 1.5)."""
import random
box = random.choice(self.boxes)
to_spawn = (1.5 - box.cx, 1.5 - box.cz)
normals = {
"x0": (-1.0, 0.0),
"x1": (1.0, 0.0),
"z0": (0.0, -1.0),
"z1": (0.0, 1.0),
}
faces = sorted(
normals,
key=lambda f: normals[f][0] * to_spawn[0] + normals[f][1] * to_spawn[1],
)[:2] # the two faces pointing away from spawn
return {
"box": box.id,
"face": random.choice(faces),
"u": 0.5,
"v": 0.55,
"size": 0.42,
"color": (255, 150, 40),
}
def marker_world_pos(self) -> tuple[float, float, float] | None:
"""World coordinates (x, y, z) of the marker center, or None."""
m = self.marker
if m is None:
return None
box = next((b for b in self.boxes if b.id == m["box"]), None)
if box is None:
return None
if m["face"] == "x0":
x, z = box.x_min(), box.cz - box.half_d + m["u"] * 2 * box.half_d
elif m["face"] == "x1":
x, z = box.x_max(), box.cz - box.half_d + m["u"] * 2 * box.half_d
elif m["face"] == "z0":
x, z = box.cx - box.half_w + m["u"] * 2 * box.half_w, box.z_min()
else:
x, z = box.cx - box.half_w + m["u"] * 2 * box.half_w, box.z_max()
return (x, m["v"] * box.height, z)
def distance_to_marker(self) -> float:
pos = self.marker_world_pos()
if pos is None:
return float("inf")
return math.hypot(pos[0] - self.capsule.x, pos[2] - self.capsule.z)
# ---------- ticks ----------