Kurter's Workshop

かーたーによるドミニオンのシミュレーション

Bone と Hone ってとっても近い…よね

あいからわずかーたーのギャグセンスは光ってるなぁ…

 

…ということで、シミュレーターのコア部分の説明の続きです。

今回は、プログラム部分です。

書いていて思ったけど、メチャクチャつまらないので、仮にどうしても読む必要性に駆られたら読むくらいで、マジで読まなくていいです…

 

 

メイン関数

実際に試行を行う関数。

各種変数の初期化・読み込み、シートのリセット、ゲームを指定回数まで繰り返す、試行終了時の結果出力、を行います。

 

属州4枚獲得ターン数や、公領20T獲得枚数等、調べたい検証を、条件を変えて複数回試行するため、テンプレートを入力した上位の関数を、幾つか用意しています。

 

 

入力関数群

個別の検証を行う上で、必要な関数、変数を指定する関数群。

現状はインターフェイスを設けておらず、直接プログラム画面で入力したり、コメント化/コメント解除したりすることで、間に合わせております。

以下に、特筆すべき関数を挙げておきます。

 

サプライ定義関数

各種カード枚数の初期化や、各種探索において、そのゲームで使用されていないカードを調べてしまうことは、ロスであると考えられ、また、これはカードの種類が膨大である程顕著になってくる傾向と思われます。

そのため、サプライに存在するカードを示す、サプライカード配列を噛ませておくことで、1段階足踏みをするものの、最小限の繰り返し回数で済むようにしました。

サプライ定義関数は、その試行で使用するカードを、あらかじめサプライカード配列に入力しておくものです。

 

アクションアルゴリズム指定関数

使用するアクションアルゴリズムを呼び出します。

また一部検証では、細かく指定したいアクション使用の条件を、変数の代入を用いて操作します。

 

購入アルゴリズム指定関数

使用する購入アルゴリズムを呼び出します。

購入フェイズ関数から、現在の金量変数、コイントークン一時預かり変数(操作の都合上、コイントークンは適宜払えるようにしているため、購入フェイズに獲得したコイントークンは、直接コイントークンに加算できない)、過払い金量変数を受け取り、必要なものを購入アルゴリズムに代入します。

また一部検証では、細かく指定したい購入決定の条件を、変数の代入を用いて操作します。

購入物の出力のため、購入アルゴリズムから受け取ったカード番号等の情報を、購入フェイズ関数に返します。

 

その他関数

試行タイトル入力関数、デバッグ用のパラメーター指定関数、特殊初手生成アルゴリズム指定関数(特定の引きについて検証したい場合等)、ターン開始時アルゴリズム指定関数(持続等)、財宝アルゴリズム指定関数(購入フェイズにおいて、簡単のため、通常は財宝のプレイを行わずに手札金量を求めるが、特殊財宝がある場合や、手札枚数等が関係してくる場合等、プレイが必要な場合に指定する。)、ナイトアルゴリズム指定関数、クリーンアップアルゴリズム指定関数、があります。

 

 

ゲーム実行関数

 1ゲームを実行する関数。

基本的には、初手を生成した後、ゲーム終了が決定するまで、30ターンまで以下のフェイズ進行を繰り返します。

ただし、対戦相手が存在する場合、ターンの進行が一人回しとは異なります。

ターンに関する変数として、ターン数を示すパブリック変数(turnNumber)、ターンプレイヤーを示すパブリック変数(trnP)、先攻プレイヤーを示すパブリック変数(firstPlayerNumber)、ターン切り替えの有無を決めるローカル変数(turnChangeKey)、ターン数の進行に作用するローカル変数(trunChangeControl)があります。

対戦相手がいる場合、turnChangeKeyが1になり、一人回しでは0になります。

turnNumberを1ループ毎に1ずつ増やしていく仕様のため、ループ処理時に先攻プレイヤーのターンであった場合は、turnNumberから1を引き、後攻プレイヤーのターンであった場合は、0を引くようにします。また、一人回しの場合は、毎回0を引くようにします。この役割がtrunChangeControlです。

trunChangeControlは、turnChangeKey ‐ trunChangeControl の形で、対戦相手がいる場合には(0→1→0→1の)Inverseを求めるように、また、一人回しの場合にはずっと0のままであるようにしています。

同時に、trnPも turnChangeKey - trnP としてInverseを求めるか、0のまま推移する形になっています。(このため、現状では2プレイヤー以降の導入はできず、また、意味はないがプレイヤー1だけで一人回しを行うことはできません)

一見、trnPだけでよいように思われますが、先攻プレイヤーがどちらになるか次第ではこれらの値は逆になるため、別の変数を用意しています。

また、trnPは、カードの手札内枚数cardInHand(プレイヤー番号、カード番号)のような二次元配列について記述する際、配列一つ、記述一種類でごく簡単に記述するような仕様(例:cardInHand(trnP, 0):現在のターンプレイヤーの手札内の銅貨枚数)とするものであるために、プレイヤー番号には0と1を使用しています。

ゲーム終了条件が満たされ、ループが打ち切られた後は、各種出力を行い、メイン関数に返ります。

以下に、重要な下位の関数を挙げておきます。

 

初手生成関数

ランダムな初期デッキを生成する場合は、ディスカードに銅7枚屋3枚を生成してから5枚引くことで、(リシャッフル処理が入るために) 特殊な処理をすることなく1ターン目の状況を作り出せます。

しかし、多くの検証においては初手を固定するため、4-3、3-4、5-2、2-5、のうち、指定されたパターンに合うように、デッキに銅7屋3を生成してから、5枚引きます。

また、より特殊な、3-4T等の引きを生成する場合には、特殊初手生成アルゴリズム指定関数を呼び出します。

 

アクションフェイズ関数

ターン開始時アルゴリズム指定関数、アクションアルゴリズム指定関数 を呼び出します。

また、ステロ検証において、アクション数がゼロであれば、アクションがプレイされたとして出力します。

 

購入フェイズ関数

 手札の金量を求め(それを出力し)、購入回数がゼロになるまで購入アルゴリズム指定関数を呼び出し続けます。

返された数値に当たるカードを購入すると同時に、それを出力します。

このとき、金量がコストの分だけ軽減された上で再代入され、足りない場合はコイントークンが消費されます。

また、購入パス(-1)が返された場合は、ループ処理を打ち切ります。

このような処理にしているのは、そうでなくては購入アルゴリズムの側の表記が大変複雑になり、また、複数の購入物を返すことは、変数を用意する都合上、大変面倒に感じられるためです。

上記の指定関数側にも書きましたが、代入する変数はこの他にもコイントークン一時預かり変数、過払い金量格納用の変数があります。(入力変数の形だが、ByReferenceなので値が変化して返ってくる)

上述しましたが、コイントークン一時預かり変数は、コイントークンが財宝プレイ後ではなく、購入物決定後に支払われる形を取っているため、商人ギルドや香辛料等で、獲得したコイントークンを、即時に使用できないようにするためのものです。

コイントークンの仕様を実際の物と違わせている理由として、上記と同様の、購入アルゴリズムの平易化が挙げられます。

1ループ1個しか購入物を決定しない仕様(例:購入数2として、13金(8金以上)で属購入→5金+トークン3で属購入)は、手札金量の確定時に予めコイントークンの使用数を確定させることと矛盾しており、併存できません。

また、イベント及びプロジェクトの購入を、通常のカードと異なるものとしつつも、同じ形で扱うため、購入アルゴリズムにおいて、イベント購入時には1000番台の修飾(0番のイベント焚火の購入時は1000番、1番のイベント借入購入時には1001番、等)を、プロジェクト購入時には2000番台の修飾をそれぞれ付与し、こちら側で区別した上で、Mod 1000を求めて各種イベント、プロジェクトの購入、およびその出力を行います。

 

クリーンアップフェイズ関数

 基本的には手札及びプレイエリアの全カードをディスカードし、5枚のカードを引くだけです。

ですが、貨物船の持続条件を満たしているかの判断や、探検、保存等の効果処理等、ここで処理しなくてはならないものもあります。

 

 

各種アルゴリズム

初手生成、ターン開始時行動、アクションプレイ、購入、等を、検証したい状況毎に場合分け定義した一連の関数群です。

これらは、個別の記事で言及することになると思われるため、こちらでは割愛します。 

 

 

アクション等プレイ定義群

各種アクションのプレイ時効果処理および、意思決定の介在する場合には、その場合分け等を定義した関数群です。

財宝等にも同様の関数があります。 

 

 

イベント等定義群

イベントの購入時効果、プロジェクトの使用時効果等を定義する、一連の関数群です。

 

 

行動定義群

一般的なドミニオンの行動をあらかじめ定義しておき、アクションのプレイ等の記述において、より直観的な構築ができるようにするものです。

アクションのプレイ、カードのドロー、リシャッフル、ディスカード、廃棄、購入、獲得、金量計算、等の定義が含まれています。

 

 

出力定義群

各ターン毎に、アクションのプレイ、購入物の内容、屋敷の廃棄、バグの内容、等の出力を定義する 部分と、各ゲーム毎に、終了ターン数等の出力、出力シートへの結果の移動を定義する部分、そして、各試行毎に、最終的なディティールの出力を定義する部分、から構成されています。

 

 

カードライブラリ

 カードの種類に固有の番号から派生した、各種カードの性質を表す変数(本当は定数だが、パブリックな配列変数の形を取ることが、扱う上で最も簡単と思われる)を初期化します。

例えば、番号 0 について言及すると、

  • カード名:"Copper" ("playCopper"等を直接指定する場合に使用する)
  • カード表示名:"銅" (出力時に使用する)
  • カードコスト:0
  • カードタイプ:1 (財宝を1、勝利点を2、アクションを4、のようにして、その和で以って性質を表す。これを取り出す際は、例えば Mod 2 (2で割った余り)が 1 であれば財宝の性質を持ち、0 であれば財宝の性質を持たないことになる。)
  • サプライカード数リセット:60 - プレイヤー数 * 7 (サプライカード枚数自体はゲーム中で変化する変数。こちらはゲーム開始時のリセット用)

以上が含まれています。

また、イベント及びプロジェクトについて、表示名とコストの初期化を行います。

 

 

 

だいたいこんなところです…´`…