JavaScript じゃんけんゲームを作成

JavaScriptを学んだので、じゃんけんゲームを作成してみました。
もし、初学者の方がいましたら、参考にして頂けますと幸いです。


#完成図
###画面遷移時
<img width="800px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390683/64aa5e7c-12b8-4f89-eb3f-8b61d9d13d8f.png">

###１０回じゃんけんした後
<img width = "800px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1390683/1e1b605d-dae3-e795-6876-f0708e0d2ae5.png">

#参考サイト
- 変数名が困ったときにおすすめ
https://codic.jp/engine
- ボタンデザイン
https://saruwakakun.com/html-css/reference/buttons
- textContentとinnerHTMLの違い
https://itsakura.com/js-textcontent-innerhtml

#特徴
- １０回行い、勝ち数、負け数、勝率を計算
- ボタンを押下する度に、相手の手がランダムに決定される
  - 結果によって表示されるコメントが変わる
      - 勝ちの場合：『あなたの勝ちです』と表示、勝ちカウント+1
      - 負けの場合：『あなたの負けです』と表示、負けカウント+1
      - 引き分け：『あいこです』と表示
- １０回に到達後
  - `グー`,`チョキ`,`パー`ボタンが非活性になる
  - アラート表示させ、勝ち越し、負け越し、引き分けか表示
  - `更新`ボタン表示させる

#関数について
- gameCountReplace（）
  - 試合数を計算する関数
- alert（）
  - 最終的な結果をアラートで表示させるための関数
- resetClick()
  - ボタン作成、reload()関数呼び出し関数
- inactive()
  - ボタンを非活性にする関数
- winRateCalc()
  - 勝率を計算する関数
- onClick()
  - グー、チョキ、パーのどれかを押下したときに処理される関数

# HTML
```index.html
(省略）
<body>
  <div id="main">
    <div id="container">
      <h1>じゃんけん</h1>
      <p class="text">試合数：
        <span id="gameCount">0</span>
      </p>

      <div class="btn">
        <button id="rock" value="0">グー</button>
        <button id="scissors" value="1">チョキ</button>
        <button id="paper" value="2">パー</button>
      </div>

      <div id="battle">
        <p id="myHand">自分の出した手：まだ出していません</p>
        <p id="enemyHand">相手の出した手：まだ出していません</p>
        <h2 id="result"></h2>
      </div>

      <div id="resultTable">
        <dl>
          <dt class="text">勝ち数</dt>
          <dt class="text">負け数</dt>
          <dt class="text">勝率</dt>
        </dl>
        <dl>
          <dd id="winCount">ー</dd>
          <dd id="loseCount">ー</dd>
          <dd id="winRate">ー</dd>
        </dl>
      </div>

      <div id="reset"></div>

    </div>
  </div>
  <script src="./main.js"></script>
</body>
```
#JavaScript
```main.js
'use strict';

(() => {
  const hands = ['グー', 'チョキ', 'パー'];
  const resultText = ['『あなたの勝ちです』', '『あなたの負けです』', '『あいこです』'];

  // 値を取得する
  const count = document.getElementById('gameCount');
  let countResult = count.innerHTML; 
  const rock = document.getElementById('rock');
  const scissors = document.getElementById('scissors');
  const paper = document.getElementById('paper');
  const myHandType = document.getElementById('myHand');
  const enemyHandType = document.getElementById('enemyHand');
  const result = document.getElementById('result');
  const reset = document.getElementById('reset');
  const winCount = document.getElementById('winCount')
  const loseCount = document.getElementById('loseCount')
  const winRate = document.getElementById('winRate');

  //試合数、勝ち数、負け数
  let gameCount = 0;
  let winResult = 0;
  let loseResult = 0;

  //試合数書き換え
  const gameCountReplace = (gameCount) => {              
    countResult = countResult.replace(countResult, gameCount);
    count.textContent = countResult;
  }

  const alert = (winResult, loseResult) => {
    if (winResult > loseResult) {
      window.alert('勝ち越しました！');
    } else if (winResult < loseResult) {
      window.alert('負け越しました！');
    } else {
      window.alert('引き分けでした！');
    }
  }

  //リセットボタン表示とreload()関数呼び出し
  const resetClick = () => {
    //ボタン要素作成
    const resetBtn = document.createElement('input');
    resetBtn.type = 'button';
    resetBtn.value = '更新';
    reset.appendChild(resetBtn); //親要素(reset)の子要素にボタンを配置する

    resetBtn.addEventListener('click', () => { //更新ボタンを押下後、画面リロードする
      location.reload(); 
    })
  }

  //10回到達したらボタンを非活性にする
  const inactive = () => {
    rock.disabled = true;
    scissors.disabled = true;
    paper.disabled = true;
  }

  //勝率計算
  const winRateCalc = (gameCount, winCount) => {
    const winRateResult = (winCount / gameCount) * 100;
    winRate.textContent = `${winRateResult}%`;
  }

  //ボタン押下関数
  const onClick = (event) => {
    const myHand = Number(event.target.value); //取得するvalue値はstring型のため、Number型に変換
    const enemyHand = Math.floor(Math.random() * hands.length);

    myHandType.textContent = `自分の出した手：${hands[myHand]}`;
    enemyHandType.textContent = `相手の出した手：${hands[enemyHand]}`;

    //勝敗判定
    const handResult = (myHand - enemyHand + 3) % hands.length;

    if (handResult === 2) {
      result.textContent = resultText[0];
      gameCount++;
      winResult++;
      gameCountReplace(gameCount);

    } else if (handResult === 1) {
      result.textContent = resultText[1];
      gameCount++;
      loseResult++;
      gameCountReplace(gameCount);

    } else {
      result.textContent = resultText[2];
    }

    if (gameCount === 10) { //試合数が１０回に到達したら実行される処理
      winCount.textContent = `${winResult}回`;
      loseCount.textContent = `${loseResult}回`;
      winRateCalc(gameCount, winResult);
      alert(winResult,loseResult)
      inactive();
      resetClick();
    }
  }

  // クリックした時の挙動はどのボタンも同じなので、関数を共通化
  rock.addEventListener('click', onClick);
  scissors.addEventListener('click', onClick);
  paper.addEventListener('click', onClick);

})();
```
#CSS
```style.css
#main {
  width: 500px;
  margin: 0 auto;
}

#container {
  text-align: center;
}

.btn {
  margin: 20px;
  padding: 10px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
}

button,
input[type="button"] {
  width: 30%;
  border: none;
  border-radius: 5px;
  color: #fff;
  padding: 8px 16px;
  font-size: 16px;
}

button#rock {
  background-color: #dc143c;
  border-bottom: solid 4px #8b0000;
}

button#scissors {
  background-color: #ffd700;
  border-bottom: solid 4px #b8860b;
}

button#paper {
  background-color: #4676d7;
  border-bottom: solid 4px #191970;
}

#rock:disabled,
#scissors:disabled,
#paper:disabled {
  background-color: #808080;
  border-bottom: none;
}

input[type="button"] {
  background-color: #b0c4de;
  border-bottom: solid 4px #708090;
  width: 40%;
  padding: 10px;
  font-size: 30px;
}

button:active,
input[type="button"]:active {
  -webkit-transform: translateY(4px);
  transform: translateY(4px); /*下に動く*/
  border-bottom: none; /*線を消す*/
}

#battle {
  border: 1px solid #ccc;
  padding: 10px;
}

#myHand,
#enemyHand {
  font-size: 20px;
}

#resultTable {
  border: 1px solid #ccc;
  margin: 15px 0;
}

dl {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  border-bottom: 1px solid #ccc;
  font-size: 20px;
}

dl:last-child {
  border-bottom: none;
}

dd {
  text-align: center;
  font-size: 20px;
}
```

# 学んだこと
- `○.disabled = true`とすることで、非活性状態にできること
- `event.target.value`で押下したボタンの`value`値を取得することが可能
- `replace()`関数の使い方（文字の書き換え）
- JavaScriptで処理した値を画面上に表示させるために、`textContent`や`innerHTML`を活用できること
  - textContent：htmlのタグを文字としてそのまま出力
  - innerHTML：htmlのタグを解釈して出力
- 関数の呼び出すタイミングとか、なんとなく掴めた気がする。
- じゃんけんの勝敗処理で、下記のように書くことで、判定処理が簡潔に済むことを知れた

```
const handResult = (myHand - enemyHand + 3) % hands.length;
```

# メモ（適当）
- 初期状態として、JS上で`まだ出していません`の箇所だけ変えるというようにしたかった。しかし今回は、再描画という形をとっている。
  - １回目は、一部だけ変更はできたのだが、２回目以降は値が変更しなかった。
    - 原因
       - 最初は、replace('まだ出していません', xxx)としていた。
         - 1回目は画面に「まだ出していません」が表示されているため、`replace()`関数が実行される。
         - ２回目以降は「まだ出していません」が書き換わっているため、`replace()`関数が実行されなかった

※ちょっと頑張ればこの処理できそうじゃんとか思いますが、面倒だったので、辞めました。また、機会があればこの辺の処理を修正したいと思います。

# まとめ
- 自分でコードを考えて、作成したので、ロジック的な部分の向上に繋がった。また、何より作成したものが、すぐに画面に反映されるのは、楽しかったし、フロント言語の醍醐味だなと感じた。
- 今回のアプリは、基礎文法でしか使っていないので、オブジェクト指向や非同期通信あたりも今後学んでいきたい。
  - 徐々に慣れてきたら、Vue.js,Node.jsなどのFWにも挑戦していきたいと考えている。
- 今後のステップアップのために、これ作ると良いよ、これ作ったよ的なものがあれば教えて欲しいです。