improved local agent, usage
This commit is contained in:
parent
fe50196dab
commit
a65acbfa9e
|
|
@ -44,3 +44,9 @@ Run the script, then start the game from the frontend UI:
|
||||||
```bash
|
```bash
|
||||||
python bot.py
|
python bot.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Example with local LLM
|
||||||
|
|
||||||
|
```
|
||||||
|
$ OLLAMA_BASE_URL="http://192.168.1.220:11434" python3 ./bot.py -n myAIBot -s 2
|
||||||
|
```
|
||||||
|
|
@ -340,6 +340,52 @@ Respond ONLY with JSON: {{"form_alliance": true|false, "reasoning": "short reaso
|
||||||
elif res.get("party_formed_triggered"):
|
elif res.get("party_formed_triggered"):
|
||||||
print(f"🤝 Formed or joined squad: {res.get('formed_party', {}).get('name')}")
|
print(f"🤝 Formed or joined squad: {res.get('formed_party', {}).get('name')}")
|
||||||
|
|
||||||
|
def _get_board_snapshot(self) -> Optional[Dict[str, Any]]:
|
||||||
|
"""Fetch the full board (players, parties, obstacles) so the LLM sees more than radar's nearest few."""
|
||||||
|
try:
|
||||||
|
res = requests.get(f"{self.base_url}/board", timeout=10)
|
||||||
|
res.raise_for_status()
|
||||||
|
return res.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"⚠️ [BOARD FETCH ERROR] {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
OBSTACLE_SYMBOLS = {"mountain": "M", "forest": "F", "valley": "V"}
|
||||||
|
LOCAL_MAP_RADIUS = 8
|
||||||
|
|
||||||
|
def _build_local_map(self, board: Dict[str, Any], center_x: int, center_y: int) -> List[str]:
|
||||||
|
"""Render an ASCII minimap centered on the bot: @ = self, A = ally, E = enemy, M/F/V = obstacles, . = open."""
|
||||||
|
config = board.get("config", {})
|
||||||
|
max_x = config.get("max_x", 64)
|
||||||
|
max_y = config.get("max_y", 64)
|
||||||
|
radius = self.LOCAL_MAP_RADIUS
|
||||||
|
|
||||||
|
obstacle_at = {(o["x"], o["y"]): o.get("type", "mountain") for o in board.get("obstacles", [])}
|
||||||
|
player_at: Dict[tuple, List[Dict[str, Any]]] = {}
|
||||||
|
for p in board.get("players", []):
|
||||||
|
if p["id"] == self.bot_id:
|
||||||
|
continue
|
||||||
|
player_at.setdefault((p["x"], p["y"]), []).append(p)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for y in range(center_y - radius, center_y + radius + 1):
|
||||||
|
row_chars = []
|
||||||
|
for x in range(center_x - radius, center_x + radius + 1):
|
||||||
|
if x == center_x and y == center_y:
|
||||||
|
row_chars.append("@")
|
||||||
|
elif x < 0 or y < 0 or x >= max_x or y >= max_y:
|
||||||
|
row_chars.append("#")
|
||||||
|
elif (x, y) in obstacle_at:
|
||||||
|
row_chars.append(self.OBSTACLE_SYMBOLS.get(obstacle_at[(x, y)], "M"))
|
||||||
|
elif (x, y) in player_at:
|
||||||
|
occupants = player_at[(x, y)]
|
||||||
|
is_ally = self.party_id and any(o.get("party_id") == self.party_id for o in occupants)
|
||||||
|
row_chars.append("A" if is_ally else "E")
|
||||||
|
else:
|
||||||
|
row_chars.append(".")
|
||||||
|
rows.append("".join(row_chars))
|
||||||
|
return rows
|
||||||
|
|
||||||
def _ask_llm_for_direction(
|
def _ask_llm_for_direction(
|
||||||
self,
|
self,
|
||||||
radar_res: Dict[str, Any],
|
radar_res: Dict[str, Any],
|
||||||
|
|
@ -350,12 +396,14 @@ Respond ONLY with JSON: {{"form_alliance": true|false, "reasoning": "short reaso
|
||||||
targets_summary = [
|
targets_summary = [
|
||||||
{
|
{
|
||||||
"name": t["name"],
|
"name": t["name"],
|
||||||
|
"x": t["x"],
|
||||||
|
"y": t["y"],
|
||||||
"distance": t["distance"],
|
"distance": t["distance"],
|
||||||
"strength": t["strength"],
|
"strength": t["strength"],
|
||||||
"is_party": bool(t.get("party_id")),
|
"party": t.get("party_name") or ("solo" if not t.get("party_id") else t.get("party_id")),
|
||||||
"is_ally": t.get("is_ally", False),
|
"is_ally": t.get("is_ally", False),
|
||||||
}
|
}
|
||||||
for t in radar_res.get("targets", [])[:6]
|
for t in radar_res.get("targets", [])
|
||||||
]
|
]
|
||||||
moves_summary = {
|
moves_summary = {
|
||||||
d: {
|
d: {
|
||||||
|
|
@ -367,17 +415,31 @@ Respond ONLY with JSON: {{"form_alliance": true|false, "reasoning": "short reaso
|
||||||
if d in available
|
if d in available
|
||||||
}
|
}
|
||||||
|
|
||||||
|
board = self._get_board_snapshot()
|
||||||
|
map_section = ""
|
||||||
|
if board:
|
||||||
|
local_map = self._build_local_map(board, my_info["x"], my_info["y"])
|
||||||
|
map_section = f"""
|
||||||
|
Local map (radius {self.LOCAL_MAP_RADIUS} around you, row = one Y line, top-to-bottom is
|
||||||
|
increasing Y, left-to-right is increasing X): @ = you, A = ally, E = enemy/neutral bot,
|
||||||
|
M = mountain, F = forest, V = valley, # = out of bounds, . = open ground.
|
||||||
|
{chr(10).join(local_map)}
|
||||||
|
"""
|
||||||
|
|
||||||
prompt = f"""{GAME_RULES_SUMMARY}
|
prompt = f"""{GAME_RULES_SUMMARY}
|
||||||
You are bot "{self.name}" (strength {self.strength}, score {my_info['score']}, party: {self.party_id or 'Solo'}).
|
You are bot "{self.name}" (strength {self.strength}, score {my_info['score']}, party: {self.party_id or 'Solo'})
|
||||||
|
at position ({my_info['x']}, {my_info['y']}).
|
||||||
Server's radar suggestion: recommended_direction={radar_res.get('recommended_direction')},
|
Server's radar suggestion: recommended_direction={radar_res.get('recommended_direction')},
|
||||||
recommended_action={radar_res.get('recommended_action')}, goal={radar_res.get('bot_goal')}.
|
recommended_action={radar_res.get('recommended_action')}, goal={radar_res.get('bot_goal')}.
|
||||||
Nearby targets: {json.dumps(targets_summary)}
|
{map_section}
|
||||||
|
All known bots/parties on the board (sorted nearest first): {json.dumps(targets_summary)}
|
||||||
Your ONLY legal moves this turn, with resulting coordinates and any strength penalty for
|
Your ONLY legal moves this turn, with resulting coordinates and any strength penalty for
|
||||||
squeezing past obstacles: {json.dumps(moves_summary)}
|
squeezing past obstacles: {json.dumps(moves_summary)}
|
||||||
|
|
||||||
Choose the direction that best serves your strategy (e.g. approach weaker solo bots to grow
|
Choose the direction that best serves your strategy (e.g. approach weaker solo bots to grow
|
||||||
your party, avoid stronger hostile parties, minimize strength penalties, or explore if nothing
|
your party, avoid stronger hostile parties, route around obstacles visible on the map, minimize
|
||||||
is nearby). You MUST pick a key from the legal moves object above.
|
strength penalties, or explore if nothing is nearby). You MUST pick a key from the legal moves
|
||||||
|
object above.
|
||||||
|
|
||||||
Respond ONLY with JSON: {{"direction": "<one of {available}>", "reasoning": "short reason"}}
|
Respond ONLY with JSON: {{"direction": "<one of {available}>", "reasoning": "short reason"}}
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue