セ〇アの四目並べ【CPU戦】
Web
2025年1月15日
みなさんこんにちは。
ケミストのWeb担当みやのです。
「Web」記事では、これまでに得たWebに関する知識を記録として残していきたいと思います。
今回は前回の四目並べのCPU戦を作ってみました。
難易度は「かんたん」です。さあレッツトライ!
Your turn
HTMLとCSS
HTMLとCSSは前回Doです。
JavaScript
前回のスクリプトに、CPUの行動を組み込みます。
function updateTurnMessage() {
const turnMessageElement = document.getElementById('turnMessage');
if (currentPlayer === 'red') {
turnMessageElement.textContent = 'Your turn';
turnMessageElement.style.color = '#dc3545';
} else {
turnMessageElement.textContent = 'Enemy turn';
turnMessageElement.style.color = '#ffc107';
}
}
updateTurnMessage();
function placePiece(col) {
if (!gameActive) return;
let targetRow = null;
for (let row = rows - 1; row >= 0; row--) {
if (board[row][col] === null) {
targetRow = row;
break;
}
}
if (targetRow !== null) {
board[targetRow][col] = currentPlayer;
const targetCell = document.querySelector(
`.row:nth-child(${targetRow + 1}) .cell:nth-child(${col + 1})`);
const pieceElement = document.createElement('div');
pieceElement.classList.add('piece', currentPlayer);
const cellWidth = targetCell.clientWidth;
const cellHeight = targetCell.clientHeight;
pieceElement.style.width = `${cellWidth}px`;
pieceElement.style.height = `${cellHeight}px`;
pieceElement.style.position = 'absolute';
pieceElement.style.left = `${targetCell.offsetLeft}px`;
pieceElement.style.top = `-${cellHeight}px`;
const gameBoard = document.getElementById('gameBoard');
gameBoard.appendChild(pieceElement);
const targetTop = targetCell.offsetTop;
setTimeout(() => {
pieceElement.style.transition = 'top 0.5s ease';
pieceElement.style.top = `${targetTop}px`;
}, 50);
setTimeout(() => {
const winningCells = checkWinner(targetRow, col);
if (winningCells) {
endGame(currentPlayer, winningCells);
setTimeout(() => {
highlightWinningPieces(winningCells);
}, 100);
} else if (isBoardFull()) {
endGame('Draw', []);
} else {
currentPlayer = currentPlayer === 'red' ? 'yellow' : 'red';
updateTurnMessage();
if (currentPlayer === 'yellow') {
setTimeout(cpuMove, 500);
}
}
renderBoard();
}, 300);
}
}
red turnをYour turn
yellow turnをEnemy turn
に変えてみました。
CPUの行動パターンを設定します。
function checkVerticalWin(color) {
for (let col = 0; col < cols; col++) {
for (let row = rows - 1; row >= 3; row--) {
if (board[row][col] === color &&
board[row - 1][col] === color &&
board[row - 2][col] === color &&
isColumnAvailable(col)) {
return col;
}
}
}
return null;
}
function checkHorizontalWin(color) {
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols - 3; col++) {
if (board[row][col] === color &&
board[row][col + 1] === color &&
board[row][col + 2] === color) {
if (col > 0 && board[row][col - 1] === null) {
return col - 1;
}
if (col + 3 < cols && board[row][col + 3] === null) {
return col + 3;
}
}
}
}
return null;
}
function checkDiagonalWin(color) {
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (row + 3 < rows && col + 3 < cols &&
board[row][col] === color &&
board[row + 1][col + 1] === color &&
board[row + 2][col + 2] === color &&
board[row + 3][col + 3] === color) {
return col;
}
if (row + 3 < rows && col - 3 >= 0 &&
board[row][col] === color &&
board[row + 1][col - 1] === color &&
board[row + 2][col - 2] === color &&
board[row + 3][col - 3] === color) {
return col;
}
}
}
return null;
}
function cpuMove() {
if (!gameActive) return;
let col;
col = checkVerticalWin('yellow');
if (col !== null) {
placePiece(col);
return;
}
col = checkHorizontalWin('yellow');
if (col !== null) {
placePiece(col);
return;
}
col = checkDiagonalWin('yellow');
if (col !== null) {
placePiece(col);
return;
}
col = checkHorizontalWin('red');
if (col !== null) {
placePiece(col);
return;
}
col = checkVerticalWin('red');
if (col !== null) {
placePiece(col);
return;
}
col = checkDiagonalWin('red');
if (col !== null) {
placePiece(col);
return;
}
const action = Math.random();
if (action < 0.47) {
col = getAdjacentColumn('yellow');
} else if (action < 0.94) {
col = getStackOnTopColumn('yellow');
} else {
do {
col = Math.floor(Math.random() * cols);
} while (!isColumnAvailable(col));
}
placePiece(col);
}
〇✕ゲームの時のように単純にはいきませんでした。
縦、横、斜めに3つ揃っているかどうかをチェックし「黄色が3つ揃っていたら勝ちに行く」「赤が3つ揃っていたら邪魔してくる」という感じにしたのですが、赤の縦3つを検知したらその列に延々置き続けるという残念な行動を取ります。
今後の課題

CPUをもっと強くしたいですね。