今回はプレイヤーがカードを出すまで 素数大富豪をJavaScriptでつくる(1)でプレイヤーが出したカードが適切なものかどうかを判定する処理を考えます。
動作確認はこちらから ⇒ Web版素数大富豪、別名 素数の出会い系サイト
Contents
全体の流れ
プレイヤーが確定ボタンをクリックしたときはカードが適切なものかどうか判定がおこなわれます。
最初に出されたカードに対して
なにも出されていない
出すべきカードの枚数が違っている
出すべき数よりも大きな数(ラマヌジャン革命が起きているときは小さな数)になっていない
といったミスに対するチェックがおこなわれます。
そのあと
ジョーカーの1枚だし
グロタンディーク素数
ラマヌジャン革命
であるかどうかの判定がおこなわれます。前2者の場合は自動的にプレイヤーが親となりますが、ラマヌジャン革命のときはコンピュータに手番が移動します。
そのあとカードが素数として出されたときは本当に素数なのか、合成数出しのときは素因数として使われているカードは素数なのか、素因数の計算結果は合成数出しされたカードと一致するかが判定されます。ここで間違っているとペナルティーの対象となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function Confirm(){ EnableBottons(false); if(!CheckPlayerCards()) return; // ジョーカー1枚だしは最強 if(CheckPlayerPutJoker()) return; // ここでコンピュータが場に出したカードを非表示にする CompPutCardsMoveInPlay(); if(CheckPlayerPut57()) return; if(CheckPlayerPut1729()) return; // 素数か? if(playerPutCardsText2 == ''){ CheckPlayerPutPrimeNumber(); return; } else CheckPlayerPutCompositeNumber(); } |
事前チェックの処理
出されたカードを事前にチェックする関数です。プレイヤーによって出されたカードの枚数の確認と、カードの番号で整数を生成し、これとnumberMinを比較して適切なカードであるかを調べています。正しくないカードの場合、ShowNotificationMessage関数(後述)をつかって注意のメッセージを表示し、カードはプレイヤーにすべて返しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
function CheckPlayerCards(){ // 場にカードが出されていない if(playerPutCards.length == 0){ ClearPlayerCards(); // 場と素因数場に出したカードはすべてプレイヤーに返される ShowNotificationMessage('カードが出されていません'); return false; } // 指定された枚数で出されていない if(cardCount != -1 && playerCards.length != cardCount){ ClearPlayerCards(); ShowNotificationMessage('カードは' + cardCount + '枚出してください'); return false; } // 一枚だしではジョーカーが最強 if(playerPutCards.length == 1 && playerPutCards[0].Suit == Suit.Joker) return true; // カードで作られる数が最低または最大の数を超えていない let number = GetNumber(playerPutCards); if(numberMin != -1){ if(!isRevolution && numberMin >= number){ ClearPlayerCards(); ShowNotificationMessage(numberMin + 'よりも大きな数を出してください'); return false; } if(isRevolution && numberMin <= number){ ClearPlayerCards(); ShowNotificationMessage(numberMin + 'よりも小さな数を出してください'); return false; } } if(number == 0) { ClearPlayerCards(); ShowNotificationMessage('素数または合成数を出してください'); return false; } return true; } |
GetNumber関数はカードの番号で整数を生成します。ジョーカーを出すときcard.Numberが文字列になって渡されてしまうのでNumber型に変換しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function GetNumber(cards) { let ret = 0; cards.forEach(card => { let num = Number(card.Number); if(num == 0) return; if(num < 10) ret *= 10; else ret *= 100; ret += num; }); return ret; } |
ShowNotificationMessage関数はメッセージを表示する処理をおこないます。
1 2 3 4 5 6 7 8 9 10 |
function ShowNotificationMessage(notifyText){ let element = CreateMessageElement(300, '#00ffff', notifyText); let buttonOK = AppendOkButton(element); buttonOK.onclick = function(e){ EnableBottons(true); element.remove(); } } |
グロタンディーク素数とジョーカーを出したときの処理
グロタンディーク素数とジョーカーを出したときは場が流され、自動的にプレイヤーが親になります。そしてその旨を示すメッセージが表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function CheckPlayerPut57(){ if( playerPrimeFactor.length == 0 && GetNumber(playerPutCards) == 57 ) { FlowThePlace(); // 自動的に場を流す ShowNotificationMessage('グロタンディーク素数 57を出したのであなたが親です。'); return true; } else return false; } function CheckPlayerPutJoker(){ if( playerPutCards.length == 1 && playerPrimeFactor.length == 0 && playerPutCards[0].Suit == Suit.joker ) { FlowThePlace(); ShowNotificationMessage('ジョーカーを出したのであなたが親です。'); return true; } else return false; } |
場を流す処理
場を流す処理を示します。プレイヤーとコンピュータが出したカードと非表示ではあるけど場に存在するカードが格納されている配列を空にしてcardCountとnumberMinを -1 にして次は好きな枚数で好きな数が出せるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function FlowThePlace(){ playerPrimeFactor.forEach(card => { shuffledCards.push(card); }); playerPutCards.forEach(card => { shuffledCards.push(card); }); compPutCards.forEach(card => { shuffledCards.push(card); }); compPrimeFactor.forEach(card => { shuffledCards.push(card); }); inplayPutCards.forEach(card => { shuffledCards.push(card); }); playerPrimeFactor = []; playerPutCards = []; compPutCards = []; compPrimeFactor = []; inplayPutCards = []; cardCount = -1; numberMin = -1; ClearText(); ShowCards(); } |
1 2 3 4 |
function ClearText(){ playerPutCardsText1 = ''; playerPutCardsText2 = ''; } |
ラマヌジャン革命かどうか?
プレイヤーによって出されたカードでラマヌジャン革命になるかどうかを判定する処理を示します。革命が成立しても自動的に場は流れず、手番がコンピュータに移ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function CheckPlayerPut1729(){ if( playerPrimeFactor.length == 0 && GetNumber(playerPutCards) == 1729 ) { // 場のカードは見えないところに移動させる PlayerPutCardsMoveInPlay(); ClearText(); ShowCards(); if(!isRevolution){ ShowRevolutionMassage('1729を出したのでラマヌジャン革命です。'); isRevolution = true; } else{ ShowRevolutionMassage('1729を出したのでラマヌジャン革命返しです。'); isRevolution = false; } return true; } else return false; } |
場のカードを非表示に
場のカードを流さずにプレイヤーとコンピュータが出したカードを非表示にする処理を示します。素因数場に出されたカードはすぐに流してしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function PlayerPutCardsMoveInPlay(){ playerPutCards.forEach(card => { inplayPutCards.push(card); }); playerPrimeFactor.forEach(card => { shuffledCards.push(card); }); playerPutCards = []; playerPrimeFactor = []; ShowCards(); } function CompPutCardsMoveInPlay(){ compPutCards.forEach(card => { inplayPutCards.push(card); }); compPrimeFactor.forEach(card => { shuffledCards.push(card); }); compPutCards = []; compPrimeFactor = []; ShowCards(); } |
素数として出されたカードの判定
プレイヤーによってカードが素数として出されたとき、本当に素数かどうかを調べる処理を示します。ここで正しくないカードであると判定された場合はIllegal関数(後述)が呼び出され、その旨を示すメッセージが表示されます。
正しいカードであると判定された場合はPlayerPutCardsMoveInPlay関数を呼び出して、場に出されたカードは見えないところに移動させ、素因数場に場されたカードはすぐに流します。また正しいカードであることを示すメッセージを表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function CheckPlayerPutPrimeNumber(){ let number = GetNumber(playerPutCards); if(!IsPrimeNumber(number)){ cardCount = -1; numberMin = -1; return Illegal(number + ' は素数ではありません!'); } cardCount = playerPutCards.length; numberMin = number; // 場のカードは見えないところに移動させ、素因数場に場されたカードは流す PlayerPutCardsMoveInPlay(); ShowGoodMessage(); ClearText(); } |
素数かどうかを判定する関数
素数かどうかを判定する関数を示します。
2以外の偶数は素数ではないし調査対象の数を偶数で割っても意味がありません(偶数なら2で割り切れるから)。そこで3以上の奇数で割り切れるかを調べています。どこまで調べるかは調査対象の平方根を切り上げた整数までです。もし割り切れるのであればそれよりも前に割り切れる数が見つかるはずです。見つからなかったということはこれ以上調べても見つかる可能性はないので素数であると判定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
function IsPrimeNumber(number) { // 0とか1は素数ではない if (number == 0 || number == 1) return false; // 2と3は素数 if (number == 2 || number == 3) return true; // 2以外の偶数は素数ではない if (number % 2 == 0) return false; // あとは3以上の奇数で割り切れるか調べる // 最後まで割り切れなければ素数である // 平方根よりも大きな最初の奇数で割ってみて割り切れないならそれ以上調べる必要はない let lim = Math.ceil(Math.sqrt(number)); if (lim % 2 == 0) lim++; for (let i = 3; i <= lim; i += 2) { if (number % i == 0) return false; } return true; } |
不正なカードを出したときの処理
素数でない場合は不正なカードである旨を示すメッセージを表示させます。そして手番をコンピュータに移します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
function Illegal(message){ let count = 0; // ペナルティーありの場合は出した枚数と同じ数だけカードを増やす if(document.getElementById('checkbox-penalty').checked) count = playerPutCards.length + playerPrimeFactor.length; for(let i=0; i<count; i++){ playerCards.push(shuffledCards[i]); } shuffledCards.splice(0, count); ClearPlayerCards(); cardCount = -1; numberMin = -1; ShowErrorMessage('不正なカードです', message); } function ShowErrorMessage(errorText1, errorText2){ let element = null; element = CreateMessageElement(350, '#aa0000', errorText1, errorText2); let buttonOK = AppendOkButton(element); buttonOK.onclick = function(e){ element.remove(); CompPutCards(); // 手番をコンピュータに } } |
正しいカードを出したときの処理
プレイヤーが出したカードが正しい場合はメッセージを表示して知らせます。OKボタンをクリックすると手番がコンピュータに移り、CompPutCards関数(後述)でコンピュータがカードを出します。
1 2 3 4 5 6 7 8 9 10 |
function ShowGoodMessage(){ let element = CreateMessageElement(300, '#00ffff', '正しいカードです'); let buttonOK = AppendOkButton(element); buttonOK.onclick = function(e){ element.remove(); CompPutCards(); // 手番をコンピュータに } } |
合成数出しをしたときの判定
プレイヤーが合成数出しをしたときの処理を示します。本判定のまえに素因数の指定が間違っている場合はやり直すようにメッセージが表示されます。正しい場合は確認のメッセージが表示されます。OKボタンをクリックすると判定がおこなわれます。
1 2 3 4 5 6 7 8 9 10 |
function CheckPlayerPutCompositeNumber(){ let number = GetNumber(playerPutCards); let evaluatePlayerText = EvaluatePlayerText(); if(evaluatePlayerText == 0) ShowNotificationMessage('素因数の指定が明らかに間違っています。素因数は複数または指数表記でだしてください') else { EnableBottons(false); ShowConfirmationMessage('合成数だし(' + number + ' = ' + playerPutCardsText2 + ')をしようとしています。本当に間違いありませんか?'); } } |
EvaluatePlayerText関数はplayerPutCardsText2の文字列を評価して選択された素因数による計算をおこなうためのものです。素因数である以上、素数でなければならないのですが、そうでない場合は不正なカードが出されたとしてその数の符号を反転した数を返します。
また入力ミスで素因数がひとつだけしか選択されていない場合、計算式として成り立っていない場合は0を返します。戻り値が0なら入力ミスですませますが、負数であれば素数でないものを素因数として指定した反則と見なしてペナルティーを与えます。
0が返されるときは入力ミスということで許されますが、負数が返されたときは反則として処理されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function EvaluatePlayerText(){ let ret = 1; let texts1 = playerPutCardsText2.split(' × '); if(texts1.length == 1){ if(texts1[0].indexOf(' ^ ') == -1){ return 0; } else{ let texts2 = texts1[0].split(' ^ '); if(!IsPrimeNumber(texts2[0])) return -texts2[0]; else return Math.pow(texts2[0], texts2[1]); } } for(let i=0; i<texts1.length; i++){ let texts2 = texts1[i].split(' ^ '); if(texts2.length > 2) return 0; if(texts2.length == 1){ console.log('texts2[0] = ' + texts2[0]); if(!IsPrimeNumber(texts2[0])) return -texts2[0]; ret *= texts2[0]; } if(texts2.length == 2){ if(!IsPrimeNumber(texts2[0])) return -texts2[0]; ret *= Math.pow(texts2[0], texts2[1]); } } return ret; } |
合成数出しの確認のメッセージを表示する関数を示します。OKボタンをクリックするとCheckPlayerCompositeNumber関数が呼び出されて判定がおこなわれます。キャンセルボタンをクリックするとカードを指定しなおすことができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function ShowConfirmationMessage(notifyText){ let element = CreateMessageElement(400, '#ff00ff', notifyText); let buttons = AppendOkCancelButton(element); buttons['OK'].onclick = function(e){ element.remove(); CheckPlayerCompositeNumber(); } buttons['Cancel'].onclick = function(e){ EnableBottons(true); ClearPlayerCards(); element.remove(); } } |
素因数の計算式を評価する関数
素因数の計算式を評価して、プレイヤーが出した合成数と一致するかが調べる処理を示します。素因数として指定した数が素数でない場合や計算の結果、合成数と一致しない場合は不正なカードであると判定されます。
判定結果を表示するメッセージがShowGoodMessage関数またはIllegal関数によって表示され、どちらの場合もコンピュータに手番が移動します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function CheckPlayerCompositeNumber(){ let number = GetNumber(playerPutCards); let evaluatePlayerText = EvaluatePlayerText(); if(playerPutCardsText2 != ''){ if(evaluatePlayerText < 0){ return Illegal('素因数として指定されている ' + -evaluatePlayerText + ' は素数ではありません!'); } else if(evaluatePlayerText != number){ return Illegal('素因数の計算が正しくありません。' + playerPutCardsText2 + 'は' + number + 'にはなりません!'); } } cardCount = playerPutCards.length; numberMin = number; // 場のカードは見えないところに移動させ、素因数場に場されたカードは流す PlayerPutCardsMoveInPlay(); ShowGoodMessage(); ClearText(); } |