Update external bot agent with obstacle-aware distance navigation and safe pass fallback
This commit is contained in:
parent
85ee13a513
commit
5fb7e07103
|
|
@ -1,5 +1,6 @@
|
||||||
"""External Bot Agent with deliberate decision-making logic:
|
"""External Bot Agent with deliberate decision-making logic:
|
||||||
- Evaluates targets via Radar sensor.
|
- 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.
|
- 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.
|
- Explicitly engages in 3-bout D20 battles when adjacent to an opposing party/refusing bot.
|
||||||
- Parses battle results, bout rolls, scores, and absorbed members.
|
- Parses battle results, bout rolls, scores, and absorbed members.
|
||||||
|
|
@ -138,10 +139,11 @@ class SmartBotAgent:
|
||||||
party = res.json()
|
party = res.json()
|
||||||
print(f"✅ [PARTY FORMED] Squad '{party['name']}' established! Leader: {party['leader_name']} | Str: {party['total_strength']}")
|
print(f"✅ [PARTY FORMED] Squad '{party['name']}' established! Leader: {party['leader_name']} | Str: {party['total_strength']}")
|
||||||
else:
|
else:
|
||||||
# Fallback: move into adjacent square to trigger engine merge
|
# Fallback: pass turn if party creation rejected
|
||||||
self._pass_or_step()
|
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Party formation error: {e}")
|
print(f"Party formation error: {e}")
|
||||||
|
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
|
||||||
|
|
||||||
def _initiate_battle(self, opponent_id: str):
|
def _initiate_battle(self, opponent_id: str):
|
||||||
"""Explicitly call the 3-Bout D20 Battle endpoint."""
|
"""Explicitly call the 3-Bout D20 Battle endpoint."""
|
||||||
|
|
@ -164,33 +166,56 @@ class SmartBotAgent:
|
||||||
else:
|
else:
|
||||||
print(f"Battle failed ({res.status_code}): {res.text}")
|
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]):
|
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()
|
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", {})
|
||||||
if available:
|
chosen = self._get_best_move_towards(target["x"], target["y"], moves)
|
||||||
# Move towards target
|
|
||||||
chosen = available[0]
|
if chosen:
|
||||||
res = requests.post(f"{BASE_URL}/players/{self.bot_id}/move", json={"direction": chosen}).json()
|
res = requests.post(f"{BASE_URL}/players/{self.bot_id}/move", json={"direction": chosen}).json()
|
||||||
if res.get("battle_triggered"):
|
if res.get("battle_triggered"):
|
||||||
print(f"⚔️ Move triggered battle! Winner: {res['battle_result']['winner_party_name']}")
|
print(f"⚔️ Move triggered battle! Winner: {res['battle_result']['winner_party_name']}")
|
||||||
elif res.get("party_formed_triggered"):
|
elif res.get("party_formed_triggered"):
|
||||||
print(f"🤝 Move resulted in party alliance!")
|
print(f"🤝 Move resulted in party alliance!")
|
||||||
else:
|
else:
|
||||||
|
print("⚠️ No passable moves adjacent to target (terrain/border constraint). Passing turn.")
|
||||||
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
|
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
|
||||||
|
|
||||||
def _navigate_towards_goal(self, radar_res: Dict[str, Any]):
|
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")
|
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()
|
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:
|
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")
|
requests.post(f"{BASE_URL}/players/{self.bot_id}/pass")
|
||||||
return
|
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')})")
|
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()
|
res = requests.post(f"{BASE_URL}/players/{self.bot_id}/move", json={"direction": chosen_dir}).json()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue