【ゲーム】JavaScript:36 じゃんけん(あっち向いてホイ)

 「✊✌️✋ & あっち向いてホイ!」は、まず「じゃんけん」で勝負し、勝者が「相手の顔を向かせる向き」を選ぶ“あっち向いてホイ”を仕掛ける二段構えの対戦ゲームです。先に3点を獲得したプレイヤーまたはCPUが勝利となります。

遊び方・操作方法

  1. タイトル画面で「スタート ▶️」をクリック。
  2. じゃんけんフェーズ:✊・✌️・✋ のいずれかをクリック。
  3. 勝敗がつくと あっち向いてホイ フェーズ に移行。
  4. 勝者(じゃんけんに勝った側)は「👈👆👇👉」のいずれかをクリック、敗者は対応する絵文字をクリック。
  5. 向きが一致すれば勝者に1点。どちらかが3点に達したらゲーム終了。

ルール

  • じゃんけん
    ・グー(✊)はチョキ(✌️)に勝ち、チョキはパー(✋)に勝つ。
    ・同じ手なら引き分けでもう一度。
  • あっち向いてホイ
    ・じゃんけんの勝者が「指差し方向(👈👆👇👉)」を選び、敗者は対応する絵文字(🙂‍↔️👈 など)を選択。
    ・クリックした向きが一致したら勝者に1点入る。
  • 勝利条件
    ・先に3点を獲得した方が最終勝利。

🎮ゲームプレイ

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

36 じゃんけん(あっち向いてホイ)

素材のダウンロード

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

janken_look_away_title.pngjanken_look_away_bg.png

ゲーム画面イメージ

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

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>✊✌️✋ & あっち向いてホイ ゲーム 🎮</title>
  <style>
    /* ================= 全体リセット ================= */
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      /* 背景画像 */
      background: url('janken_look_away_bg.png') no-repeat center center fixed;
      background-size: cover;
      font-family: 'Arial', sans-serif;
      color: #333;
      height: 100vh;
      user-select: none;
      position: relative;
    }
    .hidden {
      display: none;
    }

    /* ================= オーバーレイ共通 ================= */
    .overlay {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(255,255,255,0.95);
      padding: 20px;
      border-radius: 10px;
      width: 700px;
      max-width: 95%;
      box-shadow: 0 4px 8px rgba(0,0,0,0.3);
      z-index: 10;
    }

    /* ================= ボタン共通 ================= */
    .btn {
      display: block;              /* ブロック要素にしてセンタリング用 */
      margin: 20px auto 0;         /* 上に余白、左右自動で中央寄せ */
      padding: 10px 20px;
      font-size: 1em;
      font-weight: bold;
      color: #fff;
      background-color: #007bff;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      transition: background-color 0.2s, transform 0.1s;
    }
    .btn:hover {
      background-color: #0056b3;
      transform: translateY(-2px);
    }
    .btn:active {
      transform: translateY(0);
    }

    /* ================= タイトル画面 ================= */
    #title-screen h1 {
      text-align: center;
      font-size: 2em;
      margin-bottom: 10px;
    }
    #title-screen .title-image {
      display: block;
      margin: 0 auto 10px;
      max-width: 80%;
    }
    #title-screen .instructions-title {
      text-align: center;
      font-weight: bold;
      margin-top: 10px;
    }
    #title-screen .instructions {
      text-align: left;
      margin: 10px 0;
      line-height: 1.4;
    }

    /* ================= ゲーム画面 ================= */
    #game-screen {
      width: 700px;
      max-width: 95%;
      margin: 0 auto;
      text-align: center;
      padding-top: 40px;
    }
    .game-area {
      background: rgba(255,255,255,0.85);
      padding: 30px;
      border-radius: 10px;
      box-shadow: 0 4px 8px rgba(0,0,0,0.2);
    }
    #scoreboard {
      font-size: 1.6em;
      margin-bottom: 20px;
    }
    #choices {
      margin-top: 20px;
    }
    .choice-panel {
      display: inline-block;
      width: 100px;
      height: 100px;
      margin: 0 15px;
      font-size: 3em;
      line-height: 100px;
      text-align: center;
      border: 2px solid #333;
      border-radius: 10px;
      background: rgba(255,255,255,0.8);
      cursor: pointer;
      transition: background-color 0.2s, transform 0.1s;
    }
    .choice-panel:hover {
      background-color: rgba(0,123,255,0.2);
      transform: translateY(-3px);
    }
    #result-message {
      margin-top: 30px;
      padding: 12px 20px;
      display: inline-block;
      background: rgba(0,0,0,0.7);
      color: #fff;
      border-radius: 5px;
      font-size: 1.3em;
      text-align: center;
    }

    /* ================= 終了画面 ================= */
    #end-screen h2 {
      text-align: center;
      font-size: 2.5em;          /* メッセージを大きく */
      margin-bottom: 20px;
    }
    #end-screen .game-message {
      text-align: center;
      font-size: 1.5em;
      margin-bottom: 20px;
    }
  </style>
</head>
<body>
  <!-- ================================= タイトル画面 ================================= -->
  <div id="title-screen" class="overlay">
    <img src="janken_look_away_title.png" alt="タイトル" class="title-image" />
    <h1>✊✌️✋ & あっち向いてホイ!🎮</h1>
    <div class="instructions-title">📖 遊び方・ルール</div>
    <div class="instructions">
      <p>1. 「✊✌️✋」でじゃんけん</p>
      <p>2. 勝った方が「あっち向いてホイ!」を仕掛ける</p>
      <p>3. 勝者は「👈」「👆」「👇」「👉」を選択</p>
      <p>4. 敗者は「🙂‍↔️👈」「🙄👆」「🙂‍↕️👇」「😏👉」を選択</p>
      <p>5. 同じ向きなら勝者に1点</p>
      <p>6. 先に3点獲得した方がゲームクリア!</p>
    </div>
    <!-- スタートボタン(中央配置済み) -->
    <button id="start-btn" class="btn">スタート ▶️</button>
  </div>

  <!-- ================================= ゲーム画面 ================================= -->
  <div id="game-screen" class="hidden">
    <div class="game-area">
      <!-- スコア表示 -->
      <div id="scoreboard">👨‍🦱: 0 vs 🤖: 0</div>
      <!-- 選択パネル配置 -->
      <div id="choices"></div>
      <!-- 判定結果メッセージ -->
      <div id="result-message">✊✌️✋でじゃんけん!</div>
    </div>
  </div>

  <!-- ================================= 終了画面 ================================= -->
  <div id="end-screen" class="overlay hidden">
    <!-- ゲームクリアメッセージ(大きめフォント) -->
    <h2>🎉 ゲームクリア 🎉</h2>
    <div id="end-message" class="game-message">---</div>
    <!-- タイトルへ戻るボタン(中央配置済み) -->
    <button id="end-back-btn" class="btn">タイトルへ戻る ↩️</button>
  </div>

  <script>
    // 先に何点取ったら勝ちか
    const WIN_TARGET = 3;
    // じゃんけんの手
    const HANDS_RPS  = ['✊','✌️','✋'];
    // あっち向いてホイ 勝者用方向
    const HANDS_DIR  = ['👈','👆','👇','👉'];
    // あっち向いてホイ 敗者用表現
    const EMOS_DIR   = ['🙂‍↔️👈','🙄👆','🙂‍↕️👇','😏👉'];

    let playerScore = 0, cpuScore = 0;  // スコア
    let state = 'rps';                 // 現在のフェーズ:'rps' or 'dir'
    let lastWinner = '';               // 直前のじゃんけん勝者

    // 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 choicesDiv  = document.getElementById('choices');
    const scoreboard  = document.getElementById('scoreboard');
    const resultMsg   = document.getElementById('result-message');
    const endBackBtn  = document.getElementById('end-back-btn');
    const endMsg      = document.getElementById('end-message');

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

    /**
     * ゲーム画面を表示し、初期化する
     */
    function showGame() {
      titleScreen.classList.add('hidden');
      gameScreen.classList.remove('hidden');
      endScreen.classList.add('hidden');
      initGame();
      renderChoices();
    }

    /**
     * 終了画面を表示する
     * @param {string} winner - 'player' か 'cpu'
     * @param {string} ph - プレイヤーの表示手
     * @param {string} ch - CPUの表示手
     */
    function showEnd(winner, ph, ch) {
      gameScreen.classList.add('hidden');
      endScreen.classList.remove('hidden');
      endMsg.textContent =
        winner === 'player'
          ? `👨‍🦱${ph} vs 🤖${ch} → プレイヤーの勝ち!`
          : `👨‍🦱${ph} vs 🤖${ch} → CPUの勝ち…`;
    }

    /**
     * ゲームを初期状態に戻す
     */
    function initGame() {
      playerScore = cpuScore = 0;
      state = 'rps';
      lastWinner = '';
      updateScore();
      resultMsg.textContent = '✊✌️✋でじゃんけん!';
    }

    /**
     * スコアボードを更新する
     */
    function updateScore() {
      scoreboard.textContent = `👨‍🦱: ${playerScore} vs 🤖: ${cpuScore}`;
    }

    /**
     * 選択パネル(じゃんけん or あっち向いてホイ)を描画する
     */
    function renderChoices() {
      choicesDiv.innerHTML = '';
      if (state === 'rps') {
        // じゃんけんフェーズ
        HANDS_RPS.forEach((h, i) => {
          const btn = document.createElement('div');
          btn.className = 'choice-panel';
          btn.textContent = h;
          btn.dataset.choice = i;
          btn.addEventListener('click', onRps);
          choicesDiv.appendChild(btn);
        });
      } else {
        // あっち向いてホイ フェーズ
        const arr = lastWinner === 'player' ? HANDS_DIR : EMOS_DIR;
        arr.forEach((h, i) => {
          const btn = document.createElement('div');
          btn.className = 'choice-panel';
          btn.textContent = h;
          btn.dataset.choice = i;
          btn.addEventListener('click', onDir);
          choicesDiv.appendChild(btn);
        });
      }
    }

    /**
     * じゃんけんクリック時の処理
     */
    function onRps(e) {
      const p = +e.currentTarget.dataset.choice;
      const c = Math.floor(Math.random() * 3);
      const ph = HANDS_RPS[p], ch = HANDS_RPS[c];

      // 引き分けならもう一度
      if (p === c) {
        resultMsg.innerHTML = `👨‍🦱${ph} vs 🤖${ch} → 引き分け!<br>もう一度じゃんけん!`;
        return;
      }

      // 勝敗判定
      if ((p - c + 3) % 3 === 1) {
        lastWinner = 'player';
        resultMsg.innerHTML = `👨‍🦱${ph} vs 🤖${ch} → じゃんけん勝ち!<br>🙋‍♂️あっち向いて…ホイ!`;
      } else {
        lastWinner = 'cpu';
        resultMsg.innerHTML = `👨‍🦱${ph} vs 🤖${ch} → じゃんけん負け…<br>🙋‍♂️あっち向いて…ホイ!`;
      }

      // あっち向いてホイ フェーズへ移行
      state = 'dir';
      renderChoices();
    }

    /**
     * あっち向いてホイクリック時の処理
     */
    function onDir(e) {
      const idx = +e.currentTarget.dataset.choice;
      const cpuIdx = Math.floor(Math.random() * 4);
      let ph, ch;

      if (lastWinner === 'player') {
        ph = HANDS_DIR[idx];
        ch = EMOS_DIR[cpuIdx];
      } else {
        ph = EMOS_DIR[idx];
        ch = HANDS_DIR[cpuIdx];
      }

      let msg = `🙋‍🦱あっち向いて…ホイ!<br>👨‍🦱${ph} vs 🤖${ch} → `;

      if (idx === cpuIdx) {
        // 勝敗確定:ポイントを加算
        if (lastWinner === 'player') {
          playerScore++;
          msg += 'プレイヤー +1点!';
        } else {
          cpuScore++;
          msg += 'CPU +1点!';
        }
        updateScore();

        // クリア判定
        if (playerScore === WIN_TARGET || cpuScore === WIN_TARGET) {
          setTimeout(() => {
            showEnd(
              playerScore === WIN_TARGET ? 'player' : 'cpu',
              ph, ch
            );
          }, 500);
          return;
        }

        // 次のじゃんけんへ自動移行
        resultMsg.innerHTML = msg;
        document.querySelectorAll('.choice-panel').forEach(b => {
          b.style.pointerEvents = 'none';
        });
        setTimeout(() => {
          state = 'rps';
          renderChoices();
          resultMsg.textContent = '✊✌️✋でじゃんけん!';
        }, 1500);
      } else {
        // ミス:自動的に次のじゃんけん
        resultMsg.innerHTML = msg + 'ミス!得点なし';
        document.querySelectorAll('.choice-panel').forEach(b => {
          b.style.pointerEvents = 'none';
        });
        setTimeout(() => {
          state = 'rps';
          renderChoices();
          resultMsg.textContent = '✊✌️✋でじゃんけん!';
        }, 1500);
      }
    }

    // イベント登録
    startBtn.addEventListener('click', showGame);
    endBackBtn.addEventListener('click', showTitle);
    document.addEventListener('DOMContentLoaded', showTitle);
  </script>
</body>
</html>

アルゴリズムの流れ

ステップフェーズ概要
1初期表示タイトル画面を showTitle() で表示
2ゲーム開始「スタート」で showGame()initGame()renderChoices()
3じゃんけんonRps():プレイヤーとCPUの手をランダム生成・判定 → state='dir' → 選択UIを更新
4あっち向いてonDir():プレイヤー・CPUの方向を選択 → 一致なら勝者に1点加算 → 勝利判定 or フェーズ戻り
5終了先に3点獲得したら showEnd() で終了画面へ

関数の詳細解説

関数名役割
showTitle()タイトル画面を表示し、他の画面を非表示に設定します。
showGame()ゲーム画面を表示し、状態を初期化して選択パネルを描画します。
initGame()スコア & フェーズを初期化し、メッセージとスコアボードをリセットします。
updateScore()現在のスコアを画面上の #scoreboard に反映します。
renderChoices()フェーズに応じて「じゃんけん」または「あっち向いてホイ」のボタンを動的に生成します。
onRps()じゃんけんの勝敗を判定し、フェーズを dir に切り替えます。
onDir()あっち向いてホイの向き一致を判定し、得点加算・終了判定・次ステップ移行を行います。
showEnd()終了画面を表示し、勝者のメッセージをセットします。

改造のポイント

  • 勝利条件の変更
    const WIN_TARGET = 3; を変えるだけで「5点制」などに対応可能です。
  • UIのカスタマイズ
    背景やボタンの色、絵文字を自由に差し替えて、オリジナルデザインに仕立てましょう。
  • 効果音&アニメ
    勝敗時にサウンドを鳴らしたり、ボタンにフリップアニメーションを加えるとより楽しくなります。
  • スコア履歴
    localStorage を使い、各プレイのスコアや連勝記録を保存してハイスコア機能を追加できます。
  • 対戦モード拡張
    WebSocket や Firebase Realtime Database と連携すれば、オンラインマルチプレイヤー化も可能です。

アドバイス
じゃんけん&あっち向いてホイはプログラミング入門者にもぴったりの題材。この記事を元に、ぜひ自分なりの機能追加や演出強化にチャレンジしてみてください!