Files
Connect-5/game.js

289 lines
7.7 KiB
JavaScript

// Connect-5 Game Logic
class Connect5Game {
constructor() {
this.boardSize = 15;
this.board = [];
this.currentPlayer = "X";
this.gameActive = true;
this.scores = { X: 0, O: 0 };
this.boardElement = document.getElementById("gameBoard");
this.statusMessage = document.getElementById("statusMessage");
this.currentPlayerDisplay = document.getElementById("currentPlayer");
this.victoryOverlay = document.getElementById("victoryOverlay");
this.victoryTitle = document.getElementById("victoryTitle");
this.scoreXDisplay = document.getElementById("scoreX");
this.scoreODisplay = document.getElementById("scoreO");
this.initializeEventListeners();
this.initializeBoard();
}
initializeEventListeners() {
// Size selector buttons
document.querySelectorAll(".size-btn").forEach((btn) => {
btn.addEventListener("click", (e) => {
document
.querySelectorAll(".size-btn")
.forEach((b) => b.classList.remove("active"));
e.target.classList.add("active");
this.boardSize = parseInt(e.target.dataset.size);
this.resetGame();
});
});
// Reset button
document.getElementById("resetBtn").addEventListener("click", () => {
this.resetGame();
});
// Play again button
document.getElementById("playAgainBtn").addEventListener("click", () => {
this.hideVictoryOverlay();
this.resetGame();
});
}
initializeBoard() {
// Create empty board array
this.board = Array(this.boardSize)
.fill(null)
.map(() => Array(this.boardSize).fill(null));
// Clear and create board UI
this.boardElement.innerHTML = "";
this.boardElement.setAttribute("data-size", this.boardSize);
// Create cells
for (let row = 0; row < this.boardSize; row++) {
for (let col = 0; col < this.boardSize; col++) {
const cell = document.createElement("div");
cell.classList.add("cell");
cell.dataset.row = row;
cell.dataset.col = col;
cell.addEventListener("click", () => this.handleCellClick(row, col));
this.boardElement.appendChild(cell);
}
}
this.updateStatus();
// Set initial board class
this.boardElement.classList.add(`turn-${this.currentPlayer.toLowerCase()}`);
}
handleCellClick(row, col) {
if (!this.gameActive || this.board[row][col] !== null) {
return;
}
// Check if in multiplayer mode and if it's our turn
if (multiplayerClient && multiplayerClient.isMultiplayer) {
if (!multiplayerClient.myTurn) {
return; // Not our turn in multiplayer
}
// Send move to server
const moveSent = multiplayerClient.makeMove(row, col);
if (!moveSent) return;
}
// Place piece
this.board[row][col] = this.currentPlayer;
// Update UI
const cellIndex = row * this.boardSize + col;
const cell = this.boardElement.children[cellIndex];
cell.classList.add("occupied", this.currentPlayer.toLowerCase(), "latest-move");
// Remove brightness boost after 2 seconds
setTimeout(() => {
if (cell) cell.classList.remove("latest-move");
}, 2000);
// In local mode only, check for win/draw and switch player
if (!multiplayerClient || !multiplayerClient.isMultiplayer) {
// Check for win
if (this.checkWin(row, col)) {
this.gameActive = false;
this.scores[this.currentPlayer]++;
this.updateScores();
// Delay showing visual overlay for 5 seconds to show winning lights
setTimeout(() => {
this.showVictoryOverlay();
}, 5000);
return;
}
// Check for draw
if (this.checkDraw()) {
this.gameActive = false;
this.statusMessage.textContent = "It's a draw! Board is full.";
return;
}
// Switch player
this.currentPlayer = this.currentPlayer === "X" ? "O" : "X";
this.updateStatus();
}
}
checkWin(row, col) {
const directions = [
[0, 1], // Horizontal (right)
[1, 0], // Vertical (down)
[1, 1], // Diagonal (down-right)
[1, -1], // Diagonal (down-left)
];
for (const [dx, dy] of directions) {
const count =
1 +
this.countDirection(row, col, dx, dy) +
this.countDirection(row, col, -dx, -dy);
if (count >= 5) {
this.highlightWinningCells(row, col, dx, dy);
return true;
}
}
return false;
}
countDirection(row, col, dx, dy) {
const player = this.board[row][col];
let count = 0;
let r = row + dx;
let c = col + dy;
while (
r >= 0 &&
r < this.boardSize &&
c >= 0 &&
c < this.boardSize &&
this.board[r][c] === player
) {
count++;
r += dx;
c += dy;
}
return count;
}
highlightWinningCells(row, col, dx, dy) {
const cells = [];
const player = this.board[row][col];
// Find all cells in this direction
cells.push({ row, col });
// Forward direction
let r = row + dx;
let c = col + dy;
while (
r >= 0 &&
r < this.boardSize &&
c >= 0 &&
c < this.boardSize &&
this.board[r][c] === player
) {
cells.push({ row: r, col: c });
r += dx;
c += dy;
}
// Backward direction
r = row - dx;
c = col - dy;
while (
r >= 0 &&
r < this.boardSize &&
c >= 0 &&
c < this.boardSize &&
this.board[r][c] === player
) {
cells.push({ row: r, col: c });
r -= dx;
c -= dy;
}
// Highlight cells
cells.forEach(({ row, col }) => {
const cellIndex = row * this.boardSize + col;
this.boardElement.children[cellIndex].classList.add("winning");
});
}
checkDraw() {
return this.board.every((row) => row.every((cell) => cell !== null));
}
updateStatus() {
this.statusMessage.textContent = `Player ${this.currentPlayer}'s turn`;
this.currentPlayerDisplay.textContent = this.currentPlayer;
// Update board class for ghost piece
if (this.boardElement) {
this.boardElement.classList.remove("turn-x", "turn-o");
this.boardElement.classList.add(
`turn-${this.currentPlayer.toLowerCase()}`
);
}
// Update the player display style
const playerDisplay = document.querySelector(".player-display");
if (this.currentPlayer === "O") {
playerDisplay.style.borderColor = "hsl(195, 70%, 55%)";
this.currentPlayerDisplay.style.color = "hsl(195, 70%, 55%)";
} else {
playerDisplay.style.borderColor = "hsl(270, 70%, 60%)";
this.currentPlayerDisplay.style.color = "hsl(270, 70%, 60%)";
}
}
updateScores() {
this.scoreXDisplay.textContent = this.scores.X;
this.scoreODisplay.textContent = this.scores.O;
}
showVictoryOverlay() {
this.victoryTitle.textContent = `Player ${this.currentPlayer} Wins!`;
this.victoryOverlay.classList.add("active");
this.statusMessage.textContent = `🎉 Player ${this.currentPlayer} achieved 5 in a row!`;
}
hideVictoryOverlay() {
this.victoryOverlay.classList.remove("active");
}
resetGame() {
this.currentPlayer = "X";
this.gameActive = true;
this.hideVictoryOverlay();
// Reset turn label to default
const turnLabel = document.getElementById("turnLabel");
if (turnLabel) {
turnLabel.textContent = "Current Turn:";
}
this.initializeBoard();
}
}
// Initialize game when DOM is loaded
window.game = null;
document.addEventListener("DOMContentLoaded", () => {
window.game = new Connect5Game();
// Enable multiplayer button now that game is ready
const multiplayerBtn = document.getElementById("multiplayerModeBtn");
if (multiplayerBtn) {
multiplayerBtn.disabled = false;
console.log("✅ Game initialized, multiplayer button enabled");
}
});