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
+76
View File
@@ -114,6 +114,7 @@ class Raycaster:
self._draw_beacon(room, pixels, depth, fx, fz, rx, rz, eye, tan_pitch)
img = Image.frombuffer("RGB", (w, h), bytes(pixels), "raw", "RGB", 0, 1)
self._draw_marker(room, img, depth, fx, fz, rx, rz, eye, tan_pitch)
grid = self._depth_grid(depth, max_depth)
return img, grid
@@ -260,6 +261,65 @@ class Raycaster:
col = FLOOR_LINE
return _fog(col, t)
def _draw_marker(
self,
room: Room,
img: Image.Image,
depth: list[list[float]],
fx: float,
fz: float,
rx: float,
rz: float,
eye: float,
tan_pitch: float,
) -> None:
"""Draw the search-challenge triangle (a sprite on the crate's back
face), only when the anchor pixel is actually that face."""
m = room.marker
if m is None:
return
pos = room.marker_world_pos()
if pos is None:
return
w, h = self.width, self.height
cam = room.capsule
rel_x = pos[0] - cam.x
rel_z = pos[2] - cam.z
along = rel_x * fx + rel_z * fz
if along < 0.3:
return
right = rel_x * rx + rel_z * rz
col_c = (right / along / self.tan_fx + 1.0) / 2.0 * w
vv = (pos[1] - eye) / along - tan_pitch
row_c = (1.0 - vv / self.tan_fy) / 2.0 * h
r0, c0 = round(row_c), round(col_c)
if not (0 <= r0 < h and 0 <= c0 < w):
return
if abs(depth[r0][c0] - along) > 0.25:
return # the anchor pixel is not the marker's face (occluded)
half = max(2.5, m["size"] / along / self.tan_fx * w / 2.0)
draw = ImageDraw.Draw(img)
color = tuple(m["color"])
glow = tuple(min(255, c + 90) for c in color)
draw.ellipse(
[
col_c - half * 2.2,
row_c - half * 2.2,
col_c + half * 2.2,
row_c + half * 2.2,
],
fill=glow,
)
draw.polygon(
[
(col_c, row_c - half),
(col_c - half * 0.9, row_c + half * 0.7),
(col_c + half * 0.9, row_c + half * 0.7),
],
fill=color,
outline=(255, 255, 255),
)
def _draw_beacon(
self,
room: Room,
@@ -405,6 +465,22 @@ def render_topdown(room: Room, size: int = 400) -> Image.Image:
fx, fz = c.forward()
tip = xy(c.x + fx * 0.7, c.z + fz * 0.7)
draw.line([(cx, cz), tip], fill=(30, 30, 40), width=3)
m = room.marker
if m is not None:
pos = room.marker_world_pos()
if pos is not None:
mx, mz = xy(pos[0], pos[2])
r = m["size"] * scale
draw.polygon(
[
(mx, mz - r * 1.4),
(mx - r, mz + r),
(mx + r, mz + r),
],
fill=tuple(m["color"]),
outline=(20, 20, 26),
)
return img