#JSで作る、神経衰弱
↓完成したプロダクトはこちら
神経衰弱サンプル
##【機能】
・1ターンに2つのカードをめくる事が出来る
・2つのカードが同じ数字ならば、表のままにする
・2つのカードが異なれば、裏返す(元に戻す)
・1度にめくれるのは2枚まで
・すべて、カードが捲れれば「Game over!」などのアラート
→その後、ゲームリセット
#コード
<body>
<ul class="lists"></ul>
</body>
$(function() {
'use strict';
function cardShuffle() {
const array1 = [...Array(8).keys()].map(i => ++i), // 元の配列i → ++i(前置インクリメント:i+1の値を返す)
array2 = [...Array(8).keys()].map(i => ++i);
let array = array1.concat(array2); // 1~8までの数字を2つずつの配列:concatで結合
for(let i = 0; i < array.length; i++){
let rand = Math.floor(Math.random() * (i+1));
[array[i], array[rand]] = [array[rand], array[i]];
}
return array;
}
// <li>の作成
const makeLiContents = (function() {
let randNum = cardShuffle(), // 1~8までの数字を2つずつの配列、かつ、ランダム
html = '',
$ul = $('ul');
for(let i = 0; i < randNum.length; i++) {
html += '<li data-num=' + randNum[i] + '>?</li>';
}
$ul.append(html); // <li>要素の追加
})();
const openCard = (function(card) {
let firstCardText, secondCardText, firstClickedData, secondClickedData,
openFirst = true, // カード1枚目→true
openFlag = false, // カードをめくることができるかどうか?のフラグ
pair = 0; // そろったカード数
return function toOpenCard(card) {
let $listsLi = $(card),
clickedData, clickedText;
// フラグ=trueで処理を止める
if($listsLi.text() === '?') {
if (openFlag){
return;
} else {
openFlag = true;
}
}
// '?'のみをクリック可
if($listsLi.text() === '?') {
clickedData = $listsLi.data('num'); // クリックされたdata-numの取得
clickedText = $listsLi.text(clickedData); // クリックされたdata-numをテキストに。
// 1枚目の処理
if(openFirst) {
firstCardText = clickedText; // 1枚目にクリックされたカードの'テキスト'
firstClickedData = clickedData; // 1枚目にクリックされたカードの'データ'
openFirst = false;
openFlag = false;
// 2枚目の処理
} else {
secondCardText = clickedText;
secondClickedData = clickedData;
compareCard();
openFirst = true;
}
}
function compareCard() {
// カードが1枚目と一致する時
if(firstClickedData === secondClickedData) {
pair++;
openFlag = false;
// ペアが全て揃うと。
if(pair === 8) {
setTimeout(function(){
alert('Game Over!');
}, 1000)
}
// 1枚目と一致しなかった時の処理
} else {
setTimeout(function(){
openFlag = false; // setTimeoutが終わった後に、フラグがfalseに変わるため処理を止めることができる。
firstCardText.text('?');
secondCardText.text('?');
}, 1000)
}
}
};
})();
$('.lists').find('li').on('click', function() {
openCard(this);
});
});
#コードの説明をします
##①カードをシャッフルする処理
function cardShuffle() {
const array1 = [...Array(8).keys()].map(i => ++i),
array2 = [...Array(8).keys()].map(i => ++i);
let array = array1.concat(array2); // 1~8までの数字を2つずつの配列:concatで結合
for(let i = 0; i < array.length; i++){
let rand = Math.floor(Math.random() * (i+1));
[array[i], array[rand]] = [array[rand], array[i]];
}
return array;
}
・[...Array(8).keys()].map(i => ++i)
:
以下記事が分かりやすいです。
[...Array(10).keys()].map(i => ++i) ??
[...Array(8).keys()]は、
Array(8)
で長さが8のカラの配列を作成。
その配列のkeyを取得して配列に格納しています。
console.log([...Array(8).keys()])
[0, 1, 2, 3, 4, 5, 6, 7]
[...Array(8).keys()].map(i => ++i)
で配列のそれぞれの値に+1しています
・let array = array1.concat(array2);
:concatメソッドは、配列や文字列を結合するためのメソッドです。
結合した配列がこちら
let array = array1.concat(array2)
console.log(array);
[1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8]
・let rand = Math.floor(Math.random() * (i+1));
:
ランダムに並び替えた配列を、変数rand
に格納しています。
・[array[i], array[rand]] = [array[rand], array[i]]
:
こちらの記事を参考にした所、ここでの処理は Math.floor(Math.random())よりも精度の高いランダム処理を行っています。
Math.floor(Math.random())では、たまたま乱数の比較結果で同じものが続く場合もあります。そこでより精度の高いシャッフルを行うために「フィッシャーイェーツのアルゴリズム」を使った配列のシャッフルという方法もあります
[array[i], array[rand]] = [array[rand], array[i]]
では、インデックスに応じて、配列の値同士を入れ替えているという形になります。
##②カードを構成するli要素を作成する処理
// <li>の作成
const makeLiContents = (function() {
let randNum = cardShuffle(), // 1~8までの数字を2つずつの配列、かつ、ランダム
html = '',
$ul = $('ul');
for(let i = 0; i < randNum.length; i++) {
html += '<li data-num=' + randNum[i] + '>?</li>';
}
$ul.append(html); // <li>要素の追加
})();
ここでは、クロージャ―を使用しています。
・html = ''
:カラの文字列を用意します。
→for文で作成したDOMを格納するためです。
→一度に変数へ代入することでDOM操作の回数を減らすためです。
for(let i = 0; i < randNum.length; i++) {
html += '<li data-num=' + randNum[i] + '>?</li>';
}
↑上記では、ランダムなカードの枚数だけli要素を作成して、変数html
に代入しています
##③カードをめくる処理
return function toOpenCard(card) {
let $listsLi = $(card),
clickedData, clickedText;
// フラグ=trueで処理を止める
if($listsLi.text() === '?') {
if (openFlag){
return;
} else {
openFlag = true;
}
}
・let $listsLi = $(card)
:クリックされたli要素を取得しています
・openFlag
:trueならば、めくることができます。
初期値ではfalse。
##④'?'のカードをクリックしたときの条件分岐
if($listsLi.text() === '?') {
clickedData = $listsLi.data('num');
clickedText = $listsLi.text(clickedData);
// 1枚目の処理
if(openFirst) {
firstCardText = clickedText; // 1枚目にクリックされたカードの'テキスト'
firstClickedData = clickedData; // 1枚目にクリックされたカードの'データ'
openFirst = false;
openFlag = false;
// 2枚目の処理
} else {
secondCardText = clickedText;
secondClickedData = clickedData;
compareCard();
openFirst = true;
}
}
clickedData = $listsLi.data('num');
:クリックされたdata-numの取得をしています。
clickedText = $listsLi.text(clickedData);
:クリックされたdata-numをテキストに。
elseの後の2枚目の処理では、
compareCard();でカードの比較と、
openFirst = true;で、1枚目かどうか?をtrueにしています。
##⑤カードの比較処理
function compareCard() {
// カードが1枚目と一致する時
if(firstClickedData === secondClickedData) {
pair++;
openFlag = false;
// ペアが全て揃うと。
if(pair === 8) {
setTimeout(function(){
alert('Game Over!');
}, 1000)
}
// 1枚目と一致しなかった時の処理
} else {
setTimeout(function(){
openFlag = false; // setTimeoutが終わった後に、フラグがfalseに変わるため処理を止めることができる。
firstCardText.text('?');
secondCardText.text('?');
}, 1000)
}
}
カードが1枚目と一致する時、pair++;で、pairという揃ったカードのペアをカウントする変数に1ずつ足しています。
1枚目と一致しなかった時の処理では、
カードが空いているかどうかのフラグopenFlag
をfalseにして、カードのテキストを表にする前の?
に戻しています