Back to Game

Connect Four - Source Code

Play Game
JavaScript
const ROWS = 6;
const COLS = 7;
let board = Array(ROWS).fill().map(() => Array(COLS).fill(''));
let currentPlayer = 'red';
let gameActive = true;

function minimax(board, depth, alpha, beta, isMaximizing) {
  const winner = checkWinner(board);
  
  if (winner === 'yellow') return 1000 - depth;
  if (winner === 'red') return depth - 1000;
  if (depth >= 7) return evaluateBoard(board);
  
  if (isMaximizing) {
    let maxEval = -Infinity;
    for (let col = 0; col < COLS; col++) {
      if (isValidMove(board, col)) {
        const newBoard = makeMove([...board.map(row => [...row])], col, 'yellow');
        const eval = minimax(newBoard, depth + 1, alpha, beta, false);
        maxEval = Math.max(maxEval, eval);
        alpha = Math.max(alpha, eval);
        if (beta <= alpha) break;
      }
    }
    return maxEval;
  } else {
    let minEval = Infinity;
    for (let col = 0; col < COLS; col++) {
      if (isValidMove(board, col)) {
        const newBoard = makeMove([...board.map(row => [...row])], col, 'red');
        const eval = minimax(newBoard, depth + 1, alpha, beta, true);
        minEval = Math.min(minEval, eval);
        beta = Math.min(beta, eval);
        if (beta <= alpha) break;
      }
    }
    return minEval;
  }
}

function evaluateBoard(board) {
  let score = 0;
  
  for (let row = 0; row < ROWS; row++) {
    for (let col = 0; col < COLS - 3; col++) {
      score += evaluateWindow(board, row, col, 0, 1);
    }
  }
  
  for (let row = 0; row < ROWS - 3; row++) {
    for (let col = 0; col < COLS; col++) {
      score += evaluateWindow(board, row, col, 1, 0);
    }
  }
  
  for (let row = 0; row < ROWS - 3; row++) {
    for (let col = 0; col < COLS - 3; col++) {
      score += evaluateWindow(board, row, col, 1, 1);
      score += evaluateWindow(board, row + 3, col, -1, 1);
    }
  }
  
  return score;
}

function evaluateWindow(board, row, col, deltaRow, deltaCol) {
  let yellowCount = 0;
  let redCount = 0;
  
  for (let i = 0; i < 4; i++) {
    const r = row + i * deltaRow;
    const c = col + i * deltaCol;
    
    if (board[r][c] === 'yellow') yellowCount++;
    else if (board[r][c] === 'red') redCount++;
  }
  
  if (yellowCount === 4) return 100;
  if (redCount === 4) return -100;
  if (yellowCount === 3 && redCount === 0) return 10;
  if (redCount === 3 && yellowCount === 0) return -10;
  if (yellowCount === 2 && redCount === 0) return 2;
  if (redCount === 2 && yellowCount === 0) return -2;
  
  return 0;
}

function getBestMove() {
  let bestScore = -Infinity;
  let bestCol = 3;
  
  for (let col = 0; col < COLS; col++) {
    if (isValidMove(board, col)) {
      const newBoard = makeMove([...board.map(row => [...row])], col, 'yellow');
      const score = minimax(newBoard, 0, -Infinity, Infinity, false);
      
      if (score > bestScore) {
        bestScore = score;
        bestCol = col;
      }
    }
  }
  
  return bestCol;
}

function isValidMove(board, col) {
  return board[0][col] === '';
}

function makeMove(board, col, player) {
  for (let row = ROWS - 1; row >= 0; row--) {
    if (board[row][col] === '') {
      board[row][col] = player;
      break;
    }
  }
  return board;
}

function checkWinner(board) {
  for (let row = 0; row < ROWS; row++) {
    for (let col = 0; col < COLS - 3; col++) {
      if (board[row][col] && 
          board[row][col] === board[row][col + 1] &&
          board[row][col] === board[row][col + 2] &&
          board[row][col] === board[row][col + 3]) {
        return board[row][col];
      }
    }
  }
  
  for (let row = 0; row < ROWS - 3; row++) {
    for (let col = 0; col < COLS; col++) {
      if (board[row][col] && 
          board[row][col] === board[row + 1][col] &&
          board[row][col] === board[row + 2][col] &&
          board[row][col] === board[row + 3][col]) {
        return board[row][col];
      }
    }
  }
  
  for (let row = 0; row < ROWS - 3; row++) {
    for (let col = 0; col < COLS - 3; col++) {
      if (board[row][col] && 
          board[row][col] === board[row + 1][col + 1] &&
          board[row][col] === board[row + 2][col + 2] &&
          board[row][col] === board[row + 3][col + 3]) {
        return board[row][col];
      }
    }
  }
  
  for (let row = 3; row < ROWS; row++) {
    for (let col = 0; col < COLS - 3; col++) {
      if (board[row][col] && 
          board[row][col] === board[row - 1][col + 1] &&
          board[row][col] === board[row - 2][col + 2] &&
          board[row][col] === board[row - 3][col + 3]) {
        return board[row][col];
      }
    }
  }
  
  return null;
}

function dropPiece(col) {
  if (!gameActive || !isValidMove(board, col)) return;
  
  board = makeMove(board, col, 'red');
  updateDisplay();
  
  const winner = checkWinner(board);
  if (winner || board.every(row => row.every(cell => cell !== ''))) {
    gameActive = false;
    return;
  }
  
  setTimeout(() => {
    const aiCol = getBestMove();
    board = makeMove(board, aiCol, 'yellow');
    updateDisplay();
    
    const aiWinner = checkWinner(board);
    if (aiWinner || board.every(row => row.every(cell => cell !== ''))) {
      gameActive = false;
    }
  }, 500);
}

function updateDisplay() {
  board.forEach((row, rowIndex) => {
    row.forEach((cell, colIndex) => {
      const cellElement = document.getElementById(`cell-${rowIndex}-${colIndex}`);
      cellElement.className = `cell ${cell}`;
    });
  });
}

function resetGame() {
  board = Array(ROWS).fill().map(() => Array(COLS).fill(''));
  currentPlayer = 'red';
  gameActive = true;
  updateDisplay();
}

This code demonstrates the core game logic. The actual implementation may include additional features and optimizations.