6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptAdvent Calendar 2022

Day 25

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

Last updated at Posted at 2022-12-24

はじめに

2, 3か月前にこんなゲームを作りました。

ルール

プレイ画面
image.png

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

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

本題の前に

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

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

VS CPU 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("リゴール!3点獲得!\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;

}

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

解説

HTML

HTMLソースコード
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>1回目</th>
            <td class="scoreTd" class="myScore"></td>
            <td class="scoreTd" class="cpuScore"></td>
        </tr>
        <tr>
            <th>2回目</th>
            <td class="scoreTd" class="myScore"></td>
            <td class="scoreTd" class="cpuScore"></td>
        </tr>
        <tr>
            <th>3回目</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>

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

CSS

CSSソースコード
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);
}

工夫した点は、

#diceTable td {
    border-radius: 50px;
}

でダイスの目が表示されるところを丸くしたところ、

button:hover {
    background-color: rgb(220, 255, 220);
}
.touchable:hover {
    background-color: rgb(190, 255, 93);
}

の2行では、マウスを上に持ってきたときに背景色が少し変わるようにし、プレイヤーが操作したいマスをわかりやすくしたところ、などです。

JavaScript

JavaScriptソースコード
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();

}

順に解説していきます。

window.onload = () => {

}

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

これがないとbodyタグの中より先に

const dice1 = document.querySelector("#dice1");
const dice2 = document.querySelector("#dice2");
const dice3 = document.querySelector("#dice3");

などが読み込まれてしまい、そんなタグは存在しませんとエラーが発生します。


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


const scoreTds = Array.from(
    document.querySelectorAll('.scoreTd')
);

この記述は、クラス名がscoreTdのノードをすべて取得し、配列にしています。
実際のプレイ画面では、この灰色のtd6つです。
image.png

ちなみに、Array.from()でくくらずに

const scoreTds = document.querySelectorAll('.scoreTd');

とすると、scoreTdsには配列風オブジェクトが取得され、push()などの配列用の操作ができません。
初見は躓くと思います。


let dices = [];
let copyDices = [];

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


let turn = 1;

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


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

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関数です。

dices = [];
copyDices = [];

ここでは先ほどと同様初期化を行い、

for(let i=0;i<=2;i++){
    dices.push(random(1,3));
}

ここでは、配列diceに1~3の乱数を3つ格納しています。

copyDices = [...dices];

ここでは、配列dicesをディープコピーしたものを配列copyDicesに格納しています。
もし

copyDices = dices

としてしまうと、シャローコピーとなり、配列dicesの値を変更すると配列copyDicesの値も同時に変更されてしまいます。参照先の異なる別のオブジェクトとしてcopyDicesを作りたかったので、スプレッド演算子を用いてこのようにしています。

詳しくはこちらをご覧ください。


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のなかに書かなければこんな処理は必要なかったんじゃないかと今になって思いました。


decideBut.removeEventListener('click', noChangeDice);
decideBut.addEventListener('click', noChangeDice, false);

ここでは、「変更しない」ボタンに関数noChangeDiceが設定されていれば取り除き、その後設定しています。
上と同様にもっと良い方法があったと思います。


続いて、関数changeDiceです。

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);
}

まず、引数indexconsole.log(index)としてみると、
image.png
と表示されます。
展開してみると、下のほうに
image.png
とあり、index.srcElementとはクリックされたtd要素であることが分かります。
続いて.idでそのid名を取得し、slice(-1)でid名の最後の文字(1~3の文字)を取得し、-1で1引いています。
まとめると、クリックされたダイス(td)の番号(0~2)を取得するための記述です。

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

とりあえず、これで取得できたダイスの番号(0~2)が変数elmIdxに取得されます。
let changedNum = random(1,3);では、新しいダイスの目を決定しています。
この2つを用いて、クリックされたtdの目を変更しているのが次の記述です。

dices[elmIdx].innerText = changedNum;

これはあくまで表示上の数値を変えただけです。
得点計算のために内部の数値も変える必要があります。

dices[elmIdx] = changedNum;

そして変更まで終わった配列dicesを引数として関数writeScoreを呼び出します。


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

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

関数writeScoreです。

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();
    }
}
let sum = 0

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

dices.forEach((element)=>{
    sum += element;
});

ここでは、単純なダイスの目の合計を求め、先程初期化した変数sumとしています。

let score = sum + bonus(dices);

ここでは、sumbonus(dices)を加え、1回あたりの得点を求めています。
関数bonusに関しては後程説明します。

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という名前もどの合計なのか、どの時点でのスコアなのかなど分かりづらいため、改善の余地がありそうです。

続いて、

scoreTds[turn-1].innerText = score;

この記述で、求めたscoreを画面上に表示しています。

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

ここでは、変数turnが6未満なら1足して関数playを呼び出す、変数turnが6以上なら関数resultを呼び出しています。


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

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;
}

まず、

let bonusPoints = 0;

ここで初期化をしています。

const dice1 = dices[0];
const dice2 = dices[1];
const dice3 = dices[2];

ここで、配列dicesの各要素を新しい変数に格納しています。
これは記述を減らしたかったからだと思うのですが、それほど記述量が変わっていないどころか増えてそうなので、不要だと思いました。

if(dice1==dice2 && dice2==dice3){
    bonusPoints+=10;
}

これは、全ての値が同じ⇔役ヤッツィーが出たときの処理です。

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

これは全ての目が異なる⇔役ストレートが出たときの処理です。
ダイスの数が3つのときしか成り立たない記述で直感的でないため、こちらも配列のメソッドを使うなどして改善の余地がありそうです。

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

これは全ての目が2でない⇔役リゴールが出たときの処理です。
リゴールの条件は最小の目または最大の目が出ることですので、ダイスの数が3つであればこれで成立しますが、改善の余地がありそうです。

return bonusPoints;

求めたbonusPointを返しています。


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

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 myScore = document.querySelector('#playersScore');
const cpusScore = document.querySelector('#CPUsScore');

ここでは、下図の黄色い部分をそれぞれ取得しています。
image.png

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);

取得した灰色の部分について、偶数番目の要素のinnerTextの合計をmyPointsに、奇数番目の合計をcpuPointsに格納しています。

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='また遊んでね';

ちょっとした遊び心と、ダイスを振り直せないようにする処理です。
ゲームが終わると下のようになり、視覚的にゲームが終わったと分かりやすくしています。

image.png


最後に、関数randomです。

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

1~3の乱数を得るための関数です。こちらを参考にしました。


本当の最後に

play();

で1度目の関数playの実行です。

全体的な改善点、問題点

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

最後に

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

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?