-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from aiarena/feature/multithread-run-example
Add multithread runner example
- Loading branch information
Showing
4 changed files
with
184 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
.idea/ | ||
|
||
# files created by the arena client | ||
results | ||
client.log | ||
runners |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
version: "3.9" | ||
services: | ||
sc2_controller: | ||
image: aiarena/arenaclient-sc2:v0.4.3 | ||
environment: | ||
- "ACSC2_PORT=8083" | ||
- "ACSC2_PROXY_HOST=proxy_controller" | ||
volumes: | ||
- "./runners/${COMPOSE_PROJECT_NAME}/logs:/logs" # a sc2_controller folder will be created in the logs folder | ||
# SC2 Maps Path | ||
# Set this as "- PATH_TO_YOUR_MAPS_FOLDER:/root/StarCraftII/maps" | ||
# - C:\Program Files (x86)\StarCraft II\Maps:/root/StarCraftII/maps # Standard windows SC2 maps path | ||
- ./maps:/root/StarCraftII/maps # Local maps folder | ||
# - ~/StarCraftII/maps:/root/StarCraftII/maps # Relatively standard linux SC2 maps path | ||
|
||
bot_controller1: | ||
image: aiarena/arenaclient-bot:v0.4.3 | ||
volumes: | ||
- "./bots:/bots" | ||
- "./runners/${COMPOSE_PROJECT_NAME}/logs/bot_controller1:/logs" | ||
environment: | ||
- "ACBOT_PORT=8081" | ||
- "ACBOT_PROXY_HOST=proxy_controller" | ||
|
||
bot_controller2: | ||
image: aiarena/arenaclient-bot:v0.4.3 | ||
volumes: | ||
- "./bots:/bots" | ||
- "./runners/${COMPOSE_PROJECT_NAME}/logs/bot_controller2:/logs" | ||
environment: | ||
- "ACBOT_PORT=8082" | ||
- "ACBOT_PROXY_HOST=proxy_controller" | ||
|
||
proxy_controller: | ||
image: aiarena/arenaclient-proxy:v0.4.3 | ||
environment: | ||
- "ACPROXY_PORT=8080" | ||
- "ACPROXY_BOT_CONT_1_HOST=bot_controller1" | ||
- "ACPROXY_BOT_CONT_2_HOST=bot_controller2" | ||
- "ACPROXY_SC2_CONT_HOST=sc2_controller" | ||
volumes: | ||
- "./runners/${COMPOSE_PROJECT_NAME}/matches:/app/matches" | ||
- "./config.toml:/app/config.toml" | ||
- "./results.json:/app/results.json" | ||
- "./runners/${COMPOSE_PROJECT_NAME}/replays:/replays" | ||
- "./runners/${COMPOSE_PROJECT_NAME}/logs:/logs" # a proxy_controller folder will be created in the logs folder |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import os | ||
import random | ||
import shutil | ||
import subprocess | ||
from multiprocessing.dummy import Pool as ThreadPool | ||
|
||
from loguru import logger | ||
|
||
################### | ||
# RUNNER SETTINGS # | ||
################### | ||
|
||
# all the runner files will be inside this path | ||
root_runners_path = f"./runners/" | ||
|
||
# if True, delete all the runner files before starting | ||
clean_run = True | ||
|
||
# In case you want to use a different docker-compose file | ||
docker_compose_file = "./docker-compose-multithread-example.yml" | ||
|
||
# run this many matches at a time | ||
num_runners = 3 | ||
|
||
######################### | ||
# MATCH GENERATION CODE # | ||
######################### | ||
|
||
# This is an example of how to generate matches | ||
|
||
bot = ["basic_bot", "T", "python"] | ||
opponents = [ | ||
["loser_bot", "T", "python"], | ||
["loser_bot", "T", "python"], | ||
["loser_bot", "T", "python"], | ||
] | ||
map_list = ["BerlingradAIE"] | ||
num_games = len(opponents) | ||
|
||
|
||
def get_matches_to_play(): | ||
""" | ||
Returns a list of matches to play | ||
Edit this function to generate matches your preferred way. | ||
""" | ||
matches = [] | ||
for x in range(num_games): | ||
# we add 1 to x because we want the runner id to start at 1 | ||
runner_id = x + 1 | ||
map = random.choice(map_list) | ||
opponent = opponents[x % len(opponents)] | ||
matches.append((runner_id, bot, opponent, map)) | ||
return matches | ||
|
||
|
||
######################################################## | ||
# Hopefully you shouldn't need to edit below this line # | ||
######################################################## | ||
|
||
def play_game(match): | ||
try: | ||
# prepare the match runner | ||
runner_id = match[0] | ||
logger.info(f"[{runner_id}] {match[1][0]}vs{match[2][0]} on {match[3]} starting") | ||
runner_dir = prepare_runner_dir(runner_id) | ||
prepare_matches_and_results_files(match, runner_dir) | ||
|
||
# start the match running | ||
command = f'docker compose -p {runner_id} -f {docker_compose_file} up' | ||
subprocess.Popen( | ||
command, | ||
shell=True, | ||
).communicate() | ||
|
||
except Exception as error: | ||
logger.error("[ERROR] {0}".format(str(error))) | ||
|
||
|
||
def prepare_matches_and_results_files(match, runner_dir): | ||
file = open(f"{runner_dir}/matches", "w") | ||
# match[1][0] and match[2][1] are twice, because we re-use the bot name as the bot id | ||
file.write( | ||
f"{match[1][0]},{match[1][0]},{match[1][1]},{match[1][2]}," # bot1 | ||
f"{match[2][0]},{match[2][0]},{match[2][1]},{match[2][2]}," # bot2 | ||
f"{match[3]}") # map | ||
file.close() | ||
|
||
# touch results.json | ||
file = open(f"{runner_dir}/results.json", "w") | ||
file.close() | ||
|
||
|
||
def handleRemoveReadonly(func, path, exc): | ||
import stat | ||
if not os.access(path, os.W_OK): | ||
# Is the error an access error ? | ||
os.chmod(path, stat.S_IWUSR) | ||
func(path) | ||
else: | ||
raise | ||
|
||
|
||
def prepare_runner_dir(dir_name) -> str: | ||
runner_dir = f"{root_runners_path}/{dir_name}" | ||
if not os.path.exists(runner_dir): | ||
os.makedirs(runner_dir) | ||
return runner_dir | ||
|
||
|
||
def prepare_root_dir(): | ||
if os.path.exists(root_runners_path) and clean_run: | ||
shutil.rmtree(root_runners_path, onerror=handleRemoveReadonly) | ||
if not os.path.exists(root_runners_path): | ||
os.makedirs(root_runners_path) | ||
|
||
|
||
def main(): | ||
pool = ThreadPool(num_runners) | ||
|
||
prepare_root_dir() | ||
|
||
matches = get_matches_to_play() | ||
|
||
pool.map(play_game, matches) | ||
pool.close() | ||
pool.join() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |