Update external bot agent with obstacle-aware distance navigation and safe pass fallback

This commit is contained in:
Isaac Johnson 2026-09-05 20:04:12 -05:00
parent 85ee13a513
commit 5fb7e07103
1 changed files with 36 additions and 11 deletions

View File

@ -1,5 +1,6 @@
"""External Bot Agent with deliberate decision-making logic:
- Evaluates targets via Radar sensor.
- Intelligently navigates around impassable obstacles (mountains and forests).
- Decides whether to negotiate party formation or refuse & fight based on strength.
- Explicitly engages in 3-bout D20 battles when adjacent to an opposing party/refusing bot.
- Parses battle results, bout rolls, scores, and absorbed members.
@ -138,10 +139,11 @@ class SmartBotAgent:
party = res.json()
print(f"✅ [PARTY FORMED] Squad '{party['name']}' established! Leader: {party['leader_name']} | Str: {party['total_strength']}")
else:
# Fallback: move into adjacent square to trigger engine merge
self._pass_or_step()
# Fallback: pass turn if party creation rejected
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
except Exception as e:
print(f"Party formation error: {e}")
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
def _initiate_battle(self, opponent_id: str):
"""Explicitly call the 3-Bout D20 Battle endpoint."""
@ -164,33 +166,56 @@ class SmartBotAgent:
else:
print(f"Battle failed ({res.status_code}): {res.text}")
def _get_best_move_towards(self, target_x: int, target_y: int, moves: Dict[str, Any]) -> Optional[str]:
"""Pick the available direction that minimizes Chebyshev distance to (target_x, target_y), strictly avoiding obstacles."""
valid_moves = {d: chk for d, chk in moves.items() if chk.get("available")}
if not valid_moves:
return None
def dist(chk: Dict[str, Any]) -> int:
return max(abs(chk["target_x"] - target_x), abs(chk["target_y"] - target_y))
return min(valid_moves.keys(), key=lambda d: dist(valid_moves[d]))
def _step_or_attack(self, target: Dict[str, Any]):
"""Move adjacent/towards the target."""
"""Move adjacent/towards the target while avoiding obstacles."""
moves_res = requests.get(f"{BASE_URL}/players/{self.bot_id}/available-moves").json()
available = [d for d, chk in moves_res["moves"].items() if chk["available"]]
if available:
# Move towards target
chosen = available[0]
moves = moves_res.get("moves", {})
chosen = self._get_best_move_towards(target["x"], target["y"], moves)
if chosen:
res = requests.post(f"{BASE_URL}/players/{self.bot_id}/move", json={"direction": chosen}).json()
if res.get("battle_triggered"):
print(f"⚔️ Move triggered battle! Winner: {res['battle_result']['winner_party_name']}")
elif res.get("party_formed_triggered"):
print(f"🤝 Move resulted in party alliance!")
else:
print("⚠️ No passable moves adjacent to target (terrain/border constraint). Passing turn.")
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
def _navigate_towards_goal(self, radar_res: Dict[str, Any]):
"""Move towards the nearest target or explore unvisited areas."""
"""Move towards the nearest target routing around obstacles."""
rec_dir = radar_res.get("recommended_direction")
nearest = radar_res.get("nearest_target")
moves_res = requests.get(f"{BASE_URL}/players/{self.bot_id}/available-moves").json()
available = [d for d, chk in moves_res["moves"].items() if chk["available"]]
moves = moves_res.get("moves", {})
available = [d for d, chk in moves.items() if chk.get("available")]
if not available:
print("🚫 No available moves. Passing turn.")
print("🚫 All adjacent paths blocked by borders or obstacle terrain (mountains/forests). Passing turn.")
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
return
chosen_dir = rec_dir if rec_dir in available else available[0]
# 1. Prefer radar's obstacle-aware BFS pathfinder direction
if rec_dir and rec_dir in available:
chosen_dir = rec_dir
# 2. Otherwise pick the available direction that minimizes distance to the nearest target
elif nearest:
chosen_dir = self._get_best_move_towards(nearest["x"], nearest["y"], moves) or available[0]
# 3. Fallback to any valid open terrain cell
else:
chosen_dir = available[0]
print(f"🧭 Moving {chosen_dir} (Goal: {radar_res.get('bot_goal')}, Action: {radar_res.get('recommended_action')})")
res = requests.post(f"{BASE_URL}/players/{self.bot_id}/move", json={"direction": chosen_dir}).json()