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
+50
View File
@@ -247,3 +247,53 @@ async def test_move_accepts_duration_zero_default(bridge):
res = (await client.call_tool("move", {"forward": 1.0})).output
assert res["moved"] == pytest.approx(1.0)
assert res["duration"] == 0.0
async def test_report_verifies_near_marker(bridge):
t = InProcessTransport.start(bridge)
async with t:
client = AICCClient(t)
async with client:
await client.handshake()
out = (await client.call_tool("report", {"discovery": "triangle"})).output
assert out["success"] is True
assert out["verified"] is False # far away from spawn
pos = bridge.world.marker_world_pos()
bridge.world.capsule.x = pos[0]
bridge.world.capsule.z = pos[2]
out2 = (await client.call_tool("report", {})).output
assert out2["verified"] is True
assert "verified" in out2["message"]
async def test_turn_with_duration_is_smooth(bridge):
t1 = InProcessTransport.start(bridge)
t2 = InProcessTransport.start(bridge)
async with t1, t2:
mover = AICCClient(t1)
viewer = AICCClient(t2)
async with mover, viewer:
await mover.handshake()
await viewer.handshake()
task = asyncio.create_task(
mover.call_tool("turn", {"yaw_deg": 90.0, "duration": 0.8})
)
await asyncio.sleep(0.3)
mid = (await viewer.call_tool("proprioception", {})).output["rotation"]["yaw_deg"]
assert 0.0 < mid < 90.0, f"expected intermediate rotation, got {mid}"
res = (await task).output
assert res["rotation"]["yaw_deg"] == pytest.approx(135.0, abs=0.5)
async def test_look_at_crate(bridge):
t = InProcessTransport.start(bridge)
async with t:
client = AICCClient(t)
async with client:
await client.handshake()
out = (await client.call_tool("look_at", {"target": "crate_red"})).output
dx = bridge.world.boxes[0].cx - bridge.world.capsule.x
dz = bridge.world.boxes[0].cz - bridge.world.capsule.z
assert out["rotation"]["yaw_deg"] == pytest.approx(
math.degrees(math.atan2(dx, dz)) % 360.0, abs=0.5
)
+48
View File
@@ -131,3 +131,51 @@ def test_describe_layout():
assert d["room"]["width"] == ROOM_SIZE
assert len(d["obstacles"]) == 3
assert d["beacon"]["id"] == "beacon"
def test_marker_is_on_a_back_face():
"""The triangle must sit on a face pointing away from the spawn corner."""
for _ in range(20):
room = Room()
pos = room.marker_world_pos()
assert pos is not None
box = next(b for b in room.boxes if b.id == room.marker["box"])
face = room.marker["face"]
# spawn is at (1.5, 1.5); the marker face normal must point away from it
to_spawn = (1.5 - box.cx, 1.5 - box.cz)
normal = {
"x0": (-1, 0), "x1": (1, 0), "z0": (0, -1), "z1": (0, 1),
}[face]
assert normal[0] * to_spawn[0] + normal[1] * to_spawn[1] < 0
assert pos[0] == pytest.approx(box.x_max()) if face == "x1" else True
def test_marker_not_visible_from_spawn():
"""Looking toward the beacon from spawn must not show the triangle."""
from testbed.room.render import Raycaster
room = Room()
# Spawn looks at the beacon; the marker is on a back face, so the anchor
# pixel of the marker must be occluded (different surface) or off-frame.
pos = room.marker_world_pos()
assert pos is not None
# Stand on the marker's side of the crate, facing it.
face = room.marker["face"]
offsets = {
"x0": (-1.0, 0.0, 90.0), # west face: stand west, face +x
"x1": (1.0, 0.0, 270.0), # east face: stand east, face -x
"z0": (0.0, -1.0, 0.0), # south face: stand south, face +z
"z1": (0.0, 1.0, 180.0), # north face: stand north, face -z
}[face]
room.capsule.x = pos[0] + offsets[0]
room.capsule.z = pos[2] + offsets[1]
room.capsule.yaw_deg = offsets[2]
img, _ = Raycaster().render(room)
px = img.load()
orange = 0
for y in range(0, 120, 2):
for x in range(0, 160, 2):
r, g, b = px[x, y]
if r > 200 and 100 < g < 190 and b < 90:
orange += 1
assert orange > 3, "triangle must be visible when looking at the marker face"