【ゲーム】JavaScript:25 数当てゲーム(Hit & Blow)

 「数当てゲーム(Hit & Blow)」は、プレイヤーが4桁のランダムな数字(重複なし)を入力し、ヒット&ブローの情報をもとに正解の数字と並び順を推理して当てるパズルゲームです。ヒットは「数字も位置も一致」、ブローは「数字のみ一致」を示し、「Hitが4」になったらクリアとなります。

遊び方と操作方法

  1. タイトル画面の「スタート 🚀」ボタンをクリック
  2. ゲーム画面で4桁の数字(例:1234)を入力欄に入力
  3. 「判定 ✔️」ボタン、または Enter キーで結果を確認
  4. フィードバック欄に「X Hit / Y Blow」が表示されるので、次の推理に活用
  5. 試行を繰り返し、Hitが4になったら「ゲームクリア 🎉」画面へ

ルール

  • 正解数字は重複なしの4桁ランダム(0000〜9999のうち10P4通り)
  • Hit:同じ桁に同じ数字がある数
  • Blow:異なる桁だが数字が含まれている数
  • Hitが4になると正解
  • 試行回数は右下に表示/最大99回(任意設定)

🎮ゲームプレイ

以下のリンク先から実際にプレイできます。

25 数当てゲーム(Hit & Blow)

素材のダウンロード

以下のリンクから使用する素材をダウンロードできます。

hit_blow_title.pnghit_blow_bg.png

ゲーム画面イメージ

プログラム全文(hit_blow.html)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>🔢 数当てゲーム (Hit & Blow) 🔢</title>
  <style>
    /* ===== 全体背景設定 ===== */
    body {
      margin: 0;
      padding: 0;
      font-family: 'Arial', sans-serif;
      /* 背景画像を固定で全面に表示 */
      background: url('hit_blow_bg.png') no-repeat center center fixed;
      background-size: cover;
    }

    /* ===== 中央オーバーレイ共通スタイル ===== */
    .overlay {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      background-color: rgba(255, 255, 255, 0.9);
      padding: 20px;
      border-radius: 10px;
      text-align: center;
      width: 90%; max-width: 500px;
    }

    /* 非表示用クラス */
    .hidden {
      display: none;
    }

    /* タイトル画像サイズ調整 */
    .title-image {
      display: block;
      margin: 0 auto 10px;
      max-width: 80%;
      height: auto;
    }

    /* ===== 汎用ボタン(スタート/判定/リスタート) ===== */
    .btn {
      display: inline-block;
      padding: 12px 24px;
      font-size: 18px;
      font-weight: bold;
      margin: 15px 5px 0;
      cursor: pointer;
      border: 2px solid #e0a800;
      border-radius: 8px;
      background-color: #ffc107; /* 高コントラスト黄色 */
      color: #000;
      box-shadow: 0 4px 6px rgba(0,0,0,0.2);
      transition: background-color 0.2s, transform 0.1s;
    }
    .btn:hover {
      background-color: #e0a800;
      transform: translateY(-2px);
    }
    .btn:active {
      transform: translateY(0);
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    }

    /* ===== 入力フィールドスタイル ===== */
    #guess-input {
      font-size: 18px;
      padding: 8px;
      width: 140px;
      text-align: center;
      border: 2px solid #343a40;
      border-radius: 5px;
    }

    /* ===== フィードバックメッセージ ===== */
    .message {
      margin-top: 15px;
      padding: 10px;
      background-color: rgba(0, 0, 0, 0.7);
      color: #fff;
      border-radius: 5px;
      min-height: 1.5em;
    }

    /* ===== 試行回数表示 ===== */
    .attempt-display {
      margin-top: 10px;
      font-size: 18px;
    }
  </style>
</head>
<body>
  <!-- ===== タイトル画面 ===== -->
  <div id="title-screen" class="overlay">
    <img src="hit_blow_title.png" alt="数当てゲームタイトル" class="title-image">
    <h1>🔢 数当てゲーム (Hit & Blow) 🔢</h1>
    <p>遊び方:4桁のランダムな数字を推理して当ててください。<br>
       同じ数字は使われません。</p>
    <p>📜 ルール 📜</p>
    <ul style="text-align:left;">
      <li>Hit:数字と位置が合っている数</li>
      <li>Blow:数字だけ合っている数</li>
      <li>Hitが4になったらクリア!</li>
    </ul>
    <button id="start-btn" class="btn">スタート 🚀</button>
  </div>

  <!-- ===== ゲーム画面 ===== -->
  <div id="game-screen" class="overlay hidden">
    <h2>推理して数字を入力</h2>
    <!-- ユーザー入力 -->
    <input type="text" id="guess-input" maxlength="4" placeholder="例: 1234">
    <button id="submit-btn" class="btn">判定 ✔️</button>
    <!-- フィードバック表示 -->
    <div id="feedback" class="message"></div>
    <!-- 試行回数表示 -->
    <div class="attempt-display">試行回数: <span id="attempt-count">0</span> 回</div>
  </div>

  <!-- ===== 終了画面 ===== -->
  <div id="end-screen" class="overlay hidden">
    <h2>ゲームクリア 🎉</h2>
    <p id="final-message">正解!試行回数は 0 回でした!</p>
    <button id="restart-btn" class="btn">タイトルに戻る 🔄</button>
  </div>

  <script>
    // ================================================
    // 定数設定
    // ================================================
    const DIGITS = 4;             // 桁数
    const MAX_ATTEMPTS = 99;      // 最大試行回数(任意)

    // ================================================
    // ゲーム状態用変数
    // ================================================
    let answer = [];              // 正解配列
    let attempts = 0;             // 試行回数

    // ================================================
    // DOM要素取得
    // ================================================
    const titleScreen  = document.getElementById('title-screen');
    const gameScreen   = document.getElementById('game-screen');
    const endScreen    = document.getElementById('end-screen');
    const startBtn     = document.getElementById('start-btn');
    const submitBtn    = document.getElementById('submit-btn');
    const restartBtn   = document.getElementById('restart-btn');
    const guessInput   = document.getElementById('guess-input');
    const feedbackDiv  = document.getElementById('feedback');
    const attemptSpan  = document.getElementById('attempt-count');
    const finalMessage = document.getElementById('final-message');

    /**
     * タイトル画面表示
     */
    function showTitle() {
      titleScreen.classList.remove('hidden');
      gameScreen.classList.add('hidden');
      endScreen.classList.add('hidden');
    }

    /**
     * ゲーム開始処理
     * - 正解をランダム生成
     * - 画面切り替え
     * - 変数初期化
     */
    function startGame() {
      // 正解を生成
      answer = generateAnswer();
      // 試行回数リセット
      attempts = 0;
      attemptSpan.textContent = attempts;
      // フィードバッククリア
      feedbackDiv.textContent = '';
      // 入力欄クリア
      guessInput.value = '';

      // 画面切り替え
      titleScreen.classList.add('hidden');
      endScreen.classList.add('hidden');
      gameScreen.classList.remove('hidden');
      // フォーカスを入力欄へ
      guessInput.focus();
    }

    /**
     * 正解となるランダム4桁の配列を生成
     * @returns {string[]} 4桁の文字列配列
     */
    function generateAnswer() {
      const nums = [...Array(10).keys()]; // 0~9の配列
      const result = [];
      // 重複なくDIGITS個取得
      for (let i = 0; i < DIGITS; i++) {
        const idx = Math.floor(Math.random() * nums.length);
        result.push(String(nums.splice(idx, 1)[0]));
      }
      return result;
    }

    /**
     * ユーザーの入力をチェックし、Hit & Blowを判定
     */
    function handleGuess() {
      const guess = guessInput.value.trim();
      // 入力バリデーション:4桁かつ数字かつ重複なし
      if (!/^\d{4}$/.test(guess) || new Set(guess).size !== DIGITS) {
        feedbackDiv.textContent = '4桁の重複しない数字を入力してください。';
        return;
      }
      // 試行回数を増やす
      attempts++;
      attemptSpan.textContent = attempts;

      // Hit & Blow 判定
      let hit = 0, blow = 0;
      for (let i = 0; i < DIGITS; i++) {
        if (guess[i] === answer[i]) {
          hit++;
        } else if (answer.includes(guess[i])) {
          blow++;
        }
      }
      // フィードバック表示
      feedbackDiv.textContent = `${hit} Hit / ${blow} Blow`;

      // 正解判定
      if (hit === DIGITS) {
        endGame();
      } else {
        // 次の入力に備えてクリア&フォーカス
        guessInput.value = '';
        guessInput.focus();
      }
    }

    /**
     * ゲーム終了処理
     * - 終了画面へ切り替え
     * - 最終メッセージ表示
     */
    function endGame() {
      gameScreen.classList.add('hidden');
      endScreen.classList.remove('hidden');
      finalMessage.textContent = `正解!試行回数は ${attempts} 回でした!`;
    }

    // ================================================
    // イベントリスナー設定
    // ================================================
    document.addEventListener('DOMContentLoaded', () => {
      // スタートボタン
      startBtn.addEventListener('click', startGame);
      // 判定ボタン
      submitBtn.addEventListener('click', handleGuess);
      // Enterキーでも判定
      guessInput.addEventListener('keyup', e => {
        if (e.key === 'Enter') handleGuess();
      });
      // リスタートボタン
      restartBtn.addEventListener('click', showTitle);
      // 最初はタイトル画面
      showTitle();
    });
  </script>
</body>
</html>

アルゴリズムの流れ

ステップ処理内容主な関数/命令
1. タイトル表示タイトル画面を表示、他画面を隠すshowTitle()
2. ゲーム開始generateAnswer() で解答生成、試行回数・フィードバック初期化、画面切替startGame()
3. 正解生成0〜9の配列から重複なく4つをランダム抽出generateAnswer()
4. 推理入力&判定入力バリデーション → Hit/Blow 計算 → フィードバック表示handleGuess()
5. 終了判定Hitが4か、Enter/判定ボタンで判定後に endGame() へ分岐handleGuess()endGame()
6. 結果表示ゲーム画面を隠し、終了画面を表示、試行回数をメッセージに反映endGame()

関数・命令の解説

関数名/命令説明
showTitle()各画面の .hidden クラスを操作し、タイトル画面を表示
startGame()正解生成・試行回数リセット・画面切替・入力欄フォーカス
generateAnswer()0〜9の数値配列から重複なしで4つの要素をランダムに抽出し文字列配列として返却
handleGuess()入力検証・試行回数インクリメント・Hit/Blow 計算・フィードバック表示・終了判定
endGame()結果画面への画面切替と最終メッセージ設定
guessInput.addEventListener('keyup', …)Enter キー押下で handleGuess() を呼び出し、操作性を向上

改造のポイント

  • 桁数変更DIGITS 定数を変更すると任意の桁数に対応可能。
  • 重複ルールの緩和generateAnswer() で重複を許す、あるいは特定の数字を除外するロジックへカスタマイズ。
  • ヒント機能の追加:一定回数ごとに「〇〇は含まれている/いない」などのヒントを表示。
  • 最大試行数制限MAX_ATTEMPTS を超えたら自動でゲームオーバーにする機能。
  • UI強化:正誤時にHitは緑、Blowは黄色など色分け表示、アニメーションを追加。
  • タイムチャレンジモード:制限時間を設け、時間内にクリアできるか競うモードを追加。

アドバイス:ユーザーの推理を助けるヒント機能(例:特定の数字の有無や位置範囲の絞り込み)を追加すると初心者にも優しいゲームになります。また、クリアタイムや最少試行数を localStorage に保存して、ランキング表示を実装するとリプレイ性が向上します!