|
1 | 1 | import json |
2 | 2 | import time |
| 3 | +import uuid |
| 4 | +from concurrent.futures import ThreadPoolExecutor, as_completed |
3 | 5 | from pathlib import Path |
4 | 6 |
|
5 | 7 | from tqdm.auto import tqdm |
@@ -69,29 +71,43 @@ def execute_round(self, agents: list[Player]) -> RoundData: |
69 | 71 | log_outputs, result_outputs = [], [] |
70 | 72 | cmd = self.run_cmd_round + " " + " ".join(cmd) |
71 | 73 | self.logger.info(f"Running game: {cmd}") |
72 | | - for idx in tqdm(range(self.game_config["sims_per_round"])): |
73 | | - # Create temporary output file for results |
74 | | - output_file = f"battlesnake_output_{idx}_{int(time.time())}.json" |
75 | | - |
76 | | - # Run game |
77 | | - response = assert_zero_exit_code( |
78 | | - self.environment.execute( |
79 | | - cmd + f" -o {output_file}", |
80 | | - cwd=f"{self.environment.config.cwd}/game", |
81 | | - ) |
82 | | - ) |
83 | | - |
84 | | - # Read the output file for result information |
85 | | - result_response = self.environment.execute(f"cat game/{output_file}") |
86 | | - result_output = result_response["output"] |
87 | | - log_outputs.append(response["output"]) |
88 | | - result_outputs.append(result_output) |
89 | | - |
90 | | - # Clean up the output file |
91 | | - self.environment.execute(f"rm -f game/{output_file}") |
92 | | - time.sleep(0.05) |
| 74 | + |
| 75 | + # Use ThreadPoolExecutor for parallel execution |
| 76 | + with ThreadPoolExecutor(20) as executor: |
| 77 | + # Submit all simulations to the thread pool |
| 78 | + futures = [ |
| 79 | + executor.submit(self._run_single_simulation, cmd) for _ in range(self.game_config["sims_per_round"]) |
| 80 | + ] |
| 81 | + |
| 82 | + # Collect results as they complete |
| 83 | + 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) |
93 | 87 |
|
94 | 88 | return RoundData(logs=log_outputs, results=result_outputs) |
95 | 89 | finally: |
96 | 90 | # Kill all python servers when done |
97 | 91 | self.environment.execute("pkill -f 'python main.py' || true") |
| 92 | + |
| 93 | + def _run_single_simulation(self, cmd: str) -> tuple[str, str]: |
| 94 | + """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 | + self.environment.execute( |
| 101 | + cmd + f" -o {output_file}", |
| 102 | + cwd=f"{self.environment.config.cwd}/game", |
| 103 | + ) |
| 104 | + ) |
| 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