const express = require("express"); const http = require("http"); const WebSocket = require("ws"); const { randomBytes } = require("crypto"); const app = express(); app.use(express.static("public")); const server = http.createServer(app); const wss = new WebSocket.Server({ server }); // 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] }; 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:[] }; const rooms = {}; 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); }); } 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) ); 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"; } game.moveCount++; game.winner = checkWinConditions(game); broadcast(roomId); } wss.on("connection", (ws, req) => { ws.isAlive = true; ws.on("pong", () => ws.isAlive = true); const urlParams = new URLSearchParams(req.url.split('?')[1]); let roomId = urlParams.get("room"); // Room creation/joining logic if (!roomId || !rooms[roomId]) { if (!roomId) roomId = randomBytes(3).toString("hex"); rooms[roomId] = { game: { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null }, players: [] }; } const room = rooms[roomId]; 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 }); 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]; }); }); setInterval(() => { wss.clients.forEach(ws => { if (!ws.isAlive) return ws.terminate(); ws.isAlive = false; ws.ping(); }); }, 30000); server.listen(3030, () => console.log("Server läuft auf Port 3030"));