今回、JSの学習のため、マルバツゲームをを写経してみたので
全コードをアップしておきます(自分用の備考録)。
参考(写経)元
マルバツゲームを作ってみよう
※アロー関数で記述したり、コードのまとめ方を変更したりしていますが、ほぼ同じ内容です
Javascript
app.js
// 各種定義
let flag = false;
let counter = 9;
let winningLine = null;
const squares = document.querySelectorAll('.square');
const squaresArray = [].slice.call(squares); // IE11対策
const messages = document.querySelectorAll('.message-list li');
const messagesArray = [].slice.call(messages); // IE11対策
const resetBtn = document.querySelector('#reset-btn');
// メッセージの切り替え関数
const setMessage = (id) => {
messagesArray.forEach((message) => {
if (message.id === id) {
message.classList.remove('js-hidden');
} else {
message.classList.add('js-hidden');
}
});
}
// 勝利判定のパターン関数
const filterById = (targetArray, idArray) => {
return targetArray.filter((e) => {
return (e.id === idArray[0] || e.id === idArray[1] || e.id === idArray[2]);
});
}
// 勝利判定パターン
const line1 = filterById(squaresArray, ['1-1', '1-2', '1-3']);
const line2 = filterById(squaresArray, ['2-1', '2-2', '2-3']);
const line3 = filterById(squaresArray, ['3-1', '3-2', '3-3']);
const line4 = filterById(squaresArray, ['1-1', '2-1', '3-1']);
const line5 = filterById(squaresArray, ['1-2', '2-2', '3-2']);
const line6 = filterById(squaresArray, ['1-3', '2-3', '3-3']);
const line7 = filterById(squaresArray, ['1-1', '2-2', '3-3']);
const line8 = filterById(squaresArray, ['1-3', '2-2', '3-1']);
const lineArray = [line1, line2, line3, line4, line5, line6, line7, line8];
// 勝利判定関数
const isWinner = (symbol) => {
// some: 1つでも条件を満たしていればTrueを返す
const result = lineArray.some((line) => {
// every: 全て条件を満たしていればTrueを返す
const subResult = line.every((square) => {
if (symbol === 'maru') {
return square.classList.contains('js-maru-checked');
} else
if (symbol === 'batsu') {
return square.classList.contains('js-batsu-checked');
}
});
if (subResult) { winningLine = line }
return subResult;
});
return result;
}
// ゲーム終了時の関数
const gameOver = () => {
// 全てのマスをクリック不可にする
squaresArray.forEach((square) => {
square.classList.add('js-unclickable');
});
// 勝った時のマス目をハイライトする
if (winningLine) {
winningLine.forEach((square) => {
square.classList.add('js-highLight');
});
}
// リセットボタン表示
resetBtn.classList.remove('js-hidden');
}
// ゲームの初期化の関数
const initGame = () => {
flag = false;
counter = 9;
winningLine = null;
squaresArray.forEach((square) => {
square.classList.remove('js-maru-checked');
square.classList.remove('js-batsu-checked');
square.classList.remove('js-unclickable');
square.classList.remove('js-highLight');
});
setMessage('batsu-turn');
resetBtn.classList.add('js-hidden');
}
resetBtn.addEventListener('click', function() {
initGame();
});
// マスをクリックした時のイベント発火
squaresArray.forEach((square) => {
square.addEventListener('click', () => {
if (flag === true) {
square.classList.add('js-maru-checked');
square.classList.add('js-unclickable');
if (isWinner('maru')) {
setMessage('maru-win');
gameOver();
return;
}
setMessage('batsu-turn');
flag = false;
} else {
square.classList.add('js-batsu-checked');
square.classList.add('js-unclickable');
if (isWinner('batsu')) {
setMessage('batsu-win');
gameOver();
return;
}
setMessage('maru-turn');
flag = true;
}
counter--;
// 引き分け判定
if (counter === 0) {
setMessage('draw');
gameOver();
}
});
});
HTML
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>[Sample]○×ゲーム</title>
<link href="app.css" rel="stylesheet" />
</head>
<body>
<div class="wrapper">
<div class="game-container">
<a href="https://www.hypertextcandy.com/tic-tac-toe-1" target="_blank">参考: URL</a>
<div class="message-container">
<ul class="message-list">
<li id="maru-turn" class="js-hidden">
<span class="maru">○</span>の番
</li>
<li id="batsu-turn">
<span class="batsu">×</span>の番
</li>
<li id="maru-win" class="js-hidden">
<span class="maru">○</span>の勝ち!
</li>
<li id="batsu-win" class="js-hidden">
<span class="batsu">×</span>の勝ち!
</li>
<li id="draw" class="js-hidden">
引き分け
</li>
</ul>
</div>
<div class="squares-container">
<div class="squares-box">
<!-- 1行め -->
<div id="1-1" class="square"></div>
<div id="1-2" class="square"></div>
<div id="1-3" class="square"></div>
<!-- 2行め -->
<div id="2-1" class="square"></div>
<div id="2-2" class="square"></div>
<div id="2-3" class="square"></div>
<!-- 3行め -->
<div id="3-1" class="square"></div>
<div id="3-2" class="square"></div>
<div id="3-3" class="square"></div>
</div>
</div>
<div class="btn-container">
<div class="js-hidden" id="reset-btn">
<span class="btn btn-reset">もう一回遊ぶ</span>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
CSS
app.css
html {
box-sizing: border-box;
font-size: 16px;
}
*, *::before, *::after {
box-sizing: border-box;
}
li {
list-style: none;
}
body {
margin: 0;
line-height: normal;
}
h1 {
margin: 20px 0;
font-size: 2rem;
font-weight: bold;
}
.wrapper {
max-width: 1024px;
margin: 0 auto;
padding: 0 10px;
text-align: center;
}
.game-container {
padding: 60px 0;
}
.message-container {
margin-bottom: 20px;
font-size: 1.25rem;
font-weight: bold;
}
.batsu {
color: #00b0f0;
}
.maru {
color: #ff0000;
}
.js-hidden {
display: none;
}
.squares-container {
margin: 0 auto;
width: 202px;
}
.squares-box {
width: 202px;
height: 202px;
display: flex;
flex-wrap: wrap;
border: solid 2px #333;
}
.square {
position: relative;
width: calc(198px / 3);
height: calc(198px / 3);
border: solid 2px #333;
}
.js-maru-checked::before {
content: '';
width: 50px;
height: 50px;
border: solid 8px #ff0000;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.js-batsu-checked::before {
content: '';
width: 50px;
height: 8px;
margin: auto;
background-color: #00b0f0;
border-radius: 4px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
}
.js-batsu-checked::after {
content: '';
width: 8px;
height: 50px;
margin: auto;
background-color: #00b0f0;
border-radius: 4px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
}
.btn-container {
padding-top: 40px;
}
.btn {
display: inline-block;
width: 150px;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
.btn-reset {
color: #fff;
background-color: #ffc000;
font-weight: bold;
}
.btn-reset:hover {
background-color: #ffd347;
transition-duration: 0.4s;
}
/* JS連動 */
.js-unclickable {
pointer-events: none;
}
.js-highLight {
background-color: #fff2cc;
}