v1.5.3とv1.6.4におけるCSAプロトコルの時間管理に関する改善 - sunfish-shogi/electron-shogi GitHub Wiki

はじめに

v1.5.3v1.6.4 では CSA プロトコルの時間管理に関して本稿に記載する 3 つの改善が行われました。(v1.7.0 以降にも同じ修正が含まれる予定です。)

注意

WCSC33 で使用される方へ

WCSC へ出場する上で、このパッチバージョンアップを利用せずとも基本的には対局が可能です。

このパッチを利用しないと、何らかのトラブルで途中再開をする場合に、再開局面までの消費時間が反映されないことが問題になる可能性はあります。 ただし、過去の大会で途中再開をしたことはほとんど無いはずで、かなりのレアケースだと思われます。

Increment が 1 手目に適用されないことは持ち時間が少なくなって不利になるようにも思われますが、 WCSC の Increment は 5 秒しかありません。 むしろ、ギリギリまで時間を使って時間切れ負けする可能性があるので、このパッチを利用する場合はエンジンオプションで十分なマージンを設定するよう注意してください。

WCSC の時間管理は 1 秒単位であると説明されているので、 Time_Unit:1sec 以外の値が用いられることは無いと思われます。

v1.5.2 と v1.6.3 について

v1.5.2 と v1.6.3 をお使いでない場合は、まず v1.5.2 と v1.6.3 における CSA サーバーへの接続エラーに関する改善 を参照してください。

改善内容

【改善1】1 手目に対する Increment の付与

CSA プロトコルでは Increment で示される値が 1 手ごとに加算されます。 一般的なチェスクロック(「ザ・名人戦」や「将棋倶楽部24」)のフィッシャールールモードでは、着手後に加算が行われます。 しかし、 CSA プロトコルでは着手の直前に加算が行われるとされています。

WCSC の大会ルールでは次のように書かれています。

対戦開始当初の持ち時間は15分とし、自らの手番となるごとに5秒加算される。すなわち、自分の 手番だけ数えてn手目の手番で累積消費時間が15分+5×n秒以上となった場合、負けとなる。そ の手で相手が詰みでも負けとなる。

つまり、 WCSC の時間設定では 1 手目の思考に 15 分 5 秒まで使うことができます。 WCSC での利用実績が多い将棋所でも初手を指す前に時間が加算されるようです。

Electron 将棋では 1 手目を指す前に時間は加算されていませんでした。 今回のパッチでは 1 回加算してから対局開始するように修正しました。

修正前のバージョンでも初回の加算が無いものの対局は可能です。

【改善2】途中再開する場合の消費時間

CSA プロトコルでは開始局面がクライアントに通知される際、局面に加えて棋譜も与えられる場合があります。 これは途中から再開する場合にそれまでの指し手と消費時間を示すものです。

Electron 将棋ではその消費時間や加算時間を計算していませんでした。 今回のパッチではそれを計算して反映するように修正しました。

【改善3】時間単位が 1 秒ではない場合の消費時間計算の修正

CSA プロトコルでは Time_Unit という名前で単位時間が与えられます。 実在する CSA サーバー(CSA 公式、shogi-server、TEST56)では 1sec が使われるケースしか見たことがありませんが、規格上は 1msec なども使うことができます。

この場合に指し手に付与される消費時間( ,T の後に続く数値)の単位がいくつなのか、文面では明言されていません。 しかし、概ね CSA ファイル形式に従うとされていることから、単位は秒を示すものだと思われます。

Electron将棋は指し手の消費時間が Time_Unit を単位とする時間であるものとして扱っていました。 今回のパッチでは常に秒を示しているものとして扱うよう修正しました。

ソースコードの変更内容

https://github.com/sunfish-shogi/electron-shogi/commit/30c34aa07f6af447232f087b353afff470445b5c

次のコードで、 Game Summary を受信した直後に Increment の値を 1 回加算しています。

    // 初回分の Increment を与える。
    this.playerStates.black.time += this.gameSummary.increment;
    this.playerStates.white.time += this.gameSummary.increment;

次のコードで、 Game Summary を受信した直後に過去の消費時間(加算時間)を計算して反映しています。

    // 途中から再開する場合に、前回消費した時間を計算する。
    const record = importCSA(this.gameSummary.position);
    if (record instanceof Error) {
      this.onError(new Error("invalid game position received from CSA server"));
      return;
    }
    for (const entry of record.moves) {
      if (entry.move === SpecialMove.START) {
        continue;
      }
      const color = reverseColor(entry.nextColor);
      const time =
        this.playerStates[color].time -
        entry.elapsedMs / this.gameSummary.timeUnitMs +
        this.gameSummary.increment;
      this.playerStates[color].time = Math.max(time, 0);
    }

読み取った消費時間が秒単位であるものとして、次のコードでは Time_Unit で示す単位へ変換してから減算しています。

      const time =
        this.playerStates[color].time -
        (elapsed * 1e3) / this.gameSummary.timeUnitMs +
        this.gameSummary.increment;

参考資料