5
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?

IIT(Iwate Industrial Tecnology)Advent Calendar 2024

Day 17

jsを使った神経衰弱の作り方

Posted at

はじめに

いよいよ本格的な冬になってきましたね。
外は寒くて家の中にいることも多くなったと思います。そこで今回は、カードゲームの定番の一つの神経衰弱をjsを使って書いていこうと思います。

ソースコード一覧

HTML

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>神経衰弱 - CPU対戦</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>神経衰弱 - CPU対戦</h1>
  <p id="instructions">プレイヤー vs CPU!カードを2枚めくってペアを揃えましょう。</p>
  <div id="score-board">
    <span id="player-score">プレイヤー: 0</span> | 
    <span id="cpu-score">CPU: 0</span>
  </div>
  <div id="game-board" class="game-board"></div>
  <button id="restart-button">リスタート</button>
  <script src="script.js"></script>
</body>
</html>

javaScript

const symbols = ["🍎", "🍌", "🍇", "🍓", "🍍", "🍒"];
const cards = [...symbols, ...symbols]; // ペアを作成

const gameBoard = document.getElementById("game-board");
const playerScoreElement = document.getElementById("player-score");
const cpuScoreElement = document.getElementById("cpu-score");
const restartButton = document.getElementById("restart-button");

// 状態管理
let firstCard = null;
let secondCard = null;
let preventClick = false;
let matchedCount = 0;
let playerScore = 0;
let cpuScore = 0;
let isPlayerTurn = true; // プレイヤーのターン管理

// カードをシャッフルする関数
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}
shuffle(cards);

// カード生成
function createCards() {
  cards.forEach((symbol) => {
    const card = document.createElement("div");
    card.classList.add("card");
    card.dataset.symbol = symbol;

    card.addEventListener("click", () => {
      if (preventClick || !isPlayerTurn || card.classList.contains("flipped") || card.classList.contains("matched")) {
        return;
      }

      flipCard(card);

      if (!firstCard) {
        firstCard = card; // 最初のカード
      } else {
        secondCard = card; // 2枚目のカード
        checkMatch();
      }
    });

    gameBoard.appendChild(card);
  });
}

// カードを裏返す
function flipCard(card) {
  card.classList.add("flipped");
  card.textContent = card.dataset.symbol;
}

// カードを裏返す(元に戻す)
function unflipCards(card1, card2) {
  setTimeout(() => {
    card1.classList.remove("flipped");
    card2.classList.remove("flipped");
    card1.textContent = "";
    card2.textContent = "";
  }, 1000);
}

// ペア判定
function checkMatch() {
  if (firstCard.dataset.symbol === secondCard.dataset.symbol) {
    firstCard.classList.add("matched");
    secondCard.classList.add("matched");

    // スコア加算
    if (isPlayerTurn) {
      playerScore++;
      playerScoreElement.textContent = `プレイヤー: ${playerScore}`;
    } else {
      cpuScore++;
      cpuScoreElement.textContent = `CPU: ${cpuScore}`;
    }

    matchedCount += 1;

    // ゲーム終了判定
    if (matchedCount === cards.length / 2) {
      setTimeout(() => {
        const winner = playerScore > cpuScore ? "プレイヤーの勝ち!" : "CPUの勝ち!";
        alert(`${winner}`);
      }, 500);
    }

    // ペアが揃った場合はターンを維持
    resetTurn(false); // ターンを切り替えない
    if (!isPlayerTurn) {
      setTimeout(cpuTurn, 1000); // CPUがペアを当てたらもう一度カードをめくる
    }
  } else {
    preventClick = true;
    unflipCards(firstCard, secondCard);
    setTimeout(() => {
      resetTurn(true); // ターンを切り替える
    }, 1000);
  }
}

// ターンをリセット
function resetTurn(switchTurn) {
  firstCard = null;
  secondCard = null;
  preventClick = false;

  // ターンを切り替える場合のみフラグを変更
  if (switchTurn) {
    isPlayerTurn = !isPlayerTurn;

    // CPUのターンの場合は自動で動作
    if (!isPlayerTurn) {
      setTimeout(cpuTurn, 1000);
    }
  }
}

// CPUのターン
function cpuTurn() {
  const availableCards = Array.from(document.querySelectorAll(".card:not(.flipped):not(.matched)"));

  if (availableCards.length < 2) return;

  // ランダムで2枚のカードを選ぶ
  const firstChoice = availableCards[Math.floor(Math.random() * availableCards.length)];
  flipCard(firstChoice);

  let secondChoice;
  do {
    secondChoice = availableCards[Math.floor(Math.random() * availableCards.length)];
  } while (secondChoice === firstChoice);

  setTimeout(() => {
    flipCard(secondChoice);
    firstCard = firstChoice;
    secondCard = secondChoice;
    checkMatch();
  }, 1000);
}

// ゲームリセット
function resetGame() {
  gameBoard.innerHTML = "";
  firstCard = null;
  secondCard = null;
  preventClick = false;
  matchedCount = 0;
  playerScore = 0;
  cpuScore = 0;
  isPlayerTurn = true;

  playerScoreElement.textContent = "プレイヤー: 0";
  cpuScoreElement.textContent = "CPU: 0";

  shuffle(cards);
  createCards();
}

// 初期化
createCards();
restartButton.addEventListener("click", resetGame);

CSS

/* 基本スタイル */
body {
  font-family: 'Arial', sans-serif;
  text-align: center;
  margin: 0;
  padding: 0;
  background-color: #f4f4f4;
}

h1 {
  margin-top: 20px;
  color: #333;
}

#instructions {
  margin: 10px 0;
  font-size: 16px;
  color: #555;
}

/* ゲームボード */
.game-board {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 10px;
  max-width: 600px;
  margin: 20px auto;
}

/* カードスタイル */
.card {
  width: 100px;
  height: 150px;
  background-color: #ccc;
  border: 2px solid #333;
  border-radius: 8px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 24px;
  color: transparent;
  cursor: pointer;
  transition: transform 0.3s, background-color 0.3s;
}

.card.flipped, .card.matched {
  background-color: #fff;
  color: #000;
  cursor: default;
  transform: rotateY(180deg);
}

/* リスタートボタン */
#restart-button {
  margin-top: 20px;
  padding: 10px 20px;
  font-size: 16px;
  background-color: #007BFF;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s;
}

#restart-button:hover {
  background-color: #0056b3;
}
/* スコア表示 */
#score-board {
  margin-top: 10px;
  font-size: 18px;
  color: #333;
}

コードの解説

 カードの生成をしている関数では、配列 cards をループして、それぞれのシンボルに対応するカードを1枚ずつ作成します。最初のカードをクリックすると firstCard にセットし、2枚目のカードをクリックすると secondCard にセットし、ペア判定を行います。さらに、生成されたカードをすべて gameBoard に追加し、プレイ可能な状態にします。
 ペア判定をしている関数ではdataset.symbol を比較して、2枚のカードが同じシンボルであるかを判定しています。
 CPUの行動を設定している関数では、random関数を使用し一枚目のめくるカードを決定し、二枚目のめくるカードを決めるときはdo文を使い一枚目のカードと被らないようにしています。

実行結果

スクリーンショット 2024-12-16 143713.jpg
スタート画面
スクリーンショット 2024-12-16 144056.jpg実行中
スクリーンショット 2024-12-16 144331.jpg
ペアを当てた時
スクリーンショット 2024-12-16 150554.jpg
プレイヤー勝利時
スクリーンショット 2024-12-16 150801.jpg
CPU勝利時

まとめ

 神経衰弱を作ってみて思ったことはカードのペア判定やCPUのカードのめくり方を定義することがとても難しいと感じました。
 また、改善点として、CPUのカードのめくり方をめくられたことのあるカードの配置を記憶し、そのデータをもとにめくるカードを決めるといった機能の追加ができればさらにjsについての理解を深めることができると思いました。

5
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
5
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?