トーナメント

トーナメントは shogiarena run tournament で実行します。 エンジン同士の比較は、まずこのモードで行います。

shogiarena run tournament tournament.yaml

主なオプション

オプション説明
--dry-run設定を読み込み、実行前に検証する
--validate-only設定検証のみで終了する
--experiment-name NAME自動生成される run グループ名を上書きする
--run-dir PATHrun ディレクトリを明示指定する
--no-resume既存状態を再開せず新規実行する
--provision {cas,preplaced}SSH resourceをCAS配置またはpath/digest検証済み既配置として扱う
--path-preflight {off,warn,error}USI オプション内のパスらしき値を事前検査する

--rules KEY=VALUE、--tournament KEY=VALUE、--dashboard KEY=VALUE などで YAML の一部を CLI から上書きできます。

shogiarena run tournament tournament.yaml \
  --tournament games_per_pair=100 num_parallel=4 \
  --rules time_control.byoyomi_ms=1000

最小構成

experiment_name: "engine-comparison"

engines:
  - engine_path: "engine_a.yaml"
  - engine_path: "engine_b.yaml"

tournament:
  scheduler: round_robin
  games_per_pair: 20
  num_parallel: 2

rules:
  time_control:
    time_ms: 60000
    increment_ms: 1000

dashboard:
  enabled: true
  api_port: 8080

engines

エンジンは次のどちらかで指定します。

指定方法用途
engine_pathローカルのエンジン YAML を参照する
artifactリポジトリ定義とビルド設定からエンジンを解決する

engine_path の例:

engines:
  - engine_path: "examples/configs/resources/engines/local_example.yaml"
    name: "EngineA"
    options:
      Threads: 4

artifact の例:

engines:
  - artifact: "YaneuraOu/9f89431a"
    build_options:
      target_cpu: ZEN3
    options:
      Threads: 4
      USI_Hash: 2048

トーナメント側で options、options_overlays、time_control を指定すると、参照先のエンジン設定に上書きマージされます。

tournament

フィールド既定値説明
schedulerround_robinround_robin または gauntlet
games_per_pair41 ペアあたりの対局数
num_parallel4同時に走らせる対局数
seed42スケジュールと局面選択の乱数シード
game_orderautoauto, pairwise, interleave, shuffle
engine_lifecyclereusereuse はエンジンプロセスを再利用、per_game は各対局後に終了
baseline_count1gauntlet で先頭から何台を baseline にするか

gauntlet は、先頭側の baseline 群と残りの候補群を重点的に対局させたい場合に使います。

engine_lifecycle: per_game を指定すると、各対局の gameover 後に両エンジンプロセスを閉じ、次局で新しい USI プロセスを起動します。 対局間でメモリ状態を持ち越したくない強さ比較で使います。 既定の reuse は、長いトーナメントの起動コストを抑えるためにプロセスを idle pool へ戻します。

並列数とインスタンス容量

num_parallel は「同時に実行したい対局数」として扱われます。 ShogiArena は各エンジンの Threads / USI_Threads と Ponder / USI_Ponder から必要 slot 数を見積もり、pending schedule の連続 num_parallel 局が instance の slots / max_engines に収まるかを対局開始前に検査します。

ponderが無効、または指定されていない場合は、片側につきceil(Threads / 2)スロットを予約します。 ponderが有効な場合は、片側につきThreadsと同じ数のスロットを予約します。 たとえば Threads: 4 のエンジン同士は 1 局で 4 slot を使うため、num_parallel: 3 には同じ instance 上で 12 slot が必要です。 instance が slots: 8 の場合は既定でエラーになります。

容量に合わせて実行時に並列数を制限したい場合は、system.resource_capacity_preflight を warn または off に変更します。

system:
  resource_capacity_preflight: error  # default: error, warn, off

rules

時間制御

主な指定:

  • fixed_time_ms: 1 手固定時間
  • time_ms + increment_ms: フィッシャー式
  • time_ms + byoyomi_ms: 秒読み
  • node_limit: ノード数制限
  • depth_limit: 深さ制限

初期局面

rules:
  initial_positions:
    type: file
    source: "openings/startpos.sfen"
    flip_policy: pair_both

type は startpos または file です。 file の場合は SFEN または opening line のリストを source に指定します。 shogiarena run tournament / run sprt で YAML ファイルから実行する場合、相対 source はその YAML ファイルの場所を基準に解決されます。

selection_policy の既定値は with_replacement で、同じ行を複数回選ぶことがあります。 without_replacement にすると schedule 内で各行を一度だけ選びます。 先後入れ替えの 2 局に同じ行を使う flip_policy: pair_both では、その 2 局を 1 回の選択として数えます。 必要な選択回数よりファイルの行数が少ない場合は、対局開始前にエラーになります。

source_format は auto、sfen、usi_line を指定できます。

値意味
auto行の形から SFEN / USI line を自動判定する
sfen各行を初期局面 SFEN として読む
usi_line各行を 7g7f 3c3d ... のような USI 指し手列として読み、line 適用後の SFEN を初期局面にする

先後を入れ替えた 2 局を同じ開始局面で指すには、flip_policy: pair_both を指定します。 sync_scope: pair を併せて指定すると、flip_policy が pair_both であることを設定検証で確認します。

rules:
  initial_positions:
    type: file
    source: "openings/pair_lines.usi"
    source_format: usi_line
    selection_policy: without_replacement
    flip_policy: pair_both
    sync_scope: pair
    preserve_line_metadata: true

preserve_line_metadata: true にすると、指し手列のファイルパス、行番号、行 ID、USI 指し手列を対局記録に保存します。 ダッシュボードの Book タブの Pairs でも、ペアの開始手順を確認できます。 ファイルの空でない行はすべて開始局面として読むため、コメント行は入れず、SFEN または USI 指し手列だけを書いてください。

指定した指し手列は、対局開始前に初期局面を作るために使います。 棋譜に保存する指し手は、その初期局面からエンジンが実際に指した手だけです。 エンジン内蔵定跡が有効な場合は、開始局面からさらに定跡手が続くことがあります。 定跡の影響を除いて強さを比較するには、エンジン側の BookFile: no_book を推奨します。

adjudication

rules:
  adjudication:
    enable_max_plies: true
    max_plies: 320
    sync_max_plies_with_engine: true
    resign_threshold_cp: 800
    resign_move_count: 8

最大手数、投了判定、エンジン側の引き分け手数オプション同期を設定できます。

実行結果

--run-dir を指定した場合は、そのパスが run ディレクトリとして使われます。 指定しない場合は、標準出力先に次の形で作られます。

{output_dir}/tournament/runs/<experiment>-<hash8>/YYYYMMDDHHMMSS/
├── game.db
├── manifest.json
├── state.json
├── completed.flag
├── completion_status.json
├── data/
├── records/
└── transcripts/

結果集計:

shogiarena results summary /path/to/run
shogiarena results summary /path/to/run --format csv

終了状態

completion_status.jsonのstatusとtermination_reasonで、正常に完了したか、途中で停止したかを確認できます。 completed.flagがあっても、それだけで正常完了とは判断できません。

status意味
clean正常に終了し、無効局も出ていない
with-anomalies正常に終了したが、無効局や原因不明の時間切れを含む
failed完了した測定として扱えない。利用者が停止した場合も含む

停止の理由はtermination_reasonに記録されます。

値意味
schedule-complete予定した対局をすべて消化した
sprt-finishedSPRTが結論に達して早期終了した
cancelled利用者が停止した
timeout-burstShogiArenaの停滞に起因する時間切れが閾値に達した
timeout-attribution-unknown原因不明の時間切れが閾値に達した
transport-timeout通信やプロトコルの待機失敗が閾値に達した
incomplete予定を消化しておらず、正常終了も確認できない
runtime-error実行中にエラーが起きた
finalization-error最終処理に失敗した
cleanup-error後始末に失敗した

SPRTの早期終了では、予定より少ない対局数でも正常完了になります。 たとえば、1000局を予定して124局で検定が決着した場合は、次のように記録されます。

{
  "status": "clean",
  "termination_reason": "sprt-finished",
  "scheduled": 1000,
  "completed": 124,
  "cancelled": 8,
  "not_played": 868
}

not_playedは、予定していたが実施しなかった局数です。 completion_status.jsonがない場合は、完了した測定として扱う前に実行ログを確認してください。

SPRT終了後の対局

SPRTは停止を決めた時点で集計を止めます。 そのとき実行中だった対局は最後まで進み、DBや棋譜には保存されますが、検定には追加しません。 この局数はダッシュボードのSPRT状態やAPIのlate_gamesで確認できます。 未開始だった局はnot_playedに残ります。 sprt.min_gamesに達するまでは、LLRが境界を超えても停止しません。

時間切れと無効局

エンジンの時間切れと、ShogiArenaや通信の問題による待機失敗を区別して記録します。 completion_status.jsonのtimeouts_by_originに、それぞれの件数が入ります。

値対局結果の扱い
engine_deadlineエンジンの時間切れ負け。レーティングとSPRTに算入する
orchestrator_stall旧runとの互換用。ShogiArena側の処理の遅れとして記録された無効局を除外する
unknown原因を断定できない時間切れ。無効局として除外する
transport_timeout通信やプロトコルの待機失敗。無効局として除外する
unattributed停滞の計測がない実行。時間切れ負けとして扱う

1.3.3以降は、bestmoveを受信した後の処理時間を課金しません。 通常の受信観測経路では、期限内に観測した応答が後処理の遅れで時間切れになることはなく、orchestrator_stallを新たに生成しません。 この値と停止閾値は、旧runの読込とresume時のカウンター復元のために維持します。 期限後に観測した応答の超過をloopの停滞だけで説明できる場合は、原因を断定せずunknownとします。

無効局が出ても、設定した閾値に達するまでは対局を続けます。 この場合の終了状態はwith-anomaliesです。 閾値に達すると新規対局を止め、終了状態をfailedにします。 無効局が多い結果は、強さの比較に使う前に原因を確認してください。

この時間切れの分類はトーナメントとSPRTで使います。 SPSAの時間切れはunattributedとして扱います。 失敗した局の詳細はfailures/run_failures.jsonl、エンジン起動失敗はfailures/engine_startup_failure.jsonで確認できます。

指し手ごとの実測時間

各指し手には 2 種類の時間が記録されます。

フィールド意味
wall_time_ms持ち時間から差し引かれた時間
engine_wall_time_msthink() 呼び出しから bestmove 回収までの実測時間

engine_wall_time_ms は、game.db の game_move.engine_wall_time_ms や、results summary --format json の時間情報から確認できます。 エンジンの処理速度や wall NPS を比較するときは、engine_wall_time_ms を使います。

使用したエンジンやファイルの検証:

shogiarena results verify-provenance /path/to/run

関連