【JavaScript】簡単な数字当てゲームを作ってみた

JavaScriptのチュートリアルを通して簡単なゲームを作ってみたので、学んだことを忘れないうちにメモとして残しておこうと思います。

今回作成したゲームは「数当てゲーム」で、1から100までの数字の中からランダムに割り当てられる数字を10回以内に当てるというものです。

(↓以下がデモになります)

数字当てゲーム

1 から 100 までの数字を当ててみて!10 回以内に当てられるでしょうか。選んだ数字が大きいか小さいかを表示します。

内容はMDNのチュートリアルとほぼ同じですが、少しだけアレンジを加えています。

それでは参ります。

目次

数当てゲームの全体像

数当てゲームを作成する前に、まずはゲームの動作およびコードの全体像を見てみましょう。

10回以内に正解した場合の動作
全て不正解だった場合の動作

まずは、ゲームの動作の流れについて以下に記します。

  1. 1から100までの中からランダムな数字を1つ生成する
  2. プレイヤーが数字を予想して回答する(フォームに入力)
  3. プレイヤーが予想を回答した回数を記録する(最初は1回から)
  4. 予想が入力されたら、プレイヤーが以前の予想を見られるように、画面上に表示(記録)する
  5. 入力された数字(予想)が正しいかどうか判定する
  6. 入力された数字が正しい場合→お祝いメッセージを表示、入力フォームを非活性化する、リセットボタンを表示する
  7. 入力された数字が間違いの場合→間違いメッセージを表示、予想回数に1を加算する
  8. 入力された数字が間違いで、予想回数の上限に達した場合→ゲームオーバー、入力フォームを非活性化する、リセットボタンを表示して新しいゲームを再開できるようにする
  9. ゲームが再開したら、画面とロジックがリセットされるようにする(①に戻る)

このような処理をJavaScriptで書いていきます。

今回はindex.html内に用意した<script>タグ内にまとめてJavaScriptのコードを書きました。

<h1>数字当てゲーム</h1>
<p>1 から 100 までの数字を当ててみて!10 回以内に当てられるでしょうか。選んだ数字が大きいか小さいかを表示します。</p>

<div class="form">
  <label for="guessField">予想を入力してください: </label>
  <input type="text" id="guessField" class="guessField">
  <input type="submit" value="予想を入力" class="guessSubmit">
</div>

<!-- JavaScriptで返す値を追加するための要素 -->
<div class="resultParas">
  <p class="guesses"></p>
  <p class="lastResult"></p>
  <p class="lowOrHi"></p>
  <p class="answer"></p>
</div>

<!-- リセットボタン用のフィールド(ゲームが終了したら表示) -->
<div class="resetField"></div>

<!-- JavaScript -->
<script>
  let randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成
  
  // html の各要素を取得する
  const guesses = document.querySelector('.guesses');
  const lastResult = document.querySelector('.lastResult');
  const lowOrHi = document.querySelector('.lowOrHi');
  const answer = document.querySelector('.answer');
  const guessSubmit = document.querySelector('.guessSubmit'); // 予想を入力ボタンの要素
  const guessField = document.querySelector('.guessField'); // 入力フォーム(<input>)の要素
  const resetField = document.querySelector('.resetField'); // リセットボタンの要素

  let guessCount = 1; // 予想回数(初期値は1)
  let resetButton; // リセットボタンを定義

  function checkGuess() { // 入力された予想の正誤判定

    const userGuess = Number(guessField.value); // ユーザーが入力した値(入力フォームのvalueから取得)
    if(guessCount === 1) {
      guesses.textContent = '前回の予想: '; // 予想が入力されたら左記テキストを挿入
    }
    guesses.textContent += userGuess + ' '; // ユーザーが入力した値を挿入
    
    if(userGuess === randomNumber) { // 正解した場合
      lastResult.textContent = '正解です!おめでとう!!';
      lastResult.style.backgroundColor = 'green';
      lastResult.style.color = 'white';
      lowOrHi.textContent = '';
      setGameOver(); // ゲームオーバー時の処理を行う(後述)
    
    } else if(guessCount === 10) { // 予想回数が10回に達した場合
      lastResult.textContent = '!!!ゲームオーバー!!!';
      answer.textContent = `正解は ${randomNumber} でした`;
      answer.style.color = 'blue';
      setGameOver(); // ゲームオーバー時の処理を行う(後述)
    
    } else { // 入力した予想が間違っている、かつ予想回数が10回未満の場合
      lastResult.textContent = '間違いです!';
      lastResult.style.backgroundColor = 'red';
      lastResult.style.color = 'white';
      if(userGuess < randomNumber) {
        lowOrHi.textContent = 'もっと大きい値です!';
      } else if(userGuess > randomNumber) {
        lowOrHi.textContent = 'もっと小さい値です!';
      }
    }

    guessCount++; // 予想回数に1を加算する
    guessField.value = ''; // 入力フォームを空にする
    guessField.focus(); // 入力フォームにフォーカスを設定する
  }
  guessSubmit.addEventListener('click', checkGuess); // 予想を入力ボタンをクリックするとcheckGuess()関数が発火する

  function setGameOver() { // ゲームオーバー時の処理を行う関数
	  guessField.disabled = true; // 入力フォームを利用不可にする
	  guessSubmit.disabled = true; // 予想を入力ボタンを利用不可にする
	  resetButton = document.createElement('button'); // ボタン要素を生成してresetButtonに代入
	  resetButton.textContent = '新しいゲームを始める'; // resetButtonのテキスト
	  resetField.appendChild(resetButton); // リセットボタン用のフィールドにresetButtonを表示
	  resetButton.addEventListener('click', resetGame); // resetButtonをクリックするとresetGame()関数が発火する
  }

  function resetGame() { // 画面とロジックをリセットするための関数
	  guessCount = 1; // 予想回数を1に戻す

    const resetParas = document.querySelectorAll('.resultParas p');
    for (const resetPara of resetParas) { // .resultParas の<p>要素のテキストを全て空にする
      resetPara.textContent = '';
    }
	  resetButton.parentNode.removeChild(resetButton); // リセットボタンを削除する

	  guessField.disabled = false; // 入力フォームを利用できるようにする
	  guessSubmit.disabled = false; // 予想を入力ボタンを利用できるようにする
	  guessField.value = ''; // 入力フォームを空にする
	  guessField.focus(); // 入力フォームにフォーカスを設定する

	  lastResult.style.backgroundColor = 'white';

	  randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成して更新
  }
</script>

これらの処理をいくつかの手順に分けて実装していきます。

数当てゲームを作る手順

データを保持する定数と変数を追加する

<script>タグ内に以下のように定数constと変数letを定義します。

変数の定義にはletの他にvarも使えますが、varは推奨されていないのでletを用いて進めていきます。

詳しくはvarとletの違いを参照。

<script>

  let randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成
  
  // html の各要素を取得する
  const guesses = document.querySelector('.guesses');
  const lastResult = document.querySelector('.lastResult');
  const lowOrHi = document.querySelector('.lowOrHi');
  const answer = document.querySelector('.answer');
  const guessSubmit = document.querySelector('.guessSubmit'); // 予想を入力ボタンの要素
  const guessField = document.querySelector('.guessField'); // 入力フォーム(<input>)の要素
  const resetField = document.querySelector('.resetField'); // リセットボタンの要素

  let guessCount = 1; // 予想回数(初期値は1)
  let resetButton; // リセットボタンを定義

</script>

一番最初の変数randomNumberには、数学的なアルゴリズムにより計算された 1 から 100 までのうちランダムな数字が代入されます。

ここで、Math.random()は0 以上 1 未満 (0 は含むが、 1 は含まない) の範囲で浮動小数点の擬似乱数を返す関数です。

> Math.random()
> 0.539380900662114

このMath.random()に100を乗算してMath.floor()で与えられた数値以下の最大の整数を返すことで、0〜99の乱数を生成することができます。

> Math.floor(9.999)
> 9
> Math.floor(9.999*100)
> 99
> Math.floor(Math.random()*100) // Math.random() → 0.539380900662114
> 53

ただ、今回は1〜100までの乱数を生成したいので、Math.floor(Math.random()*100)+1とすることで範囲を1〜100までにすることができます。

また、document.querySelector('セレクタ')では、指定されたセレクターまたはセレクター群に一致する、文書内の最初のElementを返します。

> document.querySelector('.guesses');
> <p class="guesses"></p>
> document.querySelector('.guessField');
> <input type="text" id="guessField" class="guessField">

後で追加する関数には、これらquerySelectorで取得したElementを用いて、値を代入したり削除したりしていきます。

入力された予想の正誤判定を行う関数 checkGuess() を追加する

最初に、ユーザーが入力した値userGuessを取得します。

ユーザーがguessField(入力フォーム)で予想を入力すると、guessField.valueで入力した値をJavaScript内に読み込み、Number()で数値化してuserGuessに代入されます。

あとは、取得したuserGuessを用いて、入力された予想の正誤判定を行う記述をしていきます。

<script>

  ・・・

 // 追記
  function checkGuess() { // 入力された予想の正誤判定

    const userGuess = Number(guessField.value); // ユーザーが入力した値(入力フォームのvalueから取得)
    if(guessCount === 1) {
      guesses.textContent = '前回の予想: '; // 予想が入力されたら左記テキストを挿入
    }
    guesses.textContent += userGuess + ' '; // ユーザーが入力した値を挿入
    
    if(userGuess === randomNumber) { // 正解した場合
      lastResult.textContent = '正解です!おめでとう!!';
      lastResult.style.backgroundColor = 'green';
      lastResult.style.color = 'white';
      lowOrHi.textContent = '';
      setGameOver(); // ゲームオーバー時の処理を行う(後述)
    
    } else if(guessCount === 10) { // 予想回数が10回に達した場合
      lastResult.textContent = '!!!ゲームオーバー!!!';
      answer.textContent = `正解は ${randomNumber} でした`;
      answer.style.color = 'blue';
      setGameOver(); // ゲームオーバー時の処理を行う(後述)
    
    } else { // 入力した予想が間違っている、かつ予想回数が10回未満の場合
      lastResult.textContent = '間違いです!';
      lastResult.style.backgroundColor = 'red';
      lastResult.style.color = 'white';
      if(userGuess < randomNumber) {
        lowOrHi.textContent = 'もっと大きい値です!';
      } else if(userGuess > randomNumber) {
        lowOrHi.textContent = 'もっと小さい値です!';
      }
    }

    guessCount++; // 予想回数に1を加算する
    guessField.value = ''; // 入力フォームを空にする
    guessField.focus(); // 入力フォームにフォーカスを設定する
  }

</script>

このcheckGuess()関数は1回実行する度に、<div class="resultParas"></div><p>タグ内には以下ようなテキスト(およびスタイル)が追加されるようになっています。

<div class="resultParas">
  <p class="guesses">前回の予想: 50 55 77 88 90 </p>
  <p class="lastResult" style="background-color: red; color: white;">間違いです!</p>
  <p class="lowOrHi">もっと大きい値です!</p>
</div>

ただ、今のままではcheckGuess()関数を実行(発火)させることができないので、関数を実行させるためのイベントを作成します。

addEventListener でクリック時に checkGuess() を発火させる

以下のように、checkGuess()関数の直下にguessSubmitボタンに対するイベントリスナーaddEventListenerを追加します。

<script>

  ・・・

  function checkGuess() { // 入力された予想の正誤判定
  ・・・
  }

  // 追記
  guessSubmit.addEventListener('click', checkGuess);

</script>

addEventListener('click', checkGuess);とすることで、guessSubmitボタン(予想を入力ボタン)クリック時にcheckGuess()を発火させることができます。

ゲーム終了の処理を行う関数 setGameOver() を追加する

入力した予想が正解した場合、もしくは予想の回数が10回に達した場合はゲームを終了して新しいゲームを開始できるようにします。

ゲーム終了処理の具体的な内容は、

  • 入力フォームを利用できないようにする(非活性化)
  • 予想を入力ボタンを利用できないようにする(非活性化)
  • 「新しいゲームを始める」ボタンを表示させる
  • 「新しいゲームを始める」ボタンをクリックすると初期化されるようにする

これらの処理をsetGameOver()関数に記述していきます。

<script>

  ・・・

  function checkGuess() { // 入力された予想の正誤判定
  ・・・
  }
  guessSubmit.addEventListener('click', checkGuess);

 // 追記
  function setGameOver() {
	  guessField.disabled = true; // 入力フォームを利用不可にする
	  guessSubmit.disabled = true; // 予想を入力ボタンを利用不可にする
	  resetButton = document.createElement('button'); // ボタンタグを生成してresetButtonに代入
	  resetButton.textContent = '新しいゲームを始める'; // resetButtonのテキスト
	  resetField.appendChild(resetButton); // リセットボタン用のフィールドにresetButtonを表示
	  resetButton.addEventListener('click', resetGame); // resetButtonをクリックするとresetGame()関数が発火する
  }

</script>

document.createElement('button')でボタンタグを新たに生成してresetButtonに代入。

そのresetButtonを、appendChildresetField内(<div class=”resetField”></div>タグ内)に追加するようにします。

追加されたresetButtonをクリックすると、resetGame()関数を実行しゲームを初期状態にリセットして新しいゲームが始まるようにします。

ゲームを初期状態にリセットする関数 resetGame() を追加する

最後に、ゲームをリセットする関数resetGame()を追加します。

<script>

  ・・・

  function checkGuess() { // 入力された予想の正誤判定
  ・・・
  }
  guessSubmit.addEventListener('click', checkGuess);

  function setGameOver() { // ゲームオーバー時の処理を行う関数
	・・・
  }

 // 追記
  function resetGame() { 
	  guessCount = 1; // 予想回数を1に戻す

    const resetParas = document.querySelectorAll('.resultParas p');
    for (const resetPara of resetParas) { // .resultParas の<p>要素のテキストを全て空にする
      resetPara.textContent = '';
    }
	  resetButton.parentNode.removeChild(resetButton); // リセットボタンを削除する

	  guessField.disabled = false; // 入力フォームを利用できるようにする
	  guessSubmit.disabled = false; // 予想を入力ボタンを利用できるようにする
	  guessField.value = ''; // 入力フォームを空にする
	  guessField.focus(); // 入力フォームにフォーカスを設定する

	  lastResult.style.backgroundColor = 'white';

	  randomNumber = Math.floor(Math.random()*100) + 1; // 1から100までの中からランダムな数字を1つ生成して更新
  }

</script>

これで全ての記述が終わりました。

入力した値の大小を判定するだけの簡単なゲームですが、JavaScriptのコードの記述量は思いの外多くて個人的には驚きました。

もっと効率の良い書き方があると思いますが、今はJavaScriptを始めたばかりということもあり、

一つ一つのプログラムの動きをしっかりと理解するためにも、遠回りでも丁寧に書いていく必要があるのかなと思っています。

以上です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次