WIP: Starting work on running games via websocket

First crack, mostly Claude, on running each agent as an individual
connection to the server via WebSocket.
This commit is contained in:
Tyler Marques 2025-06-19 11:53:03 -07:00
parent 78d9ed6818
commit aa0e5852e5
No known key found for this signature in database
GPG key ID: CB99EDCF41D3016F
10 changed files with 2064 additions and 855 deletions

View file

@ -6,13 +6,9 @@ and interacting with a Diplomacy server via WebSocket.
"""
import asyncio
import logging
from loguru import logger
from websocket_diplomacy_client import connect_to_diplomacy_server
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
async def basic_client_example():
"""
@ -25,7 +21,7 @@ async def basic_client_example():
hostname="localhost",
port=8432,
username="test_player",
password="test_password"
password="test_password",
)
logger.info("Connected successfully!")
@ -34,8 +30,10 @@ async def basic_client_example():
games = await client.list_games()
logger.info(f"Found {len(games)} games:")
for game in games:
logger.info(f" Game {game.get('game_id', 'unknown')}: {game.get('status', 'unknown')} "
f"({game.get('n_players', 0)}/{game.get('n_controls', 0)} players)")
logger.info(
f" Game {game.get('game_id', 'unknown')}: {game.get('status', 'unknown')} "
f"({game.get('n_players', 0)}/{game.get('n_controls', 0)} players)"
)
# Get available maps
logger.info("Getting available maps...")
@ -49,7 +47,7 @@ async def basic_client_example():
rules=["NO_PRESS", "IGNORE_ERRORS", "POWER_CHOICE"],
power_name="FRANCE", # Control France
n_controls=1, # Only need 1 player to start (for testing)
deadline=None # No time pressure
deadline=None, # No time pressure
)
logger.info(f"Created game {client.game_id} as {client.power_name}")
@ -62,21 +60,21 @@ async def basic_client_example():
# Get possible orders
logger.info("Getting possible orders for France...")
possible_orders = client.get_all_possible_orders()
france_orders = possible_orders.get('FRANCE', [])
france_orders = possible_orders.get("FRANCE", [])
logger.info(f"France can make {len(france_orders)} possible orders")
if france_orders:
logger.info(f"First few orders: {france_orders[:5]}")
# Submit some orders (example: hold all units)
logger.info("Submitting hold orders for all French units...")
units = client.get_units('FRANCE')
units = client.get_units("FRANCE")
hold_orders = []
for unit in units:
# Format: "A PAR H" means Army in Paris holds
hold_orders.append(f"{unit} H")
if hold_orders:
await client.set_orders('FRANCE', hold_orders)
await client.set_orders("FRANCE", hold_orders)
logger.info(f"Submitted orders: {hold_orders}")
# Try to process the game (might fail if we don't have admin rights)
@ -84,11 +82,13 @@ async def basic_client_example():
try:
await client.process_game()
logger.info("Game processed successfully")
# Synchronize to get updated state
await client.synchronize()
logger.info(f"After processing - Current phase: {client.get_current_phase()}")
logger.info(
f"After processing - Current phase: {client.get_current_phase()}"
)
except Exception as e:
logger.warning(f"Could not process game (normal if not admin): {e}")
@ -101,7 +101,7 @@ async def basic_client_example():
logger.error(f"Error in example: {e}", exc_info=True)
finally:
# Clean up
if 'client' in locals():
if "client" in locals():
await client.close()
logger.info("Example completed")
@ -109,7 +109,7 @@ async def basic_client_example():
async def join_existing_game_example(game_id: str):
"""
Example showing how to join an existing game.
Args:
game_id: ID of the game to join
"""
@ -119,7 +119,7 @@ async def join_existing_game_example(game_id: str):
hostname="localhost",
port=8432,
username="test_player_2",
password="test_password"
password="test_password",
)
# Join as an observer first
@ -133,13 +133,15 @@ async def join_existing_game_example(game_id: str):
# List powers and their status
for power_name, power in client.powers.items():
logger.info(f"{power_name}: {len(power.centers)} centers, "
f"{len(power.units)} units, eliminated: {power.is_eliminated()}")
logger.info(
f"{power_name}: {len(power.centers)} centers, "
f"{len(power.units)} units, eliminated: {power.is_eliminated()}"
)
except Exception as e:
logger.error(f"Error joining game: {e}", exc_info=True)
finally:
if 'client' in locals():
if "client" in locals():
await client.close()
@ -149,28 +151,31 @@ async def message_sending_example():
"""
try:
client = await connect_to_diplomacy_server()
# Create a game with PRESS allowed
game = await client.create_game(
rules=["IGNORE_ERRORS", "POWER_CHOICE"], # Remove NO_PRESS to allow messages
rules=[
"IGNORE_ERRORS",
"POWER_CHOICE",
], # Remove NO_PRESS to allow messages
power_name="FRANCE",
n_controls=1
n_controls=1,
)
# Send a public message
await client.send_message(
sender="FRANCE",
recipient="GLOBAL",
message="Greetings from France! Let's have a fair game."
message="Greetings from France! Let's have a fair game.",
)
logger.info("Sent public message")
# Send a private message (would need another power to be present)
try:
await client.send_message(
sender="FRANCE",
recipient="ENGLAND",
message="Hello England, shall we discuss an alliance?"
message="Hello England, shall we discuss an alliance?",
)
logger.info("Sent private message to England")
except Exception as e:
@ -179,17 +184,18 @@ async def message_sending_example():
except Exception as e:
logger.error(f"Error in messaging example: {e}", exc_info=True)
finally:
if 'client' in locals():
if "client" in locals():
await client.close()
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
# Join existing game if game ID provided as argument
game_id = sys.argv[1]
asyncio.run(join_existing_game_example(game_id))
else:
# Run basic example
asyncio.run(basic_client_example())
asyncio.run(basic_client_example())