Files
Connect-5/index.html

587 lines
20 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Connect-5 - Premium Gomoku Game" />
<title>Connect-5 - Premium Gomoku Game</title>
<link rel="icon" type="image/png" href="Logo.png" />
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="multiplayer-styles.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<!-- Socket.io Client -->
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
</head>
<body>
<div class="container">
<header class="header">
<div class="header-content">
<h1 class="title">
<img src="Logo.png" alt="BCT Logo" class="title-icon" />
Connect-5
</h1>
<p class="subtitle">Premium Gomoku Experience</p>
<a
href="https://www.youtube.com/@beyondcloudtechnology"
target="_blank"
rel="noopener noreferrer"
class="youtube-link"
>
<svg class="youtube-icon" viewBox="0 0 24 24" fill="currentColor">
<path
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
/>
</svg>
Watch on YouTube @beyondcloudtechnology
</a>
</div>
</header>
<main class="game-container">
<!-- Game Mode Toggle -->
<div class="mode-selector">
<button
class="mode-btn active"
id="localModeBtn"
onclick="toggleGameMode('local')"
>
🎮 Local Play
</button>
<button
class="mode-btn"
id="multiplayerModeBtn"
onclick="toggleGameMode('multiplayer')"
disabled
>
🌐 Multiplayer
</button>
</div>
<!-- Local Game Controls -->
<div id="gameControls" class="controls-panel">
<div class="size-selector">
<label for="boardSize">Board Size:</label>
<div class="size-buttons">
<button class="size-btn active" data-size="15">15×15</button>
<button class="size-btn" data-size="20">20×20</button>
<button class="size-btn" data-size="25">25×25</button>
<button class="size-btn" data-size="50">50×50</button>
</div>
</div>
<div class="game-info">
<div class="game-status-bar">
<!-- Left Side: Turn Status -->
<div class="status-left">
<div class="turn-display">
<span class="label">Current Turn:</span>
<span class="player-marker" id="currentPlayer">X</span>
</div>
<!-- Status text integrated here -->
<span class="status-text-small" id="statusMessage"
>Player X starts</span
>
</div>
<!-- Right Side: Player Identity (Multiplayer only) -->
<div
class="status-right"
id="playerIdentitySection"
style="display: none"
>
<span class="label">You are:</span>
<div class="player-display">
<span class="player-marker" id="playerIdentity">?</span>
</div>
</div>
</div>
<div class="score-display">
<div class="score-item">
<span class="score-label">Player X</span>
<span class="score-value" id="scoreX">0</span>
</div>
<div class="score-item">
<span class="score-label">Player O</span>
<span class="score-value" id="scoreO">0</span>
</div>
</div>
</div>
<button class="reset-btn" id="resetBtn">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"
/>
</svg>
New Game
</button>
<button
class="surrender-btn"
id="surrenderBtn"
style="display: none"
onclick="confirmSurrender()"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
Surrender
</button>
</div>
<div class="board-wrapper">
<div class="board" id="gameBoard"></div>
</div>
<!-- Grid Brightness Controls -->
<div class="grid-controls">
<div class="control-group">
<label for="gridLineOpacity">Grid Lines</label>
<input
type="range"
id="gridLineOpacity"
min="0.1"
max="1"
step="0.1"
value="0.3"
/>
</div>
<div class="control-group">
<label for="dotOpacity">Intersections</label>
<input
type="range"
id="dotOpacity"
min="0.1"
max="1"
step="0.1"
value="0.4"
/>
</div>
</div>
<script>
const gridLineSlider = document.getElementById("gridLineOpacity");
const dotOpacitySlider = document.getElementById("dotOpacity");
gridLineSlider.addEventListener("input", (e) => {
document.documentElement.style.setProperty(
"--grid-line-opacity",
e.target.value
);
});
dotOpacitySlider.addEventListener("input", (e) => {
document.documentElement.style.setProperty(
"--grid-dot-opacity",
e.target.value
);
});
</script>
<!-- Multiplayer Panel -->
<div
id="multiplayerPanel"
class="multiplayer-panel"
style="display: none"
>
<div class="player-stats-card">
<h3>Your Stats</h3>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-label">Player:</span>
<span class="stat-value" id="playerUsername">-</span>
</div>
<div class="stat-item">
<span class="stat-label">Wins:</span>
<span class="stat-value" id="playerWins">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Losses:</span>
<span class="stat-value" id="playerLosses">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Draws:</span>
<span class="stat-value" id="playerDraws">0</span>
</div>
</div>
</div>
<div class="board-size-selector-mp">
<h3>Select Board Size</h3>
<div class="size-buttons">
<button class="size-btn-mp active" data-size="15">15×15</button>
<button class="size-btn-mp" data-size="20">20×20</button>
<button class="size-btn-mp" data-size="25">25×25</button>
<button class="size-btn-mp" data-size="50">50×50</button>
</div>
</div>
<div class="active-players-card">
<h3>Online Players</h3>
<div id="activePlayersList" class="players-list">
<div class="loading">Loading players...</div>
</div>
</div>
<button class="return-btn" onclick="toggleGameMode('local')">
← Return to Local Play
</button>
</div>
</main>
<!-- Database Status Bar -->
<div class="db-status-bar" id="dbStatusBar">
<div class="db-status-item">
<span class="db-status-label">SQL Connection:</span>
<span class="db-status-indicator" id="dbConnection">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
</span>
</div>
<div class="db-status-item">
<span class="db-status-label">Latency:</span>
<span class="db-status-value" id="dbLatency">-- ms</span>
</div>
<div class="db-status-item">
<span class="db-status-label">Write Capability:</span>
<span class="db-status-indicator" id="dbWrite">
<span class="status-dot"></span>
<span class="status-text">Checking...</span>
</span>
</div>
<div class="db-status-item db-status-timestamp">
<span class="db-status-label">Last Check:</span>
<span class="db-status-value" id="dbTimestamp">--</span>
</div>
<div class="db-status-item">
<button
class="db-retry-btn"
id="dbRetryBtn"
onclick="window.dbStatusMonitor && window.dbStatusMonitor.retryConnection()"
title="Retry SQL Connection"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"
/>
</svg>
Retry
</button>
</div>
<div class="db-status-item version-container">
<div class="version-pill" id="gitVersion" title="Git Commit Version" style="display: none;">
<span class="commit-hash" id="commitHash"></span>
<span class="version-separator"></span>
<span class="commit-age" id="commitAge"></span>
</div>
</div>
</div>
<!-- Database Status Details (for testing phase) -->
<div class="db-status-details" id="dbStatusDetails" style="display: none">
<div class="db-details-header">
<span class="db-details-title">🔍 SQL Connection Details</span>
<button
class="db-details-close"
onclick="document.getElementById('dbStatusDetails').style.display='none'"
>
×
</button>
</div>
<div class="db-details-content" id="dbDetailsContent">
<div class="db-detail-item">
<span class="db-detail-label">Status:</span>
<span class="db-detail-value" id="detailStatus">--</span>
</div>
<div class="db-detail-item">
<span class="db-detail-label">Host:</span>
<span class="db-detail-value" id="detailHost">--</span>
</div>
<div class="db-detail-item">
<span class="db-detail-label">Database:</span>
<span class="db-detail-value" id="detailDatabase">--</span>
</div>
<div class="db-detail-item">
<span class="db-detail-label">Error:</span>
<span class="db-detail-value db-detail-error" id="detailError"
>None</span
>
</div>
<div class="db-detail-logs" id="detailLogs"></div>
</div>
</div>
<!-- SQL Error Display (Testing Phase) -->
<div class="sql-error-banner" id="sqlErrorBanner" style="display: none">
<div class="sql-error-header">
<span class="sql-error-icon">⚠️</span>
<span class="sql-error-title">SQL Connection Error</span>
</div>
<div class="sql-error-content">
<div class="sql-error-message" id="sqlErrorMessage">No errors</div>
<div class="sql-error-details" id="sqlErrorDetails"></div>
</div>
</div>
<footer class="footer">
<p>© 2025 Beyond Cloud Technology. All rights reserved.</p>
</footer>
</div>
<div class="victory-overlay" id="victoryOverlay">
<div class="victory-content">
<h2 class="victory-title" id="victoryTitle">Player X Wins!</h2>
<p class="victory-subtitle">Five in a row achieved!</p>
<button class="play-again-btn" id="playAgainBtn">Play Again</button>
</div>
</div>
<!-- Username Modal -->
<div class="modal-overlay" id="usernameModal">
<div class="modal-content username-modal">
<h2>Enter Your Username</h2>
<p class="modal-subtitle">
Choose a family-friendly name (3-20 characters)
</p>
<input
type="text"
id="usernameInput"
placeholder="Your username"
maxlength="20"
/>
<div
id="usernameError"
class="error-message"
style="display: none"
></div>
<button class="submit-btn" onclick="submitUsername()">
Join Multiplayer
</button>
</div>
</div>
<!-- Game Over Modal -->
<div class="modal-overlay" id="gameOverModal">
<div class="modal-content game-over-modal">
<div class="game-over-icon" id="gameOverIcon">🎮</div>
<h2 id="gameOverTitle">Game Over</h2>
<p class="game-over-subtitle" id="gameOverMessage">
Thanks for playing!
</p>
<div class="game-over-stats">
<div class="stat-item">
<span class="stat-label">Wins:</span>
<span class="stat-value" id="gameOverWins">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Losses:</span>
<span class="stat-value" id="gameOverLosses">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Draws:</span>
<span class="stat-value" id="gameOverDraws">0</span>
</div>
</div>
<div class="game-over-actions">
<button
class="rematch-btn"
id="rematchBtn"
onclick="requestRematch()"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path
d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"
/>
</svg>
Challenge Again
</button>
<button class="lobby-btn" onclick="returnToLobby()">
← Return to Lobby
</button>
</div>
</div>
</div>
<!-- Surrender Confirmation Modal -->
<div class="modal-overlay" id="surrenderModal">
<div class="modal-content surrender-modal">
<div class="surrender-icon">⚠️</div>
<h2>Surrender Game?</h2>
<p class="modal-subtitle">
Are you sure you want to surrender? This will count as a loss.
</p>
<div class="surrender-actions">
<button class="confirm-surrender-btn" onclick="executeSurrender()">
Yes, Surrender
</button>
<button class="cancel-surrender-btn" onclick="cancelSurrender()">
Cancel
</button>
</div>
</div>
</div>
<script src="surrender-rematch.js"></script>
<script src="storage.js"></script>
<script src="db-status.js"></script>
<script src="multiplayer.js"></script>
<script src="game.js"></script>
<script>
// Game mode toggling
function toggleGameMode(mode) {
const localBtn = document.getElementById("localModeBtn");
const multiplayerBtn = document.getElementById("multiplayerModeBtn");
const gameControls = document.getElementById("gameControls");
const multiplayerPanel = document.getElementById("multiplayerPanel");
const boardWrapper = document.querySelector(".board-wrapper");
// statusMessage is now inside gameControls, so we don't need to toggle it separately
if (mode === "local") {
localBtn.classList.add("active");
multiplayerBtn.classList.remove("active");
gameControls.style.display = "grid";
multiplayerPanel.style.display = "none";
boardWrapper.style.display = "flex";
// Reset to local mode
if (window.multiplayerClient) {
window.multiplayerClient.isMultiplayer = false;
}
// Reset turn label to default
const turnLabel = document.getElementById("turnLabel");
if (turnLabel) {
turnLabel.textContent = "Current Turn:";
}
} else {
localBtn.classList.remove("active");
multiplayerBtn.classList.add("active");
gameControls.style.display = "none";
multiplayerPanel.style.display = "block";
boardWrapper.style.display = "none";
// Initialize multiplayer if not already
if (!window.multiplayerClient) {
if (!window.game) {
console.log("⏳ Waiting for game to initialize...");
// Poll for game to be ready
const checkGame = setInterval(() => {
if (window.game) {
clearInterval(checkGame);
console.log("✅ Game ready, initializing multiplayer...");
window.multiplayerClient = new MultiplayerClient(window.game);
window.multiplayerClient.connect();
}
}, 100); // Check every 100ms
return;
}
window.multiplayerClient = new MultiplayerClient(window.game);
window.multiplayerClient.connect();
}
}
}
// Submit username
function submitUsername() {
const input = document.getElementById("usernameInput");
const error = document.getElementById("usernameError");
const username = input.value.trim();
if (!username) {
error.textContent = "Please enter a username";
error.style.display = "block";
return;
}
if (username.length < 3 || username.length > 20) {
error.textContent = "Username must be 3-20 characters";
error.style.display = "block";
return;
}
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
error.textContent =
"Only letters, numbers, underscores, and hyphens allowed";
error.style.display = "block";
return;
}
error.style.display = "none";
if (window.multiplayerClient) {
window.multiplayerClient.registerPlayer(username);
} else {
console.error("Multiplayer client not initialized");
error.textContent =
"Error: Multiplayer not initialized. Refresh page.";
error.style.display = "block";
}
}
// Change username
function changeUsername() {
// Clear saved username from localStorage
localStorage.removeItem("connect5_username");
// Show username modal
const modal = document.getElementById("usernameModal");
if (modal) {
modal.classList.add("active");
// Clear the input field
document.getElementById("usernameInput").value = "";
}
}
// Allow Enter key to submit username
document.addEventListener("DOMContentLoaded", () => {
const input = document.getElementById("usernameInput");
if (input) {
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
submitUsername();
}
});
}
});
</script>
</body>
</html>