From 54a2e9a800ac52d772045a0200daffdb8e6ae84c Mon Sep 17 00:00:00 2001 From: Simon Oberzier Date: Mon, 9 Feb 2026 21:31:11 +0100 Subject: [PATCH] Added win detection --- public/index.html | 227 +++++++++++++++++++++++----------------------- server.js | 192 ++++++++++++++++++++------------------- start.sh | 0 3 files changed, 215 insertions(+), 204 deletions(-) mode change 100644 => 100755 start.sh diff --git a/public/index.html b/public/index.html index e8599cf..450e744 100644 --- a/public/index.html +++ b/public/index.html @@ -3,34 +3,38 @@ - Wolf & Schafe + Wolf & Schafe - Multiplayer -

Verbinde…

-
+

Verbinde...

+
Warte auf Mitspieler...
- - +
+ + - \ No newline at end of file diff --git a/server.js b/server.js index 7812d3c..73a1ce8 100644 --- a/server.js +++ b/server.js @@ -9,126 +9,134 @@ app.use(express.static("public")); const server = http.createServer(app); const wss = new WebSocket.Server({ server }); -// Define both movement sets +// Movement logic: Wolf can move anywhere, Sheep only forward const board_wolf = { - 0:[1,2,3], 1:[0,2,4,5], 2:[0,1,3,5], 3:[0,2,5,6], 4:[1,5,7], - 5:[1,2,3,4,6,7,8,9], 6:[3,5,9], 7:[4,5,8,10], 8:[5,7,9,10], - 9:[5,6,8,10], 10:[7,8,9] + 0:[1,2,3], 1:[0,2,4,5], 2:[0,1,3,5], 3:[0,2,5,6], 4:[1,5,7], + 5:[1,2,3,4,6,7,8,9], 6:[3,5,9], 7:[4,5,8,10], 8:[5,7,9,10], + 9:[5,6,8,10], 10:[7,8,9] }; const board_sheep = { - 0:[1,2,3], 1:[2,4,5], 2:[1,3,5], 3:[2,5,6], 4:[5,7], - 5:[4,6,7,8,9], 6:[5,9], 7:[8,10], 8:[7,9,10], 9:[8,10], 10:[] + 0:[1,2,3], 1:[2,4,5], 2:[1,3,5], 3:[2,5,6], 4:[5,7], + 5:[4,6,7,8,9], 6:[5,9], 7:[8,10], 8:[7,9,10], 9:[8,10], 10:[] }; -// Rooms: roomId -> { game, players } const rooms = {}; -// Broadcast state to all players in a room function broadcast(roomId) { - const room = rooms[roomId]; - if (!room) return; - const msg = JSON.stringify({ type:"state", game: room.game }); - room.players.forEach(p => { - if(p.ws.readyState === WebSocket.OPEN) p.ws.send(msg); - }); + const room = rooms[roomId]; + if (!room) return; + const msg = JSON.stringify({ type: "state", game: room.game }); + room.players.forEach(p => { + if (p.ws.readyState === WebSocket.OPEN) p.ws.send(msg); + }); } -// Select the correct allowed moves based on role -function handleMove(roomId, role, {from, to}) { - const room = rooms[roomId]; - if(!room) return; - - const game = room.game; - if(game.turn !== role) return; - - // Select the correct allowed moves based on role - const allowedMoves = (role === "wolf") ? board_wolf[from] : board_sheep[from]; +function checkWinConditions(game) { + // 1. Wolf reaches node 0 + if (game.wolf === 0) return "wolf"; + + // 2. Turn limit (20 moves) + if (game.moveCount >= 20) return "wolf"; + + // Mobility Check helpers + const canWolfMove = board_wolf[game.wolf].some(to => + !game.sheep.includes(to) + ); - if(!allowedMoves?.includes(to)) return; - if(game.sheep.includes(to) || game.wolf === to) return; - - if(role === "wolf") { - if(from !== game.wolf) return; - game.wolf = to; - game.turn = "sheep"; + const canSheepMove = game.sheep.some(from => + board_sheep[from].some(to => to !== game.wolf && !game.sheep.includes(to)) + ); + + // 3. Wolf trapped + if (!canWolfMove) return "sheep"; + + // 4. Sheep cannot move + if (!canSheepMove && game.turn === "sheep") return "wolf"; + + return null; +} + +function handleMove(roomId, role, { from, to }) { + const room = rooms[roomId]; + if (!room || room.game.winner) return; + + const game = room.game; + if (game.turn !== role) return; + + const allowed = (role === "wolf") ? board_wolf[from] : board_sheep[from]; + if (!allowed?.includes(to)) return; + if (game.sheep.includes(to) || game.wolf === to) return; + + if (role === "wolf") { + if (from !== game.wolf) return; + game.wolf = to; + game.turn = "sheep"; } else { - const i = game.sheep.indexOf(from); - if(i === -1) return; - game.sheep[i] = to; - game.turn = "wolf"; + const i = game.sheep.indexOf(from); + if (i === -1) return; + game.sheep[i] = to; + game.turn = "wolf"; } + + game.moveCount++; + game.winner = checkWinConditions(game); broadcast(roomId); - } +} -// Heartbeat -function heartbeat() { this.isAlive = true; } - -wss.on("connection", (ws, req) => { // Added 'req' parameter +wss.on("connection", (ws, req) => { ws.isAlive = true; - ws.on("pong", heartbeat); - - // Use req.url to get the query string + ws.on("pong", () => ws.isAlive = true); + const urlParams = new URLSearchParams(req.url.split('?')[1]); let roomId = urlParams.get("room"); - - // Logic for creating or joining + + // Room creation/joining logic if (!roomId || !rooms[roomId]) { - // If no ID provided, or the provided ID doesn't exist, create a NEW one - // UNLESS you want people to be able to create specific room names: - if (!roomId) roomId = randomBytes(3).toString("hex"); - - if (!rooms[roomId]) { + if (!roomId) roomId = randomBytes(3).toString("hex"); rooms[roomId] = { - game: { wolf: 5, sheep: [0, 1, 3], turn: "sheep" }, - players: [] + game: { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null }, + players: [] }; - } } - + const room = rooms[roomId]; - - // Reject if room full - if(room.players.length >= 2){ - ws.send(JSON.stringify({type:"full"})); - ws.close(); - return; - } - - const role = room.players.length === 0 ? "sheep" : "wolf"; - room.players.push({ ws, role, roomId }); - - // Send role + room info - ws.send(JSON.stringify({ type:"role", role, roomId })); - broadcast(roomId); - - ws.on("message", msg=>{ - let data; - try{ data = JSON.parse(msg); } catch{return;} - - if(data.type==="restart"){ - room.game = { wolf:5, sheep:[0,1,3], turn:"sheep" }; - broadcast(roomId); - return; + if (room.players.length >= 2) { + ws.send(JSON.stringify({ type: "full" })); + ws.close(); + return; } - handleMove(roomId, role, data); - }); + const role = room.players.length === 0 ? "sheep" : "wolf"; + room.players.push({ ws, role }); - ws.on("close", ()=>{ - room.players = room.players.filter(p => p.ws !== ws); - if(room.players.length === 0) delete rooms[roomId]; // cleanup empty rooms - }); + ws.send(JSON.stringify({ type: "role", role, roomId })); + broadcast(roomId); + + ws.on("message", msg => { + let data; + try { data = JSON.parse(msg); } catch { return; } + + if (data.type === "restart") { + room.game = { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null }; + broadcast(roomId); + return; + } + handleMove(roomId, role, data); + }); + + ws.on("close", () => { + room.players = room.players.filter(p => p.ws !== ws); + if (room.players.length === 0) delete rooms[roomId]; + }); }); -// Ping-pong to prevent idle disconnect -const interval = setInterval(()=>{ - wss.clients.forEach(ws=>{ - if(!ws.isAlive) return ws.terminate(); - ws.isAlive = false; - ws.ping(); - }); +setInterval(() => { + wss.clients.forEach(ws => { + if (!ws.isAlive) return ws.terminate(); + ws.isAlive = false; + ws.ping(); + }); }, 30000); -server.on("close", ()=>clearInterval(interval)); -server.listen(3030, ()=>console.log("Server läuft auf Port 3030")); \ No newline at end of file +server.listen(3030, () => console.log("Server läuft auf Port 3030")); \ No newline at end of file diff --git a/start.sh b/start.sh old mode 100644 new mode 100755