yu-neのドミニオン検証ブログ

ドミニオン(ボードゲーム)に関することを書きます。主に自作シミュレータでの検証結果の記事が多めです。

ドミニオンシミュレータについて

本ブログでは検証ツールとして自作のドミニオンシミュレータを使用しています。 ここではツールの簡単な説明をします。

動作環境

OS:Windows
標準でインストールされているWSHというソフトウェア上で動作します。

ソースコード

ソースコードGitHubで公開しています。
書きっぷりはイマイチな部分も多いですが、まだまだ発展途上なので、ちまちま機能を追加していくつもりです。
ファイル拡張子は.jsとなっていますが、JavaScriptではなくJScriptというスクリプト言語です。
Windows上でソースだけあれば動作可能ということでこの言語を選択しました。

github.com

ダウンロード方法

  • ブラウザで上記GitHubにアクセスする。

  • 下記画像右上の「Code」ボタン(緑のボタン)を押下し、その後「Download ZIP」をクリック。

f:id:yune_dominion:20211206123740p:plain

  • ファイルエクスプローラからダウンロードしたzipファイルを解凍し、出力されたフォルダ(DomSim-main)を任意のフォルダに配置する。
    ※解凍はzipファイルを右クリック→「すべて展開」を押下など実施してください。

実行方法

  • ファイルエクスプローラから「DomSim-main」内に移動。
  • 「domsim.bat」をダブルクリック。 (完了まではしばらく時間がかかります。)
    Windows Defenderなどにより、信頼されないアプリうんぬんのダイアログが出る可能性がありますが、その場合は、「続行」をクリックして進めてください。

実行すると、以下のようなコンソール画面が出力されます。

f:id:yune_dominion:20211206130220p:plain

ダウンロード直後の状態だと、鍛冶屋1枚ステロのシミュレート結果が表示されます。 属州4の平均ターン数として、おそらく14.8~15.0あたりの数値が出るかと思います。

  • Enterキーを押すことでコンソール画面が閉じます。

AIの設定方法

setting.jsをテキストエディタ(メモ帳など)で開いて編集します。 今回は簡単な箇所だけ説明します。 そのうちもう少し詳細な説明を書きたいところ。。

購入手順の変更方法

購入手順はprocessBuyPhaseという関数内に設定します。

  /**
   * 購入フェーズでの処理を設定する。
   *
   * 本メソッドがコールされたときの購入カードは1枚のみとすること。
   * 1ターンの間でも、購入権が存在する間、本メソッドがコールされ続けるため、
   * 1ターン内に複数枚カードを購入したい場合は、この仕組みを利用すること。
   *
   * カードを購入した場合は戻り値としてtrueを返却してください。
   * 購入しなかった場合はfalseを返却してください。
   *
   * @param {!Player} player プレイヤー
   * @returns カードを購入した場合true、そうでない場合false
   */
  this.processBuyPhase = function(player) {

    // 財宝カードをすべてプレイ
    player.playAllTreasure();

    // ① 8金以上、かつデッキ内の金貨が1枚以上の場合、属州を購入
    if(player.coinNum >= 8 && player.countByCardNameInDeck(Card.NAME.GOLD) >= 1) {
      // 属州が購入を試みる。(サプライに存在しなかったなどの理由で購入できなかった場合は、②移行のロジックに移る)
      if(player.tryBuy(Card.NAME.PROVINCE) === true) {
        return true;
      }
    }

    // ② 6金以上の場合、金貨を購入
    if(player.coinNum >= 6) {
      if(player.tryBuy(Card.NAME.GOLD) === true) {
        return true;
      }
    }

    // ③ 4金以上かつ、デッキの鍛冶屋が1枚未満の場合は鍛冶屋を購入
    if(player.coinNum >= 4 && player.countByCardNameInDeck(Card.NAME.SMITHY) < 1) {
      if(player.tryBuy(Card.NAME.SMITHY) === true) {
        return true;
      }
    }

    // ④ 3金以上の場合、銀貨を購入
    if(player.coinNum >= 3) {
      if(player.tryBuy(Card.NAME.SILVER) === true) {
        return true;
      }
    }

    // 何も購入しない
    return false;
  }

ダウンロード直後のsetting.jsは①~④で示した手順で購入対象を決定しています。
例として以下のように編集すれば購入手順を変更できます。

  • 8金出た場合、金貨がデッキになくても属州を買いたい
    if(player.coinNum >= 8 && player.countByCardNameInDeck(Card.NAME.GOLD) >= 1) {
    を以下に修正する。
    if(player.coinNum >= 8 && player.countByCardNameInDeck(Card.NAME.GOLD) >= 0) {

  • 鍛冶屋を2枚デッキにいれたい
    if(player.coinNum >= 4 && player.countByCardNameInDeck(Card.NAME.SMITHY) < 1) {
    を以下に修正する。
    if(player.coinNum >= 4 && player.countByCardNameInDeck(Card.NAME.SMITHY) < 2) {

  • 鍛冶屋ではなく学者を買いたい(後述の学者をプレイするための設定も必要)
    if(player.coinNum >= 4 && player.countByCardNameInDeck(Card.NAME.SMITHY) < 1) {
    を以下に修正する。
    if(player.coinNum >= 5 && player.countByCardNameInDeck(Card.NAME.SCHOLAR) < 1) {

  • 公領を買いたい
    ②と③の間に以下を記載する。

    //  5金以上の場合、公領を購入
    if(player.coinNum >= 5) {
      if(player.tryBuy(Card.NAME.DUCHY) === true) {
        return true;
      }
    }    

アクションカードプレイ手順の設定方法

アクションカードプレイ手順はprocessBuyPhaseという関数内に設定します。

  /**
   * アクションフェーズでの処理を設定する。
   *
   * 本メソッドがコールされたときのアクションカードのプレイは1枚のみとすること。
   * 1ターンの間でも、手札にアクションカードがありアクション権が存在する間、本メソッドがコールされ続けるため、
   * 1ターン内に複数枚アクションカードをプレイしたい場合は、この仕組みを利用すること。
   *
   * アクションカードをプレイした場合は戻り値としてtrueを返却してください。
   * プレイしなかった場合はfalseを返却してください。
   *
   * @param {!Player} player プレイヤー
   * @returns アクションをプレイした場合true、そうでない場合false
   */
  this.processActionPhase = function(player) {

    // 鍛冶屋のプレイを試みる。(手札に鍛冶屋があればプレイされる)
    if(player.tryPlayCard(Card.NAME.SMITHY) === true) {
      return true;
    }

    return false;
  }

ダウンロード直後のsetting.jsはアクションカードは鍛冶屋のみプレイします。 例として以下のように編集すればアクションカードのプレイ手順を変更できます。

  • 学者をプレイしたい(上述の学者を購入する手順を記載も必要) if(player.tryPlayCard(Card.NAME.SMITHY) === true) {
    を以下に修正する。
    if(player.tryPlayCard(Card.NAME.SCHOLAR) === true) {

ゲーム終了条件の設定方法

ゲーム終了条件はisGameEndという関数内に設定します。

/**
 * ゲーム終了条件を設定する。
 * 各プレイヤーのターン終了時にコールされる。
 * ゲーム終了条件を満たしている場合はtrueを返却すること。
 * @returns {boolean} ゲーム終了条件を満たしている場合true、そうでない場合false
 */
Setting.isGameEnd = function() {

  // プレイヤー1のデッキ内の属州枚数が4枚になったらゲーム終了
  if(board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 4) {
    return true;
  }

  return false;
}

ダウンロード直後のsetting.jsは属州4枚購入したタイミングでゲームが終了します。 例として以下のように編集すればゲーム終了手順を変更できます。

  • 属州5枚購入したらゲーム終了としたい
    if(board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 4) {
    を以下に修正する。
    if(board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 5) {

  • 属州4、または属州3公領2でゲーム終了としたい if(board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 4) {
    を以下に修正する。

  if((board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 4)
    || 
    (board.players[0].countByCardNameInDeck(Card.NAME.PROVINCE) >= 3
      && board.players[0].countByCardNameInDeck(Card.NAME.DUCHY) >= 2)) {

最後に

(いるとは思えませんが)もし本シミュレータについて質問などがある場合、TwitterやDiscordでDMいただければと思います。 ソースに対するツッコミなども歓迎です。

他にもドミニオンオンラインのゲームログと観戦チャットを時系列で横に並べて取得するGoogle Chrome拡張機能なんかも作ったりしてます。以下みたいなイメージです。

f:id:yune_dominion:20211206222655p:plain

有名人同士の試合だと観戦者も多くチャット欄が盛り上がるので記録のために使っています。 こちらについてもそのうちGitHubで公開できたらいいなと思っています。