agents.base_agent
1import json 2import logging 3import os 4import sys 5from typing import Any, Dict, List, Optional 6 7from websockets.asyncio.client import connect 8 9# Add the parent directory to sys.path to allow running agents as scripts from the root 10sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 11 12logging.basicConfig(level=logging.INFO, format="%(asctime)s - AGENT - %(message)s") 13 14 15class BaseC4Agent: 16 """ 17 Abstract base class for Connect Four agents. 18 Subclasses MUST implement the deliberate(valid_actions) method. 19 """ 20 21 def __init__(self, server_uri: Optional[str] = None) -> None: 22 """ 23 Initializes the agent with the server URI. 24 25 Args: 26 server_uri: The WebSocket URI of the Connect Four server. 27 """ 28 self.server_uri: str = server_uri or os.environ.get("SERVER_URI", "ws://localhost:8765") 29 self.player_id: Optional[int] = None 30 31 async def run(self) -> None: 32 """ 33 Connects to the server and enters the main communication loop. 34 """ 35 try: 36 async with connect(self.server_uri) as websocket: 37 await websocket.send(json.dumps({"client": "agent"})) 38 39 async for message in websocket: 40 if isinstance(message, bytes): 41 message = message.decode("utf-8") 42 data: Dict[str, Any] = json.loads(message) 43 44 if data.get("type") == "setup": 45 self.player_id = data.get("player_id") 46 logging.info(f"Connected! Assigned Player {self.player_id}") 47 48 elif data.get("type") == "state": 49 current_turn = data.get("current_turn") 50 valid_actions = data.get("valid_actions") 51 52 if current_turn == self.player_id and isinstance(valid_actions, list): 53 # It's our turn! Ask the subclass to make a decision 54 action: Optional[int] = await self.deliberate(valid_actions) 55 56 if action is not None: 57 await websocket.send(json.dumps({"action": "move", "column": action})) 58 59 elif data.get("type") == "game_over": 60 logging.info(f"Round Over: {data.get('message')}") 61 logging.info("Waiting for next round to start...") 62 63 except Exception as e: 64 logging.error(f"Connection lost: {e}") 65 66 async def deliberate(self, valid_actions: List[int]) -> Optional[int]: 67 """ 68 MUST be implemented by subclasses. 69 Returns an integer representing the chosen column [0-6]. 70 71 Args: 72 valid_actions: A list of valid column indices where a piece can be dropped. 73 74 Returns: 75 The chosen column index, or None if no move is made. 76 """ 77 raise NotImplementedError("Subclasses must implement deliberate()")
class
BaseC4Agent:
16class BaseC4Agent: 17 """ 18 Abstract base class for Connect Four agents. 19 Subclasses MUST implement the deliberate(valid_actions) method. 20 """ 21 22 def __init__(self, server_uri: Optional[str] = None) -> None: 23 """ 24 Initializes the agent with the server URI. 25 26 Args: 27 server_uri: The WebSocket URI of the Connect Four server. 28 """ 29 self.server_uri: str = server_uri or os.environ.get("SERVER_URI", "ws://localhost:8765") 30 self.player_id: Optional[int] = None 31 32 async def run(self) -> None: 33 """ 34 Connects to the server and enters the main communication loop. 35 """ 36 try: 37 async with connect(self.server_uri) as websocket: 38 await websocket.send(json.dumps({"client": "agent"})) 39 40 async for message in websocket: 41 if isinstance(message, bytes): 42 message = message.decode("utf-8") 43 data: Dict[str, Any] = json.loads(message) 44 45 if data.get("type") == "setup": 46 self.player_id = data.get("player_id") 47 logging.info(f"Connected! Assigned Player {self.player_id}") 48 49 elif data.get("type") == "state": 50 current_turn = data.get("current_turn") 51 valid_actions = data.get("valid_actions") 52 53 if current_turn == self.player_id and isinstance(valid_actions, list): 54 # It's our turn! Ask the subclass to make a decision 55 action: Optional[int] = await self.deliberate(valid_actions) 56 57 if action is not None: 58 await websocket.send(json.dumps({"action": "move", "column": action})) 59 60 elif data.get("type") == "game_over": 61 logging.info(f"Round Over: {data.get('message')}") 62 logging.info("Waiting for next round to start...") 63 64 except Exception as e: 65 logging.error(f"Connection lost: {e}") 66 67 async def deliberate(self, valid_actions: List[int]) -> Optional[int]: 68 """ 69 MUST be implemented by subclasses. 70 Returns an integer representing the chosen column [0-6]. 71 72 Args: 73 valid_actions: A list of valid column indices where a piece can be dropped. 74 75 Returns: 76 The chosen column index, or None if no move is made. 77 """ 78 raise NotImplementedError("Subclasses must implement deliberate()")
Abstract base class for Connect Four agents. Subclasses MUST implement the deliberate(valid_actions) method.
BaseC4Agent(server_uri: Optional[str] = None)
22 def __init__(self, server_uri: Optional[str] = None) -> None: 23 """ 24 Initializes the agent with the server URI. 25 26 Args: 27 server_uri: The WebSocket URI of the Connect Four server. 28 """ 29 self.server_uri: str = server_uri or os.environ.get("SERVER_URI", "ws://localhost:8765") 30 self.player_id: Optional[int] = None
Initializes the agent with the server URI.
Args: server_uri: The WebSocket URI of the Connect Four server.
async def
run(self) -> None:
32 async def run(self) -> None: 33 """ 34 Connects to the server and enters the main communication loop. 35 """ 36 try: 37 async with connect(self.server_uri) as websocket: 38 await websocket.send(json.dumps({"client": "agent"})) 39 40 async for message in websocket: 41 if isinstance(message, bytes): 42 message = message.decode("utf-8") 43 data: Dict[str, Any] = json.loads(message) 44 45 if data.get("type") == "setup": 46 self.player_id = data.get("player_id") 47 logging.info(f"Connected! Assigned Player {self.player_id}") 48 49 elif data.get("type") == "state": 50 current_turn = data.get("current_turn") 51 valid_actions = data.get("valid_actions") 52 53 if current_turn == self.player_id and isinstance(valid_actions, list): 54 # It's our turn! Ask the subclass to make a decision 55 action: Optional[int] = await self.deliberate(valid_actions) 56 57 if action is not None: 58 await websocket.send(json.dumps({"action": "move", "column": action})) 59 60 elif data.get("type") == "game_over": 61 logging.info(f"Round Over: {data.get('message')}") 62 logging.info("Waiting for next round to start...") 63 64 except Exception as e: 65 logging.error(f"Connection lost: {e}")
Connects to the server and enters the main communication loop.
async def
deliberate(self, valid_actions: List[int]) -> Optional[int]:
67 async def deliberate(self, valid_actions: List[int]) -> Optional[int]: 68 """ 69 MUST be implemented by subclasses. 70 Returns an integer representing the chosen column [0-6]. 71 72 Args: 73 valid_actions: A list of valid column indices where a piece can be dropped. 74 75 Returns: 76 The chosen column index, or None if no move is made. 77 """ 78 raise NotImplementedError("Subclasses must implement deliberate()")
MUST be implemented by subclasses. Returns an integer representing the chosen column [0-6].
Args: valid_actions: A list of valid column indices where a piece can be dropped.
Returns: The chosen column index, or None if no move is made.