合成数出しにおける指数表記にも対応させようとしたらコードを書き直す部分が多くなったので全部を掲載します。

Form1クラス以外のクラス

Form1クラス以外のクラスを示します。

Permutationクラス

Permutationクラスはコンピュータが出すカードを決めるときに必要な番号の順列を取得するためのものです。

PrimeNumberFromCardsクラス

PrimeNumberFromCardsクラスはカードによって作られた素因数を表わすためのものです。

CardsCandidateクラス

CardsCandidateクラスはコンピュータが次に出すカードの候補を扱うためのものです。

AlertControlクラス

AlertControlクラスはカードの選択ミスを指摘するメッセージを表示するためのものです。これまでのものと違ってTextプロパティを追加しています。

Form1クラスの初期化

フィールド変数

Form1クラスのフィールド変数は以下のとおりです。

コンストラクタにおける処理

コンストラクタにおける処理を示します。ラベルのTextを空文字にしてユーザーコントロールとボタンを初期化します。

ボタン、ユーザーコントロールの初期化

ボタンを初期化します。はじめにクリックできるのは[ゲームスタート]のボタンだけです。

各ユーザーコントロールを初期化します。コントロールのボタンをクリックしたときのイベントに対応できるようにします。アプリケーションが開始されたときはどのコントロールも表示されません。必要なときに表示され、ボタンをクリックすると消えます。

プロパティ

革命が起きているかを示すプロパティです。ゲーム開始時は革命は起きていない状態に戻されます。

プレイヤーが出したカードの数字を表示するプロパティです。PlayerText2に表示されている文字列を解析することで素数がだされているかどうか、合成数出しの計算が合っているかどうかを調べます。

コンピュータが出したカードの数字を表示するプロパティです。

ゲーム開始時の処理

ゲームの開始時の処理を示します。ゲームをリセットしてカードを新しく生成しシャッフルしたあとカードを配ります。そして最後にShowCardsメソッドでカードをフォーム上に表示させます。

ゲームをリセットする

ゲームの途中でボタンが押されるかもしれないので、いったん両者が持っているカードをクリアします。同時に素因数場に出されているカードの情報もクリアします。

ゲーム開始直後であれば好きな枚数で好きな数を出せるのでフィールド変数 CountCardsPutMustとMinValueCardsには -1を設定します。革命はもとに戻します。

カードの生成とシャッフル

カードを生成するメソッドです。

カードをシャッフルするメソッドです。

カードを配るメソッドです。カードを配り終えたら見やすくするために番号が小さい順に並べます。

カードを表示する

カードを表示するために呼び出されるメソッドです。プレイヤーとコンピュータが持っているカードの位置、場と素因数場に出されているカードの位置を指定します。

カードを描画する処理を示します。コンピュータのカードは設定で表向きになるように設定されている場合は数字が見えるように、されていない場合は裏向きで表示されます。

カードを出す処理

プレイヤーのカードがクリックされたときの処理を示します。カードを大きい順に並べ替えたリストを取得することで番号が見える部分をクリックするとそのカードが出された処理がおこなわれます。

カードを出す処理です。カードが出される場所はどこかをCheckBoxの状態から判断してカードの表示位置を変更します。それと同時にPlayerText1プロパティまたはPlayerText2プロパティに数字を追加します。

×ボタンが押されたときの処理を示します。PlayerText1プロパティまたはPlayerText2プロパティに文字列” × “を追加します。

^ボタンが押されたときの処理を示します。PlayerText1プロパティまたはPlayerText2プロパティに文字列” ^ “を追加します。

[一枚取る]ボタンがクリックされたときの処理を示します。出したカードはいったん返却されます。

[クリア]ボタンがクリックされたときの処理を示します。場と素因数場に出されているカードをプレイヤーの元に戻します。

プレイヤーが出したカードは正しいか?

[決定]ボタンがクリックされたときの処理を示します。合成数出しをする場合だけ確認のメッセージを表示します。また明らかに出されているカードが間違っている場合はエラーを示すメッセージを表示させ、プレイヤーにカードの選択のやり直しを要求します。

素数かどうか、合成数出しの計算が合っているかどうかは実際にカードを出す処理のあとおこなわれます。間違っていた場合、設定によってはペナルティーの対象になります。

事前チェック

プレイヤーが場に出したカードをチェックするメソッドです。[確認]ボタンがクリックされたときなにも出されていない、15桁を超える数になっている(素数の判定に時間がかかるので制限している)、親が出したカードの枚数と違っている場合などはやり直しです。

プレイヤーが素因数場に出したカードをチェックするメソッドです。合成数出しなのに複数の素数が指定されていない、たとえば2×5のつもりで25になっていた、×や^の間に数字が入っていないなど実際のゲームではありえないミスの場合はペナルティーではなくやり直しの対象となります。

エラーを表示するユーザーコントロールを表示するメソッドです。実行されると必ずtrueを返します。

カードの番号をつないで値をつくります。

確定後の処理

[決断]ボタンがクリックされ、確認のメーセージに対しても[OK]がクリックされたときの処理を示します。ここでは出したカードが正しいかどうかの判定がおこなわれ、そのあとコンピュータの手が示されます。

ボタンの有効無効の切り替え

EableButtonsメソッドは判定中にプレイヤーによって他のボタンが押されないようにボタンの有効無効を切り替えるためのメソッドです。

勝敗判定

手札がすべてなくなっていればそのプレイヤーの勝利です。

プレイヤーが出したカードが正しいかどうかの判定をおこなうメソッドです。

特殊な数への対応

グロタンディーク素数切り(グロタンカット)かどうかを判定する処理を示します。57が出されていても合成数として出されている場合はなにもおきません。グロタンカットの場合、強制的に場が流されます。

ジョーカーが1枚だけ出されたときは場が流れますが、その判定をおこなっています。

素数として出されたカードの判定

プレイヤーが出したカードが素数として出された場合はCheckPlayerCards1メソッドによって判定がおこなわれます。素数であればカードをCardsPutByAll(非表示なだけでまだ流されていないカード)に移動させます。素数でない場合、カードは返却され、設定によってはペナルティーとして出したカードと同数枚カードを取らされます。

その値が素数かどうかを調べるためのメソッドです。

合成数として出された数の判定

プレイヤーが出したカードが合成数として出された場合はCheckPlayerCards2メソッドによって判定がおこなわれます。CalculatePlayerText2メソッドによってPlayerText2プロパティの文字列が評価され、合成数が生成されます。素因数場に出された素因数のなかに素数でないものが入っていた場合は最初にみつかった非素数が負数として返されます。

CalculatePlayerText2メソッドの戻り値と場に出されているカードの数と一致しない場合は不正な手であると判断され、ペナルティーの対象となります。

PlayerText2プロパティ文字列の評価

PlayerText2プロパティの文字列を評価して合成数を生成するためのメソッドです。文字列を”×”で分割し、さらに”^”で分割することで計算をしています。不正な値である場合は0または負数が返されます。

コンピュータがカードを出す処理

コンピュータがカードを出す処理を示します。SearchNumberメソッドで候補手を探し、みつからない場合はパスとなります。SearchNumberメソッドの戻り値はShowResultメソッドに渡され、カードの移動がおこなわれます。

コンピュータが出したカードを移動させ、結果を表示するためのメソッドです。

出せるカードを探す

コンピュータが出せるカードを探す処理を示します。isPrimeNumberがtrueのときは素数として出せるを探し、falseのときは合成数出しできるカードを探します。最初は大量にカードを処理できる合成数出しから探します。ただこれではコンピュータが強くなりすぎるので逆にしたほうがいいかも。

GetCardNumbersPermutationメソッドでカードの組み合わせをできるかぎり探して、そのなかから出せるカードを探し、そのなかから最適に近いものを探すという方法をとっています。

GetCardNumbersPermutationメソッドとGetCardsCanPutメソッドは第一引数のカードの番号のリストのなかから指定された枚数のカードの組み合わせを候補として返します。

GetCardsCanPutメソッドは第二引数の枚数でつくることができるカードの番号の順列をつくります。そしてこれを素数と合成数にわけます。合成数の場合は合成数出しができるかどうかをGetPutCompositeNumberメソッドで判断しています。

また生成した順列のなかにはジョーカーが含まれているかもしれません。その場合は0~13までのすべての数を候補として考えます。

第一引数のなかにジョーカーがあるかどうかを調べてコンピュータが出す素数の候補として返します。

候補として生成された数が合成数の場合、「合成数出し」ができるか調べるメソッドです。

まず素因数分解は可能かを調べます。最初の1行でコンピュータが持っているカードをつかって素因数をつくります。これを合成数とともにCanPrimeFactorizationメソッドに渡して素因数分解可能かを調べます。

素因数分解可能である場合はこれをさらにJudgeCompositeNumberメソッドにわたして持っているカードで合成数と素因数の両方をつくることができるかを調べます。両方の条件を満たしている場合だけが最終候補として残ります。

素因数分解は可能か?

コンピュータが持っているカードのなかで素因数分解に使うことができる素数を取得する処理を示します。

CanPrimeFactorizationメソッドは第一引数で渡された候補を第二引数の素因数のリストで素因数分解できるかどうかを調べるためのものです。

第二引数の素因数のリストをつかって合成数の候補を割っていき、最終的に1になれば素因数分解可能と見なします。また単純なかけ算ではなく指数表記にも対応させるために同じ素数でできなくなるまで何回も割り続けます。

合成数の生成は可能か?

素因数分解可能であったとしても同じカードを複数回使ってしまい、実際には合成数出しできない場合もあります。JudgeCompositeNumberメソッドはそのような候補を排除して本当に合成数出し可能な候補だけを残すためのメソッドです。

コンピュータが持っているカードの番号のコピーを作り、そこから合成数をつくるために必要な番号と素因数と指数をつくるために必要な番号を取り除いていきます。最後までこの処理ができた場合だけが本当に合成数出し可能な候補です。

8 = 2の3乗ですが、2×2×2でもあります。そこでこの場合は先に2と3があるかどうかでなく、2が3つあるかどうかを調べています。ない場合は2と3を探します。同じ素因数が指数個見つかった場合は指数表記から通常のかけ算の形に戻しています。

最善手を探す

コンピュータの候補となるカードの出し方ですべてのカードを出せるかどうかを調べています。複数の候補があるならその場で勝てる候補を選ぶべきだからです。

カードを出した後偶数ばかりになっては次の手を出すことができなくなります。そこでカードを出すときにはその場で勝利を決めることができるかを最初に考えますが、次に次回カードを出すときに困らないかを考えることにします。

ExistNextPutCardsメソッドは第一引数の番号のリストのなかに第二引数の枚数で次に出せるカードがあるか調べるメソッドです。素数がある場合とジョーカーが含まれる場合は次に出せるカードがあると見なしています。

コンピュータの次の一手の候補から一番よさそうなものを選ぶメソッドです。この

下のラベルにつぎにプレイヤーがすべきことを表示させます。いつまでも表示されたままになると見当外れな内容になってしまう場合があるので2秒で消しています。

パスをするときの処理

パスをするときの処理を示します。場にカードが出されている場合は回収してからコンピュータに次のカードを出させます。

パスをするときは場が流されるのでそのための処理を記述しています。

確認のメッセージに対する処理

合成数出しをするときに確認のメッセージが表示されますが、[OK]ボタンをクリックするとカードを出したときの処理が行なわれます。

カードの指定ミスを指摘するコントロールが表示されているときに[OK]ボタンをクリックするとコントロールが非表示になります。

ジョーカーを出すときに数を指定します。[OK]ボタンをクリックすると値が指定されたことになります。