Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff87159ca3 |
@@ -1933,6 +1933,101 @@ def get_latency_stats() -> dict:
|
||||
"recent": _latencies[-10:],
|
||||
}
|
||||
|
||||
class SpatialSearch:
|
||||
"""Find nearest user or object by name using BFS on the room graph."""
|
||||
|
||||
def find(self, target: str, from_room: str, rooms: dict, presence_mgr) -> list[dict]:
|
||||
"""Search all rooms for target (player name or object).
|
||||
Returns list of {name, type, room, distance, first_direction} sorted by distance.
|
||||
"""
|
||||
target_lower = target.lower()
|
||||
results = []
|
||||
|
||||
# BFS from from_room
|
||||
visited = set()
|
||||
queue = [(from_room, 0, None)] # (room, distance, first_direction)
|
||||
while queue:
|
||||
room, dist, first_dir = queue.pop(0)
|
||||
if room in visited:
|
||||
continue
|
||||
visited.add(room)
|
||||
|
||||
room_data = rooms.get(room, {})
|
||||
|
||||
# Search players in this room
|
||||
players = presence_mgr.get_players_in_room(room)
|
||||
for p in players:
|
||||
if target_lower in p['username'].lower():
|
||||
results.append({
|
||||
'name': p['username'],
|
||||
'type': 'player',
|
||||
'room': room,
|
||||
'distance': dist,
|
||||
'first_direction': first_dir,
|
||||
})
|
||||
|
||||
# Search objects in this room
|
||||
for obj in room_data.get('objects', []):
|
||||
if target_lower in obj.lower():
|
||||
results.append({
|
||||
'name': obj,
|
||||
'type': 'object',
|
||||
'room': room,
|
||||
'distance': dist,
|
||||
'first_direction': first_dir,
|
||||
})
|
||||
|
||||
# Search whiteboard entries
|
||||
for entry in room_data.get('whiteboard', []):
|
||||
if target_lower in entry.lower():
|
||||
results.append({
|
||||
'name': entry[:60],
|
||||
'type': 'writing',
|
||||
'room': room,
|
||||
'distance': dist,
|
||||
'first_direction': first_dir,
|
||||
})
|
||||
|
||||
# Enqueue neighbors
|
||||
exits = room_data.get('exits', {})
|
||||
for direction, dest_room in exits.items():
|
||||
if dest_room not in visited:
|
||||
# First step sets the direction, subsequent steps preserve it
|
||||
next_dir = direction if first_dir is None else first_dir
|
||||
queue.append((dest_room, dist + 1, next_dir))
|
||||
|
||||
# Sort by distance, then by type priority (player > object > writing)
|
||||
type_priority = {'player': 0, 'object': 1, 'writing': 2}
|
||||
results.sort(key=lambda r: (r['distance'], type_priority.get(r['type'], 9)))
|
||||
return results
|
||||
|
||||
def format_results(self, results: list[dict], from_room: str) -> str:
|
||||
"""Format search results as a readable string."""
|
||||
if not results:
|
||||
return "Nothing found."
|
||||
|
||||
lines = []
|
||||
for r in results[:5]: # Cap at 5 results
|
||||
dist = r['distance']
|
||||
if dist == 0:
|
||||
loc = "right here"
|
||||
elif dist == 1:
|
||||
loc = f"one room away ({r['first_direction']})"
|
||||
else:
|
||||
loc = f"{dist} rooms away (head {r['first_direction']})"
|
||||
|
||||
type_label = {'player': '👤', 'object': '📦', 'writing': '📝'}.get(r['type'], '?')
|
||||
lines.append(f" {type_label} {r['name']} — {r['room']} ({loc})")
|
||||
|
||||
if len(results) > 5:
|
||||
lines.append(f" ... and {len(results) - 5} more")
|
||||
|
||||
return "Found:\n" + "\n".join(lines)
|
||||
|
||||
|
||||
_spatial_search = SpatialSearch()
|
||||
|
||||
|
||||
class BridgeHandler(BaseHTTPRequestHandler):
|
||||
"""HTTP handler for multi-user bridge."""
|
||||
|
||||
@@ -2642,10 +2737,24 @@ class BridgeHandler(BaseHTTPRequestHandler):
|
||||
"session_messages": len(session.messages),
|
||||
}
|
||||
|
||||
elif verb == 'find':
|
||||
if not arg:
|
||||
return {"command": "find", "error": "Find whom? Usage: find <name>"}
|
||||
target = arg.strip().lower()
|
||||
# Search all rooms for a matching player or object
|
||||
results = _spatial_search.find(target, room, rooms, presence_manager)
|
||||
return {
|
||||
"command": "find",
|
||||
"query": arg,
|
||||
"from_room": room,
|
||||
"results": results,
|
||||
"description": _spatial_search.format_results(results, room),
|
||||
}
|
||||
|
||||
else:
|
||||
return {
|
||||
"command": verb,
|
||||
"error": f"Unknown command: '{verb}'. Try: look, go <dir>, examine <obj>, say <msg>, ask <msg>",
|
||||
"error": f"Unknown command: '{verb}'. Try: look, go <dir>, examine <obj>, say <msg>, ask <msg>, find <name>",
|
||||
}
|
||||
|
||||
def _extract_exits(self, description: str) -> dict:
|
||||
|
||||
Reference in New Issue
Block a user