예제 #1
0
    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()
예제 #2
0
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
예제 #3
0
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()
예제 #4
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()