mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 18:26:01 +00:00
289 lines
7.7 KiB
JavaScript
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");
|
|
}
|
|
});
|