「Easy Yathzee」というゲームを作ったので解説しつつ自身でコードレビューっぽいことしてみる

# はじめに

2, 3か月前にこんな[ゲーム](https://good-teal-chipmunk-cape.cyclic.app/)を作りました。

https://github.com/Scythercas/easy-yathzee2

[ルール](https://github.com/Scythercas/easy-yathzee2/blob/main/README.md)

プレイ画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/d0849399-62c0-c0a0-6d14-81eaed460bc6.png)

「Easy Yathzee」(イージーヤッツィー)という名前にしていますが、元ネタとして「Yathzee」というゲームがあります。
BGAというサイトで遊ぶことができるので興味のある方は以下のリンクで遊んでみてください。

https://ja.boardgamearena.com/gamepanel?game=yatzy

また、switchのアソビ大全では「ヨット」という名前であった気がします。（間違えていたらすみません。）
このゲームをより簡単に遊べるようにしたものが私が作ったゲームです。

# 本題の前に

綴りを間違えていました。
×「Yathzee」
〇「Yahtzee」
Githubやソースコード内、すべてで`Yathzee`と記述しておりました^^;許してください

まだ未完全な状態での公開となっております。
数字を変えて役が成立した場合、本来なら数字が変わってから`alert()`で役名を表示したいのですが、先に`alert()`されてしまい変えた後の数字が見えないまま次のターンに進んでしまいます。
また、本来はPlayer 2ではなくCPUを実装したいのですがまだやっておりません。別言語でCPUと対戦出来るものを作ってあるので、もし興味があればそちらで遊んでください。
<details><summary>VS CPU C言語</summary>

```c
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include <unistd.h>


int main(void){

	int a, b, c, i, j, r, s=0, t=0, score, bonus;
	int acpu, bcpu, ccpu, jcpu, scpu=0, tcpu=0, cs, cb;
	int arr[100];
	int ca[100];

	printf("ヤッツィーを始めますか？\n始めるならEnterを押してください。\n");
	getchar();
/*たまにある getchar(); は、プレイヤーの好きなタイミングで次の動作を進めるためのもの。
 Enterで次に進む。*/

	printf("ルールを説明します！！\n"
			"1~3の乱数を出します。\n"
			"1回につき1個まで乱数を振り直すことができます。\n"
			"それらの数字で役を作れた場合、ボーナス点を獲得できます。\n"
			"乱数3つの合計点に、ボーナス点を加算したものが1回の得点となります！\n\n"
			"これを3回繰り返し、3回の合計点を競います。\n\n"
			"～ボーナス～\n"
			"・ ヤッツィー ・・・ 全て同じ目が出た場合。10 点\n"
			"・ ストレート ・・・ 全て異なる目が出た場合。5 点\n"
			"・ リゴール ・・・ 最小の目または最大の目のみを出した場合。3 点\n"
			"※なお、ボーナスは重複しません。複数のボーナスに該当する場合、"
			"最もボーナス点の高いものだけが適用されます。\n\n"
			"このゲームを進行するためには、Enterを押してください。\n\n");

	getchar();

	srand((unsigned int)time(NULL));

	for(i=0;i<=2;i++){
		//プレイヤーの処理
		printf("プレイヤーの%d回目の挑戦！\n\n", i+1);
		for(j=0;j<=2;j++){//3個の乱数を発生させる
		arr[j] = rand()%3+1;
		printf("%d ", arr[j]);//発生した乱数を出力
		}
		printf("\n\n");
		printf("振り直すなら何番目を振り直すか(1,2,3)、振り直さないなら0を入力！！\n入力→ ");
		scanf("%d", &r);

		if(r==0){//振り直さない場合
			a = arr[0];
			b = arr[1];
			c = arr[2];
			printf("振り直しませんでした。\n%d, %d, %d\n\n", a, b, c);
		}

		if(r!=0){//振り直す場合
			arr[r-1] = rand()%3+1;//r番目を振り直す
			a = arr[0];
			b = arr[1];
			c = arr[2];
			printf("振り直した結果 → ");
			getchar();
			printf("%d %d %d\n\n", a, b, c);
		}

		score = a + b + c;

		if(a==b && b==c){//ヤッツィーの処理
		bonus = 10;
		printf("ヤッツィー！！！10点獲得！！！\\\\\\\\おめでとう////\n\n");
		}

		else if(a!=2 && b!=2 && c!=2){//ヤムスの処理
				bonus = 3;
				printf("リゴール！３点獲得！\n\n");
		}

		else if(a!=b && b!=c && c!=a){//ストレートの処理
			bonus = 5;
			printf("ストレート！！5点獲得！！\n\n");
		}

		else {bonus =0;}//無役の処理
		getchar();


		s = score + bonus;
		printf("合計点=%d点\n\n", s);

		t += s;

		getchar();

		printf("CPU の%d回目の挑戦\n\n", i+1);
		getchar();

		for(jcpu=0;jcpu<=2;jcpu++){//3個の乱数を発生させる

		ca[jcpu] = rand()%3+1;

		printf("%d ", ca[jcpu]);//発生させた乱数の出力
		}
		getchar();
		printf("\n\n");

//役がある場合、CPUは振り直さない。役がない場合、最も得点が高くなる可能性がある方法で振り直す。
		if(ca[0]==ca[1] && ca[1]==ca[2]){//ヤッツィーの処理。全て同じ。
			printf("CPU は振り直しませんでした。\n\n");
		}
		else if(ca[0]!=ca[1] && ca[1]!=ca[2] && ca[2]!=ca[0]){//ストレートの処理。全て異なる。
			printf("CPU は振り直しませんでした。\n\n");
		}
		else if(ca[0]!=2 && ca[1]!=2 && ca[2]!=2){//リゴールの処理。
			printf("CPU は振り直しませんでした。\n\n");
		}
		else if(ca[0]+ca[1]+ca[2]==7){//2,2,3の場合。else ifなので、1,3,3は除外。
			printf("CPU は3が出ている目を振り直します。\n\n");
			if(ca[0]==3){
				ca[0] = rand()%3+1;
			}
			if(ca[1]==3){
				ca[1] = rand()%3+1;
			}
			if(ca[2]==3){
				ca[2] = rand()%3+1;
			}
			printf("振り直した結果・・・%d %d %d\n\n", ca[0],ca[1],ca[2]);
		}
		else if(ca[0]+ca[1]+ca[2]==5){//2,2,1の場合。else ifなので1,1,3は除外。
			printf("CPU は1が出ている目を振り直します。\n\n");
			if(ca[0]==1){
				ca[0] = rand()%3+1;
			}
			if(ca[1]==1){
				ca[1] = rand()%3+1;
			}
			if(ca[2]==1){
				ca[2] = rand()%3+1;
			}
			printf("振り直した結果・・・%d %d %d\n\n", ca[0],ca[1],ca[2]);
		}
		else if(ca[0]+ca[1]+ca[2]==4){//1,1,2の場合。
			printf("CPU は2が出ている目を振り直します。\n\n");
			if(ca[0]==2){
				ca[0] = rand()%3+1;
			}
			if(ca[1]==2){
				ca[1] = rand()%3+1;
			}
			if(ca[2]==2){
				ca[2] = rand()%3+1;
			}
			printf("振り直した結果・・・%d %d %d\n\n", ca[0],ca[1],ca[2]);
		}
		else if(ca[0]+ca[1]+ca[2]==8){//3,3,2の場合。
			printf("CPU は2が出ている目を振り直します。\n\n");
			if(ca[0]==2){
				ca[0] = rand()%3+1;
			}
			if(ca[1]==2){
				ca[1] = rand()%3+1;
			}
			if(ca[2]==2){
				ca[2] = rand()%3+1;
			}
			printf("振り直した結果・・・%d %d %d\n\n", ca[0],ca[1],ca[2]);
		}


		acpu = ca[0];//[]打つのがめんどくさいので
		bcpu = ca[1];
		ccpu = ca[2];

		if(acpu==bcpu && bcpu==ccpu){//ヤッツィーの処理
			cb = 10;
		}

		else if(acpu!=bcpu && bcpu!=ccpu && ccpu!=acpu){//ストレートの処理
			cb = 5;
		}

		else if(acpu!=2 && bcpu!=2 && ccpu!=2){//リゴールの処理
			cb = 3;
		}

		else{//無役の処理
			cb = 0;
		}

		if(cb==0){
			printf("CPU はボーナスを獲得しませんでした。\n\n");
		}
		else{
			printf("CPU はボーナスとして%d点を獲得しました。\n\n", cb);
		}
		getchar();

		cs = acpu + bcpu + ccpu;//出た目の合計
		scpu = cs + cb;//出た目の合計 + ボーナス点

		printf("CPUの %d回目の挑戦・・・%d点\n\n\n", i+1, scpu);

		tcpu += scpu;//i回目までの合計点

		getchar();
	}

	printf("結果はっぴょう！！Enterを押してください。");

	getchar();
	printf("\n\n");

	printf("あなたの得点は %d 点です！！！\n\n\n", t);
	printf("そして、CPUの得点は %d 点です。\n\n\n", tcpu);

	getchar();
//勝敗
	if(t>tcpu){
		printf("おめでとうございます！！\n%d点差であなたの勝ちです！！\n", t-tcpu);
		if(t-tcpu>=10){//大差で勝利した場合のみ特別演出
			printf("圧勝だね！！\n\n");
		}
	}
	else if(t==tcpu){
		printf("同点でした。次は勝てるかな？\n\n");
	}
	else if(t<tcpu){
		printf("残念でした。\n%d点差であなたの負けです。次は頑張って^^\n", tcpu-t);
	}

	printf("遊んでくれてありがとう！！");

	getchar();
	getchar();

	return 0;

}

```
</details>

コードが汚いのは許してください。昔何の知識も持たずに作ったものをそのまま載せます。


# 解説

## HTML

<details><summary>HTMLソースコード</summary>

```index.html
<!DOCTYPE html>
<html lang="ja">
<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>Easy Yathzee</title>
    <script src="main.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <table id="diceTable">
        <tr>
            <td id="dice1" class="touchable"></td>
            <td id="dice2" class="touchable"></td>
            <td id="dice3" class="touchable"></td>
        </tr>
    </table>
    <button id="decide">変更しない</button>
    <table id="scoreTable">
        <thead>
            <tr>
                <th></th>
                <th>Player 1</th>
                <th>Player 2</th>
            </tr>
        </thead>
        <tr>
            <th>１回目</th>
            <td class="scoreTd" class="myScore"></td>
            <td class="scoreTd" class="cpuScore"></td>
        </tr>
        <tr>
            <th>２回目</th>
            <td class="scoreTd" class="myScore"></td>
            <td class="scoreTd" class="cpuScore"></td>
        </tr>
        <tr>
            <th>３回目</th>
            <td class="scoreTd" class="myScore"></td>
            <td class="scoreTd" class="cpuScore"></td>
        </tr>
        <tr id="resultRow">
            <th>結果</th>
            <td id="playersScore"></td>
            <td id="CPUsScore"></td>
        </tr>
    </table>
</body>
</html>
```
</details>

解説することがないかもしれません。
id名が`myScore`, `cpuScore`となっているのはCPU対戦機能を付けたかった名残です。


## CSS

<details><summary>CSSソースコード</summary>

```style.css
body {
    padding-top: 10px;
}
#diceTable {
    display: inline;
    border-spacing: 30px 0px;
}
#diceTable td {
    border: 2px solid black;
    margin-right: 50px;
    width: 100px;
    height: 100px;
    text-align: center;
    font-size: 300%;
    border-radius: 50px;
}
button {
    background-color: honeydew;
    border-radius: 5px;
}
button:hover {
    background-color: rgb(220, 255, 220);
}
#scoreTable {
    border: 2px solid black;
    margin-top: 30px;
    margin-left: 30px;
}
#resultRow {
    background-color: rgb(255, 224, 85);
}

#scoreTable td , th {
    border: 1px solid black;
    width: 120px;
    height: 60px;
    font-size: 150%;
    text-align: center;
}
.touchable {
    background-color: rgb(206, 255, 132);
}
.touchable:hover {
    background-color: rgb(190, 255, 93);
}
.scoreTd  {
    background-color: rgb(214, 214, 214);
}
```
</details>

工夫した点は、
```css
#diceTable td {
    border-radius: 50px;
}
```
でダイスの目が表示されるところを丸くしたところ、
```css
button:hover {
    background-color: rgb(220, 255, 220);
}
.touchable:hover {
    background-color: rgb(190, 255, 93);
}
```
の2行では、マウスを上に持ってきたときに背景色が少し変わるようにし、プレイヤーが操作したいマスをわかりやすくしたところ、などです。

## JavaScript

<details><summary>JavaScriptソースコード</summary>

```main.js
window.onload = () => {

const dice1 = document.querySelector("#dice1");
const dice2 = document.querySelector("#dice2");
const dice3 = document.querySelector("#dice3");
const diceBoxes = [dice1, dice2, dice3];
const decideBut = document.querySelector("#decide");
const scoreTds = Array.from(
    document.querySelectorAll('.scoreTd')
);
let dices = [];
let copyDices = [];
let turn = 1;

const play = () => {
    dices = [];
    copyDices = [];
    for(let i=0;i<=2;i++){
        dices.push(random(1,3));
    }
    copyDices = [...dices];
    diceBoxes.forEach((element, index)=>{
        element.innerText = copyDices.shift();
        element.removeEventListener('click', changeDice);
        element.addEventListener('click',  changeDice, false);
    })
    decideBut.removeEventListener('click', noChangeDice);
    decideBut.addEventListener('click', noChangeDice, false);
}

const changeDice = (index) => {
    let elmIdx = index.srcElement.id.slice(-1)-1;
    let changedNum = random(1,3);
    console.log(turn, elmIdx, dices[elmIdx]);
    console.log(dices);
    dices[elmIdx].innerText = changedNum;
    dices[elmIdx] = changedNum;
    writeScore(dices);
}

const noChangeDice = () => {
    writeScore(dices);
}

const writeScore = (dices) => {
    let sum = 0;
    dices.forEach((element)=>{
        sum += element;
    });
    let score = sum + bonus(dices);
    switch(score-sum){
        case 10:
            alert('YATHZEE !!!');
            break;
        case 5:
            alert("Straight !!");
            break;
        case 3:
            alert("Regoll !");
            break;
    }
    scoreTds[turn-1].innerText = score;

    if(turn<6){
        turn+=1;
        play();
    }else{
        result();
    }
}

const bonus = (dices) => {
    let bonusPoints = 0;
    const dice1 = dices[0];
    const dice2 = dices[1];
    const dice3 = dices[2];

    if(dice1==dice2 && dice2==dice3){
        bonusPoints+=10;
    }else if(dice1!==dice2 && dice2!==dice3 && dice3!==dice1){
        bonusPoints+=5;
    }else if(dice1!==2 && dice2!==2 && dice3!==2){
        bonusPoints+=3;
    }

    return bonusPoints;
}

const result = () => {
    const myScore = document.querySelector('#playersScore');
    const cpusScore = document.querySelector('#CPUsScore');
    const scores = Array.from(document.querySelectorAll('.scoreTd'));
    let myPoints = Number(scores[0].innerText) + Number(scores[2].innerText) + Number(scores[4].innerText);
    let cpuPoints = Number(scores[1].innerText) + Number(scores[3].innerText) + Number(scores[5].innerText);
    myScore.innerText = myPoints;
    cpusScore.innerText = cpuPoints;
    decideBut.disabled = true;

    dice1.innerText = 'お';
    dice2.innerText = 'わ';
    dice3.innerText = 'り';
    diceBoxes.forEach((element)=>{
        element.removeEventListener('click', changeDice);
        element.style.backgroundColor = 'white';
    })
    decideBut.innerText='また遊んでね';
}

const random = (min, max) => {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max-min+1)+min);
}

play();

}
```
</details>

順に解説していきます。

```js
window.onload = () => {

}
```
これは、HTMLのheadタグにscriptタグを挿入したため、必要となった記述です。

https://developer.mozilla.org/ja/docs/Web/API/Window/load_event

これがないとbodyタグの中より先に
```js
const dice1 = document.querySelector("#dice1");
const dice2 = document.querySelector("#dice2");
const dice3 = document.querySelector("#dice3");
```
などが読み込まれてしまい、そんなタグは存在しませんとエラーが発生します。
- - -
```js
const dice1 = document.querySelector("#dice1");
const dice2 = document.querySelector("#dice2");
const dice3 = document.querySelector("#dice3");
const diceBoxes = [dice1, dice2, dice3];
const decideBut = document.querySelector("#decide");
```
この部分は、ゲームの得点や進行に関わるノードを取得しています。
4行目では、3つのダイスを操作しやすいように1つの配列に格納しています。
実際のプレイ画面ではここにあたります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/5d91d729-55a2-6727-6c7b-87df70e0e837.png)

- - -

```js
const scoreTds = Array.from(
    document.querySelectorAll('.scoreTd')
);
```
この記述は、クラス名が`scoreTd`のノードをすべて取得し、配列にしています。
実際のプレイ画面では、この灰色の`td`6つです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/4d1084db-4eda-4124-5e04-ed01bd9fb2e7.png)

ちなみに、`Array.from()`でくくらずに
```js
const scoreTds = document.querySelectorAll('.scoreTd');
```
とすると、`scoreTds`には**配列風オブジェクト**が取得され、`push()`などの配列用の操作ができません。
初見は躓くと思います。

-  - -

```js
let dices = [];
let copyDices = [];
```
ここでは、下の`play`関数で繰り返し使うための配列が宣言されています。
`const`での宣言でよかったかもしれません。

- - -

 ```js
let turn = 1;
```
ターンの宣言、初期化です。
`play`関数ではこれが6未満のとき、`turn`に`1`加算して`play`関数を呼び出すようにしています。

- - -

中心部分に入っていきます。

```js
const play = () => {
    dices = [];
    copyDices = [];
    for(let i=0;i<=2;i++){
        dices.push(random(1,3));
    }
    copyDices = [...dices];
    diceBoxes.forEach((element, index)=>{
        element.innerText = copyDices.shift();
        element.removeEventListener('click', changeDice);
        element.addEventListener('click',  changeDice, false);
    })
    decideBut.removeEventListener('click', noChangeDice);
    decideBut.addEventListener('click', noChangeDice, false);
}
```
`play`関数です。
```js
dices = [];
copyDices = [];
```
ここでは先ほどと同様初期化を行い、
```js
for(let i=0;i<=2;i++){
    dices.push(random(1,3));
}
```
ここでは、配列`dice`に1~3の乱数を3つ格納しています。
```js
copyDices = [...dices];
```
ここでは、配列`dices`をディープコピーしたものを配列`copyDices`に格納しています。
もし
```js
copyDices = dices
```
としてしまうと、シャローコピーとなり、配列`dices`の値を変更すると配列`copyDices`の値も同時に変更されてしまいます。参照先の異なる別のオブジェクトとして`copyDices`を作りたかったので、スプレッド演算子を用いてこのようにしています。

詳しくは[こちら](https://qiita.com/_kt15_/items/0ae5085d61fa5598c76e)をご覧ください。

- - -

```js
diceBoxes.forEach((element, index)=>{
    element.innerText = copyDices.shift();
    element.removeEventListener('click', changeDice);
    element.addEventListener('click',  changeDice, false);
})
```

ここでは、配列`diceBoxes`の各要素に配列`copyDices`の中身(1~3の数字3つ)を順に設定し、関数`changeDice`があれば取り除き、その後関数`changeDice`を設定しています。

`remove`してやらないと何度も関数`changeDice`が呼び出されてしまいます。しかし、そもそももっと良い書き方があったと思います。関数`play`のなかに書かなければこんな処理は必要なかったんじゃないかと今になって思いました。

- - -

```js
decideBut.removeEventListener('click', noChangeDice);
decideBut.addEventListener('click', noChangeDice, false);
```
ここでは、「変更しない」ボタンに関数`noChangeDice`が設定されていれば取り除き、その後設定しています。
上と同様にもっと良い方法があったと思います。

- - -

続いて、関数`changeDice`です。
```js
const changeDice = (index) => {
    let elmIdx = index.srcElement.id.slice(-1)-1;
    let changedNum = random(1,3);
    dices[elmIdx].innerText = changedNum;
    dices[elmIdx] = changedNum;
    writeScore(dices);
}
```
まず、引数`index`は`console.log(index)`としてみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/6b8cf027-629c-5841-e209-854310c2bf62.png)
と表示されます。
展開してみると、下のほうに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/8f267998-b008-01c7-11db-30be812c70c5.png)
とあり、`index.srcElement`とはクリックされた`td`要素であることが分かります。
続いて`.id`でそのid名を取得し、`slice(-1)`でid名の最後の文字(1~3の文字)を取得し、`-1`で1引いています。
まとめると、**クリックされたダイス(td)の番号(0~2)を取得する**ための記述です。

あまりにも無理やりですね。
コードを見ただけでは何が取得できているのか分かりづらいですし、この引数名`index`は`diceBoxes.forEach((element, index)=>{};`の`index`と混同されかねないです。

とりあえず、これで取得できたダイスの番号(0~2)が変数`elmIdx`に取得されます。
`let changedNum = random(1,3);`では、新しいダイスの目を決定しています。
この2つを用いて、クリックされたtdの目を変更しているのが次の記述です。
```js
dices[elmIdx].innerText = changedNum;
```
これはあくまで表示上の数値を変えただけです。
得点計算のために内部の数値も変える必要があります。
```js
dices[elmIdx] = changedNum;
```
そして変更まで終わった配列`dices`を引数として関数`writeScore`を呼び出します。

- - -

関数`writeScore`の記述の前に、ダイスを振り直さなかった場合(変更しないボタンを押した場合)に呼ばれる関数`noChangeDice`の記述があります。
```js
const noChangeDice = () => {
    writeScore(dices);
}
```
- - -

関数`writeScore`です。
```js
const writeScore = (dices) => {
    let sum = 0;
    dices.forEach((element)=>{
        sum += element;
    });
    let score = sum + bonus(dices);
    switch(score-sum){
        case 10:
            alert('YATHZEE !!!');
            break;
        case 5:
            alert("Straight !!");
            break;
        case 3:
            alert("Regoll !");
            break;
    }
    scoreTds[turn-1].innerText = score;

    if(turn<6){
        turn+=1;
        play();
    }else{
        result();
    }
}
```

```js
let sum = 0
```
ここで1回あたりの合計点に用いる変数の初期化を行っています。

```js
dices.forEach((element)=>{
    sum += element;
});
```
ここでは、単純なダイスの目の合計を求め、先程初期化した変数`sum`としています。

```js
let score = sum + bonus(dices);
```
ここでは、`sum`に`bonus(dices)`を加え、1回あたりの得点を求めています。
関数`bonus`に関しては後程説明します。
```js
switch(score-sum){
    case 10:
        alert('YATHZEE !!!');
        break;
    case 5:
        alert("Straight !!");
        break;
    case 3:
        alert("Regoll !");
        break;
}
```
ここでは、`score-sum`(=bonus得点)に応じて、`alert()`しています。
`score-sum`という周りくどい書き方が気になるので、
```改善前.js
let score = sum + bonus(dices);
switch(score-sum){
    case 10:
        alert('YATHZEE !!!');
        break;
    case 5:
        alert("Straight !!");
        break;
    case 3:
        alert("Regoll !");
        break;
}
```
を
```改善後.js
let bonusPoint = bonus(dices)
let score = sum + bonusPoiunt;
switch(bonusPoint){
    case 10:
        alert('YATHZEE !!!');
        break;
    case 5:
        alert("Straight !!");
        break;
    case 3:
        alert("Regoll !");
        break;
}
```
のようにした方がよさそうです。
また、`sum`, `score`という名前もどの合計なのか、どの時点でのスコアなのかなど分かりづらいため、改善の余地がありそうです。

続いて、
```js
scoreTds[turn-1].innerText = score;
```
この記述で、求めた`score`を画面上に表示しています。
```js
if(turn<6){
    turn+=1;
    play();
}else{
    result();
}
```
ここでは、変数`turn`が6未満なら1足して関数`play`を呼び出す、変数`turn`が6以上なら関数`result`を呼び出しています。

- - -

関数`bonus`に関しての説明です。

```js
const bonus = (dices) => {
    let bonusPoints = 0;
    const dice1 = dices[0];
    const dice2 = dices[1];
    const dice3 = dices[2];

    if(dice1==dice2 && dice2==dice3){
        bonusPoints+=10;
    }else if(dice1!==dice2 && dice2!==dice3 && dice3!==dice1){
        bonusPoints+=5;
    }else if(dice1!==2 && dice2!==2 && dice3!==2){
        bonusPoints+=3;
    }

    return bonusPoints;
}
```

まず、
```js
let bonusPoints = 0;
```
ここで初期化をしています。

```js
const dice1 = dices[0];
const dice2 = dices[1];
const dice3 = dices[2];
```
ここで、配列`dices`の各要素を新しい変数に格納しています。
これは記述を減らしたかったからだと思うのですが、それほど記述量が変わっていないどころか増えてそうなので、不要だと思いました。

```js
if(dice1==dice2 && dice2==dice3){
    bonusPoints+=10;
}
```
これは、全ての値が同じ⇔役ヤッツィーが出たときの処理です。

```js
else if(dice1!==dice2 && dice2!==dice3 && dice3!==dice1){
    bonusPoints+=5;
}
```
これは全ての目が異なる⇔役ストレートが出たときの処理です。
ダイスの数が3つのときしか成り立たない記述で直感的でないため、こちらも配列のメソッドを使うなどして改善の余地がありそうです。

```js
else if(dice1!==2 && dice2!==2 && dice3!==2){
    bonusPoints+=3;
}
```
これは全ての目が2でない⇔役リゴールが出たときの処理です。
リゴールの条件は最小の目または最大の目が出ることですので、ダイスの数が3つであればこれで成立しますが、改善の余地がありそうです。

```js
return bonusPoints;
```
求めた`bonusPoint`を返しています。

- - -

最後に、関数`result`に関する説明です。

```js
const result = () => {
    const myScore = document.querySelector('#playersScore');
    const cpusScore = document.querySelector('#CPUsScore');
    const scores = Array.from(document.querySelectorAll('.scoreTd'));
    let myPoints = Number(scores[0].innerText) + Number(scores[2].innerText) + Number(scores[4].innerText);
    let cpuPoints = Number(scores[1].innerText) + Number(scores[3].innerText) + Number(scores[5].innerText);
    myScore.innerText = myPoints;
    cpusScore.innerText = cpuPoints;
    decideBut.disabled = true;

    dice1.innerText = 'お';
    dice2.innerText = 'わ';
    dice3.innerText = 'り';
    diceBoxes.forEach((element)=>{
        element.removeEventListener('click', changeDice);
        element.style.backgroundColor = 'white';
    })
    decideBut.innerText='また遊んでね';
}
```

まず、
```js
const myScore = document.querySelector('#playersScore');
const cpusScore = document.querySelector('#CPUsScore');
```
ここでは、下図の黄色い部分をそれぞれ取得しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/b72e313b-d298-0780-5641-ef3f67ff2440.png)

```js
const scores = Array.from(document.querySelectorAll('.scoreTd'));
```
ここでは、上図の灰色の部分を左上から右上、左中...の順に取得しています。
```js
let myPoints = Number(scores[0].innerText) + 
    Number(scores[2].innerText) + Number(scores[4].innerText);
let cpuPoints = Number(scores[1].innerText) + 
    Number(scores[3].innerText) + Number(scores[5].innerText);
```
取得した灰色の部分について、偶数番目の要素の`innerText`の合計を`myPoints`に、奇数番目の合計を`cpuPoints`に格納しています。

```js
myScore.innerText = myPoints;
cpusScore.innerText = cpuPoints;
```
求めた合計得点を画面上に表示させています。

```js
decideBut.disabled = true;
```
ゲームが終わったため、変更しないボタンを使えないようにしています。

```js
dice1.innerText = 'お';
dice2.innerText = 'わ';
dice3.innerText = 'り';
diceBoxes.forEach((element)=>{
    element.removeEventListener('click', changeDice);
    element.style.backgroundColor = 'white';
})
decideBut.innerText='また遊んでね';
```
ちょっとした遊び心と、ダイスを振り直せないようにする処理です。
ゲームが終わると下のようになり、視覚的にゲームが終わったと分かりやすくしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666100/194772be-9bad-0e6a-b03b-a2f3cc0624f3.png)

- - -

最後に、関数`random`です。
```js
const random = (min, max) => {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max-min+1)+min);
}
```

1~3の乱数を得るための関数です。[こちら](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random)を参考にしました。

- - -
本当の最後に
```js
play();
```
で1度目の関数`play`の実行です。



# 全体的な改善点、問題点

+ 変数`turn`の値が奇数ならPlayer 1、偶数ならPlayer 2のターンとしているところが直感的でないと感じた。記述は増えるが改善すべきだと思う。
+ id名、クラス名が、`myScore`, `cpuScore`, `playersScore`, `CPUsScore`など、 統一感がない。
+ 気分で関数を分けたが、これで良いのか？どう分けるのか知識を身に付けた方が良さそう。
+ そもそも設計してない、知識がないからできない。
+ 文字列を数値に暗黙に変換してて気持ち悪い。


# 最後に
ここまでちゃんと自分のコードを振り返るのは初めてだった。自分に足りない知識など気付くことが多くあった。
リファクタリングの知識、設計の知識をもっとつけたい。
そしてどうやったらバグは直るんだT^T
TypeScriptで書いたらひどいことになりそう。そっちもやる