134 lines
4.6 KiB
JavaScript
134 lines
4.6 KiB
JavaScript
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 });
|
|
|
|
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 getRoomList() {
|
|
return Object.keys(rooms).map(id => ({
|
|
id,
|
|
players: rooms[id].players.length,
|
|
turn: rooms[id].game.turn
|
|
}));
|
|
}
|
|
|
|
function broadcastRoomList() {
|
|
const list = JSON.stringify({ type: "room_list", rooms: getRoomList() });
|
|
wss.clients.forEach(client => {
|
|
if (client.readyState === WebSocket.OPEN && !client.roomId) {
|
|
client.send(list);
|
|
}
|
|
});
|
|
}
|
|
|
|
wss.on("connection", (ws, req) => {
|
|
const urlParams = new URLSearchParams(req.url.split('?')[1]);
|
|
let roomId = urlParams.get("room");
|
|
let sessionId = urlParams.get("sessionId");
|
|
|
|
if (!roomId || roomId === "null") {
|
|
ws.send(JSON.stringify({ type: "room_list", rooms: getRoomList() }));
|
|
return;
|
|
}
|
|
|
|
if (!rooms[roomId]) {
|
|
rooms[roomId] = {
|
|
game: { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null, lastMove: null },
|
|
players: [], nextFirstRole: "sheep"
|
|
};
|
|
}
|
|
|
|
const room = rooms[roomId];
|
|
ws.roomId = roomId;
|
|
|
|
let player = room.players.find(p => p.sessionId === sessionId);
|
|
if (!player && room.players.length >= 2) {
|
|
ws.send(JSON.stringify({ type: "full" }));
|
|
return;
|
|
}
|
|
|
|
if (player) {
|
|
if (player.timeout) clearTimeout(player.timeout);
|
|
player.ws = ws;
|
|
} else {
|
|
const role = room.players.length === 0 ? room.nextFirstRole : (room.nextFirstRole === "sheep" ? "wolf" : "sheep");
|
|
player = { ws, role, sessionId, timeout: null };
|
|
room.players.push(player);
|
|
}
|
|
|
|
ws.send(JSON.stringify({ type: "init", role: player.role, roomId }));
|
|
broadcastRoomList();
|
|
broadcastState(roomId);
|
|
|
|
ws.on("message", msg => {
|
|
let data = JSON.parse(msg);
|
|
if (data.type === "restart") {
|
|
room.players.forEach(p => p.role = (p.role === "wolf" ? "sheep" : "wolf"));
|
|
room.nextFirstRole = room.nextFirstRole === "sheep" ? "wolf" : "sheep";
|
|
room.game = { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null, lastMove: null };
|
|
broadcastState(roomId);
|
|
} else if (data.from !== undefined && data.to !== undefined) {
|
|
handleMove(roomId, player.role, data);
|
|
}
|
|
});
|
|
|
|
ws.on("close", () => {
|
|
if (player) {
|
|
player.timeout = setTimeout(() => {
|
|
room.players = room.players.filter(p => p.sessionId !== sessionId);
|
|
if (room.players.length === 0) {
|
|
delete rooms[roomId];
|
|
}
|
|
broadcastRoomList();
|
|
broadcastState(roomId);
|
|
}, 1000);
|
|
}
|
|
});
|
|
});
|
|
|
|
function broadcastState(roomId) {
|
|
const room = rooms[roomId];
|
|
if (!room) return;
|
|
const online = room.players.filter(p => p.ws?.readyState === WebSocket.OPEN).length;
|
|
room.players.forEach(p => {
|
|
if (p.ws?.readyState === WebSocket.OPEN) {
|
|
p.ws.send(JSON.stringify({ type: "state", game: room.game, yourRole: p.role, onlineCount: online }));
|
|
}
|
|
});
|
|
}
|
|
|
|
function handleMove(roomId, role, { from, to }) {
|
|
const room = rooms[roomId];
|
|
if (!room || room.game.winner || room.game.turn !== role) return;
|
|
const game = room.game;
|
|
const allowed = (role === "wolf") ? board_wolf[from] : board_sheep[from];
|
|
if (!allowed?.includes(to) || game.sheep.includes(to) || game.wolf === to) return;
|
|
|
|
if (role === "wolf") { game.wolf = to; game.turn = "sheep"; }
|
|
else { game.sheep[game.sheep.indexOf(from)] = to; game.turn = "wolf"; }
|
|
|
|
game.moveCount++;
|
|
game.lastMove = { from, to, role };
|
|
game.winner = checkWinConditions(game);
|
|
broadcastState(roomId);
|
|
}
|
|
|
|
function checkWinConditions(game) {
|
|
if (game.wolf === 0) return "wolf";
|
|
if (game.moveCount >= 40) return "wolf";
|
|
const canWolfMove = board_wolf[game.wolf].some(to => !game.sheep.includes(to));
|
|
if (!canWolfMove) return "sheep";
|
|
return null;
|
|
}
|
|
|
|
server.listen(3030, () => console.log("Server running on 3030")); |