We Blog Weblog

セ〇アの四目並べ【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をもっと強くしたいですね。

この記事を書いた人
みやの
Web・DTP担当

Contact Us

ご意見、ご相談、料金のお見積もりなど、お気軽にお問い合わせください。

お問い合わせはこちら

TOP