Added win detection

This commit is contained in:
2026-02-09 21:31:11 +01:00
parent b5d7d53bdb
commit 54a2e9a800
3 changed files with 215 additions and 204 deletions

192
server.js
View File

@@ -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"));
server.listen(3030, () => console.log("Server läuft auf Port 3030"));