【ゲーム】JavaScript:30 ワールドゲーム

 「🟩 ワールドゲーム 🟨」は、Wordle(ワードル)ライクな5文字英単語推測ゲームです。プレイヤーは6回以内に隠された英単語を推理し、入力した単語の各文字ごとに「Correct(緑)」「Present(黄)」「Absent(灰)」のフィードバックを得ながら正解を目指します。

遊び方と操作方法

  1. タイトル画面の「スタート 🚀」ボタンをクリック。
  2. 6行 × 5列のグリッドが表示される。
  3. 下の入力欄に5文字の英単語を入力して「判定 ✔️」ボタンをクリック(または Enter)。
  4. 緑(.correct)は同じ文字・同じ位置、黄(.present)は文字はあるが位置が違う、灰(.absent)は単語に含まれないことを示す。
  5. ヒントをもとに単語を塗り替え、6回以内に正解を当てる。
  6. 正解すると「🎉 正解! 🎉」、6回使い切ると「残念…」と結果画面へ。

ルール

  • 単語長:5 文字
  • 試行回数:最大 6 回
  • フィードバック
    Correct:文字と位置が一致 → 緑背景
    Present:文字はあるが位置が不一致 → 黄背景
    Absent:単語に含まれない → 灰背景
  • 勝利条件:いずれかの試行で全5文字が「Correct」
  • 敗北条件:6回試行しても正解できない。

🎮ゲームプレイ

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

30 ワールドゲーム

素材のダウンロード

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

world_game_title.pngworld_game_bg.png

ゲーム画面イメージ

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

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>🟩 ワールドゲーム 🟨</title>
  <style>
    /* ===== 全体背景設定 ===== */
    body {
      margin: 0;
      padding: 0;
      font-family: 'Arial', sans-serif;
      /* 背景画像を固定表示 */
      background: url('world_game_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: 10px 20px;
      margin: 10px 5px;
      font-size: 16px;
      font-weight: bold;
      color: #fff;
      background-color: #007BFF;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
      transition: background-color 0.2s, transform 0.1s;
    }
    .btn:hover { background-color: #0056b3; transform: translateY(-2px); }
    .btn:active { transform: translateY(0); box-shadow: 0 1px 2px rgba(0,0,0,0.2); }
    /* ===== タイトル画面テキスト ===== */
    .instructions p { margin: 5px 0; text-align: left; }
    /* ===== ゲーム画面 ===== */
    #guess-grid { display: grid; grid-template-rows: repeat(6, 50px); gap: 5px; margin: 10px 0; }
    .row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; }
    .cell {
      display: flex;
      align-items: center;
      justify-content: center;
      border: 2px solid #999;
      font-size: 24px;
      font-weight: bold;
      text-transform: uppercase;
      background-color: #fff;
    }
    .correct { background-color: #6aaa64; color: #fff; border-color: #6aaa64; }
    .present { background-color: #c9b458; color: #fff; border-color: #c9b458; }
    .absent { background-color: #787c7e; color: #fff; border-color: #787c7e; }
    /* 入力欄 */
    #guess-input { width: 200px; padding: 8px; font-size: 16px; text-align: center; border: 2px solid #999; border-radius: 5px; }
    /* メッセージ */
    .message {
      margin-top: 10px;
      padding: 8px;
      background-color: rgba(0,0,0,0.7);
      color: #fff;
      border-radius: 5px;
      min-height: 1.5em;
    }
  </style>
</head>
<body>
  <!-- タイトル画面 -->
  <div id="title-screen" class="overlay">
    <img src="world_game_title.png" alt="ワールドゲームタイトル" class="title-image">
    <h1>🟩 ワールドゲーム 🟩</h1>
    <div class="instructions">
      <p>5文字の英単語を6回以内に当てるゲームです。</p>
      <p>ルール:</p>
      <p>・コンピューターがランダムに5文字の英単語を選びます。</p>
      <p>・ユーザーは5文字の英単語を入力して推測します。</p>
      <p>・各試行でフィードバックが与えられます:</p>
      <p>  Correct:文字と位置の両方が正しい</p>
      <p>  Present:文字は含まれるが位置が違う</p>
      <p>  Absent:単語に含まれない</p>
      <p>例:</p>
      <p> 正解:W O R D S</p>
      <p> 推測:W O R L D</p>
      <p> フィードバック:C C C A P (Correct, Correct, Correct, Absent, Present)</p>
    </div>
    <button id="start-btn" class="btn">スタート 🚀</button>
  </div>

  <!-- ゲーム画面 -->
  <div id="game-screen" class="overlay hidden">
    <div id="guess-grid"></div>
    <input type="text" id="guess-input" maxlength="5" placeholder="単語を入力">
    <button id="submit-btn" class="btn">判定 ✔️</button>
    <div class="message" id="message">英単語を入力してください</div>
  </div>

  <!-- 終了画面 -->
  <div id="end-screen" class="overlay hidden">
    <h2 id="end-title">結果発表</h2>
    <p id="final-message"></p>
    <button id="restart-btn" class="btn">タイトルに戻る 🔄</button>
  </div>

  <script>
    // ===== 単語リスト =====
    const WORDS = ['APPLE','WORLD','TRAIN','CRANE','PLANT','STONE','BRAIN','LIGHT','HOUSE','FRUIT']; // テスト用単語
    const MAX_ATTEMPTS = 6; // 最大試行回数

    // ===== ゲーム状態 =====
    let secretWord = '';
    let attempt = 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 restartBtn = document.getElementById('restart-btn');
    const guessGrid = document.getElementById('guess-grid');
    const guessInput = document.getElementById('guess-input');
    const submitBtn = document.getElementById('submit-btn');
    const messageDiv = document.getElementById('message');
    const endTitle = document.getElementById('end-title');
    const finalMessage = document.getElementById('final-message');

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

    /**
     * ゲーム開始処理
     */
    function startGame() {
      // 秘密の単語をランダム選択
      secretWord = WORDS[Math.floor(Math.random() * WORDS.length)];
      console.log('Secret:', secretWord); // デバッグ用
      attempt = 0;
      // グリッド初期化
      initGrid();
      // 入力欄クリア&フォーカス
      guessInput.value = '';
      guessInput.focus();
      messageDiv.textContent = '英単語を入力して「判定」を押してください';
      // 画面切り替え
      titleScreen.classList.add('hidden');
      endScreen.classList.add('hidden');
      gameScreen.classList.remove('hidden');
    }

    /**
     * 6x5のグリッドを作成
     */
    function initGrid() {
      guessGrid.innerHTML = '';
      for (let i = 0; i < MAX_ATTEMPTS; i++) {
        const row = document.createElement('div'); row.className = 'row';
        for (let j = 0; j < 5; j++) {
          const cell = document.createElement('div'); cell.className = 'cell';
          row.append(cell);
        }
        guessGrid.append(row);
      }
    }

    /**
     * 推測を判定してグリッドに反映
     */
    function handleGuess() {
      const guess = guessInput.value.trim().toUpperCase();
      // 入力チェック
      if (!/^[A-Z]{5}$/.test(guess)) {
        messageDiv.textContent = '5文字の英単語を入力してください';
        return;
      }
      // 回数+1
      attempt++;
      const row = guessGrid.children[attempt - 1];
      // 正解文字配列
      const answerArr = secretWord.split('');
      const guessArr = guess.split('');
      const result = Array(5).fill('absent');
      // 1. Correct判定
      for (let i = 0; i < 5; i++) {
        if (guessArr[i] === answerArr[i]) {
          result[i] = 'correct';
          answerArr[i] = guessArr[i] = null;
        }
      }
      // 2. Present判定
      for (let i = 0; i < 5; i++) {
        if (guessArr[i] && answerArr.includes(guessArr[i])) {
          result[i] = 'present';
          answerArr[answerArr.indexOf(guessArr[i])] = null;
        }
      }
      // グリッド更新
      for (let i = 0; i < 5; i++) {
        const cell = row.children[i];
        cell.textContent = guess[i];
        cell.classList.add(result[i]);
      }
      // 結果判定
      if (result.every(r => r === 'correct')) {
        endGame(true);
      } else if (attempt >= MAX_ATTEMPTS) {
        endGame(false);
      } else {
        messageDiv.textContent = `結果: ${result.join(', ')}`;
        guessInput.value = '';
        guessInput.focus();
      }
    }

    /**
     * ゲーム終了処理
     */
    function endGame(win) {
      gameScreen.classList.add('hidden');
      endScreen.classList.remove('hidden');
      if (win) {
        endTitle.textContent = '🎉 正解! 🎉';
        finalMessage.textContent = `おめでとう!${attempt} 回で当てました!`;
      } else {
        endTitle.textContent = '残念…';
        finalMessage.textContent = `正解: ${secretWord}`;
      }
    }

    // イベントリスナー設定
    document.addEventListener('DOMContentLoaded', () => {
      startBtn.addEventListener('click', startGame);
      submitBtn.addEventListener('click', handleGuess);
      guessInput.addEventListener('keyup', e => { if (e.key === 'Enter') handleGuess(); });
      restartBtn.addEventListener('click', showTitle);
      showTitle();
    });
  </script>
</body>
</html>

アルゴリズムの流れ

ステップ処理内容関数/命令
1. タイトル表示タイトル画面のみ表示し、他画面を非表示showTitle()
2. ゲーム開始ランダム単語選択→試行回数リセット→グリッド初期化→画面切替startGame()
3. グリッド初期化6行×5列の .cell 要素を動的生成initGrid()
4. 入力検証正規表現で5文字 A–Z のみかチェックhandleGuess()
5. Correct判定同じ位置・同じ文字を判定し、該当セルに correct クラスを追加handleGuess()
6. Present判定残りの文字同士で色マッチングし、present クラス追加handleGuess()
7. Absent判定判定済みでない文字は全て absent クラス追加CSS と result 配列
8. 終了判定全文字 Correct で勝利、6回使い切りで敗北handleGuess()endGame()
9. 終了画面表示結果メッセージを設定し終了画面表示endGame()

関数の詳しい解説

関数名説明
showTitle()各画面の .hidden を切り替え、タイトル画面のみ表示
startGame()- secretWord をランダムに選択 (WORDS[...])- attempt を 0 にリセット- initGrid() 呼出- UI 初期化
initGrid()6行×5列のグリッド要素をクリア&再構築
handleGuess()- 入力検証(/^[A-Z]{5}$/)- attempt++- Correct→Present→Absent の順で判定- グリッドにクラス付与- 終了判定
endGame(win)- ゲーム画面を非表示、終了画面を表示- win に応じたタイトル・メッセージを設定

改造のポイント

  • 単語リスト拡張
    WORDS 配列に辞書的な単語を大量追加し、より幅広い語彙力テストに。
  • 大文字⇔小文字対応
    入力を自動で大文字化していますが、表示も見やすく小文字対応 UI に変更可。
  • 試行回数変更
    MAX_ATTEMPTS を増減して難易度調整。試行回数制限なしモードも実装可能。
  • アニメーション/音声
    Correct/Present/Absent でセルにアニメーションや効果音を付けると爽快感アップ。
  • テーマ切替
    CSS カラースキームをダークモードなどで切り替え、好みに合わせた見た目に。
  • 履歴保存
    localStorage にクリア回数やクリア率を保存し、ハイスコア表示を追加するとリプレイ性向上。

アドバイス:結果画面に「Wordle風チャレンジ共有用文字列」を生成してコピーできる機能を付けると、SNS で自慢しやすくなり、楽しさが広がります。さらに、難易度別(4文字単語/7文字単語)モードや、タイムアタック機能を加えてみるのもおすすめです!