11import json
22import time
3- import uuid
43from concurrent .futures import ThreadPoolExecutor , as_completed
54from pathlib import Path
65
76from tqdm .auto import tqdm
87
98from codeclash .agents .player import Player
109from codeclash .constants import RESULT_TIE
11- from codeclash .games .game import CodeGame , RoundData , RoundStats
12- from codeclash .utils .environment import assert_zero_exit_code
10+ from codeclash .games .game import CodeGame , RoundStats
11+ from codeclash .utils .environment import assert_zero_exit_code , copy_from_container
1312
1413
1514class BattleSnakeGame (CodeGame ):
@@ -39,9 +38,18 @@ def _wait_for_ports(self, ports: list[int], timeout: float = 3.0) -> None:
3938
4039 time .sleep (0.1 )
4140
42- def get_stats (self , result_outputs : list [str ], agents : list [Player ]) -> RoundStats :
41+ def copy_logs_from_env (self , round_num ):
42+ super ().copy_logs_from_env (round_num )
43+ copy_from_container (
44+ container = self .environment ,
45+ src_path = f"{ self .environment .config .cwd } /game/logs" ,
46+ dest_path = self .log_local / "rounds" / str (round_num ),
47+ )
48+
49+ def get_stats (self , agents : list [Player ]) -> RoundStats :
4350 scores = {}
44- for ro in result_outputs :
51+ for idx in range (self .game_config ["sims_per_round" ]):
52+ ro = self .environment .execute (f"cat game/logs/sim_out_{ idx } .json" )["output" ]
4553 lines = ro .strip ().split ("\n " )
4654 results = json .loads (lines [- 1 ]) if lines else {} # Get the last line which contains the game result
4755 winner = RESULT_TIE if results ["isDraw" ] else results ["winnerName" ]
@@ -51,7 +59,7 @@ def get_stats(self, result_outputs: list[str], agents: list[Player]) -> RoundSta
5159 winner = RESULT_TIE if list (scores .values ()).count (scores [winner ]) > 1 else winner
5260 return RoundStats (winner = winner , scores = scores )
5361
54- def execute_round (self , agents : list [Player ]) -> RoundData :
62+ def execute_round (self , agents : list [Player ]):
5563 self .logger .debug ("Starting game servers" )
5664 cmd = []
5765 ports = []
@@ -68,46 +76,30 @@ def execute_round(self, agents: list[Player]) -> RoundData:
6876 self .logger .debug ("All ports are ready" )
6977
7078 try :
71- log_outputs , result_outputs = [], []
7279 cmd = self .run_cmd_round + " " + " " .join (cmd )
7380 self .logger .info (f"Running game: { cmd } " )
81+ self .environment .execute ("rm -rf logs; mkdir logs" , cwd = f"{ self .environment .config .cwd } /game" )
7482
7583 # Use ThreadPoolExecutor for parallel execution
7684 with ThreadPoolExecutor (20 ) as executor :
7785 # Submit all simulations to the thread pool
7886 futures = [
79- executor .submit (self ._run_single_simulation , cmd ) for _ in range (self .game_config ["sims_per_round" ])
87+ executor .submit (self ._run_single_simulation , cmd , idx )
88+ for idx in range (self .game_config ["sims_per_round" ])
8089 ]
8190
8291 # Collect results as they complete
8392 for future in tqdm (as_completed (futures ), total = len (futures )):
84- log_output , result_output = future .result ()
85- log_outputs .append (log_output )
86- result_outputs .append (result_output )
87-
88- return RoundData (logs = log_outputs , results = result_outputs )
93+ future .result ()
8994 finally :
9095 # Kill all python servers when done
9196 self .environment .execute ("pkill -f 'python main.py' || true" )
9297
93- def _run_single_simulation (self , cmd : str ) -> tuple [str , str ]:
98+ def _run_single_simulation (self , cmd : str , idx : int ) -> tuple [str , str ]:
9499 """Run a single battlesnake simulation and return log and result outputs."""
95- # Create temporary output file for results
96- output_file = f"battlesnake_output_{ uuid .uuid4 ().hex } .json"
97-
98- # Run game
99- response = assert_zero_exit_code (
100+ assert_zero_exit_code (
100101 self .environment .execute (
101- cmd + f" -o { output_file } " ,
102+ cmd + f" -o logs/sim_out_ { idx } .json " ,
102103 cwd = f"{ self .environment .config .cwd } /game" ,
103104 )
104105 )
105-
106- # Read the output file for result information
107- result_response = self .environment .execute (f"cat game/{ output_file } " )
108- result_output = result_response ["output" ]
109-
110- # Clean up the output file
111- self .environment .execute (f"rm -f game/{ output_file } " )
112-
113- return response ["output" ], result_output
0 commit comments