from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from ..environment.environment import Environment class MoveTool: name = "move" schema = { "type": "function", "function": { "name": "move", "description": "Move to an adjacent tile. Auto-loots any items on the destination tile.", "parameters": { "type": "object", "properties": { "dx": {"type": "integer", "description": "Change in x (-1, 0, or 1)"}, "dy": {"type": "integer", "description": "Change in y (-1, 0, or 1)"}, }, "required": ["dx", "dy"], }, }, } @staticmethod def run(env: Environment, aid: str, args: dict) -> str: agent = env.agents[aid] x, y = agent.pos dx, dy = args.get("dx", 0), args.get("dy", 0) if abs(dx) > 1 or abs(dy) > 1: return "Invalid move: can only move to adjacent tiles (±1 in x or y)" nx, ny = x + dx, y + dy if not env.is_in_bounds(nx, ny): return "Invalid move: out of bounds" if not env.is_empty(nx, ny): return "Invalid move: tile is occupied" agent.pos = [nx, ny] tile = env.get_tile(nx, ny) loot_results = [] if tile.loot: for item in tile.loot: if item.type == "ability": from ..agent.state import AbilityInstance from ..environment.ability_registry import ABILITY_REGISTRY ab_def = ABILITY_REGISTRY.get(item.ability_name) max_uses = ab_def.max_uses if ab_def else None agent.abilities.append( AbilityInstance(name=item.ability_name, uses_remaining=max_uses) ) desc = env.get_ability_description(item.ability_name) cd = env.get_ability_cooldown(item.ability_name) ab_def = ABILITY_REGISTRY.get(item.ability_name) uses_str = "unlimited" if ab_def and ab_def.max_uses is None else (f"{ab_def.max_uses} use(s)" if ab_def else "?") loot_results.append( f"picked up ability '{item.ability_name}': {desc} " f"(cooldown {cd} turn(s), {uses_str})" ) elif item.type == "points": agent.score += item.points or 0 loot_results.append(f"picked up {item.points} points") tile.loot = None msg = f"Moved to ({nx}, {ny})" if loot_results: msg += ". Loot: " + ", ".join(loot_results) return msg