diff --git a/game.js b/game.js index e052af6..959f733 100644 --- a/game.js +++ b/game.js @@ -35,6 +35,8 @@ let roomId = ""; let gameMode = "practice"; let multiplayerState = "local"; let botTimer = 0; +let lobbyTimer = 0; +let waitingPlayers = 0; function createBoard() { return Array.from({ length: size }, () => Array(size).fill(empty)); @@ -54,6 +56,7 @@ function setupGame() { function connectToServer() { gameMode = "online"; + stopLobbyPolling(); closeSocket(); localPlayer = ""; roomId = ""; @@ -103,7 +106,8 @@ function startPracticeMode() { roomId = ""; multiplayerState = "local"; setupGame(); - setMultiplayerState("local", "BOT practice. You are X. Random O replies after each move."); + setPracticeStatus("BOT practice. You are X. Random O replies after each move."); + startLobbyPolling(); updateModeButtons(); } @@ -115,6 +119,40 @@ function closeSocket() { socket = null; } +function startLobbyPolling() { + stopLobbyPolling(); + checkLobby(); + lobbyTimer = window.setInterval(checkLobby, 3000); +} + +function stopLobbyPolling() { + if (lobbyTimer) { + window.clearInterval(lobbyTimer); + lobbyTimer = 0; + } +} + +async function checkLobby() { + if (gameMode !== "practice") { + return; + } + + try { + const response = await fetch("/lobby", { cache: "no-store" }); + if (!response.ok) { + return; + } + + const status = await response.json(); + waitingPlayers = Number(status.waitingPlayers || 0); + if (!gameOver && currentPlayer === localPlayer) { + setPracticeStatus("BOT practice. You are X. Random O replies after each move."); + } + } catch { + waitingPlayers = 0; + } +} + function handleServerMessage(payload) { if (payload.type === "waiting") { localPlayer = ""; @@ -255,7 +293,7 @@ function playLocalMove(row, col) { } if (!gameOver) { - setMultiplayerState("local", "Bot thinking..."); + setPracticeStatus("Bot thinking..."); botTimer = window.setTimeout(playBotMove, 360); } } @@ -276,14 +314,14 @@ function applyMove(row, col, player, source) { if (win.length > 0) { winningCells = win; gameOver = true; - setMultiplayerState(activeState(), `${player} wins with four in a row.`); + setActiveStatus(`${player} wins with four in a row.`); render(); return true; } if (isDraw()) { gameOver = true; - setMultiplayerState(activeState(), "Draw. The board is full."); + setActiveStatus("Draw. The board is full."); render(); return true; } @@ -291,7 +329,7 @@ function applyMove(row, col, player, source) { currentPlayer = opponentOf(player); const captureMessage = captured.length > 0 ? `${player} captured ${captured.length}. ` : ""; const turnMessage = currentPlayer === localPlayer ? "Your turn." : waitingMessage(); - setMultiplayerState(activeState(), `${captureMessage}${turnMessage}`); + setActiveStatus(`${captureMessage}${turnMessage}`); render(); flashCapturedCells(captured); @@ -306,6 +344,15 @@ function activeState() { return gameMode === "practice" ? "local" : "paired"; } +function setActiveStatus(text) { + if (gameMode === "practice") { + setPracticeStatus(text); + return; + } + + setMultiplayerState(activeState(), text); +} + function waitingMessage() { return gameMode === "practice" ? "Bot turn." : `Waiting for ${currentPlayer}.`; } @@ -401,6 +448,11 @@ function setMultiplayerState(state, text) { messageElement.textContent = text; } +function setPracticeStatus(text) { + const suffix = waitingPlayers > 0 ? " NET: player waiting." : " NET: no one waiting."; + setMultiplayerState("local", `${text}${suffix}`); +} + function playBotMove() { if (gameMode !== "practice" || gameOver || currentPlayer !== "O") { return; @@ -471,7 +523,7 @@ resetButton.addEventListener("click", () => { setupGame(); if (gameMode === "practice") { - setMultiplayerState("local", "BOT practice. You are X. Random O replies after each move."); + setPracticeStatus("BOT practice. You are X. Random O replies after each move."); return; } @@ -482,6 +534,7 @@ resetButton.addEventListener("click", () => { practiceButton.addEventListener("click", startPracticeMode); onlineButton.addEventListener("click", () => { gameMode = "online"; + stopLobbyPolling(); localPlayer = ""; roomId = ""; multiplayerState = "connecting"; diff --git a/index.html b/index.html index bbd10ac..633b112 100644 --- a/index.html +++ b/index.html @@ -58,7 +58,7 @@

MODES

-

BOT is local practice against random moves. NET pairs the first two connected players automatically.

+

BOT is local practice against random moves and shows if NET has a player waiting. NET pairs the first two connected players automatically.

    diff --git a/server.js b/server.js index 6772b57..d967201 100644 --- a/server.js +++ b/server.js @@ -76,6 +76,15 @@ const server = http.createServer((request, response) => { return; } + if (requestUrl.pathname === "/lobby") { + response.writeHead(200, { + "Cache-Control": "no-store", + "Content-Type": "application/json; charset=utf-8", + }); + response.end(request.method === "HEAD" ? undefined : JSON.stringify(lobbyStatus())); + return; + } + const staticName = staticFiles.get(requestUrl.pathname); if (!staticName) { response.writeHead(404); @@ -257,6 +266,13 @@ function pairClients(xClient, oClient) { send(oClient, { type: "paired", player: "O", roomId }); } +function lobbyStatus() { + return { + activeGames: rooms.size, + waitingPlayers: waitingClientId && clients.has(waitingClientId) ? 1 : 0, + }; +} + function handleSocketData(client, buffer) { if (!clients.has(client.id)) { return;