diff --git a/backend/package.json b/backend/package.json
index ca32e79..0c67519 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -23,6 +23,7 @@
"socket.io": "^4.7.4",
"socketcluster-client": "^19.1.0",
"socketcluster-server": "^19.0.1",
+ "stockfish": "^10.0.2",
"uuid": "^9.0.1"
},
"keywords": [
@@ -43,6 +44,7 @@
"chess": "node test/illegalChess.js"
},
"devDependencies": {
+ "@types/express": "^4.17.21",
"@types/node": "^20.11.5",
"socket.io-client": "^4.7.4",
"typescript": "^5.3.3"
diff --git a/backend/src/server.ts b/backend/src/server.ts
index 77a92d1..b4206c9 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -9,6 +9,7 @@ import { Chess } from "chess.js";
import "./utils/globals.js";
import { FailedMove, interpretMove } from "./chess/engine.js";
import { assertUnreachable } from "./utils/assertions.js";
+import stockfish from "stockfish";
dotenv.config();
@@ -17,9 +18,16 @@ globalThis.roomFen = new Map();
const ENVIRONMENT = process.env.ENV || "dev";
const START_FEN = new Chess().fen();
-const chess = new Chess();
+
+const engine = stockfish();
+engine.onmessage = function (msg) {
+ console.log(msg);
+};
+engine.postMessage("uci");
let expressApp = express();
+expressApp.use(express.json());
+expressApp.use(express.urlencoded({ extended: true }));
if (ENVIRONMENT === "dev") {
expressApp.use(morgan("dev"));
expressApp.use(cors());
@@ -40,6 +48,28 @@ expressApp.get("/health-check", (req, res) => {
res.status(200).send("OK");
});
+expressApp.post("/stockfish", async (req, res) => {
+ console.log(req.body);
+ console.log("QUERY", req.body.fen);
+ // if chess engine replies
+ engine.onmessage = function (msg) {
+ console.log(msg);
+ // in case the response has already been sent?
+ if (res.headersSent) {
+ return;
+ }
+ // only send response when it is a recommendation
+ if (typeof (msg == "string") && msg.match("bestmove")) {
+ res.send(msg.split(" ")[1]);
+ }
+ };
+
+ // run chess engine
+ engine.postMessage("ucinewgame");
+ engine.postMessage("position fen " + req.body.fen);
+ engine.postMessage("go depth 20");
+});
+
io.on("connection", (socket) => {
console.log(`${socket.id} connected`);
@@ -57,17 +87,22 @@ io.on("connection", (socket) => {
socket.on("join", (roomId, callback) => {
console.log(socket.id, "join:", roomId);
- if (!io.sockets.adapter.rooms.has(roomId)) {
- callback("denied");
+ if (roomId !== "ai" && !io.sockets.adapter.rooms.has(roomId)) {
+ callback(false);
return;
}
socket.join(roomId);
- globalThis.roomFen.set(roomId, START_FEN);
- if (io.sockets.adapter.rooms.get(roomId).size === 2)
- io.to(roomId).emit("start", START_FEN, socket.id);
+ globalThis.roomFen.set(roomId === "ai" ? socket.id : roomId, START_FEN);
+
+ if (roomId === "ai" || io.sockets.adapter.rooms.get(roomId).size === 2)
+ io.to(roomId === "ai" ? socket.id : roomId).emit(
+ "start",
+ START_FEN,
+ socket.id
+ );
- callback("answer");
+ callback(true);
});
socket.on("leave", () => {
@@ -78,41 +113,64 @@ io.on("connection", (socket) => {
socket.on("move", async (move, callback) => {
console.log(socket.id, move);
- const allowed = Math.random() < 0.7; //TODO LLM
const roomIter = socket.rooms.values();
let roomId = "";
for (let room of roomIter) {
if (room !== socket.id) {
roomId = room;
- break;
+ if (room === "ai") break;
}
}
+ // const allowed = Math.random() < 0.7;
+ // if (allowed) {
+ // const chess = new Chess(
+ // globalThis.roomFen.get(roomId === "ai" ? socket.id : roomId)
+ // );
+ // const availableMoves = chess.moves();
+ // chess.move(
+ // availableMoves[Math.floor(Math.random() * availableMoves.length)]
+ // );
+ // io.to(roomId === "ai" ? socket.id : roomId).emit(
+ // "update",
+ // chess.fen(),
+ // socket.id,
+ // move
+ // );
+ // globalThis.roomFen.set(roomId === "ai" ? socket.id : roomId, chess.fen());
+ // console.log(globalThis.roomFen);
+ // callback("Allowed " + move);
+ // } else callback("Denied");
+
const res = await interpretMove(move, globalThis.roomFen.get(roomId));
if (res instanceof FailedMove) {
callback(res.error);
} else if (typeof res === "string") {
io.to(roomId).emit("update", res, socket.id, move);
+ globalThis.roomFen.set(roomId, res);
callback(res);
+
+ if (roomId === "ai") {
+ fetch("http://localhost:8080/stockfish", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ fen: globalThis.roomFen.get(socket.id),
+ }),
+ }).then((response) =>
+ response.text().then((res) => {
+ const chess = new Chess(globalThis.roomFen.get(socket.id));
+ console.log(globalThis.roomFen.get(socket.id));
+ chess.move(res);
+ io.to(socket.id).emit("update", chess.fen(), "ai", res);
+ globalThis.roomFen.set(socket.id, chess.fen());
+ })
+ );
+ }
} else {
assertUnreachable(res);
}
-
- // setTimeout(() => {
- // if (allowed) {
- // //TODO execute parsed move from LLM
- // const availableMoves = chess.moves();
- // chess.move(
- // availableMoves[Math.floor(Math.random() * availableMoves.length)]
- // );
- // socket.rooms.forEach((roomId) => {
- // if (roomId !== socket.id)
- // io.to(roomId).emit("update", chess.fen(), socket.id, move);
- // });
- // callback("Allowed " + move);
- // } else
- // }, 1000);
});
socket.on("disconnecting", () => {
diff --git a/backend/yarn.lock b/backend/yarn.lock
index b2c8060..7d8d2f5 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -12,6 +12,21 @@
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
+"@types/body-parser@*":
+ version "1.19.5"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
+ integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/connect@*":
+ version "3.4.38"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
+ integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
+ dependencies:
+ "@types/node" "*"
+
"@types/cookie@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
@@ -24,6 +39,41 @@
dependencies:
"@types/node" "*"
+"@types/express-serve-static-core@^4.17.33":
+ version "4.17.41"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6"
+ integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+ "@types/send" "*"
+
+"@types/express@^4.17.21":
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d"
+ integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.33"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/http-errors@*":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
+ integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
+
+"@types/mime@*":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"
+ integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==
+
+"@types/mime@^1":
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
+ integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
+
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^20.11.5":
version "20.11.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e"
@@ -31,6 +81,33 @@
dependencies:
undici-types "~5.26.4"
+"@types/qs@*":
+ version "6.9.11"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda"
+ integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==
+
+"@types/range-parser@*":
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
+ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
+
+"@types/send@*":
+ version "0.17.4"
+ resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
+ integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==
+ dependencies:
+ "@types/mime" "^1"
+ "@types/node" "*"
+
+"@types/serve-static@*":
+ version "1.15.5"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033"
+ integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==
+ dependencies:
+ "@types/http-errors" "*"
+ "@types/mime" "*"
+ "@types/node" "*"
+
abbrev@1:
version "1.1.1"
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
@@ -1047,6 +1124,11 @@ statuses@2.0.1:
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+stockfish@^10.0.2:
+ version "10.0.2"
+ resolved "https://registry.yarnpkg.com/stockfish/-/stockfish-10.0.2.tgz#f4ba642f27e323b3adee9396d0ccfaf32553af18"
+ integrity sha512-MuDhChwRwFe4nkn5OKuDj2byf4lTVlpJK47D99kNi7Qr1QlM8PIi940AVdF4SMmFQnPtyYeT2xzlX69d5v8WQg==
+
stream-demux@^10.0.0, stream-demux@^10.0.1:
version "10.0.1"
resolved "https://registry.npmjs.org/stream-demux/-/stream-demux-10.0.1.tgz"
diff --git a/frontend/src/Landing.tsx b/frontend/src/Landing.tsx
index 4a99f63..007b049 100644
--- a/frontend/src/Landing.tsx
+++ b/frontend/src/Landing.tsx
@@ -57,6 +57,17 @@ export default function Landing({
>
) : (
<>
+
+
+
@@ -93,7 +104,7 @@ export default function Landing({
>
) : (
)}
diff --git a/frontend/src/utils/socket.tsx b/frontend/src/utils/socket.ts
similarity index 100%
rename from frontend/src/utils/socket.tsx
rename to frontend/src/utils/socket.ts