【ゲーム】JavaScript:65 パネル反転チャレンジ

 「💡 パネル反転チャレンジ 💡」は、クリックしたパネルとその上下左右のパネルが一緒に反転(ON↔OFF)する仕組みを使った“ライトアウト”風パズルゲームです。全パネルをOFFにできればクリアとなり、クリック手数を競います。

遊び方・操作方法

  1. スタートボタンを押してゲーム開始。
  2. 5×5 のグリッド上で、ON状態のパネルは電球マーク💡が表示。
  3. パネルをクリックすると、そのパネルと上下左右のパネルがON⇄OFFに切り替わる。
  4. すべてのパネルをOFF状態(電球マークなし)にすればクリア。
  5. 手数(クリック回数)がそのままスコアに。少ない手数を目指しましょう!

ルール

  • 盤面サイズ:5行×5列
  • 初期状態:ランダムに12回ほど反転操作を繰り返した状態からスタート
  • 操作:パネルをクリックすると「自身+上下左右」の5か所が反転
  • クリア条件:全25枚をOFF(電球なし)にする
  • スコア:クリック手数(少なければ少ないほど高評価)

🎮ゲームプレイ

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

65 パネル反転チャレンジ

素材のダウンロード

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

panel_flip_challenge_title.pngpanel_flip_challenge_bg.png

ゲーム画面イメージ

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

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>💡 パネル反転チャレンジ 💡</title>
  <style>
    html, body {
      margin: 0; padding: 0;
      width: 100vw; height: 100vh;
      font-family: 'Yu Gothic', 'Meiryo', sans-serif;
      background: url('panel_flip_challenge_bg.png') no-repeat center center fixed;
      background-size: cover;
    }
    body {
      width: 100vw;
      height: 100vh;
      overflow: auto;
    }
    .container {
      width: 800px;
      margin: 40px auto;
      background: rgba(255,255,255,0.96);
      border-radius: 22px;
      box-shadow: 0 4px 24px rgba(0,0,0,0.13);
      padding-bottom: 32px;
      min-height: 520px;
      position: relative;
    }
    .title-img {
      display: block;
      margin: 20px auto 18px auto;
      width: 340px;
      max-width: 80%;
      height: auto;
    }
    h1 {
      text-align: center;
      font-size: 2.1em;
      margin: 0.6em 0 0.15em 0;
      color: #1976d2;
      text-shadow: 1px 1px 7px #fff;
      letter-spacing: 0.04em;
    }
    .rule-section {
      background: rgba(235,245,255,0.98);
      border-radius: 14px;
      margin: 28px 32px 14px 32px;
      padding: 16px 24px;
      box-shadow: 0 2px 8px #4d90fe22;
    }
    .rule-title {
      text-align: center;
      font-weight: bold;
      font-size: 1.3em;
      margin-bottom: 10px;
      color: #1976d2;
    }
    .rule-text {
      text-align: left;
      font-size: 1.09em;
      line-height: 1.65;
      color: #174078;
      letter-spacing: 0.01em;
    }
    .btn {
      display: block;
      margin: 28px auto 0 auto;
      padding: 14px 50px;
      font-size: 1.2em;
      border: none;
      border-radius: 28px;
      background: linear-gradient(90deg, #87c9fa, #1565c0);
      color: #fff;
      font-weight: bold;
      box-shadow: 0 2px 8px #1976d266;
      cursor: pointer;
      transition: background 0.2s;
    }
    .btn:hover { background: linear-gradient(90deg, #b7e4fe, #1976d2); }
    .game-area {
      width: 740px;
      margin: 36px auto 0 auto;
      background: rgba(240,248,255,0.96);
      border-radius: 16px;
      box-shadow: 0 1px 10px #1565c055;
      display: flex;
      flex-direction: column;
      align-items: center;
      min-height: 320px;
      padding: 16px 0 20px 0;
      user-select: none;
    }
    .info {
      text-align: center;
      color: #1976d2;
      font-size: 1.19em;
      margin: 5px 0 0 0;
    }
    .panel-board {
      display: grid;
      grid-template-columns: repeat(5, 65px);
      grid-gap: 16px 20px;
      justify-content: center;
      margin-top: 32px;
      margin-bottom: 10px;
    }
    .panel-cell {
      width: 65px; height: 65px;
      background: #eee;
      border: 2.5px solid #1976d2;
      border-radius: 14px;
      box-shadow: 0 2px 8px #1976d266;
      cursor: pointer;
      font-size: 2.1em;
      display: flex; align-items: center; justify-content: center;
      transition: background 0.14s, border-color 0.14s;
      user-select: none;
      position: relative;
      outline: none;
    }
    .panel-cell.on {
      background: #fffde7;
      border-color: #ffd600;
      color: #ffab00;
      box-shadow: 0 0 12px #ffe08277;
    }
    .panel-cell:active {
      background: #c3fdff;
      transform: scale(0.96);
    }
    .message-box {
      text-align: center;
      background: rgba(235,245,255,0.98);
      font-size: 1.7em;
      color: #1976d2;
      font-weight: bold;
      border-radius: 16px;
      box-shadow: 0 4px 24px #1976d233;
      width: 80%;
      max-width: 480px;
      margin: 34px auto 0 auto;
      padding: 32px 18px;
      position: relative;
      z-index: 2;
    }
    .center-btn { margin: 22px auto 0 auto; }
  </style>
</head>
<body>
  <div class="container" id="main-container"></div>
  <script>
    // ==========================================
    // 💡 パネル反転チャレンジ(ライトアウト風)
    // ==========================================

    const ROWS = 5, COLS = 5;
    let panel = [];
    let moveCount = 0;

    // タイトル画面表示
    function showTitleScreen() {
      document.getElementById('main-container').innerHTML = `
        <h1>💡 パネル反転チャレンジ 💡</h1>
        <img src="panel_flip_challenge_title.png" class="title-img" alt="パネル反転チャレンジ タイトル">
        <div class="rule-section">
          <div class="rule-title">💡 遊び方・ルール 💡</div>
          <div class="rule-text">
            ・クリックしたパネルと、その上下左右のパネルが反転します。<br>
            ・すべてのパネルを消灯(OFF)できればクリア!<br>
            ・手数(クリック回数)が少ないほど高難度!
          </div>
        </div>
        <button class="btn" id="start-btn">スタート</button>
      `;
      document.getElementById('start-btn').onclick = startGame;
    }

    // ゲーム開始
    function startGame() {
      // ランダムな状態からスタート
      panel = Array.from({length: ROWS}, ()=>Array(COLS).fill(false));
      // 適当にランダムな手でかき混ぜる
      for(let i=0; i<12; i++) {
        let r = Math.floor(Math.random()*ROWS), c = Math.floor(Math.random()*COLS);
        flipPanel(r, c, false);
      }
      moveCount = 0;
      showGameScreen();
    }

    // ゲーム画面
    function showGameScreen() {
      let html = `<h1>💡 パネル反転チャレンジ 💡</h1>
      <div class="game-area">
        <div class="info">パネルをクリックして全てOFFにしよう!(手数: ${moveCount})</div>
        <div class="panel-board" id="panel-board"></div>
      </div>
      `;
      document.getElementById('main-container').innerHTML = html;
      renderBoard();
    }

    // パネル描画
    function renderBoard() {
      let html = "";
      for(let r=0; r<ROWS; r++) {
        for(let c=0; c<COLS; c++) {
          html += `<div class="panel-cell${panel[r][c] ? " on" : ""}" onclick="clickPanel(${r},${c})">${panel[r][c] ? "💡" : ""}</div>`;
        }
      }
      document.getElementById('panel-board').innerHTML = html;
    }

    // パネル反転処理
    function flipPanel(r, c, user=true) {
      // 範囲内の時だけ反転
      function flip(rr, cc){
        if(rr>=0 && rr<ROWS && cc>=0 && cc<COLS){
          panel[rr][cc] = !panel[rr][cc];
        }
      }
      flip(r, c); // 自分
      flip(r-1, c); // 上
      flip(r+1, c); // 下
      flip(r, c-1); // 左
      flip(r, c+1); // 右

      if(user) {
        moveCount++;
        showGameScreen();
        if(checkClear()) {
          setTimeout(showEndScreen, 400);
        }
      }
    }

    // クリック時
    window.clickPanel = function(r, c) {
      flipPanel(r, c);
    };

    // 全OFFかチェック
    function checkClear() {
      return panel.flat().every(v=>!v);
    }

    // 終了画面
    function showEndScreen() {
      document.getElementById('main-container').innerHTML = `
        <h1>💡 パネル反転チャレンジ 💡</h1>
        <img src="panel_flip_challenge_title.png" class="title-img" alt="パネル反転チャレンジ タイトル">
        <div class="message-box">
          🎉 全てのパネルを消灯しました!<br>あなたの手数:<b>${moveCount}</b>
        </div>
        <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button>
      `;
      document.getElementById('back-title-btn').onclick = showTitleScreen;
    }

    // 初期表示
    showTitleScreen();
  </script>
</body>
</html>

アルゴリズムの流れ

ステップ関数処理内容
1showTitleScreenタイトル画面の描画。ルール&スタートボタン設置。
2startGame盤面を全OFFで初期化→ランダムに12手で反転 → 手数リセット → ゲーム画面表示
3showGameScreenゲーム画面HTML生成 → renderBoard() 呼び出し
4renderBoardpanel 配列を読み込み、各パネル要素をON/OFF状態に応じて描画
5clickPanelクリック時に flipPanel(r,c,true) を呼び出し
6flipPanel指定パネル+上下左右を反転。user===true なら手数+画面更新+クリア判定
7checkClear全パネルがOFFか判定
8showEndScreenクリアメッセージ+手数表示+タイトルに戻るボタン設置

関数の詳細

関数名役割説明
showTitleScreen()タイトル画面描画初期画面をDOM生成。スタートボタンのクリックで startGame()
startGame()ゲーム初期化&開始全OFF盤面を用意し、ランダムに12回反転してからゲーム画面を表示
showGameScreen()ゲーム画面生成&描画スコア表示と盤面ボード用コンテナを生成、renderBoard() 実行
renderBoard()盤面(パネル)の描画panel の二次元配列を走査し、ON/OFFに応じたパネルDIVを生成しDOMに埋め込む
flipPanel(r,c,user)パネル反転ロジッククリック or 初期シャッフル用に指定パネルと上下左右を反転。user=true時は手数加算・画面更新・クリア判定
clickPanel(r,c)クリックイベントハンドラパネルクリック時に flipPanel(r,c,true) を呼び出し
checkClear()クリア判定盤面がすべてOFF(false)の場合に true を返す
showEndScreen()終了画面描画クリアメッセージと手数を表示、タイトルに戻るボタン設置

改造のポイント

  • 難易度設定:初期シャッフル回数(ここでは12)を増減して複雑さを調整。少なめで初心者向け、多めで上級者向けに。
  • 盤面サイズの変更ROWSCOLS を変更して 4×4 や 6×6 モードを追加。
  • 手数制限モード:制限手数以内でクリアしないとゲームオーバーにするチャレンジモードを実装。
  • 演出強化:反転時のアニメーション(CSSアニメ or JavaScriptでトランジション)、サウンド効果を追加。
  • 段階ステージ:複数ステージを用意し、ステージクリアごとにシャッフル回数や盤面を変える「ステージ制」にすると継続プレイ性UP。

アドバイス
まずは反転ロジックとクリア判定をしっかりテストし、安定動作を確保。その後、難易度や演出を段階的に強化すると、ユーザーのモチベーションを維持しやすくなります!