1
0
mirror of https://github.com/thib8956/tic-tac-toe-ws.git synced 2024-11-17 09:26:33 +00:00
tic-tac-toe-ws/server.ts

189 lines
5.4 KiB
TypeScript
Raw Permalink Normal View History

2024-09-25 18:21:02 +00:00
import { Message, Response, Hello, EndGame } from "common.js"
2024-09-25 09:58:56 +00:00
import { WebSocket, WebSocketServer, MessageEvent } from "ws";
2024-09-02 20:34:24 +00:00
const port = 1234
const wss = new WebSocketServer({ port });
let grid = [0, 0, 0, 0, 0, 0, 0, 0, 0]
2024-09-24 15:30:35 +00:00
let endGame = false;
2024-09-02 20:34:24 +00:00
console.log(`waiting for connection on ws://localhost:${port}`);
2024-09-08 10:58:00 +00:00
interface Client {
id: number,
2024-09-24 13:50:08 +00:00
symbol: "x" | "o",
2024-09-08 10:58:00 +00:00
ws: WebSocket
}
let id = 1;
let clients: Client[] = [];
2024-09-24 13:50:08 +00:00
let currentPlayer: Client | undefined = undefined;
2024-09-03 12:07:13 +00:00
function getPlayerSymbol(): "o" | "x" {
console.assert(clients.length < 2, "there should never be more than 2 clients");
if (clients.length === 0) return "o";
return clients[0].symbol === "o" ? "x" : "o";
}
2024-09-25 20:30:03 +00:00
wss.on("connection", (ws, req) => {
id += 1;
2024-09-25 20:30:03 +00:00
if (clients.length === 2) {
2024-09-08 10:58:00 +00:00
console.log("too many players");
ws.close();
return;
}
2024-09-03 12:07:13 +00:00
const symbol = getPlayerSymbol();
2024-09-25 09:58:56 +00:00
const helloMsg: Message = {
kind: "hello",
data: { id, symbol } as Hello
}
2024-09-24 13:50:08 +00:00
clients.push({id, ws, symbol});
2024-09-25 09:58:56 +00:00
ws.send(JSON.stringify(helloMsg));
2024-09-25 20:30:03 +00:00
const addr = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
console.log(`player #${id} connected with address ${addr}. total clients ${clients.length}`);
2024-09-02 20:34:24 +00:00
2024-09-25 09:58:56 +00:00
ws.addEventListener("message", (event: MessageEvent) => {
const message = JSON.parse(event.data as string);
const {x, y} = message;
2024-09-24 13:50:08 +00:00
const player = clients.find(x => x.ws === ws);
2024-09-25 09:58:56 +00:00
if (!player) throw new Error("player not found");
2024-09-25 20:30:03 +00:00
console.log("received message", message, "player", player!.id, "currentPlayer", currentPlayer?.id);
2024-09-24 13:50:08 +00:00
if (!currentPlayer) {
currentPlayer = player;
}
2024-09-09 12:09:00 +00:00
2024-09-25 09:58:56 +00:00
if (clients.length < 2 || player.id != currentPlayer?.id || endGame) {
2024-09-24 13:50:08 +00:00
return;
}
2024-09-09 12:09:00 +00:00
2024-09-08 10:58:00 +00:00
if (grid[y*3+x] === 0) {
2024-09-25 09:58:56 +00:00
grid[y*3+x] = player.id;
2024-09-08 10:58:00 +00:00
for (const c of clients) {
const msg: Message = {
kind: "update",
2024-09-25 11:40:22 +00:00
data: {
last: { x, y, symbol: player.symbol }
} as Response,
2024-09-24 13:50:08 +00:00
}
c.ws.send(JSON.stringify(msg));
}
const winnerId = checkWin(grid);
if (winnerId == -1) {
currentPlayer = clients.find(x => x.id !== currentPlayer?.id); // change player
console.assert(currentPlayer);
console.log(`current player is #${currentPlayer?.id}`);
} else if (winnerId == 0) {
for (const c of clients) {
const msg: Message = {
kind: "endgame",
data: { issue: "draw" } as EndGame
};
c.ws.send(JSON.stringify(msg));
2024-09-25 09:58:56 +00:00
endGame = true;
2024-09-24 13:50:08 +00:00
}
} else {
console.log(`player ${winnerId} won !`);
const winner = clients.find(x => x.id === winnerId);
winner?.ws?.send(JSON.stringify({
kind: "endgame",
data: { issue: "win" } as EndGame
2024-09-25 09:58:56 +00:00
} as Message));
2024-09-24 13:50:08 +00:00
const loser = clients.find(x => x.id !== winnerId);
loser?.ws?.send(JSON.stringify({
kind: "endgame",
data: { issue: "lose" } as EndGame
2024-09-25 09:58:56 +00:00
} as Message));
endGame = true;
2024-09-24 13:50:08 +00:00
}
}
2024-09-02 20:34:24 +00:00
});
2024-09-08 10:58:00 +00:00
ws.on("close", (code: number) => {
clients = clients.filter(x => x.ws.readyState !== 3); // 3 == CLOSED
2024-09-25 20:30:03 +00:00
console.log(`player disconnected. Resetting game. Total clients ${clients.length}`);
// reset game state
2024-09-25 20:30:03 +00:00
grid = [0, 0, 0, 0, 0, 0, 0, 0, 0];
currentPlayer = undefined;
2024-09-25 20:30:03 +00:00
endGame = false;
for (const c of clients) {
c.ws.send(JSON.stringify({
kind: "reset"
} as Message));
}
2024-09-08 10:58:00 +00:00
});
2024-09-02 20:34:24 +00:00
});
2024-09-09 12:09:00 +00:00
function checkWin(grid: number[]): number {
2024-09-25 09:58:56 +00:00
const clone = [...grid];
const grid2d = [];
while(clone.length) grid2d.push(clone.splice(0,3));
if (
grid2d[0][0] !== 0 &&
grid2d[0][0] === grid2d[0][1] &&
grid2d[0][1] === grid2d[0][2]
) {
return grid2d[0][0];
}
if (
grid2d[1][0] !== 0 &&
grid2d[1][0] === grid2d[1][1] &&
grid2d[1][1] === grid2d[1][2]
) {
return grid2d[1][0];
}
if (
grid2d[2][0] !== 0 &&
grid2d[2][0] === grid2d[2][1] &&
grid2d[2][1] === grid2d[2][2]
) {
return grid2d[2][0];
}
if (
grid2d[0][0] !== 0 &&
grid2d[0][0] === grid2d[1][0] &&
grid2d[1][0] === grid2d[2][0]
) {
return grid2d[0][0];
}
if (
grid2d[0][1] !== 0 &&
grid2d[0][1] === grid2d[1][1] &&
grid2d[1][1] === grid2d[2][1]
) {
return grid2d[0][1];
}
if (
grid2d[0][2] !== 0 &&
grid2d[0][2] === grid2d[1][2] &&
grid2d[1][2] === grid2d[2][2]
) {
return grid2d[0][2];
}
if (
grid2d[0][0] !== 0 &&
grid2d[0][0] === grid2d[1][1] &&
grid2d[1][1] === grid2d[2][2]
) {
return grid2d[0][0];
}
if (
grid2d[0][2] !== 0 &&
grid2d[0][2] === grid2d[1][1] &&
grid2d[1][1] === grid2d[2][0]
) {
return grid2d[0][2];
}
for (const row of grid2d) {
if (row[0] === 0 || row[1] === 0 || row[2] === 0) {
return -1;
}
}
return 0;
2024-09-09 12:09:00 +00:00
}