"""Fixed Main Monopoly agent that orchestrates the complete game.This module provides the corrected main agent implementation that: - Ensures BaseModel consistency throughout (no dict conversions) - Properly handles state schema compatibility - Fixes the validation error by maintaining BaseModel state"""# Standard library importsimportloggingfromdatetimeimportdatetimefromtypingimportAnyfromhaive.core.engine.agent.agentimportAgent,register_agentfromhaive.games.monopoly.configimportMonopolyGameAgentConfigfromhaive.games.monopoly.game_agentimportMonopolyGameAgent,MonopolyGameAgentConfigfromhaive.games.monopoly.stateimportMonopolyState# Local importslogger=logging.getLogger(__name__)
[docs]@register_agent(MonopolyGameAgentConfig)classMonopolyAgent(Agent[MonopolyGameAgentConfig]):"""Main Monopoly agent that orchestrates the complete game. This agent combines: - Game rule enforcement and turn management - Player decision delegation to subgraphs - Complete game state management - Game end detection and winner determination """def__init__(self,config:MonopolyGameAgentConfig):"""Initialize the monopoly agent."""# Set up player agent engines firstconfig.setup_player_agent_engines()# Create the player decision agentself.player_agent=config.create_player_agent()# Create initial stateself.initial_state=config.create_initial_state()# CRITICAL: Validate initial statelogger.debug(f"Initial state created with {len(self.initial_state.players)} players")ifself.initial_state.players:logger.debug(f"Player names: {[p.nameforpinself.initial_state.players]}")else:raiseValueError("CRITICAL: No players in initial state!")super().__init__(config)
[docs]defsetup_workflow(self)->None:"""Set up the complete monopoly workflow. This creates the main game workflow nodes and connects them properly. """# Import the game agent here to avoid circular dependency# Create game agent config with the player agent referencegame_config=MonopolyGameAgentConfig(name="monopoly_game_orchestrator",player_names=self.config.player_names,max_turns=self.config.max_turns,enable_trading=self.config.enable_trading,player_agent=self.player_agent,# Pass the created player agent)# Create the game orchestration agentself.game_agent=MonopolyGameAgent(game_config)# Use the game agent's workflow as our main workflowself.graph=self.game_agent.graph
[docs]defstart_game(self)->MonopolyState:"""Start a new monopoly game. CRITICAL: Keep everything as BaseModel - no dict conversions! Returns: Final game state as MonopolyState BaseModel """# CRITICAL FIX: Pass the BaseModel directly, no conversion to dicttry:# Use the BaseModel directly with the graphfinal_state=self.app.invoke(self.initial_state,# Pass BaseModel directlyconfig=self.runnable_config,)# Ensure final_state is MonopolyStateifnotisinstance(final_state,MonopolyState):final_state=MonopolyState.from_state_object(final_state)self._display_final_results(final_state)returnfinal_stateexceptException:raise
def_display_final_results(self,final_state:MonopolyState)->None:"""Display final game results."""# Display player final standingsplayers_by_worth=[]forplayerinfinal_state.players:ifnotplayer.bankrupt:net_worth=player.net_worth(final_state.properties)players_by_worth.append((player.name,net_worth,player.money,len(player.properties)))# Sort by net worthplayers_by_worth.sort(key=lambdax:x[1],reverse=True)fori,(name,net_worth,_money,_prop_count)inenumerate(players_by_worth):"👑"ifname==final_state.winnerelsef"{i+1}."# Display bankrupted playersbankrupt_players=[pforpinfinal_state.playersifp.bankrupt]ifbankrupt_players:forplayerinbankrupt_players:pass# Display some game statistics# Count different event typesevent_counts={}foreventinfinal_state.game_events:event_type=event.event_typeevent_counts[event_type]=event_counts.get(event_type,0)+1forevent_type,_countinsorted(event_counts.items()):pass
[docs]defsave_game_history(self,filename:str|None=None)->None:"""Save game history to a file."""iffilenameisNone:timestamp=datetime.now().strftime("%Y%m%d_%H%M%S")filename=f"monopoly_game_{timestamp}.json"
# This would save the game state history# Implementation would depend on desired format
[docs]defget_game_summary(self)->dict[str,Any]:"""Get a summary of the game configuration and status."""return{"players":self.config.player_names,"max_turns":self.config.max_turns,"settings":{"trading_enabled":self.config.enable_trading,"building_enabled":self.config.enable_building,"auctions_enabled":self.config.enable_auctions,},"initial_state_type":type(self.initial_state).__name__,"initial_players_count":len(self.initial_state.players),}