// blog

ハイ&ロー(カード数字当てゲーム)

★☆☆

このゲームは「次のカードは今より高い(High)か低い(Low )か」を当てるシンプルなゲームです。
画像なし・外部ライブラリなし——Math.random() と if 文だけで動く、最小構成のゲームを一緒に作りましょう。

学べること

  • HTML:ボタン要素・カード表示エリアの構造化
  • CSS:カードのデザイン+裏返りアニメーション(@keyframe s)
  • JavaScript:乱数生成(Math.random())・条件分岐 (if / else if)・勝敗カウント

完成イメージ:1〜13の数字が書かれたカードが表示され、「▲ High」か「▼ Low」を選ぶと次のカードがめくれて正解・不正解が表示されます。

ステップ1:HTMLの骨格を作る

1

ゲームに必要な要素は4つです。まず全体像を把握してから書き始めましょう。

  • 現在のカード番号を表示する #current-card
  • 「High」「Low」を選ぶ2つのボタン
  • 正解・不正解を表示する #result
  • スコア(正解数・不正解数)を表示する #score
HTML
<div id="game">
    <p id="score">正解: 0 / 不正解: 0</p>

    <div id="card-area">
      <div id="current-card">?</div>
    </div>

    <p id="message">次のカードは高い?低い?</p>

    <div id="btn-area">
      <button onclick="guess('high')">▲ High</button>
      <button onclick="guess('low')">▼ Low</button>
    </div>

    <p id="result"></p>
    <button id="btn-next" onclick="nextRound()" style="display:none">次のカード
  ▶</button>
    <button onclick="resetGame()">↺ リセット</button>
  </div>

style="display:none" を最初からつけておくことで、ゲーム開始時に 「次のカード」ボタンを隠せます。ボタンを押したときに JavaScript で表示・非表示を切り替えます。

ステップ2:CSSでカードをデザインする

2

カードらしく見えるようにスタイルを整えます。テーマのカラー変数(--primar y・--accent など)を活用することで、サイト全体のデザインと統一感を出せます。

CSS(追加CSSとして記述)
#game {
    text-align: center;
    padding: 2rem 1rem;
    font-family: 'Share Tech Mono', monospace;
    color: var(--text);
  }
  #card-area {
    margin: 1.5rem auto;
    width: 120px;
    height: 160px;
  }
  #current-card {
    width: 120px;
    height: 160px;
    border: 3px solid var(--primary);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 3rem;
    font-family: 'Press Start 2P', monospace;
    color: var(--primary);
    background: var(--bg-mid);
    box-shadow: 0 0 16px rgba(0,255,65,0.4);
  }
  /* アニメーションクラス(JS から付与) */
  #current-card.flip {
    animation: cardFlip 0.4s ease;
  }
  #btn-area {
    display: flex;
    gap: 1rem;
    justify-content: center;
    margin: 1rem 0;
  }
  #btn-area button {
    padding: 0.7rem 1.8rem;
    font-family: 'Press Start 2P', monospace;
    font-size: 0.75rem;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: transform 0.1s, opacity 0.2s;
  }
  #btn-area button:first-child { background: var(--primary); color: var(--bg-darkest);
  }
  #btn-area button:last-child  { background: var(--accent);  color: var(--bg-darkest);
  }
  #btn-area button:hover { opacity: 0.85; transform: scale(1.05); }
  #result {
    font-family: 'Press Start 2P', monospace;
    font-size: 1.1rem;
    min-height: 2rem;
    margin: 0.5rem 0;
  }
  #btn-next,
  #game > button:last-of-type {
    margin: 0.4rem;
    padding: 0.5rem 1.4rem;
    font-family: 'Share Tech Mono', monospace;
    background: var(--bg-mid);
    color: var(--text);
    border: 1px solid var(--primary);
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
  }
  #btn-next:hover,
  #game > button:last-of-type:hover {
    background: var(--primary);
    color: var(--bg-darkest);
  }

カードがめくれるアニメーション(@keyframes cardFlip)は「追加CSS」の末尾に記述します。

ステップ3:乱数でカードの数字を生成する

3

Math.random() は 0以上1未満のランダムな小数を返します。これを1〜1 3の整数に変換する手順を確認しましょう。

JavaScript
// 1〜13 のランダムな整数を返す関数
  function getCard() {
    return Math.floor(Math.random() * 13) + 1;
  }

仕組みを1行ずつ分解すると:
Math.random() ——例:0.742…
× 13 ——→ 0 〜 12.999…
Math.floor() ——→ 0 〜 12(小数切り捨て)
+ 1 ——→ 1 〜 13

ブラウザの開発者ツール(F12 → コンソール)で何度か実行して確認してみましょう。

コンソールで動作確認
Math.floor(Math.random() * 13) + 1
  // → 実行するたびに 1〜13 のどれかが返ってくる

ステップ4:if 文で勝敗を判定する

4

プレイヤーの選択('high' or 'low')と、次に生成され たカードの数字を比較して正解・不正解を決めます。

判定条件を整理すると次のようになります:

  • 「High を選んだ」かつ「次 > 現在」→ 正解
  • 「Low を選んだ」かつ「次 < 現在」→ 正解
  • それ以外(同じ数字を含む)→ 不正解
JavaScript
function guess(choice) {
    const nextNum = getCard();   // 次のカードを生成

    // 2つの条件を && で同時に確認する
    let win = false;
    if (choice === 'high' && nextNum > currentNum) {
      win = true;
    } else if (choice === 'low' && nextNum < currentNum) {
      win = true;
    }

    // 結果を表示
    const resultEl = document.getElementById('result');
    if (win) {
      resultEl.textContent = '✔ 正解!';
      resultEl.style.color = 'var(--primary)';
      wins++;
    } else {
      resultEl.textContent = '✘ ハズレ';
      resultEl.style.color = '#ff4444';
      losses++;
    }

    currentNum = nextNum;  // カードを次の数字に更新
  }

&&(アンド)は「AかつB」を表す論理演算子です。左右どちらかが false なら全体が false になります。「High を選んだだけ」「次の数字が大きいだけ」では正解になりません。

ステップ5:完成コードをまとめる

5

ここまでのコードをひとつのHTMLファイルにまとめた完成版です。そのままローカルに 保存してブラウザで開けば動きます。

index.html(完成版)
<!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ハイ&ロー</title>
    <style>
      body {
        background: #0a0a0a;
        color: #c8c8c8;
        font-family: 'Courier New', monospace;
        display: flex;
        justify-content: center;
        padding: 2rem;
      }
      #game { text-align: center; }
      #current-card {
        width: 120px; height: 160px;
        margin: 1rem auto;
        border: 3px solid #00ff41;
        border-radius: 10px;
        display: flex; align-items: center; justify-content: center;
        font-size: 3rem; color: #00ff41; background: #111;
        animation: none;
      }
      #current-card.flip { animation: cardFlip 0.4s ease; }
      @keyframes cardFlip {
        0%   { transform: scaleX(1); opacity: 1; }
        40%  { transform: scaleX(0); opacity: 0.2; }
        60%  { transform: scaleX(0); opacity: 0.2; }
        100% { transform: scaleX(1); opacity: 1; }
      }
      #btn-area { display: flex; gap: 1rem; justify-content: center; margin: 1rem 0; }
      button { padding: 0.7rem 1.8rem; font-size: 0.9rem; border: none; border-radius:
  6px; cursor: pointer; }
      #btn-area button:first-child { background: #00ff41; color: #050505; }
      #btn-area button:last-child  { background: #ffd700; color: #050505; }
      #result { font-size: 1.2rem; min-height: 2rem; }
      #btn-next, #btn-reset {
        background: #111; color: #c8c8c8;
        border: 1px solid #00ff41; margin: 0.3rem;
      }
    </style>
  </head>
  <body>
  <div id="game">
    <h1 style="font-size:1.2rem; color:#00ff41;">ハイ&ロー</h1>
    <p id="score">正解: 0 / 不正解: 0</p>
    <div id="current-card">?</div>
    <p>次のカードは高い?低い?</p>
    <div id="btn-area">
      <button onclick="guess('high')">▲ High</button>
      <button onclick="guess('low')">▼ Low</button>
    </div>
    <p id="result"></p>
    <button id="btn-next" onclick="nextRound()" style="display:none">次のカード
  ▶</button>
    <button id="btn-reset" onclick="resetGame()">↺ リセット</button>
  </div>

  <script>
  let currentNum = getCard();
  let wins = 0, losses = 0;
  document.getElementById('current-card').textContent = currentNum;

  function getCard() {
    return Math.floor(Math.random() * 13) + 1;
  }
  function guess(choice) {
    const nextNum = getCard();
    const win = (choice === 'high' && nextNum > currentNum)
             || (choice === 'low'  && nextNum < currentNum);

    // カードフリップ
    const cardEl = document.getElementById('current-card');
    cardEl.classList.add('flip');
    setTimeout(() => {
      cardEl.textContent = nextNum;
      cardEl.classList.remove('flip');
    }, 200);

    const resultEl = document.getElementById('result');
    if (win) {
      wins++;
      resultEl.textContent = '✔ 正解!';
      resultEl.style.color = '#00ff41';
    } else {
      losses++;
      resultEl.textContent = '✘ ハズレ';
      resultEl.style.color = '#ff4444';
    }
    currentNum = nextNum;
    document.getElementById('score').textContent =
      '正解: ' + wins + ' / 不正解: ' + losses;
    document.getElementById('btn-area').style.display = 'none';
    document.getElementById('btn-next').style.display = 'inline-block';
  }
  function nextRound() {
    document.getElementById('result').textContent = '';
    document.getElementById('btn-area').style.display = 'flex';
    document.getElementById('btn-next').style.display = 'none';
  }
  function resetGame() {
    wins = 0; losses = 0;
    currentNum = getCard();
    document.getElementById('current-card').textContent = currentNum;
    document.getElementById('score').textContent = '正解: 0 / 不正解: 0';
    document.getElementById('result').textContent = '';
    document.getElementById('btn-area').style.display = 'flex';
    document.getElementById('btn-next').style.display = 'none';
  }
  </script>
  </body>
  </html>

完成デモ

正解: 0 / 不正解: 0

?

次のカードは高い?低い?

応用・発展のヒント

  • 連続正解ボーナス:正解が続くとスコアが倍になる「コンボシステ ム」を追加してみましょう。変数 combo を用意して正解のたびにインクリメント、不正解でリセットします。
  • スート(マーク)を追加:♠♥♦♣ をランダムに表示するとよりリアルなカードになります。配列から Math.random() で要素を選ぶ練習になります。
  • デッキ管理:52枚のデッキを用意して一度出たカードを取り除くと 、より本格的なゲームになります(Array.splice() の練習に最適)。
  • ハイスコア保存:localStorage を使えばページをリロードしても記録が残ります。localStorage.setItem() / getItem() を調べてみましょう。

// work with me

記事のような仕組みづくりを、お仕事としても承っています。

お仕事のご依頼について