11import json
2+ import random
23import time
34from concurrent .futures import ThreadPoolExecutor , as_completed
45from pathlib import Path
@@ -46,10 +47,20 @@ def _wait_for_ports(self, requested_ports: list[int], timeout: float = 3.0) -> l
4647
4748 return list (available_ports )
4849
49- def _run_single_simulation (self , cmd : str , idx : int ) -> str :
50+ def _run_single_simulation (self , player2port : dict [ str , int ] , idx : int ) -> str :
5051 """Run a single battlesnake simulation and return log and result outputs."""
52+ # Build command with player URLs in randomized order
53+ players = list (player2port .items ())
54+ random .shuffle (players )
55+
56+ cmd_args = []
57+ for player_name , port in players :
58+ cmd_args .append (f"--url http://0.0.0.0:{ port } -n { player_name } " )
59+
60+ cmd = self .run_cmd_round + " " + " " .join (cmd_args ) + f" -o { self .log_env / f'sim_{ idx } .jsonl' } "
61+
5162 output = self .environment .execute (
52- cmd + f" -o { self . log_env / f'sim_ { idx } .jsonl' } " ,
63+ cmd ,
5364 cwd = f"{ self .environment .config .cwd } /game" ,
5465 )
5566 if output ["returncode" ] != 0 :
@@ -60,15 +71,13 @@ def _run_single_simulation(self, cmd: str, idx: int) -> str:
6071
6172 def execute_round (self , agents : list [Player ]):
6273 self .logger .debug ("Starting game servers" )
63- cmd = []
6474 player2port = {}
6575 for idx , agent in enumerate (agents ):
6676 port = 8001 + idx
6777 player2port [agent .name ] = port
6878 # Surprisingly slow despite using &
6979 # Start server in background - just add & to run in background!
7080 self .environment .execute (f"PORT={ port } python main.py &" , cwd = f"/{ agent .name } " )
71- cmd .append (f"--url http://0.0.0.0:{ port } -n { agent .name } " )
7281
7382 self .logger .debug (f"Waiting for ports: { player2port } " )
7483 available_ports = self ._wait_for_ports (list (player2port .values ()))
@@ -83,17 +92,19 @@ def execute_round(self, agents: list[Player]):
8392 self ._failed_to_start_player .append (player )
8493 return
8594
95+ if len (available_ports ) < len (agents ):
96+ raise RuntimeError (f"Only { len (available_ports )} players started: { available_ports } " )
97+
8698 self .logger .debug ("All ports are ready" )
8799
88100 try :
89- cmd = self .run_cmd_round + " " + " " .join (cmd )
90- self .logger .info (f"Running game: { cmd } " )
101+ self .logger .info (f"Running game with players: { list (player2port .keys ())} " )
91102
92103 # Use ThreadPoolExecutor for parallel execution
93104 with ThreadPoolExecutor (20 ) as executor :
94105 # Submit all simulations to the thread pool
95106 futures = [
96- executor .submit (self ._run_single_simulation , cmd , idx )
107+ executor .submit (self ._run_single_simulation , player2port , idx )
97108 for idx in range (self .game_config ["sims_per_round" ])
98109 ]
99110
0 commit comments