def test_ayane5(self): print("test_ayane5 : ") server = ayane.AyaneruServer() for engine in server.engines: engine.set_engine_options({"Hash":"128","Threads":"1","NetworkDelay":"0","NetworkDelay2":"0","MaxMovesToDraw":"320" \ , "MinimumThinkingTime":"0"}) #engine.debug_print = True engine.connect("exe/YaneuraOu.exe") # 持ち時間設定。 server.set_time_setting("byoyomi 100") # 1手0.1秒 # server.set_time_setting("time 1000 inc 2000") # 1秒 + 1手2秒 # これで対局が開始する server.game_start() # 対局が終了するのを待つ while not server.game_result.is_gameover(): time.sleep(1) # 対局棋譜の出力 print("game sfen = " + server.sfen) print("game_result = " + str(server.game_result)) server.terminate()
def test_ayane2(self): print("test_ayane2 : ") usi = ayane.UsiEngine() # usi.debug_print = True usi.set_engine_options({ "Hash": "128", "Threads": "4", "NetworkDelay": "0", "NetworkDelay2": "0" }) usi.connect("exe/YaneuraOu.exe") # usi.send_position("startpos moves 7g7f") # → 局面を指定しなければ初期局面のはず。 # "isready"~"readyok"は完了してからしかusi_go()は実行されないのでここで待機などをする必要はない。 # 時間を指定せずに思考させてみる。 usi.usi_go("infinite") # 3秒待ってからstopコマンドを送信して、エンジンがbestmoveを返してくるまで待機する。 time.sleep(3) usi.usi_stop() usi.wait_bestmove() # 思考内容を表示させてみる。 print("=== UsiThinkResult ===\n" + usi.think_result.to_string()) usi.disconnect() self.assertEqual(usi.engine_state, ayane.UsiEngineState.Disconnected)
def test_ayane3(self): print("test_ayane3 : ") sfens = [ "sfen 5R3/8k/9/9/7+b1/9/PP1+p5/LS7/KN7 b GSNrb3g2s2n3l15p", "sfen 5B1k1/9/9/5R3/9/9/1+P7/PP1+p5/K+P7 b Srb4g3s4n4l13p", "sfen 8k/6+Pg1/4+Bs2N/9/7+b1/9/PP1+p5/LS7/KN7 b GN2r2g2sn3l14p", ] usi = ayane.UsiEngine() # usi.debug_print = True usi.set_engine_options({ "Hash": "128", "Threads": "4", "NetworkDelay": "0", "NetworkDelay2": "0" }) usi.connect(self.__class__.engine_path) for sfen in sfens: usi.usi_position(sfen) # MultiPVで探索してMultiPVの2番目の指し手がMateスコアであるかで判定する。 usi.send_command("multipv 2") # 5秒考えてみる usi.usi_go_and_wait_bestmove("btime 0 wtime 0 byoyomi 5000") if len(usi.think_result.pvs) < 2: print(f"sfen = {sfen} : only one move") else: print(f"sfen = {sfen} : 1つ目の指し手の評価値 {usi.think_result.pvs[0].eval.to_string()}," f" 2つ目の指し手の評価値 {usi.think_result.pvs[1].eval.to_string()} ," f" 余詰めあり? {ayane.UsiEvalValue.is_mate_score(usi.think_result.pvs[1].eval)}") usi.disconnect()
def test_ayane4(self): print("test_ayane4 : ") # エンジン二つ usis = [] for _ in range(2): usi = ayane.UsiEngine() # usi.debug_print = True usi.set_engine_options({ "Hash": "128", "Threads": "1", "NetworkDelay": "0", "NetworkDelay2": "0", "MaxMovesToDraw": "256", "MinimumThinkingTime": "0" }) usi.connect(self.__class__.engine_path) usis.append(usi) # 棋譜 sfen = "startpos moves" # 手数 game_ply = 1 # 手番(先手=0 , 後手=1) turn = 0 # 256手ルール while game_ply < 256: usi = usis[turn] # 局面を設定する usi.usi_position(sfen) # 0.1秒思考させる usi.usi_go_and_wait_bestmove("time 0 byoyomi 100") bestmove = usi.think_result.bestmove # 評価値を表示させてみる # print(usi.think_result.pvs[0].eval) # print(usi.think_result.pvs[0].eval.to_string()) # 投了 or 宣言勝ち if bestmove == "resign" or bestmove == "win": break # 棋譜にこのbestmoveを連結 sfen += " " + bestmove # 手番反転 turn ^= 1 game_ply += 1 # 棋譜の出力 print("game sfen = " + sfen) for usi in usis: usi.disconnect()
def test_ayane6(self): print("test_ayane6 : ") server = ayane.MultiAyaneruServer() # 並列4対局 # server.debug_print = True server.init_server(4) options = { "Hash": "128", "Threads": "1", "NetworkDelay": "0", "NetworkDelay2": "0", "MaxMovesToDraw": "320", "MinimumThinkingTime": "0" } # 1P,2P側のエンジンそれぞれを設定して初期化する。 server.init_engine(0, "exe/YaneuraOu.exe", options) server.init_engine(1, "exe/YaneuraOu.exe", options) # 持ち時間設定。 # server.set_time_setting("byoyomi 100") # 1手0.1秒 server.set_time_setting( "byoyomi1p 100 byoyomi2p 200") # 1P側、1手0.1秒 2P側1手0.2秒 # これで対局が開始する server.game_start() # 10試合終了するのを待つ last_total_games = 0 # ゲーム数が増えていたら、途中結果を出力する。 def output_info(): nonlocal last_total_games, server if last_total_games != server.total_games: last_total_games = server.total_games print(server.game_info()) # 10局やってみる。 while server.total_games < 10: output_info() time.sleep(1) output_info() server.game_stop() # 対局棋譜の出力 # 例えば100局やるなら # "17 - 1 - 82(17.17% R-273.35[-348.9,-197.79]) winrate black , white = 48.48% , 51.52%"のように表示される。(はず) for kifu in server.game_kifus: print( f"game sfen = {kifu.sfen} , flip_turn = {kifu.flip_turn} , game_result = {str(kifu.game_result)}" ) server.terminate()
def test_ayane2(self): print("test_ayane2 : ") usi = ayane.UsiEngine() # usi.debug_print = True usi.set_engine_options({ "Hash": "128", "Threads": "4", "NetworkDelay": "0", "NetworkDelay2": "0" }) usi.connect(self.__class__.engine_path) self.do_test_ayane2(usi)
def test_ayane1(self): print("test_ayane1 : ") # エンジンとやりとりするクラス usi = ayane.UsiEngine() # デバッグ用にエンジンとのやりとり内容を標準出力に出力する。 # usi.debug_print = True # エンジンオプション自体は、基本的には"engine_options.txt"で設定する。(やねうら王のdocs/を読むべし) # 特定のエンジンオプションをさらに上書きで設定できる usi.set_engine_options({ "Hash": "128", "Threads": "4", "NetworkDelay": "0", "NetworkDelay2": "0" }) # エンジンに接続 # 通常の思考エンジンであるものとする。 usi.connect("exe/YaneuraOu.exe") # 開始局面から76歩の局面 # ※ "position"コマンド、"go"コマンドなどについては、USIプロトコルの説明を参考にしてください。 # cf.「USIプロトコルとは」: http://shogidokoro.starfree.jp/usi.html usi.usi_position("startpos moves 7g7f") # 現局面での指し手の集合を得る moves = usi.get_moves() self.assertEqual( moves, "1c1d 2c2d 3c3d 4c4d 5c5d 6c6d 7c7d 8c8d 9c9d 1a1b 9a9b 3a3b 3a4b 7a6b 7a7b 8b3b 8b4b 8b5b 8b6b 8b7b 8b9b 4a3b 4a4b 4a5b 5a4b 5a5b 5a6b 6a5b 6a6b 6a7b" ) # 現在の局面の手番を得る turn = usi.get_side_to_move() self.assertEqual(turn, ayane.Turn.WHITE) # multipv 4で探索させてみる # 2秒思考して待機させる。 usi.send_command("multipv 4") usi.usi_go_and_wait_bestmove("btime 0 wtime 0 byoyomi 2000") # 思考内容を表示させてみる。 print("=== UsiThinkResult ===\n" + usi.think_result.to_string()) # エンジンを切断 usi.disconnect() self.assertEqual(usi.engine_state, ayane.UsiEngineState.Disconnected)
def test_ayane1_ssh(self): print("test_ayane1_ssh : ") # エンジンとやりとりするクラス usi = ayane.SshUsiEngine() # デバッグ用にエンジンとのやりとり内容を標準出力に出力する。 # usi.debug_print = True # エンジンオプション自体は、基本的には"engine_options.txt"で設定する。(やねうら王のdocs/を読むべし) # 特定のエンジンオプションをさらに上書きで設定できる usi.set_engine_options({ "Hash": "128", "Threads": "4", "NetworkDelay": "0", "NetworkDelay2": "0" }) # エンジンに接続 # 通常の思考エンジンであるものとする。 usi.connect('localhost', self.__class__.engine_path) self.do_test_ayane1(usi)
def AyaneruColosseum(arglist): # --- コマンドラインのparseここから --- parser = argparse.ArgumentParser("ayaneru-colosseum.py") # 持ち時間設定。デフォルト1秒 parser.add_argument( "--time", type=str, default="byoyomi 100", help="持ち時間設定 AyaneruServer.set_time_setting()の引数と同じ。", ) # home folder parser.add_argument("--home", type=str, default="", help="hole folder") # engine path parser.add_argument("--engine1", type=str, default="exe/YaneuraOu.exe", help="engine1 path") parser.add_argument("--engine2", type=str, default="exe/YaneuraOu.exe", help="engine2 path") # Hashサイズ。デフォルト64MB parser.add_argument("--hash1", type=int, default=128, help="engine1 hashsize[MB]") parser.add_argument("--hash2", type=int, default=128, help="engine2 hashsize[MB]") # 対局回数 parser.add_argument("--loop", type=int, default=100, help="number of games") # CPUコア数 parser.add_argument("--cores", type=int, default=8, help="cpu cores(number of logical thread)") # エンジンに割り当てるスレッド数 parser.add_argument("--thread1", type=int, default=2, help="number of engine1 thread") parser.add_argument("--thread2", type=int, default=2, help="number of engine2 thread") # engine folder parser.add_argument("--eval1", type=str, default="eval", help="engine1 eval") parser.add_argument("--eval2", type=str, default="eval", help="engine2 eval2") # flip_turn parser.add_argument("--flip_turn", type=bool, default=True, help="flip turn every game") # book_file parser.add_argument("--book_file", type=str, default=None, help="book filepath") # start_gameply parser.add_argument("--start_gameply", type=int, default=24, help="start game ply in the book") # BookDir parser.add_argument("--bookdir1", type=str, default="book", help="book directory for engine1") parser.add_argument("--bookdir2", type=str, default="book", help="book directory for engine2") # BookFile parser.add_argument("--bookfile1", type=str, default="no_book", help="book file name for engine1") parser.add_argument("--bookfile2", type=str, default="no_book", help="book file name for engine2") # Arbitrary options parser.add_argument( "--options", "-o", type=str, default=None, help="set arbitrary options as name:value for both engines") parser.add_argument( "--options1", "-o1", type=str, default=None, help="set arbitrary options as name:value for the 1st engine") parser.add_argument( "--options2", "-o2", type=str, default=None, help="set arbitrary options as name:value for the 2nd engine") # Early stopping parser.add_argument("--early_stopping", action="store_true") parser.add_argument( "--early_stopping_limit", type=int, default=0, help="stop when the lower limit get below the rating limit") args = parser.parse_args(arglist) # --- コマンドラインのparseここまで --- print("home : {0}".format(args.home), flush=True) print("engine1 : {0}".format(args.engine1), flush=True) print("engine2 : {0}".format(args.engine2), flush=True) print("eval1 : {0}".format(args.eval1), flush=True) print("eval2 : {0}".format(args.eval2), flush=True) print("hash1 : {0}".format(args.hash1), flush=True) print("hash2 : {0}".format(args.hash2), flush=True) print("loop : {0}".format(args.loop), flush=True) print("cores : {0}".format(args.cores), flush=True) print("time : {0}".format(args.time), flush=True) print("flip_turn : {0}".format(args.flip_turn), flush=True) print("book file : {0}".format(args.book_file), flush=True) print("start_gameply : {0}".format(args.start_gameply), flush=True) print("bookdir1 : {0}".format(args.bookdir1), flush=True) print("bookdir2 : {0}".format(args.bookdir2), flush=True) print("bookfile1 : {0}".format(args.bookfile1), flush=True) print("bookfile2 : {0}".format(args.bookfile2), flush=True) print("options : {0}".format(args.options), flush=True) print("options1 : {0}".format(args.options1), flush=True) print("options2 : {0}".format(args.options2), flush=True) print("early_stopping : {0}".format(args.early_stopping), flush=True) print("early_stopping_limit : {0}".format(args.early_stopping_limit), flush=True) # directory home = args.home engine1 = os.path.join(home, args.engine1) engine2 = os.path.join(home, args.engine2) eval1 = os.path.join(home, args.eval1) eval2 = os.path.join(home, args.eval2) bookdir1 = os.path.join(home, args.bookdir1) bookdir2 = os.path.join(home, args.bookdir2) # マルチあやねるサーバーをそのまま用いる server = ayane.MultiAyaneruServer() # 1対局に要するスレッド数 # (先後、同時に思考しないので大きいほう) thread_total = max(args.thread1, args.thread2) # 何並列で対局するのか? 2スレほど余らせておかないとtimeupになるかもしれん。 # メモリが足りるかは知らん。メモリ足りないとこれまたメモリスワップでtimeupになる。 cores = max(args.cores - 2, 1) game_server_num = int(cores / thread_total) # エンジンとのやりとりを標準出力に出力する # server.debug_print = True # あやねるサーバーを起動 server.init_server(game_server_num) # エンジンオプション options_common = { "NetworkDelay": "0", "NetworkDelay2": "0", "MaxMovesToDraw": "320", "MinimumThinkingTime": "0", # "BookFile": "no_book" } # command line options def split_opt(arg): argopt = {} if arg: for item in args.options.split(" "): name, value = item.split(":") argopt.update({name: value}) return argopt #argopt = {} # if args.options: # for item in args.options.split(" "): # name, value = item.split(":") # argopt.update({name: value}) argopt = split_opt(args.options) argopt1 = split_opt(args.options1) argopt2 = split_opt(args.options2) print(f"argopt: {argopt}") print(f"argopt1: {argopt1}") print(f"argopt2: {argopt2}") options1p = { "USI_Hash": str(args.hash1), "Threads": str(args.thread1), "EvalDir": eval1, "BookDir": bookdir1, "BookFile": args.bookfile1 } options2p = { "USI_Hash": str(args.hash2), "Threads": str(args.thread2), "EvalDir": eval2, "BookDir": bookdir2, "BookFile": args.bookfile2 } # 1P,2P側のエンジンそれぞれを設定して初期化する。 server.init_engine(0, engine1, { **options_common, **options1p, **argopt, **argopt1 }) server.init_engine(1, engine2, { **options_common, **options2p, **argopt, **argopt2 }) # 持ち時間設定。 server.set_time_setting(args.time) # flip_turnを反映させる server.flip_turn_every_game = args.flip_turn # 定跡 # テスト用の定跡ファイル # args.book_file = "book/records2016_10818.sfen" if args.book_file is None: start_sfens = ["startpos"] else: book_filepath = os.path.join(home, args.book_file) with open(book_filepath) as f: start_sfens = f.readlines() server.start_sfens = start_sfens server.start_gameply = args.start_gameply # 対局スレッド数、秒読み設定などを短縮文字列化する。 if args.thread1 == args.thread2: game_setting_str = "t{0}".format(args.thread1) else: game_setting_str = "t{0},{1}".format(args.thread1, args.thread2) game_setting_str += (args.time.replace("byoyomi", "b").replace( "time", "t").replace("inc", "i").replace(" ", "")) # これで対局が開始する server.game_start() # loop回数試合終了するのを待つ last_total_games = 0 loop = args.loop # ゲーム数が増えていたら、途中結果を出力する。 def output_info(): nonlocal last_total_games, server if last_total_games != server.total_games: last_total_games = server.total_games print(game_setting_str + "." + server.game_info(), flush=True) def early_stopping(): nonlocal last_total_games, server, args, loop if args.early_stopping and (server.total_games > (loop / 10)) and ( -server.game_rating().rating_lowerbound < args.early_stopping_limit): return True return False while server.total_games < loop and not early_stopping(): output_info() time.sleep(1) output_info() server.game_stop() # 対局棋譜の出力 # for kifu in server.game_kifus: # print("game sfen = {0} , flip_turn = {1} , game_result = {2}".format(kifu.sfen , kifu.flip_turn , str(kifu.game_result))) server.terminate() return server.game_rating().win_rate
def AyaneruGate(): # --- コマンドラインのparseここから --- parser = argparse.ArgumentParser("ayaneru-gate.py") # 持ち時間設定。デフォルト0.1秒。 # エンジンのほうの設定でノード数を固定するとき秒数を固定するとか(MinimumThinkingTimeで)してエンジンを固定化するといいかも? parser.add_argument( "--time", type=str, default="byoyomi 100", help="持ち時間設定 AyaneruServer.set_time_setting()の引数と同じ。", ) # home folder parser.add_argument("--home", type=str, default="AyaneruGate", help="home folder") # イテレーション回数 parser.add_argument("--iteration", type=int, default=10, help="number of iterations") # 対局回数 parser.add_argument("--loop", type=int, default=10, help="number of games") # CPUコア数 parser.add_argument("--cores", type=int, default=8, help="cpu cores(number of logical threads)") # flip_turn parser.add_argument("--flip_turn", type=bool, default=True, help="flip turn every game") # book_file parser.add_argument( "--book_file", type=str, default="book/records2016_10818.sfen", help="book filepath", ) # start_gameply parser.add_argument("--start_gameply", type=int, default=24, help="start game ply in the book") args = parser.parse_args() # --- コマンドラインのparseここまで --- print("home : {0}".format(args.home)) print("iteration : {0}".format(args.iteration)) print("loop : {0}".format(args.loop)) print("cores : {0}".format(args.cores)) print("time : {0}".format(args.time)) print("flip_turn : {0}".format(args.flip_turn)) print("book file : {0}".format(args.book_file)) print("start_gameply : {0}".format(args.start_gameply)) # directory home = args.home log = ayane.Log(os.path.join(home, "log")) log.print("iteration start", output_datetime=True) # エンジンの列挙 engines_folder = os.path.join(home, "engines") if not os.path.exists(engines_folder): print("Error : {0} folder is not exist.".format(engines_folder)) return engine_infos = [] # List[EngineInfo] for engine_rel_path in os.listdir(engines_folder): info = EngineInfo() info.engine_folder = engine_rel_path info.read_engine_define(home) engine_infos.append(info) # 取得できたエンジンの一覧を表示 if False: # こんなん表示せんでええやろ。 print("engines :") for i, engine_info in enumerate(engine_infos): print("== Engine {0} ==".format(i)) engine_info.print() # レーティングが変動するエンジンが少なくとも2つないと意味がない。 non_fixed_rating_engines = 0 for info in engine_infos: if not info.rating_fix: non_fixed_rating_engines += 1 if non_fixed_rating_engines < 2: print("Error! : non fixed rating engine < 2") raise ValueError() # それぞれのエンジンのレーティングを表示する。 def output_engine_rating(): nonlocal log, engine_infos log.print("== engine rating list ==", also_print=True) for info in engine_infos: log.print( "engine : {0} , rating = {1} , rating_fix = {2} , threads = {3}" .format( info.engine_display_name, info.rating, info.rating_fix, info.engine_threads, ), also_print=True, ) output_engine_rating() # サーバーを一つ起動して、任意の2エンジンで100対局ほど繰り返して、レーティングを変動させる。 # あとは、それをloop回数だけ繰り返す。 for it in range(args.iteration): log.print("iteration : {0}".format(it), output_datetime=True) # マルチあやねるサーバーの起動 server = ayane.MultiAyaneruServer() # エンジンとのやりとりを標準出力に出力する # server.debug_print = True # 2つのエンジンを選択 info1 = None info2 = None while True: num_of_engines = len(engine_infos) p1 = random.randint(0, num_of_engines - 1) p2 = random.randint(0, num_of_engines - 1) # 同じプレイヤー同士の対局には意味がない if p1 == p2: continue # engine番号が若い順になって欲しい。 if p1 > p2: p1, p2 = p2, p1 # pythonのswapテクニック info1 = engine_infos[p1] info2 = engine_infos[p2] # 両側がレーティング固定であっても意味がない。 if info1.rating_fix and info2.rating_fix: continue # 条件を満たしたので抜ける break # 今回対局するエンジン名を出力 log.print( "engine : {0} vs {1}".format(info1.engine_display_name, info2.engine_display_name), also_print=True, ) # エンジンの設定 engine1 = info1.engine_exe_fullpath(home) engine2 = info2.engine_exe_fullpath(home) thread1 = info1.engine_threads thread2 = info2.engine_threads # 1対局に要するスレッド数 # (先後、同時に思考しないので大きいほう) thread_total = max(thread1, thread2) # 何並列で対局するのか? 2スレほど余らせておかないとtimeupになるかもしれん。 # メモリが足りるかは知らん。メモリ足りないとこれまたメモリスワップでtimeupになる。 cores = max(args.cores - 2, 1) game_server_num = int(cores / thread_total) # あやねるサーバーを起動 server.init_server(game_server_num) # エンジンオプション options_common = { "NetworkDelay": "0", "NetworkDelay2": "0", "MaxMovesToDraw": "320", "MinimumThinkingTime": "0", "BookFile": "no_book", } # 1P,2P側のエンジンそれぞれを設定して初期化する。 server.init_engine(0, engine1, options_common) server.init_engine(1, engine2, options_common) # 持ち時間設定。 server.set_time_setting(args.time) # flip_turnを反映させる server.flip_turn_every_game = args.flip_turn # 定跡 if args.book_file is None: start_sfens = ["startpos"] else: book_filepath = os.path.join(home, args.book_file) with open(book_filepath) as f: start_sfens = f.readlines() server.start_sfens = start_sfens server.start_gameply = args.start_gameply # 対局スレッド数、秒読み設定などを短縮文字列化する。 if thread1 == thread2: game_setting_str = "t{0}".format(thread1) else: game_setting_str = "t{0},{1}".format(thread1, thread2) game_setting_str += (args.time.replace("byoyomi", "b").replace( "time", "t").replace("inc", "i").replace(" ", "")) # loop回数試合終了するのを待つ last_total_games = 0 loop = args.loop # ゲーム数が増えていたら、途中結果を出力する。 def output_info(): nonlocal last_total_games, server, log if last_total_games != server.total_games: last_total_games = server.total_games log.print(game_setting_str + "." + server.game_info()) # これで対局が開始する server.game_start() while server.total_games < loop: output_info() time.sleep(1) output_info() server.game_stop() # 対局棋譜の出力(ログとしてフォルダに書き出しておく) for kifu in server.game_kifus: log.print( "game sfen = {0} , flip_turn = {1} , game_result = {2}".format( kifu.sfen, kifu.flip_turn, str(kifu.game_result)), also_print=False, ) # 対局が終わったのでレーティングの移動を行う elo = server.game_rating() # 1P側は2P側よりどれだけ勝るか。 # 完勝のときは+無限大扱いでいいと思う。(以下でclipするので) rating_diff = elo.rating # レーティングの移動量の絶対値の上限は、対局回数に比例させておく。 # (少ない対局回数で思いっきり変動してしまうのを防ぐため) rating_diff = min(max(rating_diff, -loop), loop) player1_add = 0 player2_add = 0 if info1.rating_fix: player2_add = -rating_diff elif info2.rating_fix: player1_add = +rating_diff else: player1_add = +int(rating_diff / 2) player2_add = -int(rating_diff / 2) log.print( "Player1 : {0} , rating {1} -> {2}".format( info1.engine_display_name, info1.rating, info1.rating + player1_add), also_print=True, ) log.print( "Player2 : {0} , rating {1} -> {2}".format( info2.engine_display_name, info2.rating, info2.rating + player2_add), also_print=True, ) info1.rating += player1_add info2.rating += player2_add # レーティングが変動したのなら、エンジン設定ファイルに書き戻す if player1_add != 0: info1.write_engine_define(home) if player2_add != 0: info2.write_engine_define(home) # iteration回数だけ繰り返したので終了する。 output_engine_rating() log.print("iteration end", also_print=True, output_datetime=True) server.terminate() log.close()
# 勝ち,負け,引き分けの結果を示す定数 WIN = 0 DRAW = 1 LOSE = 2 # Ayaneにおける結果をここでの結果に変換する辞書 result_converter = { ayane.GameResult.BLACK_WIN: WIN, ayane.GameResult.WHITE_WIN: LOSE, ayane.GameResult.DRAW: DRAW, ayane.GameResult.MAX_MOVES: DRAW } # インスタンス生成 server = ayane.AyaneruServer() # サーバの設定 server.error_print = True server.set_time_setting(f"byoyomi1p {args.time1} byoyomi2p {args.time2}") server.moves_to_draw = 320 # YaneuraOuの設定 server.engines[1].set_engine_options({ "USI_Ponder": "false", "NodesLimit": args.NodesLimit, "USI_Hash": hash_size, "BookMoves": 0, "NetworkDelay": 0, "NetworkDelay2": 0 })
def AyaneruColosseum(): # --- コマンドラインのparseここから --- parser = argparse.ArgumentParser("ayaneru-colosseum.py") # 持ち時間設定。デフォルト1秒 parser.add_argument("--time",type=str,default="byoyomi 100",help="持ち時間設定 AyaneruServer.set_time_setting()の引数と同じ。") # home folder parser.add_argument("--home",type=str,default="",help="hole folder") # engine path parser.add_argument("--engine1",type=str,default="exe/YaneuraOu.exe",help="engine1 path") parser.add_argument("--engine2",type=str,default="exe/YaneuraOu.exe",help="engine2 path") # Hashサイズ。デフォルト64MB parser.add_argument("--hash1",type=int,default=128,help="engine1 hashsize[MB]") parser.add_argument("--hash2",type=int,default=128,help="engine2 hashsize[MB]") # 対局回数 parser.add_argument("--loop",type=int,default=100,help="number of games") # CPUコア数 parser.add_argument("--cores",type=int,default=8,help="cpu cores(number of logical thread)") # エンジンに割り当てるスレッド数 parser.add_argument("--thread1",type=int,default=2,help="number of engine1 thread") parser.add_argument("--thread2",type=int,default=2,help="number of engine2 thread") # engine folder parser.add_argument("--eval1",type=str,default="eval",help="engine1 eval") parser.add_argument("--eval2",type=str,default="eval",help="engine2 eval2") # flip_turn parser.add_argument("--flip_turn",type=bool,default=True,help="flip turn every game") # book_file parser.add_argument("--book_file",type=str,default=None,help="book filepath") # start_gameply parser.add_argument("--start_gameply",type=int,default=24,help="start game ply in the book") args = parser.parse_args() # --- コマンドラインのparseここまで --- print("home : {0}".format(args.home)) print("engine1 : {0}".format(args.engine1)) print("engine2 : {0}".format(args.engine2)) print("eval1 : {0}".format(args.eval1)) print("eval2 : {0}".format(args.eval2)) print("hash1 : {0}".format(args.hash1)) print("hash2 : {0}".format(args.hash2)) print("loop : {0}".format(args.loop)) print("cores : {0}".format(args.cores)) print("time : {0}".format(args.time)) print("flip_turn : {0}".format(args.flip_turn)) print("book file : {0}".format(args.book_file)) print("start_gameply : {0}".format(args.start_gameply)) # directory home = args.home engine1 = os.path.join(home,args.engine1) engine2 = os.path.join(home,args.engine2) eval1 = os.path.join(home,args.eval1) eval2 = os.path.join(home,args.eval2) # マルチあやねるサーバーをそのまま用いる server = ayane.MultiAyaneruServer() # 1対局に要するスレッド数 # (先後、同時に思考しないので大きいほう) thread_total = max(args.thread1 , args.thread2) # 何並列で対局するのか? 2スレほど余らせておかないとtimeupになるかもしれん。 # メモリが足りるかは知らん。メモリ足りないとこれまたメモリスワップでtimeupになる。 cores = max(args.cores - 2 , 1) game_server_num = int(cores / thread_total) # エンジンとのやりとりを標準出力に出力する # server.debug_print = True # あやねるサーバーを起動 server.init_server(game_server_num) # エンジンオプション options_common = {"NetworkDelay":"0","NetworkDelay2":"0","MaxMovesToDraw":"320","MinimumThinkingTime":"0","BookFile":"no_book"} options1p = {"Hash":str(args.hash1),"Threads":str(args.thread1),"EvalDir":eval1} options2p = {"Hash":str(args.hash2),"Threads":str(args.thread2),"EvalDir":eval2} # 1P,2P側のエンジンそれぞれを設定して初期化する。 server.init_engine(0,engine1, {}.update(**options_common , **options1p)) server.init_engine(1,engine2, {}.update(**options_common , **options2p)) # 持ち時間設定。 server.set_time_setting(args.time) # flip_turnを反映させる server.flip_turn_every_game = args.flip_turn # 定跡 # テスト用の定跡ファイル # args.book_file = "book/records2016_10818.sfen" if args.book_file is None: start_sfens = ["startpos"] else: book_filepath = os.path.join(home,args.book_file) with open(book_filepath) as f: start_sfens = f.readlines() server.start_sfens = start_sfens server.start_gameply = args.start_gameply # 対局スレッド数、秒読み設定などを短縮文字列化する。 if args.thread1 == args.thread2: game_setting_str = "t{0}".format(args.thread1) else: game_setting_str = "t{0},{1}".format(args.thread1,args.thread2) game_setting_str += args.time.replace("byoyomi","b").replace("time","t").replace("inc","i").replace(" ","") # これで対局が開始する server.game_start() # loop回数試合終了するのを待つ last_total_games = 0 loop = args.loop # ゲーム数が増えていたら、途中結果を出力する。 def output_info(): nonlocal last_total_games , server if last_total_games != server.total_games: last_total_games = server.total_games print(game_setting_str + "." + server.game_info()) while server.total_games < loop : output_info() time.sleep(1) output_info() server.game_stop() # 対局棋譜の出力 # for kifu in server.game_kifus: # print("game sfen = {0} , flip_turn = {1} , game_result = {2}".format(kifu.sfen , kifu.flip_turn , str(kifu.game_result))) server.terminate()