前回のプレイヤーが出したカードの判定 素数大富豪をJavaScriptでつくる(2)の続きです。今回はコンピュータにカードを出させる処理を考えます。

動作確認はこちらから ⇒ Web版素数大富豪、別名 素数の出会い系サイト

CandidateクラスとPrimeNumberFromCardsクラス

最初にこの処理で使うクラスを示します。Candidateクラスはコンピュータが出すカードの候補になるものです。カードの番号でつくられる素数または合成数と素数または合成数をつくるカードの番号の配列だけでなく、素因数をつかった計算式も取得できるようにしています。

カードによって作られる素因数の情報を管理するクラスです。

全体の流れ

全体の流れはコンピュータが持っているカードを整数の配列に変換して、そこから出さなければならない枚数を使って順列をつくります。そこから整数をつくってそれが素数であれば候補のひとつとみなします。

合成数であった場合は合成数出しが可能かもしれないので、コンピュータが持っているカードで作ることができる素因数で割り切ることができるかどうかを調べます。素因数分解が可能である場合はほんとうにコンピュータがもっているカードだけで合成数と素因数と指数を出すことができるかを調べて、候補に加えます。

プレイヤーがカードを出して、しばらくしてコンピュータにカードを出させたいので0.5秒間の時間をおいています。

GetCandidates関数で候補になりそうな手を取得してそのなかから最善に近い手を探します。カードは偶数のものは使いにくいので偶数のカードを多く使う手を探します。そのあとできるだけたくさんの枚数を消費できる手を採用します。

また残りが偶数ばかりになり、奇数のカードを引くとそれを1枚だけ出してなかなか上がれない問題が起きることがわかったので、1枚出しの結果、偶数のカードばかりになる場合はパスすることにしました。

コンピュータがパスをするときの処理

コンピュータがパスをするときの処理です。カードを1枚取ります。そしてその旨を示すメッセージを表示させます。

ラマヌジャン革命のときの処理

ラマヌジャン革命のメッセージを表示させる関数を示します。革命なので背景色を赤にしています。

カードの番号の順列をつくる

コンピュータのカードから順列を取得する処理を示します。GetPermutationCardNumber関数にコンピュータが持っているカードと枚数を渡して順列を取得します。それらをまとめて重複を取り除きます。

そのあと自作のDivideNumberArray関数で素数が生成される順列と合成数が生成されるものにわけます。素数が生成される順列はそのまま候補に加え、合成数が生成される順列は素因数分解可能かを調べます。

GetPermutationCardNumber関数は以下の様になっています。

これは単純に渡された配列から順列を生成する関数です。

順列の重複を取り除く

順列が格納された配列のなかから重複しているものを取り除く関数です。重複を取り除くためにSetを使いたいのですがそのままではうまくいかないので一旦文字列に変換しています。そのあと重複を取り除き、残ったものを配列に戻しています。

順列が生成するのは素数か合成数か?

コンピュータのカードから取得した順列を素数をつくるものと合成数をつくるものにわける関数です。

DivideNumberArray関数によって素数をつくる順列であると分類されたものを候補のリストに追加する関数です。このときcardCountとnumberMinで指定された条件を満たさないものは弾いています。

素因数のリストをつくる

これはDivideNumberArray関数によって素数をつくる順列であると分類されたものから素因数のリストをつくる関数です。最後に素因数が小さい順にソートしています。

素数の候補を取得する

DivideNumberArray関数によって合成数をつくる順列であると分類されたものから素因数分解可能なものを取得する関数です。ここでもcardCountとnumberMinで指定された条件を満たさないものは弾いています。

そのあとGetCandidatesPrimeFactorization関数で素因数分解可能なものを集めています。さらにそのなかからコンピュータのカードだけで合成数と素因数と指数をすべてそろえることができるものだけを返しています。

合成数は素因数分解可能か?

GetCandidatesPrimeFactorization関数は素因数分解可能なものを集めて返します。第一引数は合成数出しの候補の配列、第二引数は素因数の配列です。

数字の配列から素数または合成数を取得する関数です。

合成数出しの候補を取得する

素因数分解できそうな候補のなかから本当に出すことができる候補なのかを調べる関数です。コンピュータが持っているカードの番号のコピーを作成して、そこから合成数と素因数、指数があるか調べて見つかったら取り除きます。全部の数が見つかって取り除くことができたら素因数分解可能と判断できます。

最適そうな手を探す

候補が取得できたらそのなかから最適に近いものを探します。偶数のカードは使いにくいのでできるだけたくさん消費できる手を探します。

これは偶数のカードをできるだけ多く消費する候補を取得する関数です。

できるだけ多くのカードを消費する候補を探す関数です。

カードを場に出す処理

コンピュータが出すべきカードが決まったら場もしくは素因数場に移動させます。もし番号のカードが見つからない場合はジョーカーが使われます。ジョーカーの1枚出しであることを検出するためにその場合はtrueを返すようにしています。

パスとゲームの終了

コンピュータがカードを出す前にプレイヤーのカードがなくなっていたらプレイヤーの勝利です。コンピュータがカードを出したあとコンピュータのカードがなくなっていればコンピュータの勝利です。この場合はメッセージを表示させます。

コンピュータがパスをする場合はメッセージを表示させます。