simanのブログ

ゆるふわプログラマー。競技プログラミングやってます。Ruby好き

CodinGame のローカル対戦ツール psyleague を試してみた

CodinGame のコンテストでは終盤になるとサブミットしたボットがすべての対戦を終了するのに数時間かかることも珍しくないので、ローカル環境で開発 & 検証を出来ることが重要になっています。

Spring Challenge 2023 では cg-brutaltester を利用してローカル対戦を行っていたのですが

github.com

psyleague も同じことが出来るみたいなので試してみました。

github.com

対戦環境の構築

ローカルの対戦環境については Jiro さんのブログを参考にしました。

shuu0914.hatenablog.com

以降は docker 上での環境が整った状態で話をします。

psyleague のインストール

README に従って pip コマンドで psyleague をインストールします。

pip install psyleague --upgrade

対戦スクリプトの作成

Create a script that given two bots, simulates the game and prints out 4 integers on a single line: "P1_rank P2_rank P1_error P2_error". The first two are ranks of the bots, i.e. if first player wins print "0 1", if the second player wins print "1 0", in case of a draw print "0 0". The last two signify that bot crashed during the game: 0 = no error, 1 = error.

ボットの名前を 2つ受け取って対戦を行いその結果を返すスクリプトを用意します。 ファイル名は play_game.py にしておきます。

import sys
import subprocess

args = sys.argv
bot1 = args[1]
bot2 = args[2]

result = subprocess.run(f'java -jar spring-2023-ants-1.0-SNAPSHOT.jar -p1 {bot1} -p2 {bot2}', shell=True, capture_output=True)
output = result.stdout.split()
score1 = int(output[0])
score2 = int(output[1])
rank1 = 0 if score1 > score2 else 1
rank2 = 0 if score1 < score2 else 1
error1 = 0 if score1 != -1 else 1
error2 = 0 if score2 != -1 else 1

print(f'{rank1} {rank2} {error1} {error2}')

今回のコンテストだとスコアが高ければ勝利、また -1 だとエラー扱いなのでこんな感じになりました。(このあたりはコンテストごとに変わるはずです)

設定ファイルの作成

以下のコマンドで設定ファイルを作成します。

psyleague config

デフォルトだとこんな感じのファイルが生成されるはずです

version = "0.2.0"

# [MATCHMAKING]
n_workers = 1
mm_min_matches = 200
mm_min_matches_preference = 0.75
mm_coeff = 2.5

# [SHOW]
# ? at the end means that the column is optional and it's only showed when it contains different values
date_format = "%Y/%m/%d %H:%M:%S" #more about the format: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
leaderboard = "POS,NAME,SCORE,GAMES,MU,SIGMA,ACTIVE?,ERRORS?,DATE,DESCRIPTION?"

# [RANKING MODEL]
model = "trueskill"
draw_prob = 0.0001
tau = 0.00025

# [COMMANDS]
dir_bots = "bots"
cmd_bot_setup = "g++ -std=c++17 %SRC%.cpp -o %DIR%/%NAME%.exe && cp %SRC%.cpp %DIR%/%NAME%.cpp"
cmd_play_game = "python play_game.py %DIR%/%P1%.exe %DIR%/%P2%.exe"

# [FILES]
file_log = "psyleague.log"
file_msg = "psyleague.msg"
file_db = "psyleague.db"
file_games = "psyleague.games"

これを自分の環境に合わせて修正します。今回は n_workerscmd_bot_setupcmd_play_game の 3つを修正しました。

 version = "0.2.0"
 
 # [MATCHMAKING]
-n_workers = 1
+n_workers = 4
 mm_min_matches = 200
 mm_min_matches_preference = 0.75
 mm_coeff = 2.5
@@ -18,12 +18,11 @@ tau = 0.00025
 
 # [COMMANDS]
 dir_bots = "bots"
-cmd_bot_setup = "g++ -std=c++17 %SRC%.cpp -o %DIR%/%NAME%.exe && cp %SRC%.cpp %DIR%/%NAME%.cpp"
-cmd_play_game = "python play_game.py %DIR%/%P1%.exe %DIR%/%P2%.exe"
+cmd_bot_setup = "docker exec local_battle g++ -std=c++17 %SRC% -o %DIR%/%NAME% && cp %SRC% %DIR%/%NAME%.cpp"
+cmd_play_game = "docker exec local_battle python3 play_game.py %DIR%/%P1% %DIR%/%P2%"

n_workers は同時に実行するプロセスの数を決定します。速くたくさん試合したいので 4 にしました。強いマシンを持ってる人ならもっと増やしてもいいかも。

cmd_bot_setup にはボットをビルドするときのコマンドを設定します。

cmd_play_game にはボットを対戦させるときのコマンドを設定します。

対戦サーバーの起動

以下のコマンドでサーバーを起動します。

psyleague run

何も無い状態だとこんな感じの表示になります。

Starting Psyleague server, press Ctrl+C to kill it
Active Bots: 0  Games since launch: 0  Games in the last 60s: 0 

ボットの追加

bot add コマンドでボットを追加します。

$ psyleague bot add cur_bot -s my_bot.cpp
Running Setup: docker exec local_battle g++ -std=c++17 my_bot.cpp -o bots/cur_bot && cp my_bot.cpp bots/cur_bot.cpp

コマンドが成功すると dir_bots で指定したディレクトリ以下にボットの実行ファイルとソースコードが追加されます。

$ tree bots 
bots
├── cur_bot
└── cur_bot.cpp

またサーバー側のボットの数も増えます。

Starting Psyleague server, press Ctrl+C to kill it
ADD_BOT : cur_bot : n/a                                                            
Active Bots: 1  Games since launch: 0  Games in the last 60s: 0 

1体だとまだ動作しないのでもう一体追加を行います。

$ psyleague bot add silver_bot -s silver_bot.cpp 
Running Setup: docker exec local_battle g++ -std=c++17 silver_bot.cpp -o bots/silver_bot && cp silver_bot.cpp bots/silver_bot.cpp
$ tree bots 
bots
├── cur_bot
├── cur_bot.cpp
├── silver_bot
└── silver_bot.cpp

2体目が追加されるとすぐに対戦が開始されます。

Starting Psyleague server, press Ctrl+C to kill it
ADD_BOT : cur_bot : n/a                                                            
ADD_BOT : silver_bot : n/a                                                         
Active Bots: 2  Games since launch: 83  Games in the last 60s: 28 

対戦結果の確認

psyleague show で対戦の結果を確認することが出来ます。

$ psyleague show
Pos  Name         Score  Games      Mu  Sigma  Errors  Created
---  ----------  ------  -----  ------  -----  ------  -------------------
  1  cur_bot     23.815    111  26.143  0.776       6  2023/06/07 00:03:55
  2  silver_bot  21.528    111  23.857  0.776       4  2023/06/07 00:09:25

対戦回数については何も指定しない場合 sys.maxsize 回行われるので (64ビット環境だと 9223372036854775807) 適当なところで止めてあげると良いです。

games_total = args.games or sys.maxsize

止めるのが面倒な場合は --games オプションで試合する回数の上限を設定してあげると良いでしょう。

$ psyleague run --games 100
Starting Psyleague server, press Ctrl+C to kill it
Active Bots: 2  Games since launch: 2 / 100  Games in the last 60s: 2

まとめ

今回は psyleague の Quick Setup Guide を試してみました。ローカルで実際の CodinGame のようなランキングシステムを構築出来るのは良いと思いましたし、過去の bot とまとめて対戦出来るのは便利でした。次回がいつになるかわかりませんが、その際には psyleague を使ってみようかなと思います。他にも便利な機能があるみたいなので興味がある人は是非試してみてください。