属州場の鍛冶屋ステロ
そんなこんなであんなどんな、なんなんだ…
どうやら、鍛冶屋ステロについてのシミュレーション結果が貼られようとしているわけです。
最初期の計測なので、シミュレーターの骨格が変わっており、それだけで結果が変わる可能性もあります。
平均は14.57T程度と、だいたい巷で言われている鍛冶屋ステロの速度くらいになっていますね。
5-2については現状特に検証していませんが、わりと(1Tくらい??)遅くなる気がします。
アルゴリズムの詳細を示します。
鍛冶屋ステロのアルゴリズムは、以下のようにしました。
Sub actionAlgorithmProvinceSmithyAdjusted()
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim smithyInDeck As Boolean
smithyInDeck = False
If actionNumber = 0 Then
'Do Nothing
Else
If cardInHand(trnP, 20) >= 1 Then
j = howManyCoins(2) '手札金量を求める
If j >= 8 Then '8金以上
If deckNumber(trnP) = 0 Then
actionCardNotPlayed = True
ElseIf deckNumber(trnP) <= 5 Then 'デッキが5枚以下
For i = 1 To deckNumber(trnP) Step 1
If Worksheets("Sheet1").Cells(1 + opFld * trnP, i).Value = 20 Then
smithyInDeck = True 'デッキ内に鍛冶屋がある
Exit For
Else
'Do Nothing
End If
Next
If smithyInDeck = True Then
actionCardNotPlayed = True
Else
k = howManyCoins(1) 'デッキ内金量を求める
If deckNumber(trnP) > 3 Then
If k < 8 Then
Call playAction(20)
Call playSmithy
Else
actionCardNotPlayed = True
End If
ElseIf deckNumber(trnP) = 3 Then
If k < 6 Then
Call playAction(20)
Call playSmithy
Else
actionCardNotPlayed = True
End If
ElseIf deckNumber(trnP) = 2 Then
If k < 3 Then
Call playAction(20)
Call playSmithy
Else
actionCardNotPlayed = True
End If
Else
If k = 0 Then
Call playAction(20)
Call playSmithy
Else
actionCardNotPlayed = True
End If
End If
End If
Else
Call playAction(20)
Call playSmithy
End If
Else
Call playAction(20)
Call playSmithy
End If
Else
'Do Nothing
End If
End If
End Sub
20番は、鍛冶屋に割り当てられた番号です。
If Worksheets("Sheet1").Cells(1 + opFld * trnP, i).Value = 20 then の部分の意味を補足しておきます。
「Sheet1」は骨格部分で説明した、データ格納用のシート1です。
次の部分に、セルの行、列の順に座標が書かれています。
行の部分は、「1」がデッキを表します。「opFld」は、対戦相手のフィールドとの座標のズレ(同時に、1プレイヤー分のシート1に占めるエリアの行数)を意味しているパブリック変数で、これは20行相当になります。そして、「trnP」は現在のターンプレイヤーを意味するパブリック変数で、プレイヤーにあわせ、0と1を切り替えられます。つまり、プレイヤー0のターンであれば1行目、プレイヤー1のターンであれば21行目を表していて、これらを同一のアルゴリズムで賄うための表記になっています。
列の部分は、繰り返し用のローカル変数「i」で定義されており、デッキの底から順に一番上まで検索されていく中で、逐次切り替わっていきます。
最後のValueは内容を表していて、「= 20」だけであればこのセルの内容に20(鍛冶屋)を代入せよということですが、ここは条件文の中なので、このセルの内容が20であれば、ということになります。
playAction(20)は、アクションを手札からプレイエリアに持って行く動作そのものです。引数として渡された番号のアクションカードを、実際にCardInHandからCardInPlayに移動させています。
playSmithyは、鍛冶屋のアクションとしての効果、+3カードを引く、の処理になっています。
これらが分割されている理由ですが、召喚や玉座の間等、手札からのアクションプレイが行われるとは必ずしも限らない以上、分割していないと、それらすべてに対応する鍛冶屋を記述しなくてはならなくなるためです。
現在の仕様であれば、playAction(20)を、例えばplayActionBySummon(20)にするだけで、後半のplaySmithyを複数用意する必要がありません。意思決定が介在するカードの場合は、使用元次第では挙動が変化する可能性もあり、この限りではありませんが、少なくとも全く同じ動作については、先頭だけが違うプロシージャを用意する必要はないです。
actionCardNotPlayedは、アクションを敢えて使わなかったターンの出力用に使う、パブリック変数です。それ以外の意思決定にも絡んでくる可能性があるので、作ってあります。
先頭の、アクションが0ならば、という区切りはあまり意味はないですが、アクションアルゴリズムを連結して使う場合等も想定されたため、いちおう入れています。
手札内にある鍛冶屋を使わない判断に必要な条件として、手札内の金量が8金以上であることが第一、デッキが5枚以下であることが第二(0枚ならば、以下を調べない)、デッキ内に鍛冶屋があることが、そうでないならばデッキ枚数に応じた金量があることが、第三の条件になります。
第一の8金以上の条件ですが、金貨を持っていないかつ6金以上、も加えた方がいいかもしれないです。
第三の、デッキ枚数に応じた金量、はてきとうに決めているので、それよりも回転を重視すべきである可能性もあります。
現状は4-5枚ならば8金以上、3枚ならば6金以上、2枚ならば3金以上、1枚ならば1金以上で鍛冶屋を使いません。2枚以下では、回転した上に鍛冶屋や金貨を引いてしまうリスクを考えて、小さ目にしています。
また、考慮されていない条件ですが、手札の鍛冶屋が2枚あるときは、よりデッキの底が回転しない選択を取るべきな気もしますね…そうなると、デッキ枚数が3枚と2枚の間に、手札の鍛冶屋が2枚(以上)ならば、を加えることになりますね。
…いや、そもそも、デッキ内探索をする必要がないので、それ以前に分岐すべきなのか…
鍛冶屋ステロの購入アルゴリズムは、以下のようになってます。
Function buyAlgorithmProvinceSmithy2Split(i As Integer) As Integer
Dim j As Integer
If i >= 8 Then
If cardOwned(trnP, 2) >= 1 Then
j = 12
Else
j = 2
End If
ElseIf i >= 6 Then
j = 2
ElseIf i >= 4 Then
If turnNumber <= 2 Then
j = 20
ElseIf turnNumber = 3 Then '分割管理
If actionNumber > 0 Then '鍛冶屋を撃っていない
j = 20
Else
j = 1
End If
ElseIf turnNumber >= 5 Then
If cardOwned(trnP, 20) <= 1 Then
j = 20
Else
j = 1
End If
Else
j = 1
End If
ElseIf i >= 3 Then
j = 1
Else
j = -1
End If
buyAlgorithmProvinceSmithy2Split = j
End Function
1番は銀貨、2番は金貨、12番は属州、20番は鍛冶屋です。また、-1は購入パスです。
3ターンに鍛冶屋を撃っていない場合、鍛冶屋を購入出来れば、鍛冶屋を連続して撃てる可能性の上がる、所謂「分割管理」状態を作り、3ターン鍛冶屋撃ちに追いすがることができます(と考えられていますが、100%の確信があるわけではないです)。
分割でない場合の鍛冶屋2枚目は、5ターン以降で4金以上が出たターンにしていますが、6金以上が出続けた場合は、2枚目を導入しない可能性はあります。
前後してしまいますが、鍛冶屋を敢えて使わないことの是非を調べるため、鍛冶屋が手札にあれば、無条件に仕様するアルゴリズムのものも、10000回検証しています。
以下にその結果を示します。
平均ターンの変化としては、約0.03ターンと、分散の大きさに比べると、あまり確実な違いとまでは言えない気もしますが…
参考までに、上の通常の鍛冶屋ステロ検証で、アクション使用阻止が働いたのは、10000ゲーム中516回だったので、簡単のため1ゲーム1回の判定とすると、約5%のゲームにおいて、この判断が働いていることになります。
0.03ターンといえども、5%程度の話と考えると、その中では0.6T程の差をもたらしていると見ることができるかもしれないです。(ただ、そのターン以降の分岐にどのように波及していっているかを考えると、単純には言い切れない気もしますね。)
本来であるならば、何らかの形でこの状況を抽出して、比較するべきなのですが、稀なパターンを集積するのはわりと大変な気がするので、現状は不十分な検証となってしまっています。
いちおう、アクションアルゴリズムは以下のようになります。
Sub actionAlgorithmProvinceSmithy()
If actionNumber = 0 Then
'Do Nothing
Else
If cardInHand(trnP, 20) >= 1 Then
Call playAction(20)
Call playSmithy
Else
'Do Nothing
End If
End If
End Sub
購入アルゴリズムは、上記と同じものを使っています。
(追記)金貨のない8金において、属州を購入するパターンも検証しておいたので、追記しておきます。
こちらは、アクションアルゴリズムにおける回転防止は導入しております。
あまり極端な差はないですが、これを見る限りでは、金貨のないうちに属州を買うのは、全体で平均して0.05T程のマイナスをもたらすようです。
例によって残りコイントークンの平均は、金貨のない8金で属州を購入した判断の回数なので、全体では1621回あったことになります。
お金プレイと比較して、鍛冶屋ステロの場合、回転が速く、金貨の枚数が減るために、その少ない金貨が更に少なくなると、マイナスの影響が無視できなくなってくるのではないかと思われます。(以上追記)
(追記)鍛冶屋を初手の1枚しか買わないものも、ついでに検証しておいたので、載せておきます。
アクションアルゴリズムにおける回転防止は取り入れています。購入アルゴリズムにおいて、初手以外の鍛冶屋の購入条件を一切設けない形にしています。
2枚の検証が最適なタイミングとは必ずしも言えませんが、それでも0.15T程平均が後退しているようです。
分散は小さくなっているので、鍛冶屋を2枚入れることによる被りの影響は、沈みの影響よりも大きいということなのかもしれないですね。(以上追記)
(追記)鍛冶屋の総評としては、純粋にドローだけに性能を振り切っているため、4コストとしては高いポテンシャルを持っているのだが、調整力が皆無なので安定性に欠けていて、分散が大きく、悪いパターンが平均を押し下げている、というところでしょうか。
このため、調整力のあるイベントとの複合で平均ターン数の改善が見込まれます。
また、鍛冶屋は、回転によってより少ない金貨を効率よく運用できるようになることが強みである反面、弱いカードである屋敷の登場回数も同様に多くなります。屋敷を除去できる組み合わせでも、出力の向上が見込まれるでしょう。(以上追記)
とりあえずこんなところで、勘弁してやってください…--…