multiple changes
- add gameover overlay - fix active tile background - redirect to home on invalid page - compute checkmate and draw after making a move - cleanup code - error handling on making moves
This commit is contained in:
@@ -169,7 +169,7 @@ const Tile: React.FC<{
|
||||
const color = squareColor(tileNumber);
|
||||
|
||||
const { bg: bgColor, text: textColor } = TILE_COLORS[color];
|
||||
const activeColor = isActive ? TILE_COLORS[color].active : "";
|
||||
const activeColor = TILE_COLORS[color].active;
|
||||
|
||||
const tileFile = file(tileNumber);
|
||||
const tileRank = rank(tileNumber);
|
||||
@@ -180,7 +180,10 @@ const Tile: React.FC<{
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative aspect-square font-bold text-xl isolate group [&>*]:pointer-events-none ${bgColor} ${activeColor}`}
|
||||
className={
|
||||
"relative aspect-square font-bold text-xl isolate group [&>*]:pointer-events-none " +
|
||||
(isActive ? activeColor : bgColor)
|
||||
}
|
||||
data-tile={tileNumber}
|
||||
>
|
||||
{piece == null ? <></> : <img src={PIECES[piece.color][piece.type]} />}
|
||||
|
||||
+21
-2
@@ -1,8 +1,21 @@
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import {
|
||||
createBrowserRouter,
|
||||
RouterProvider,
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import Home from "./routes/Home";
|
||||
import Game from "./routes/Game";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const ErrorElement = () => {
|
||||
const naviagte = useNavigate();
|
||||
|
||||
useEffect(() => naviagte("/"), []);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@@ -13,9 +26,15 @@ const router = createBrowserRouter([
|
||||
path: "/game",
|
||||
element: <Game />,
|
||||
},
|
||||
{
|
||||
path: "*",
|
||||
element: <ErrorElement />,
|
||||
},
|
||||
]);
|
||||
|
||||
// React's StricMode doesn't play well with how I implemented the socket connection
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<RouterProvider router={router} />
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-zinc-200 -z-10">
|
||||
<RouterProvider router={router} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { socket } from "../sockets/socket";
|
||||
import { CopyIcon } from "../utils/icons";
|
||||
import Show from "../utils/Show";
|
||||
import ErrorNorification from "../utils/ErrorNotification";
|
||||
import Modal, { ModalButton } from "../utils/Modal";
|
||||
|
||||
const Game = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -18,7 +19,7 @@ const Game = () => {
|
||||
|
||||
const [chess] = useState(Chess.load());
|
||||
const [game, setGame] = useState(false);
|
||||
const [, setRerender] = useState(false);
|
||||
const [, rerender] = useState(false);
|
||||
const [err, setErr] = useState<Error>();
|
||||
|
||||
const makeMove = (move: Move) => socket.emit("make move", id, move);
|
||||
@@ -36,7 +37,6 @@ const Game = () => {
|
||||
});
|
||||
|
||||
socket.on("join error", () => {
|
||||
socket.disconnect();
|
||||
navigate("/", {
|
||||
state: { error: "Could not join, please try again." },
|
||||
});
|
||||
@@ -50,7 +50,7 @@ const Game = () => {
|
||||
|
||||
socket.on("receive move", (move: Move) => {
|
||||
chess.makeMove(move);
|
||||
setRerender((prev) => !prev);
|
||||
rerender((prev) => !prev);
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -71,6 +71,22 @@ const Game = () => {
|
||||
blackPerspective={color === COLOR.BLACK}
|
||||
disabled={color !== chess.getTurn()}
|
||||
/>
|
||||
<Show when={chess.isGameOver()}>
|
||||
<Modal overlay>
|
||||
<div className="text-center w-40 max-w-full">
|
||||
<h2 className="text-2xl mb-2">Game Over</h2>
|
||||
<h1 className="text-lg font-bold">
|
||||
{chess.isCheckMate()
|
||||
? (chess.getTurn() == color ? "Opponent" : "You") + " won!"
|
||||
: "It's a draw!"}
|
||||
</h1>
|
||||
<div className="w-full h-[4px] rounded-b-lg bg-white mb-4"></div>
|
||||
<ModalButton onClick={() => navigate("/")}>
|
||||
Go to main page
|
||||
</ModalButton>
|
||||
</div>
|
||||
</Modal>
|
||||
</Show>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
+55
-63
@@ -3,14 +3,16 @@ import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { Color, COLOR } from "../../../server/src/engine";
|
||||
import Show from "../utils/Show";
|
||||
import ErrorNorification from "../utils/ErrorNotification";
|
||||
import Modal, { ModalButton } from "../utils/Modal";
|
||||
|
||||
const Home = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [id, setID] = useState<string>("");
|
||||
const [join, setJoin] = useState(false);
|
||||
const [color, setColor] = useState<Color>(COLOR.WHITE);
|
||||
const [join, setJoin] = useState(false);
|
||||
const [create, setCreate] = useState(false);
|
||||
|
||||
const [err, setErr] = useState<Error>();
|
||||
const { error } =
|
||||
@@ -22,8 +24,8 @@ const Home = () => {
|
||||
if (res.ok) return res.text();
|
||||
throw res;
|
||||
})
|
||||
.then((id) => {
|
||||
setID(id);
|
||||
.then((text) => {
|
||||
navigate("/game", { state: { id: text, color } });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
@@ -56,89 +58,79 @@ const Home = () => {
|
||||
|
||||
const errObj = err?.message
|
||||
? {
|
||||
error: err.message,
|
||||
removeError: () => setErr(undefined),
|
||||
}
|
||||
error: err.message,
|
||||
removeError: () => setErr(undefined),
|
||||
}
|
||||
: {
|
||||
error,
|
||||
removeError: removeLocationState,
|
||||
};
|
||||
error,
|
||||
removeError: removeLocationState,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ErrorNorification key={errObj.error} {...errObj} />
|
||||
<div className="text-xl">
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 m-auto p-6 bg-gray-800 text-white w-fit h-fit rounded-[25px]">
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors mb-3"
|
||||
onClick={createGame}
|
||||
>
|
||||
Create Game
|
||||
</button>
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors"
|
||||
onClick={() => setJoin(true)}
|
||||
>
|
||||
Join Game
|
||||
</button>
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 m-auto w-fit h-fit">
|
||||
<Modal>
|
||||
<ModalButton onClick={() => setCreate(true)}>Create Game</ModalButton>
|
||||
<ModalButton onClick={() => setJoin(true)}>Join Game</ModalButton>
|
||||
</Modal>
|
||||
</div>
|
||||
<Show when={join == false && id != ""}>
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex justify-center items-center bg-zinc-800 bg-opacity-70">
|
||||
<div className="bg-gray-900 text-white p-4 rounded-lg">
|
||||
<Show when={create}>
|
||||
<Modal overlay>
|
||||
<div className="w-[20rem] max-w-full">
|
||||
<p>Select color:</p>
|
||||
<div className="grid grid-cols-[auto,minmax(0,1fr)] text-center ">
|
||||
<input
|
||||
type="radio"
|
||||
name="color"
|
||||
id="white"
|
||||
checked={color == COLOR.WHITE}
|
||||
onChange={() => setColor(COLOR.WHITE)}
|
||||
/>
|
||||
<label htmlFor="white">White</label>
|
||||
<input
|
||||
type="radio"
|
||||
name="color"
|
||||
id="black"
|
||||
checked={color == COLOR.BLACK}
|
||||
onChange={() => setColor(COLOR.BLACK)}
|
||||
/>
|
||||
<label htmlFor="black">Black</label>
|
||||
<div className="flex flex-row justify-evenly">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
name="color"
|
||||
id="white"
|
||||
checked={color == COLOR.WHITE}
|
||||
onChange={() => setColor(COLOR.WHITE)}
|
||||
/>
|
||||
<label htmlFor="white">White</label>
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
name="color"
|
||||
id="black"
|
||||
checked={color == COLOR.BLACK}
|
||||
onChange={() => setColor(COLOR.BLACK)}
|
||||
/>
|
||||
<label htmlFor="black">Black</label>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors mt-3"
|
||||
onClick={() => navigate("/game", { state: { id, color } })}
|
||||
<ModalButton
|
||||
onClick={createGame}
|
||||
>
|
||||
Start Game
|
||||
</button>
|
||||
</ModalButton>
|
||||
<ModalButton
|
||||
onClick={() => setCreate(false)}
|
||||
>
|
||||
Cancel
|
||||
</ModalButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</Show>
|
||||
<Show when={join}>
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex justify-center items-center bg-zinc-800 bg-opacity-70">
|
||||
<div className="bg-gray-900 text-white p-4 rounded-lg">
|
||||
<Modal overlay>
|
||||
<div className="w-[20rem] max-w-full">
|
||||
<label htmlFor="game-id">Insert game ID: </label>
|
||||
<input
|
||||
className="block bg-zinc-700 p-1 mt-2"
|
||||
className="block bg-zinc-700 p-1 mt-2 w-full"
|
||||
type="text"
|
||||
name="game-id"
|
||||
id="game-id"
|
||||
value={id}
|
||||
onChange={(e) => setID(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors mt-3"
|
||||
onClick={joinGame}
|
||||
>
|
||||
Join
|
||||
</button>
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors mt-3"
|
||||
onClick={() => setJoin(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<ModalButton onClick={joinGame}>Join</ModalButton>
|
||||
<ModalButton onClick={() => setJoin(false)}>Cancel</ModalButton>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</Show>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import Show from "./Show";
|
||||
|
||||
const Modal: React.FC<{
|
||||
children: React.ReactNode;
|
||||
overlay?: boolean;
|
||||
}> = ({ children, overlay }) => {
|
||||
const modal = (
|
||||
<div className="bg-gray-900 text-white p-4 rounded-lg w-fit">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
return overlay ? (
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex justify-center items-center bg-zinc-800 bg-opacity-70">
|
||||
{modal}
|
||||
</div>
|
||||
) : (
|
||||
modal
|
||||
);
|
||||
};
|
||||
export const ModalButton: React.FC<{
|
||||
children: React.ReactNode;
|
||||
onClick: () => void;
|
||||
}> = ({ children, onClick }) => (
|
||||
<button
|
||||
className="block cursor-pointer border-2 rounded-md p-2 w-full hover:bg-white hover:text-gray-800 transition-colors [&:nth-child(n+2)]:mt-3"
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default Modal;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "server",
|
||||
"name": "budget chess.com",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
||||
+48
-67
@@ -201,7 +201,7 @@ export function validateFEN(fen: string): void {
|
||||
if (!isPieceValid(symbol.toLowerCase()))
|
||||
throw new Error(
|
||||
"Invalid FEN - board position contains an invalid piece symbol: " +
|
||||
symbol
|
||||
symbol
|
||||
);
|
||||
|
||||
numSquares++;
|
||||
@@ -504,6 +504,8 @@ export default class Chess {
|
||||
private _attacks: number[] = [];
|
||||
private _history: string[] = [];
|
||||
private _enableProcessMoves = true;
|
||||
private _checkMate = false;
|
||||
private _draw = false;
|
||||
|
||||
private constructor(
|
||||
board: Board,
|
||||
@@ -523,7 +525,7 @@ export default class Chess {
|
||||
this._fullMoves = fullMoves;
|
||||
this._kings = kings;
|
||||
this._enableProcessMoves = enableProcessMoves;
|
||||
this._computeMoves();
|
||||
this._processBoardState();
|
||||
}
|
||||
|
||||
private _computeMoves() {
|
||||
@@ -588,7 +590,17 @@ export default class Chess {
|
||||
}
|
||||
}
|
||||
|
||||
static load(fen = DEFAULT_POSITION, enableProcessMoves = true) {
|
||||
private _processBoardState() {
|
||||
this._computeMoves();
|
||||
this._checkMate = this.isCheck() && this._moves.length === 0;
|
||||
this._draw =
|
||||
this._halfMoves >= 100 || // 50 moves per side = 100 half moves
|
||||
this.isStalemate() ||
|
||||
this.isInsufficientMaterial() ||
|
||||
this.isThreefoldRepetition();
|
||||
}
|
||||
|
||||
private static _load(fen: string, enableProcessMoves: boolean) {
|
||||
validateFEN(fen);
|
||||
|
||||
const builder = new Chess.Builder();
|
||||
@@ -622,6 +634,10 @@ export default class Chess {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
static load(fen = DEFAULT_POSITION) {
|
||||
return this._load(fen, true);
|
||||
}
|
||||
|
||||
reset() {
|
||||
const chess = Chess.load();
|
||||
this._board = chess._board;
|
||||
@@ -724,7 +740,7 @@ export default class Chess {
|
||||
private _processMoves() {
|
||||
const currentFEN = this.getFEN();
|
||||
this._moves = this._moves.filter((move) => {
|
||||
const chess = Chess.load(currentFEN, false);
|
||||
const chess = Chess._load(currentFEN, false);
|
||||
chess._makeMove(move);
|
||||
return !chess._isKingAttacked(this._turn);
|
||||
}, this);
|
||||
@@ -743,9 +759,9 @@ export default class Chess {
|
||||
this._board[squareIndex(move.to)] =
|
||||
move.flags & MOVE_FLAGS.PROMOTION
|
||||
? {
|
||||
type: move.promotion as PieceType,
|
||||
color: myColor,
|
||||
}
|
||||
type: move.promotion as PieceType,
|
||||
color: myColor,
|
||||
}
|
||||
: this._board[squareIndex(move.from)];
|
||||
this._board[squareIndex(move.from)] = null;
|
||||
let keepEpSquare = false;
|
||||
@@ -800,9 +816,7 @@ export default class Chess {
|
||||
|
||||
this._turn = theirColor;
|
||||
this._updateCastling();
|
||||
|
||||
if (this.isGameOver()) this._moves = [];
|
||||
else this._computeMoves();
|
||||
this._processBoardState();
|
||||
}
|
||||
|
||||
makeMove(move: Move) {
|
||||
@@ -820,69 +834,41 @@ export default class Chess {
|
||||
}
|
||||
}
|
||||
|
||||
if (moveObj == null)
|
||||
throw new Error("Move not found");
|
||||
if (moveObj == null) throw new Error("Move not found");
|
||||
|
||||
this._makeMove(moveObj);
|
||||
}
|
||||
|
||||
private _updateCastling() {
|
||||
const forWhite = () => {
|
||||
const castling = this._castling[COLOR.WHITE];
|
||||
const uptate = (color: Color) => {
|
||||
const castling = this._castling[color];
|
||||
|
||||
if (castling == 0) return;
|
||||
|
||||
const whiteKing = this.getPiece("e1");
|
||||
const king = this.getPiece(color == COLOR.WHITE ? "e1" : "e8");
|
||||
|
||||
if (whiteKing?.type != PIECE.KING && whiteKing?.color != COLOR.WHITE) {
|
||||
this._castling[COLOR.WHITE] = 0;
|
||||
if (king?.type != PIECE.KING && king?.color != color) {
|
||||
this._castling[color] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (castling & MOVE_FLAGS.K_CASTLE) {
|
||||
const rook = this.getPiece("h1");
|
||||
const rook = this.getPiece(color == COLOR.WHITE ? "h1" : "h8");
|
||||
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != COLOR.WHITE)
|
||||
this._castling[COLOR.WHITE] ^= MOVE_FLAGS.K_CASTLE;
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != color)
|
||||
this._castling[color] ^= MOVE_FLAGS.K_CASTLE;
|
||||
}
|
||||
|
||||
if (castling & MOVE_FLAGS.Q_CASTLE) {
|
||||
const rook = this.getPiece("a1");
|
||||
const rook = this.getPiece(color == COLOR.WHITE ? "a1" : "a8");
|
||||
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != COLOR.WHITE)
|
||||
this._castling[COLOR.WHITE] ^= MOVE_FLAGS.Q_CASTLE;
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != color)
|
||||
this._castling[color] ^= MOVE_FLAGS.Q_CASTLE;
|
||||
}
|
||||
};
|
||||
|
||||
const forBlack = () => {
|
||||
const castling = this._castling[COLOR.BLACK];
|
||||
|
||||
if (castling == 0) return;
|
||||
|
||||
const blackKing = this.getPiece("e8");
|
||||
|
||||
if (blackKing?.type != PIECE.KING && blackKing?.color != COLOR.BLACK) {
|
||||
this._castling[COLOR.BLACK] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (castling & MOVE_FLAGS.K_CASTLE) {
|
||||
const rook = this.getPiece("h8");
|
||||
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != COLOR.BLACK)
|
||||
this._castling[COLOR.BLACK] ^= MOVE_FLAGS.K_CASTLE;
|
||||
}
|
||||
|
||||
if (castling & MOVE_FLAGS.Q_CASTLE) {
|
||||
const rook = this.getPiece("a8");
|
||||
|
||||
if (rook?.type != PIECE.ROOK && rook?.color != COLOR.BLACK)
|
||||
this._castling[COLOR.BLACK] ^= MOVE_FLAGS.Q_CASTLE;
|
||||
}
|
||||
};
|
||||
|
||||
forWhite();
|
||||
forBlack();
|
||||
uptate(COLOR.WHITE);
|
||||
uptate(COLOR.BLACK);
|
||||
}
|
||||
|
||||
isSquareAttacked(square: Square | number, attackedBy: Color) {
|
||||
@@ -892,7 +878,7 @@ export default class Chess {
|
||||
: (this._attacks[square] & COLOR_MASKS[attackedBy]) != 0;
|
||||
}
|
||||
|
||||
_isKingAttacked(color: Color) {
|
||||
private _isKingAttacked(color: Color) {
|
||||
const square = this._kings[color];
|
||||
return this.isSquareAttacked(square, swapColor(color));
|
||||
}
|
||||
@@ -902,7 +888,7 @@ export default class Chess {
|
||||
}
|
||||
|
||||
isCheckMate() {
|
||||
return this.isCheck() && this._moves.length === 0;
|
||||
return this._checkMate;
|
||||
}
|
||||
|
||||
isStalemate() {
|
||||
@@ -934,10 +920,10 @@ export default class Chess {
|
||||
remainingPieces[PIECE.BISHOP][COLOR.WHITE] == 2 ||
|
||||
remainingPieces[PIECE.BISHOP][COLOR.BLACK] == 2 ||
|
||||
remainingPieces[PIECE.BISHOP][COLOR.WHITE] +
|
||||
remainingPieces[PIECE.BISHOP][COLOR.BLACK] +
|
||||
remainingPieces[PIECE.KNIGHT][COLOR.WHITE] +
|
||||
remainingPieces[PIECE.KNIGHT][COLOR.BLACK] >=
|
||||
3
|
||||
remainingPieces[PIECE.BISHOP][COLOR.BLACK] +
|
||||
remainingPieces[PIECE.KNIGHT][COLOR.WHITE] +
|
||||
remainingPieces[PIECE.KNIGHT][COLOR.BLACK] >=
|
||||
3
|
||||
)
|
||||
return false;
|
||||
|
||||
@@ -950,12 +936,7 @@ export default class Chess {
|
||||
}
|
||||
|
||||
isDraw() {
|
||||
return (
|
||||
this._halfMoves >= 100 || // 50 moves per side = 100 half moves
|
||||
this.isStalemate() ||
|
||||
this.isInsufficientMaterial() ||
|
||||
this.isThreefoldRepetition()
|
||||
);
|
||||
return this._draw;
|
||||
}
|
||||
|
||||
isGameOver() {
|
||||
@@ -967,7 +948,7 @@ export default class Chess {
|
||||
const lastFen = this._history.pop();
|
||||
if (lastFen == undefined) return;
|
||||
|
||||
const chess = Chess.load(lastFen, this._enableProcessMoves);
|
||||
const chess = Chess._load(lastFen, this._enableProcessMoves);
|
||||
this._board = chess._board;
|
||||
this._turn = chess._turn;
|
||||
this._castling = chess._castling;
|
||||
@@ -975,7 +956,7 @@ export default class Chess {
|
||||
this._halfMoves = chess._halfMoves;
|
||||
this._fullMoves = chess._fullMoves;
|
||||
this._kings = chess._kings;
|
||||
this._computeMoves();
|
||||
this._processBoardState();
|
||||
}
|
||||
|
||||
getTurn() {
|
||||
@@ -983,7 +964,7 @@ export default class Chess {
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
history() { }
|
||||
history() {}
|
||||
|
||||
toString() {
|
||||
return this.getFEN();
|
||||
|
||||
@@ -42,6 +42,7 @@ io.on("connection", (socket) => {
|
||||
const room = rooms.get(id);
|
||||
|
||||
if (
|
||||
socket.rooms.size >= 2 ||
|
||||
room == undefined ||
|
||||
(room[color] != null && room[color] != socket.id)
|
||||
) {
|
||||
@@ -69,10 +70,13 @@ io.on("connection", (socket) => {
|
||||
|
||||
try {
|
||||
chess.makeMove(move);
|
||||
io.to(id).emit("receive move", move);
|
||||
} catch (e) {
|
||||
socket.emit("move error", (e as Error).message);
|
||||
return socket.emit("move error", (e as Error).message);
|
||||
}
|
||||
|
||||
io.to(id).emit("receive move", move);
|
||||
|
||||
if (chess.isGameOver()) rooms.delete(id);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user