【JavaScript】じゃんけんゲーム

# 下準備

じゃんけんゲームを作ります。

まずhtmlのひな形を用意します。

※ lang を en から jp に書き換えてください。

```html: index.html
<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>
```

bodyタグの中にscriptタグを書きます

```html: index.html
<body>
    <script>

    </script>
</body>
```

じゃんけんゲームの下準備をします。
以下はscriptタグの中に書きます。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

//ここに勝敗を判定するコードを書く
/////////////////////////
```

まず答えを見ずに考えてみてください。
(下に答えがあります)

# レベル1

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 勝敗の判定
// プレイヤーがグーのとき
if (plHand === 0) {
    if (cpHand === 0) {
        console.log("あいこ");
    }
    else if (cpHand === 1) {
        console.log("勝ち");
    }
    else if (cpHand === 2) {
        console.log("負け");
    }
}
// プレイヤーがチョキのとき
else if (plHand === 1) {
    if (cpHand === 0) {
        console.log("負け");
    }
    else if (cpHand === 1) {
        console.log("あいこ");
    }
    else if (cpHand === 2) {
        console.log("勝ち");
    }
}
// プレイヤーがパーのとき
else if (plHand === 2) {
    if (cpHand === 0) {
        console.log("勝ち");
    }
    else if (cpHand === 1) {
        console.log("負け");
    }
    else if (cpHand === 2) {
        console.log("あいこ");
    }
}
```

書けましたか？
面倒臭かったですよね。
でも、これはまだ綺麗なコードとは言えません。
限度はありますが、コードはなるべくシンプルで、少なく、軽いことが求められます。
このコードはかなり無駄が多いです。
では次に、少し無駄を減らします

# レベル2

条件分岐が多いときは、条件を減らせるか考えてみてください。
上のコードも、いくつかの条件式は省略できるはずです。考えてみてください。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 勝敗の判定
// プレイヤーがグーのとき
if (plHand === 0) {
    if (cpHand === 0) {
        console.log("あいこ");
    }
    else if (cpHand === 1) {
        console.log("勝ち");
    }
    else {
        console.log("負け");
    }
}
// プレイヤーがチョキのとき
else if (plHand === 1) {
    if (cpHand === 0) {
        console.log("負け");
    }
    else if (cpHand === 1) {
        console.log("あいこ");
    }
    else {
        console.log("勝ち");
    }
}
// プレイヤーがパーのとき
else {
    if (cpHand === 0) {
        console.log("勝ち");
    }
    else if (cpHand === 1) {
        console.log("負け");
    }
    else {
        console.log("あいこ");
    }
}
```

少しすっきりしたと思います。
上のコードは、if, else if に続く三つ目の条件分岐を else のみにし、条件式を省いています。

でも、まだまだ削れます。

# レベル3

どこを削るべきでしょうか。
あいこの条件を少し工夫するといいかもしれません。

「あいこ」とは、どういう状況でしょうか。
一人がグーで、もう一人もグー、
一人がチョキで、もう一人もチョキ、、、

つまり、二人の手が同じときにあいこになります。

では、勝敗判定の初めに、二人の手が同じなら「あいこ」にするようにしましょう。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 勝敗の判定
// あいこのとき
if (plHand === cpHand) {
    console.log("あいこ");
}
// プレイヤーがグーのとき
else if (plHand === 0) {
    if (cpHand === 1) {
        console.log("勝ち");
    }
    else {
        console.log("負け");
    }
}
// プレイヤーがチョキのとき
else if (plHand === 1) {
    if (cpHand === 0) {
        console.log("負け");
    }
    else {
        console.log("勝ち");
    }
}
// プレイヤーがパーのとき
else {
    if (cpHand === 0) {
        console.log("勝ち");
    }
    else {
        console.log("負け");
    }
}
```

最初のものと比べると、かなりすっきりしたと思います。

次は、console.log を減らしましょう。

# レベル4

ここまで何度も console.log を書いてきてうんざりしていると思いますが、もう終わります。
従来のコードでは、条件分岐の中ですぐに結果を出力しています。すると、条件の数だけ出力のコードを書かなければなりません。
それを解消するために、結果を result 変数に格納し、最後に result を出力することにします。
勝ちを0、負けを1、あいこを2 とします。

しかし、そのままでは出力結果が 0,1,2 の単なる数字になってしまい、味気ないです。
それを解消するには、配列を使うことをおすすめします。そのための 0,1,2 です。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// 勝ち: 0
// 負け: 1
// あいこ: 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 結果を文字に変換するための配列
let resultWords = ["勝ち", "負け", "あいこ"];

// 結果を格納する変数
let result;

// 勝敗の判定
// あいこのとき
if (plHand === cpHand) {
    result = 2;
}
// プレイヤーがグーのとき
else if (plHand === 0) {
    if (cpHand === 1) {
        result = 0;
    }
    else {
        result = 1;
    }
}
// プレイヤーがチョキのとき
else if (plHand === 1) {
    if (cpHand === 0) {
        result = 1;
    }
    else {
        result = 0;
    }
}
// プレイヤーがパーのとき
else {
    if (cpHand === 0) {
        result = 0;
    }
    else {
        result = 1;
    }
}

// 結果を出力
console.log(resultWords[result]);
```

結果の言葉を配列で事前に決めておくことで、誤字などの不具合を防ぐことができます。
また、後に文字を変更したいときも、書き換えを最小限に抑えることができます。

しかし、それでもまだ条件分岐が多いです。

# レベル5

ではもし、条件分岐を一つも書かずに済むとしたらどうでしょうか。
前章では、「手」の種類だけでなく「勝ち負け」も数字で表しました。
少し整理してみましょう。

|plHand|cpHand|result|
|------|------|------|
|0     |0     |2     |
|0     |1     |0     |
|0     |2     |1     |
|1     |0     |1     |
|1     |1     |2     |
|1     |2     |0     |
|2     |0     |0     |
|2     |1     |1     |
|2     |2     |2     |

なんとかして、plHand と cpHand から result を求められる式を立てたいところ。
これを求めるのはすごく面倒なので、正直答えを見てしまったほうが早いです。
が、ぜひ考えてほしいです。

ヒント
(○ - plHand + cpHand) % ○ = result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

答え
(2 - plHand + cpHand) % 3 = result

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// 勝ち: 0
// 負け: 1
// あいこ: 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 結果を文字に変換するための配列
let resultWords = ["勝ち", "負け", "あいこ"];

// 結果を格納する変数
let result;

// 勝敗の判定
result = (2 - plHand + cpHand) % 3;

// 結果を出力
console.log(resultWords[result]);
```

今までたくさん条件分岐を書いていたのが馬鹿らしくなってきます。

これなら、result を定義するときに、すでに計算してしまえばもっとすっきりします。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// 勝ち: 0
// 負け: 1
// あいこ: 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 結果を文字に変換するための配列
let resultWords = ["勝ち", "負け", "あいこ"];

// 結果を格納する変数
let result = (2 - plHand + cpHand) % 3;

// 結果を出力
console.log(resultWords[result]);
```

こんな感じです。

しかし、これにもまだ少し問題があります。

# レベル6
前章のコードは、コード量としては最小です。しかし、速度においてはまだ改良の余地があります。

|plHand|cpHand|result|
|------|------|------|
|0     |0     |2     |
|0     |1     |0     |
|0     |2     |1     |
|1     |0     |1     |
|1     |1     |2     |
|1     |2     |0     |
|2     |0     |0     |
|2     |1     |1     |
|2     |2     |2     |

さっきの表を書き換えます。

<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3339477/2c3c45c8-4614-0c0f-5375-2f9afafd8feb.png">

こんな感じです。
これをコードで表すにはどうすればいいでしょうか。
答えは二次元配列です。

```javascript:
let resultsList = [
    [2, 0, 1],
    [1, 2, 0],
    [0, 1, 2]
];
```

こんな感じになると思います。

これを利用すれば、計算をせずに勝敗を求めることができます。
例えば、plHand = 0、cpHand = 1 のときは、result = resultsList[0][1]
という風に、カッコの中にそれぞれの手の数字を入れるだけで、結果を出力できます。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// 勝ち: 0
// 負け: 1
// あいこ: 2

// プレイヤーの手
let plHand = 0;

// コンピューターの手
let cpHand = 0;

// 結果を文字に変換するための配列
let resultWords = ["勝ち", "負け", "あいこ"];

// 結果を求めるための二次元配列
let resultsList = [
    [2, 0, 1],
    [1, 2, 0],
    [0, 1, 2]
];

// 結果を格納する変数
let result = resultsList[plHand][cpHand];

// 結果を出力
console.log(resultWords[result]);
```

これで、計算や条件分岐を一回もせずに、じゃんけんの勝敗を求めることができるようになりました。

# まとめ

今回、じゃんけんゲームのコードをレベル別に書いていきました。
どんどんコードが短くなっていきましたね。こんな感じで、工夫次第で、コード量を減らすことができます。ぜひ、たくさん考えてコードを書いてみてください。

ちなみに、説明のコメントや、不要な改行などを消すとこうなります。

```javascript: index.html
let plHand=0;
let cpHand=0;
let resultWords=["勝ち","負け","あいこ"];
let resultsList=[[2,0,1],[1,2,0],[0,1,2]];
console.log(resultWords[resultsList[plHand][cpHand]]);
```

更に、変数も最短にし、改行も無理やり消すなど、いろいろ短さに全振りするとこうなります。
※勝敗の判定にはレベル5の式を使っています。

```javascript: index.html
console.log(["勝ち","負け","あいこ"][(2-(p=0)+(c=0))%3])
```

レベル1と比べると、とんでもないですよね。でも、流石にこれはやめましょう。可読性が最悪です。

また、ゲーム性や可読性を意識してとても丁寧に書くとこうなります。
※結構変わってます。

```javascript: index.html
// グー: 0
// チョキ: 1
// パー : 2

// 勝ち: 0
// 負け: 1
// あいこ: 2

// プレイヤーの手
const plHand = 0;

// コンピューターの手
const cpHand = 0;

// 手の種類を文字に変換するための配列
const handWords = ["グー", "チョキ", "パー"];

// 結果を文字に変換するための配列
const resultWords = ["勝ち", "負け", "あいこ"];

// 結果を求めるための関数
const calcResult = (pl, cp) => {
    // 結果を求めるための二次元配列
    const resultsList = [
        [2, 0, 1],
        [1, 2, 0],
        [0, 1, 2]
    ];
    return resultsList[pl][cp];
}

// 結果を格納する変数
const result = calcResult(plHand, cpHand);

// 結果を出力
console.log(`あなたの手:${handWords[plHand]}`);
console.log(`コンピューターの手:${handWords[cpHand]}`);
console.log(`結果:${resultWords[result]}`);
```