1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JSで作る神経衰弱

Last updated at Posted at 2020-12-22

#JSで作る、神経衰弱

スクリーンショット 2020-10-14 11.28.00.png

↓完成したプロダクトはこちら
神経衰弱サンプル

##【機能】
・1ターンに2つのカードをめくる事が出来る
・2つのカードが同じ数字ならば、表のままにする
・2つのカードが異なれば、裏返す(元に戻す)
・1度にめくれるのは2枚まで
・すべて、カードが捲れれば「Game over!」などのアラート
→その後、ゲームリセット

#コード

sample.html
<body>
  <ul class="lists"></ul>
</body>
sample.js
$(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);
  });
});

#コードの説明をします

##①カードをシャッフルする処理

sample.js
  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要素を作成する処理

sample.js
  // <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に代入しています

##③カードをめくる処理

sample.js
    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。

##④'?'のカードをクリックしたときの条件分岐

sample.js
      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にしています。

##⑤カードの比較処理

sample.js
      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にして、カードのテキストを表にする前の?に戻しています

#参考
【JavaScript】神経衰弱で2枚のカードの判定前に3枚目のカードをクリックできないようにしたい

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?