AI_Diplomacy/ai_diplomacy/planning.py
2025-07-03 14:27:50 -07:00

89 lines
3.9 KiB
Python

import logging
import concurrent.futures
from typing import Dict
from .game_history import GameHistory
from .agent import DiplomacyAgent
logger = logging.getLogger(__name__)
async def planning_phase(
game,
agents: Dict[str, DiplomacyAgent],
game_history: GameHistory,
model_error_stats,
log_file_path: str,
):
"""
Lets each power generate a strategic plan using their DiplomacyAgent.
"""
logger.info(f"Starting planning phase for {game.current_short_phase}...")
active_powers = [p_name for p_name, p_obj in game.powers.items() if not p_obj.is_eliminated()]
eliminated_powers = [p_name for p_name, p_obj in game.powers.items() if p_obj.is_eliminated()]
logger.info(f"Active powers for planning: {active_powers}")
if eliminated_powers:
logger.info(f"Eliminated powers (skipped): {eliminated_powers}")
else:
logger.info("No eliminated powers yet.")
board_state = game.get_state()
with concurrent.futures.ThreadPoolExecutor(max_workers=len(active_powers)) as executor:
futures = {}
for power_name in active_powers:
if power_name not in agents:
logger.warning(f"Agent for {power_name} not found in planning phase. Skipping.")
continue
agent = agents[power_name]
client = agent.client
future = executor.submit(
client.get_plan,
game,
board_state,
power_name,
gather_possible_orders(game, power_name),
game_history,
agent_goals=agent.goals,
agent_relationships=agent.relationships,
log_file_path=log_file_path,
agent_private_diary_str=agent.format_private_diary_for_prompt(),
)
futures[future] = power_name
logger.debug(f"Submitted get_plan task for {power_name}.")
logger.info(f"Waiting for {len(futures)} planning results...")
for future in concurrent.futures.as_completed(futures):
power_name = futures[future]
try:
plan_result = future.result()
logger.info(f"Received planning result from {power_name}.")
if plan_result.startswith("Error:"):
logger.warning(f"Agent {power_name} reported an error during planning: {plan_result}")
if power_name in model_error_stats:
model_error_stats[power_name].setdefault("planning_generation_errors", 0)
model_error_stats[power_name]["planning_generation_errors"] += 1
else:
model_error_stats.setdefault(f"{power_name}_planning_generation_errors", 0)
model_error_stats[f"{power_name}_planning_generation_errors"] += 1
elif plan_result:
agent.add_journal_entry(f"Generated plan for {game.current_short_phase}: {plan_result[:100]}...")
game_history.add_plan(game.current_short_phase, power_name, plan_result)
logger.debug(f"Added plan for {power_name} to history.")
else:
logger.warning(f"Agent {power_name} returned an empty plan.")
except Exception as e:
logger.error(f"Exception during planning result processing for {power_name}: {e}")
if power_name in model_error_stats:
model_error_stats[power_name].setdefault("planning_execution_errors", 0)
model_error_stats[power_name]["planning_execution_errors"] += 1
else:
model_error_stats.setdefault(f"{power_name}_planning_execution_errors", 0)
model_error_stats[f"{power_name}_planning_execution_errors"] += 1
logger.info("Planning phase processing complete.")
return game_history