今回は「グロタンカット」と「ラマヌジャン革命」を実装します。

「グロタンカット」と「ラマヌジャン革命」

素数大富豪には「グロタンカット」と「ラマヌジャン革命」があります。革命なら普通の大富豪でもありますね。同じ数字のカードを4枚出したらカードの強弱が逆になります。そしてもう一度同じ数字のカードを4枚出したら「革命返し」となり、カードの強弱は元に戻ります。

素数大富豪では1729が出されるとラマヌジャン革命が成立します。この数は数学者シュリニヴァーサ・ラマヌジャンの逸話からタクシー数とも呼ばれています。また素数ではないにもかかわらず確率的素数であると判定される数でもあります。

また素数大富豪では57を単体で出すことができます。57が出ると即座に場が流れ、57を出した者が次の親になります。「グロタンカット」とも呼ばれます。

57は素数ではないのですが、ドイツ出身のユダヤ系フランス人の数学者であるアレクサンドル・グロタンディークが素数に関する一般論について講演をした際に、素数ではない57を誤って選んだという逸話があります。数学者でもこんなミスをするのですね。そのことから57は素数として出すことができ、しかも57が出されると即座に場が流れ、このカードを出した人が親になるという「グロタンカット」というルールができたのです。

グロタンカット

まずグロタンカットの実装から考えます。プレイヤーからカードが素数として出されたときに57であれば正当な手とみなして、同時に場を流す処理をすればよいのです。またコンピュータが次の手を考えるときに2枚出しのときは57も選択肢のひとつにすればよいのです。

グロタンカットはどのタイミングでやればいいのでしょうか? ただでさえ素数にしても合成数にしても人間業では見つけることが困難な手をコンピュータが繰り出してくるので、57が出せるときはランダムで決めます。これ以上強くなってはどうやっても勝つことはできないので、あまり深くは考えないことにします。

プレイヤーによって決定ボタンが押されたときの処理

決定ボタンが押されたときの処理ですが、いくつかのメソッドにわけました。

まず出されたカードの枚数とカードによってつくられる数が正しいかどうかを判定する部分をCheckPlayerCards0メソッドとして分離しました。これはラマヌジャン革命が起きたときに処理を変えなければならないので、いまの段階で分離しておきます。

そのあとグロタンディーク素数である57が出されたのかを確認します。素数として57が出された場合は強制的に場が流されます。カードが「素数」57として出されたのかのチェックと場を流す処理はCheckPlayerGrothendieckメソッドのなかでおこなわれています。

そのあとプレイヤーとコンピュータが持っているカードがなくなっているかどうかも別のメソッドをつくって処理をしています。

CheckPlayerCards0メソッドでは出されたカードの枚数とカードによってつくられる数が出さなければならない最低の数を上回っているかだけ確認しています。これはラマヌジャン革命時には最大の数を下回っていなければなりません。すぐに書き換えなければならないメソッドです。

グロタンディーク素数が出されて場を流す処理

そのあとグロタンディーク素数である57が出されたのかを確認します。素数として57が出された場合は強制的に場が流されます。カードが「素数」57として出されたのかのチェックと場を流す処理はCheckPlayerGrothendieckメソッドのなかでおこなわれています。

プレイヤーとコンピュータの勝敗判定のメソッドを示します。

勝負が決着せずゲーム続行の場合、プレイヤーになにを出すべきかをLabelに表示させる処理を示します。この部分もラマヌジャン革命がおきると書き換えが必要です。

コンピュータがグロタンディーク素数を出した場合の処理

次にコンピュータがグロタンディーク素数を出した場合の処理を考えます。

現状では57は素数ではないのでコンピュータにとっては選択肢に入っていません。そこでコンピュータが出すカードを探すときに57も選択肢に加えます。

出せるカードの番号を調べる処理はSearchNumberメソッドでおこなっていますが、ちょっと長いので分割しました。ラマヌジャン革命がおきると書き換えないといけないので本体部分は短くしておきたいです。

別メソッドにまとめたカードの番号の順列を生成する処理をする部分です。

CheckWinPutCardsメソッドはResultCardsにオブジェクトに格納されている手でカードを全部出すことができるかどうかを判定します。

次の手で勝利を決めることができないなら、次の手が存在する方法で手を作らなければなりません。CheckNextPutCardsはその候補手を選択した場合、次の手が存在するか調べます。

候補手が複数存在する場合、偶数のカードばかり残るとやりにくいのでコンピュータには偶数のカードを使う手を優先させます。消費する偶数のカードがもっとも大きいものを次の一手としますが、それが複数存在する場合は乱数で決めます。奇数のカードしか使えない場合はそのなかからランダムに選びます。

指定された枚数のカードのすべての組み合わせからコンピュータの候補手をすべて取得する処理をするのがGetCardsCanPutメソッドです。ここでは数字の組み合わせが素数になるか、合成数の場合は合成数出しができる組み合わせを探します。この処理はGetPutCompositeNumberメソッドで行なっています。

またグロタンカットとラマヌジャン革命に対応させるために57 と 1729 は素数として扱っています。

合成数の場合、GetPutCompositeNumberメソッドで「合成数出し」ができるか調べます。

ラマヌジャン革命

ラマヌジャン革命ですが、1729も素数出しの場合は素数として判定することようにアルゴリズムを変えます。またラマヌジャン革命が成立したらカードの強弱を変えるとともに、背景色を赤に変えます。赤は革命の色だからです。

Ramanujanプロパティ

こんなプロパティをつくります。最初はfalseにしておき、ゲーム中にラマヌジャン革命がおきたらtrueにします。もう一度ゲームをするときはfalseに再設定しなければなりません。

プレイヤーに見せるメッセージの変更

プレイヤーが素数として1729を出したとき、ラマヌジャン革命がおきます。すでにラマヌジャン革命の状態のときは元に戻されます。それから次に出すカードをフィールド変数minValueに代入してそれ以上の数を出すようにしていました。革命のときはminValueよりも小さなカードを出さなければなりません。

変数名をいまさら変えられないので、minValue「以上」ではなく「より大きい」数を出さなければならず、革命時にはminValue「より小さい」数を出さなければならない仕様に変更します。

コンピュータが1729を出してきたときの処理

コンピュータも1729を出してくる可能性があります。

出されたカードに対する評価の変更

革命がおきたら出せるカードを選ぶ方法も変えなければなりません。まずプレイヤー側ですが、CheckPlayerCards0メソッドを書き換えなければなりません。革命勃発時はコンピュータが作った数より小さくなければなりません。

コンピュータがカードを選ぶ処理の変更

次にコンピュータがカードを選ぶときですが、これも同様にRamanujanプロパティがtrueかfalseで処理を切り分けなければなりません。

プレイヤーのターンになったときに表示されるLabelのテキストも変更しなければなりません。これはプレイヤーが出すカードを決定するボタンをクリックしたときに最後に行なわれる処理です。

これはプレイヤーがパスをしたときの処理です。