Skip to content

Commit

Permalink
Integrate LLM engine
Browse files Browse the repository at this point in the history
  • Loading branch information
jloh02 committed Jan 20, 2024
1 parent f933219 commit 904289c
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 82 deletions.
126 changes: 64 additions & 62 deletions backend/src/chess/engine.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,89 @@
import { Chess, validateFen } from "chess.js";
import { llmInterpretPrompt } from "./llm";
import { assertUnreachable } from "../utils/assertions";

import { llmInterpretPrompt } from "./llm.js";
import { assertUnreachable } from "../utils/assertions.js";

class NormalMove {
square1: string;
square2: string;
constructor(square1: string, square2: string) {
this.square1 = square1;
this.square2 = square2;
}
square1: string;
square2: string;
constructor(square1: string, square2: string) {
this.square1 = square1;
this.square2 = square2;
}
}

class PromotionMove extends NormalMove {
piece: "q" | "r" | "b" | "n";
constructor(square1: string, square2: string, piece: "q" | "r" | "b" | "n") {
super(square1, square2)
this.piece = piece;
}
piece: "q" | "r" | "b" | "n";
constructor(square1: string, square2: string, piece: "q" | "r" | "b" | "n") {
super(square1, square2);
this.piece = piece;
}
}

class InvalidMove {
prompt: string;
constructor(prompt: string) {
this.prompt = prompt;
}
prompt: string;
constructor(prompt: string) {
this.prompt = prompt;
}
}

class FailedMove {
error: string;
constructor(error: string) {
this.error = error;
}

error: string;
constructor(error: string) {
this.error = error;
}
}

type Move = InvalidMove | NormalMove | PromotionMove;

const FEN = "rnbqkb1r/1p2ppp1/3p4/p2n3p/3P4/3B1N2/PPP2PPP/RNBQK2R w KQkq - 0 7";
const chess = new Chess(FEN);
// const FEN = "rnbqkb1r/1p2ppp1/3p4/p2n3p/3P4/3B1N2/PPP2PPP/RNBQK2R w KQkq - 0 7";
const chess = new Chess();

function movePiece(square1, square2): string | FailedMove {
const piece = chess.remove(square1);
if (!piece) {
console.log(piece)
return null;
}
chess.put(piece, square2);
const validate = validateFen(chess.fen());
if (validate.ok) {
return chess.fen();
}else {
console.log(validateFen(chess.fen()));
return new FailedMove(validate.error);
}
const piece = chess.remove(square1);
if (!piece) {
console.log(piece);
return null;
}
chess.put(piece, square2);
const validate = validateFen(chess.fen());
if (validate.ok) {
return chess.fen();
} else {
console.log(validateFen(chess.fen()));
return new FailedMove(validate.error);
}
}

function promotePiece(square1, square2, piece): string | FailedMove {
const pawn = chess.remove(square1);
if (!pawn) {
console.log(pawn)
return null;
}
chess.put({ type: piece, color: pawn.color }, square2);
const validate = validateFen(chess.fen());
if (validate.ok) {
return chess.fen();
} else {
console.log(validateFen(chess.fen()));
return new FailedMove(validate.error);
}
const pawn = chess.remove(square1);
if (!pawn) {
console.log(pawn);
return null;
}
chess.put({ type: piece, color: pawn.color }, square2);
const validate = validateFen(chess.fen());
if (validate.ok) {
return chess.fen();
} else {
console.log(validateFen(chess.fen()));
return new FailedMove(validate.error);
}
}

export async function interpretMove(prompt: string, fen: string): Promise<string | FailedMove> {
const move = await llmInterpretPrompt(prompt, fen);
if (move instanceof NormalMove) {
return movePiece(move.square1, move.square2);
} else if (move instanceof PromotionMove) {
return promotePiece(move.square1, move.square2, move.piece);
} else if (move instanceof InvalidMove) {
assertUnreachable(move);
}
export async function interpretMove(
prompt: string,
fen: string
): Promise<string | FailedMove> {
const move = await llmInterpretPrompt(prompt, fen);
if (move instanceof NormalMove) {
return movePiece(move.square1, move.square2);
} else if (move instanceof PromotionMove) {
return promotePiece(move.square1, move.square2, move.piece);
} else if (move instanceof InvalidMove) {
return new FailedMove(move.prompt);
} else {
assertUnreachable(move);
}
}


export {Move, NormalMove, PromotionMove, InvalidMove}
export { Move, NormalMove, PromotionMove, InvalidMove, FailedMove };
52 changes: 36 additions & 16 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import dotenv from "dotenv";
import { Server } from "socket.io";
import { Chess } from "chess.js";
import "./utils/globals.js";
import { FailedMove, interpretMove } from "./chess/engine.js";
import { assertUnreachable } from "./utils/assertions.js";

dotenv.config();

Expand Down Expand Up @@ -74,25 +76,43 @@ io.on("connection", (socket) => {
});
});

socket.on("move", (move, callback) => {
socket.on("move", async (move, callback) => {
console.log(socket.id, move);
const allowed = Math.random() < 0.7; //TODO LLM

//TODO remove artificial delay
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 callback("Denied");
}, 1000);
const roomIter = socket.rooms.values();
let roomId = "";
for (let room of roomIter) {
if (room !== socket.id) {
roomId = room;
break;
}
}

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);
callback(res);
} 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", () => {
Expand Down
5 changes: 3 additions & 2 deletions backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"outDir": "./build",
"allowJs": true,
"esModuleInterop": true,
"moduleResolution": "Node"
"moduleResolution": "Node",
"downlevelIteration": true
},
"include": ["./src/**/*.ts"],
"exclude": ["node_modules"]
}
}
7 changes: 5 additions & 2 deletions frontend/src/Game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ export default function Game({
// ];

useEffect(() => {
onUpdate((_, isTurn, lastMove) => {
onUpdate((_, isTurn /*, lastMove*/) => {
// TODO update on opponent move
if (isTurn)
setMessages((msgs) => msgs.concat({ outgoing: false, text: lastMove }));
setMessages((msgs) =>
msgs.concat({ outgoing: false, text: "Your turn!" })
);
});
}, []);

Expand Down

0 comments on commit 904289c

Please sign in to comment.