From e065007a67b4bfa7f9502eb617d76ba196321a75 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sat, 14 Feb 2026 17:49:47 -0600 Subject: [PATCH 01/18] seems like the socket connection from the frontend simulator to backend is failing,and that's why the simulator isn't getting updates, but it only happens some of the time so i have to see why --- src/client/debug/simulator.tsx | 1 + src/server/robot/robot-manager.ts | 4 ++++ src/server/simulator.ts | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/client/debug/simulator.tsx b/src/client/debug/simulator.tsx index 118e9038..6741ac9e 100644 --- a/src/client/debug/simulator.tsx +++ b/src/client/debug/simulator.tsx @@ -77,6 +77,7 @@ export function Simulator() { // update the simulator when a message comes in useSocket((message) => { if (message instanceof SimulatorUpdateMessage) { + console.log("Updating bot!") dispatch({ type: "UPDATE_ROBOT", payload: { robotId: message.robotId, state: message.location }, diff --git a/src/server/robot/robot-manager.ts b/src/server/robot/robot-manager.ts index 052ad0df..96e75df0 100644 --- a/src/server/robot/robot-manager.ts +++ b/src/server/robot/robot-manager.ts @@ -88,6 +88,10 @@ export class RobotManager { getRobotAtIndices(indices: GridIndices): Robot { const indicesToIds = this.getIndicesToIds(); const robotId = indicesToIds.get(indices.toString()); + const theKeys = indicesToIds.keys() + const keyArr = [...theKeys] + console.log(`Indices is ${indices.toString()}, current id's are,`, keyArr); + if (robotId === undefined) { throw new Error("Failed to find robot at indices " + indices); } diff --git a/src/server/simulator.ts b/src/server/simulator.ts index d822e8f7..450a011c 100644 --- a/src/server/simulator.ts +++ b/src/server/simulator.ts @@ -150,6 +150,7 @@ export class VirtualBotTunnel extends BotTunnel { } async send(packet: Packet): Promise { + const packetId = randomUUID(); let stack: StackFrame[] = []; try { @@ -243,6 +244,7 @@ export class VirtualRobot extends Robot { export const virtualRobots = createVirtualRobots(); function createVirtualRobots() { + console.log("Creating virtual bots!") const virtualBotIds = Array(32) .fill(undefined) .map((_, i) => `robot-${(i + 1).toString()}`); From 2bab49590e30ff4ebe4ec4daa132f88a3fdbd81f Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 14:17:02 -0600 Subject: [PATCH 02/18] took forever, but fixed the issue of simulator not updating when the player made moves, had to do with sockets being overwritten basically. Next it's time to fix puzzles --- src/client/api.ts | 9 ++++++++- src/client/debug/simulator.tsx | 1 + src/server/api/api.ts | 10 +++++++++- src/server/api/socket-manager.ts | 2 ++ src/server/simulator.ts | 1 + 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/client/api.ts b/src/client/api.ts index 5dd48508..b94ec6af 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -33,7 +33,9 @@ export function useEffectQuery( */ const USE_SSL = window.location.protocol === "https:"; const WS_PROTOCOL = USE_SSL ? "wss" : "ws"; -const WEBSOCKET_URL = `${WS_PROTOCOL}://${new URL(window.location.href).host}/ws`; +const PAGE_LOCATION = window.location.pathname +const WEBSOCKET_URL = `${WS_PROTOCOL}://${new URL(window.location.href).host}/ws?page=${encodeURIComponent(PAGE_LOCATION)}`; + /** * A custom hook which allows using a websocket to connect to the server. @@ -49,6 +51,7 @@ export function useSocket( // handle sending a message and opening it const { sendMessage } = useWebSocket(WEBSOCKET_URL, { onOpen: () => { + console.log(`SOCKET URL IS: ${WEBSOCKET_URL}`) console.log("Connection established"); sendMessage(new RegisterWebsocketMessage().toJson()); if (onOpen !== undefined) { @@ -64,6 +67,10 @@ export function useSocket( onMessage(message); } }, + + onClose: () => { + console.log("IT'S CLOSED BRO!") + } }); // handle how a message is sent diff --git a/src/client/debug/simulator.tsx b/src/client/debug/simulator.tsx index 6741ac9e..f20c243b 100644 --- a/src/client/debug/simulator.tsx +++ b/src/client/debug/simulator.tsx @@ -76,6 +76,7 @@ export function Simulator() { // update the simulator when a message comes in useSocket((message) => { + console.log("Any message received!") if (message instanceof SimulatorUpdateMessage) { console.log("Updating bot!") dispatch({ diff --git a/src/server/api/api.ts b/src/server/api/api.ts index c074eaf1..787256fc 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -100,6 +100,7 @@ async function setupDefaultRobotPositions( export const websocketHandler: WebsocketRequestHandler = (ws, req) => { // on close, delete the cookie id ws.on("close", () => { + console.log("We closed the connection") socketManager.handleSocketClosed(req.cookies.id); }); @@ -109,7 +110,14 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { console.log("Received message: " + message.toJson()); if (message instanceof RegisterWebsocketMessage) { - socketManager.registerSocket(req.cookies.id, ws); + console.log(`Register a new socket with request ${req.url}`) + const cutoffIndex = req.url.indexOf("page=") + 5; + const pageValue = req.url.substring(cutoffIndex) + "|o|o|"; + console.log(req.cookies.id) + const finalSocketId = pageValue.concat(req.cookies.id) + + + socketManager.registerSocket(finalSocketId, ws); } else if ( message instanceof GameInterruptedMessage || message instanceof MoveMessage || diff --git a/src/server/api/socket-manager.ts b/src/server/api/socket-manager.ts index cf451efb..4b247e63 100644 --- a/src/server/api/socket-manager.ts +++ b/src/server/api/socket-manager.ts @@ -8,6 +8,7 @@ export class SocketManager { constructor(private sockets: Record) {} public registerSocket(id: string, socket: WebSocket): void { + console.log(`Id is: ${id}`) this.sockets[id] = socket; } @@ -47,6 +48,7 @@ export class SocketManager { */ public sendToAll(message: Message): boolean { const sockets = Object.values(this.sockets); + console.log(`Current list of connections are: ${sockets.length}`) for (const socket of sockets) { socket.send(message.toJson()); } diff --git a/src/server/simulator.ts b/src/server/simulator.ts index 450a011c..39892891 100644 --- a/src/server/simulator.ts +++ b/src/server/simulator.ts @@ -209,6 +209,7 @@ export class VirtualBotTunnel extends BotTunnel { { ...packet, packetId }, stack, ); + console.log("Pushing message!") VirtualBotTunnel.messages.push({ ts: new Date(), message }); socketManager.sendToAll(message); }); From c40271a465cba5e5a703e76c7387934d89ad2c65 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 15:27:46 -0600 Subject: [PATCH 03/18] gonna merge having piece type attributes into this for my next ingenious plan --- src/server/api/api.ts | 8 +++++++- src/server/api/puzzles.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 787256fc..8e8dc72f 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -68,7 +68,7 @@ import { /** * Helper function to move all robots from their home positions to their default positions - * for regular chess games + * for regular chess games. IsMoving basically controls whether or not to actually move the robots to the right position. */ async function setupDefaultRobotPositions( isMoving: boolean = true, @@ -292,6 +292,8 @@ apiRouter.post("/start-puzzle-game", async (req, res) => { const moves = puzzle.moves; const difficulty = puzzle.rating; + console.log(`Fein is ${fen}, moves are ${moves}, difficulty is ${difficulty}`) + if (puzzle.robotDefaultPositions) { // Convert puzzle.robotDefaultPositions from Record to Map const defaultPositionsMap = new Map(); @@ -322,6 +324,10 @@ apiRouter.post("/start-puzzle-game", async (req, res) => { defaultPositionsMap, ); } + else + { + throw Error("Should have the default positions set up, but the config is missing.") + } setGameManager( new PuzzleGameManager( new ChessEngine(), diff --git a/src/server/api/puzzles.ts b/src/server/api/puzzles.ts index 83692cf0..564673dc 100644 --- a/src/server/api/puzzles.ts +++ b/src/server/api/puzzles.ts @@ -10,6 +10,8 @@ export interface PuzzleComponents { robotDefaultPositions?: Record; } + + export const puzzles: Record = { "Puzzle 1": { fen: "8/1p3p1k/8/p1p2Kr1/P2pP3/1P1P4/2P5/8 w - - 0 1", From 5d6256ff1c176eb854878d357040a5741a8b45ea Mon Sep 17 00:00:00 2001 From: MahdMalik <157554762+MahdMalik@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:30:10 -0600 Subject: [PATCH 04/18] Piece type attribute (#269) * put back to tracking against main branch, dang that was quite a headache * not sure why this is failing on the main branch * added some comments * finally finished this optimization, wow that took a while. THink it's quite a bit better htough * ngl im listneing to the gummy bear song, no cap it might just be a banger * time to lock in on the website again --- src/common/game-types.ts | 18 +- src/server/api/bot-server-config.json | 129 +++++++--- src/server/robot/path-materializer.ts | 345 +++++++++++++++----------- src/server/robot/robot-manager.ts | 14 +- src/server/robot/robot.ts | 15 +- src/server/simulator.ts | 9 +- src/server/utils/env.ts | 3 +- 7 files changed, 340 insertions(+), 193 deletions(-) diff --git a/src/common/game-types.ts b/src/common/game-types.ts index faecab82..1d8eaaaa 100644 --- a/src/common/game-types.ts +++ b/src/common/game-types.ts @@ -1,6 +1,5 @@ import type { Square } from "chess.js"; import type { Robot } from "../server/robot/robot"; -import { DEGREE } from "./units"; /** * Defines a specific piece. @@ -34,14 +33,15 @@ export function oppositeSide(side: Side) { return side === Side.WHITE ? Side.BLACK : Side.WHITE; } -/** - * get the robot's start heading based on the side it is on - * @param side - the current side - * @returns angle in radians - */ -export function getStartHeading(side: Side) { - return side === Side.WHITE ? 90 * DEGREE : 270 * DEGREE; -} +// I don't think we need this anymore. DIE! +// /** +// * get the robot's start heading based on the side it is on +// * @param side - the current side +// * @returns angle in radians +// */ +// export function getStartHeading(side: Side) { +// return side === Side.WHITE ? 90 * DEGREE : 270 * DEGREE; +// } /** * holds the piece side, type, robot, and square diff --git a/src/server/api/bot-server-config.json b/src/server/api/bot-server-config.json index 01624aef..f35d8c59 100644 --- a/src/server/api/bot-server-config.json +++ b/src/server/api/bot-server-config.json @@ -11,7 +11,9 @@ "80:65:99:4e:70:2c": "robot-7" }, "robot-1": { - "attributes": {}, + "attributes": { + "piece_type": "w_rook" + }, "homeIndices": { "x": 2, "y": 0 @@ -23,7 +25,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-2": { - "attributes": {}, + "attributes": { + "piece_type": "w_knight" + }, "homeIndices": { "x": 3, "y": 0 @@ -35,7 +39,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-3": { - "attributes": {}, + "attributes": { + "piece_type": "w_bishop" + }, "homeIndices": { "x": 4, "y": 0 @@ -47,7 +53,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-4": { - "attributes": {}, + "attributes": { + "piece_type": "w_queen" + }, "homeIndices": { "x": 5, "y": 0 @@ -59,7 +67,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-5": { - "attributes": {}, + "attributes": { + "piece_type": "w_king" + }, "homeIndices": { "x": 6, "y": 0 @@ -71,7 +81,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-6": { - "attributes": {}, + "attributes": { + "piece_type": "w_bishop" + }, "homeIndices": { "x": 7, "y": 0 @@ -83,7 +95,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-7": { - "attributes": {}, + "attributes": { + "piece_type": "w_knight" + }, "homeIndices": { "x": 8, "y": 0 @@ -95,7 +109,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-8": { - "attributes": {}, + "attributes": { + "piece_type": "w_rook" + }, "homeIndices": { "x": 9, "y": 0 @@ -107,7 +123,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-9": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 0, "y": 2 @@ -119,7 +137,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-10": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 0, "y": 3 @@ -131,7 +151,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-11": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 0, "y": 4 @@ -143,7 +165,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-12": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 0, "y": 5 @@ -155,7 +179,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-13": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 11, "y": 5 @@ -167,7 +193,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-14": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 11, "y": 4 @@ -181,7 +209,8 @@ "robot-15": { "attributes": { "MOTOR_A_DRIVE_MULTIPLIER": 1.3, - "MOTOR_B_DRIVE_MULTIPLIER": 2 + "MOTOR_B_DRIVE_MULTIPLIER": 2, + "piece_type": "w_pawn" }, "homeIndices": { "x": 11, @@ -194,7 +223,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-16": { - "attributes": {}, + "attributes": { + "piece_type": "w_pawn" + }, "homeIndices": { "x": 11, "y": 2 @@ -206,7 +237,9 @@ "startHeadingRadians": 1.5707963268 }, "robot-17": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 0, "y": 9 @@ -218,7 +251,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-18": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 0, "y": 8 @@ -230,7 +265,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-19": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 0, "y": 7 @@ -242,7 +279,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-20": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 0, "y": 6 @@ -254,7 +293,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-21": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 11, "y": 6 @@ -266,7 +307,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-22": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 11, "y": 7 @@ -278,7 +321,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-23": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 11, "y": 8 @@ -290,7 +335,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-24": { - "attributes": {}, + "attributes": { + "piece_type": "b_pawn" + }, "homeIndices": { "x": 11, "y": 9 @@ -302,7 +349,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-25": { - "attributes": {}, + "attributes": { + "piece_type": "b_rook" + }, "homeIndices": { "x": 2, "y": 11 @@ -314,7 +363,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-26": { - "attributes": {}, + "attributes": { + "piece_type": "b_knight" + }, "homeIndices": { "x": 3, "y": 11 @@ -326,7 +377,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-27": { - "attributes": {}, + "attributes": { + "piece_type": "b_bishop" + }, "homeIndices": { "x": 4, "y": 11 @@ -338,7 +391,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-28": { - "attributes": {}, + "attributes": { + "piece_type": "b_queen" + }, "homeIndices": { "x": 5, "y": 11 @@ -350,7 +405,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-29": { - "attributes": {}, + "attributes": { + "piece_type": "b_king" + }, "homeIndices": { "x": 6, "y": 11 @@ -362,7 +419,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-30": { - "attributes": {}, + "attributes": { + "piece_type": "b_bishop" + }, "homeIndices": { "x": 7, "y": 11 @@ -374,7 +433,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-31": { - "attributes": {}, + "attributes": { + "piece_type": "b_knight" + }, "homeIndices": { "x": 8, "y": 11 @@ -386,7 +447,9 @@ "startHeadingRadians": 4.7123889804 }, "robot-32": { - "attributes": {}, + "attributes": { + "piece_type": "b_rook" + }, "homeIndices": { "x": 9, "y": 11 @@ -473,4 +536,4 @@ "default_value": 4.375 } ] -} +} \ No newline at end of file diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index 8a93ea23..48075000 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -32,8 +32,9 @@ enum CollisionType { HORSE = 3, } -const arrayOfCornersIndicies = [0, 9, 18, 27]; +// const arrayOfCornersIndicies = [0, 9, 18, 27]; +// creates a "Border" around the geamboard that serves as a deadzone const arrayOfDeadzone = [ new GridIndices(1, 1), new GridIndices(1, 2), @@ -73,6 +74,7 @@ const arrayOfDeadzone = [ new GridIndices(2, 1), ]; +// converts from the Move object to the GridMove object. function moveToGridMove(move: Move): GridMove { return { from: GridIndices.squareToGrid(move.from), @@ -80,6 +82,7 @@ function moveToGridMove(move: Move): GridMove { }; } +// detects what type of collision may be found when moving to a location function calcCollisionType(gridMove: GridMove): CollisionType { const from = gridMove.from; const to = gridMove.to; @@ -101,6 +104,7 @@ function calcCollisionType(gridMove: GridMove): CollisionType { } } +// takes in a coordinate, sees if a robot is there. If there is, add it to a list of collisions function addToCollisions(collisions: string[], x: number, y: number) { const square = new GridIndices(x, y); if (robotManager.isRobotAtIndices(square)) { @@ -108,6 +112,7 @@ function addToCollisions(collisions: string[], x: number, y: number) { } } +// detects collisions by cecking whats in the way depending on the collision type. Note we don't check the destination square function detectCollisions( gridMove: GridMove, collisionType: CollisionType, @@ -119,23 +124,27 @@ function detectCollisions( switch (collisionType) { // Horizontal case CollisionType.HORIZONTAL: { + // if we're moving left, check each index at left, seeing if there's any robots there if (to.i < from.i) { for (let i = from.i - 1; i > to.i; i--) { addToCollisions(collisions, i, from.j); } + //If we didn't have a collision, yay, we have a clear path. If we didn't though, we'll have to go left or right (the closer one to the midpoint), + //and check if we travel along there, what collisions we'll have. if (collisions.length > 0) { - addToCollisions(collisions, from.i, from.j + direction[1]); - for (let i = from.i - 1; i > to.i; i--) { + for (let i = from.i; i > to.i; i--) { addToCollisions(collisions, i, from.j + direction[1]); } } - } else { + } + // if we're moving right now, check each index as we move right + else { for (let i = from.i + 1; i < to.i; i++) { addToCollisions(collisions, i, from.j); } + //same thing; if we got collisiions, now go closer to midpoint and see if traveling there what collisions we'd have. if (collisions.length > 0) { - addToCollisions(collisions, from.i, from.j + direction[1]); - for (let i = from.i + 1; i < to.i; i++) { + for (let i = from.i; i < to.i; i++) { addToCollisions(collisions, i, from.j + direction[1]); } } @@ -144,17 +153,21 @@ function detectCollisions( } // Vertical case CollisionType.VERTICAL: { + //if we are moving downwards if (to.j < from.j) { for (let j = from.j - 1; j > to.j; j--) { addToCollisions(collisions, from.i, j); } + // try moving to column closer to middle, see collisions that way if (collisions.length > 0) { addToCollisions(collisions, from.i + direction[0], from.j); for (let j = from.j - 1; j > to.j; j--) { addToCollisions(collisions, from.i + direction[0], j); } } - } else { + } + //means we are moving upwards + else { for (let j = from.j + 1; j < to.j; j++) { addToCollisions(collisions, from.i, j); } @@ -169,15 +182,13 @@ function detectCollisions( } // Diagonal case CollisionType.DIAGONAL: { - // Will be either positive or negative depending on direction - const dx = to.i - from.i; - const dy = to.j - from.j; // For diagonal, x and y offset by the same amount (not including signs) // thus, absolute value of either will be the same + const dx = to.i - from.i; const distance = Math.abs(dx); // Normalized to 1 or -1 to get direction (dividing by absolute value of self) - const nx = dx / distance; - const ny = dy / distance; + const nx = (dx) / distance; + const ny = (to.j - from.j) / distance; // Loop through the tiles along the diagonal excluding beginning and end // (Beginning is the moving piece, and end is capture piece. Capture handled separately) @@ -188,6 +199,7 @@ function detectCollisions( // Above or below the tile, depends on direction const square1 = new GridIndices(midx, midy + ny); + // if robot there, add to collisions if (robotManager.isRobotAtIndices(square1)) { const piece: string = robotManager.getRobotAtIndices(square1).id; @@ -195,6 +207,7 @@ function detectCollisions( } // Left or right of tile, depends on direction const square2 = new GridIndices(midx + nx, midy); + // robot there, add to collisions if (robotManager.isRobotAtIndices(square2)) { const piece: string = robotManager.getRobotAtIndices(square2).id; @@ -211,11 +224,12 @@ function detectCollisions( // Normalized to 1 or -1 (can also be directly used to get first piece) const nx = dx / Math.abs(dx); const ny = dy / Math.abs(dy); - // Shifted to get second piece, shift direction based on sign + // Shifted to get second piece, shift direction based on sign. Reduces the distance + // in its direction by 1 const sx = dx - nx; const sy = dy - ny; - // Same sign horse moves share this square. Will always be 1 diagonal + // Same-sign horse moves share this square. Will always be 1 diagonal // of moving piece const square1 = new GridIndices(from.i + nx, from.j + ny); if (robotManager.isRobotAtIndices(square1)) { @@ -232,16 +246,23 @@ function detectCollisions( collisions.push(piece); } break; + + // do we not check the other bots in the way of the L-shaped movement? } } return collisions; } +// note to self: each "i" is a column, each "J" is a row + +// finds the location that a robot that would normally collide should shimmy towards. Move in this case +// is the movement of the roiginal robot that causes the collision function findShimmyLocation( pieceId: string, move: GridMove, collisionType: CollisionType, ): Position { + // get current position of robot that may shimmy const shimmyPos: Position = robotManager.getRobot(pieceId).position; const axisShimmyAmount: number = 1 / 3; switch (collisionType) { @@ -249,6 +270,9 @@ function findShimmyLocation( case CollisionType.HORIZONTAL: { const direction: [number, number] = directionToEdge(move.to); const gridY: number = Math.floor(shimmyPos.y); + // if the collision happened while the original robot was moving horizontally, and + // this robot is on the same row, then move away from the center of the board; otherwise, + // move closer to the center of the board if (gridY === move.to.j) { const augmentY: number = shimmyPos.y + direction[1] * -axisShimmyAmount; @@ -262,7 +286,9 @@ function findShimmyLocation( // Vertical case CollisionType.VERTICAL: { const direction: [number, number] = directionToEdge(move.to); - const gridX: number = Math.floor(shimmyPos.x); + const gridX: number = Math.floor(shimmyPos.y); + // if vertical collision, and on same row, move away from center; otherwise, + // move closer to center. if (gridX === move.to.i) { const augmentX: number = shimmyPos.x + direction[0] * -axisShimmyAmount; @@ -273,16 +299,24 @@ function findShimmyLocation( return new Position(augmentX, shimmyPos.y); } } + //if diagonal or horse, use same idea case CollisionType.DIAGONAL: case CollisionType.HORSE: { const moveDistance: number = 0.5; const signedDistX: number = move.to.i - move.from.i; const signedDistY: number = move.to.j - move.from.j; + // gets total distance of the moving bot that it has to travel const distHypot = Math.hypot(signedDistX, signedDistY); + // distance normalized to a unit vector holding direction const normalX: number = signedDistX / distHypot; const normalY: number = signedDistY / distHypot; + + // gets the vector perpendicular to the normal vector. These are the two options to take const orth1: Position = new Position(-normalY, normalX); const orth2: Position = new Position(normalY, -normalX); + + // adds orthogonal vector to final position, so orthPos1 is a point slightly close to one side + // facing away from the destination, while orthPos2 is the same but in the opposite side. const orthPos1: Position = orth1.add( Position.fromGridIndices(move.to), ); @@ -292,10 +326,13 @@ function findShimmyLocation( // distance calculations :) const val1: Position = shimmyPos.sub(orthPos1); - const val2: Position = shimmyPos.sub(orthPos2); const dist1: number = Math.hypot(val1.x, val1.y); + + const val2: Position = shimmyPos.sub(orthPos2); const dist2: number = Math.hypot(val2.x, val2.y); + // between the two possible shimmy options, chooses the one that travels less distance, and move it in the direction + // of the orthogonal vector. Basically if there's a vector (from, to) of the Robot, this shimmy moves it away from that line. return dist1 < dist2 ? new Position( shimmyPos.x + orth1.x * moveDistance, @@ -310,6 +347,8 @@ function findShimmyLocation( return new Position(0, 0); } +// constructs the drive command, how much the robot should drive FORWARD from its +// urrent heading function constructDriveCommand( pieceId: string, endLocation: Position, @@ -321,6 +360,7 @@ function constructDriveCommand( return new DriveCommand(pieceId, distance); } +//constructs rotat command of how m uch the robot should rotate function constructRotateCommand( pieceId: string, location: Position, @@ -328,11 +368,33 @@ function constructRotateCommand( ): ReversibleRobotCommand { const robot = robotManager.getRobot(pieceId); const offset = location.sub(startLocation ?? robot.position); + // angle that the offset vector makes, so where to rotate towards const angle = Math.atan2(offset.y, offset.x); console.log("rotate cmd construct", robot.position, offset, angle); return new ReversibleAbsoluteRotateCommand(pieceId, () => angle); } +// takes in the 3 positions, main piece, and computes the sequential command sequence. Made to reduce duplicated code +function getMoveSequence(mainPiece : string, pos1 : Position, pos2 : Position, pos3 : Position) : SequentialCommandGroup +{ + const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); + const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); + const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); + + const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); + const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); + + return new SequentialCommandGroup([ + mainDrive1, + mainTurn2, + mainDrive2, + mainTurn3, + mainDrive3, + ]); +} + +// constructs final command for the robots +//move in this case is the move of the original robot function constructFinalCommand( move: GridMove, driveCommands: DriveCommand[], @@ -343,70 +405,68 @@ function constructFinalCommand( const from = move.from; const robotAtFrom = robotManager.getRobotAtIndices(from); const mainPiece = robotAtFrom.id; + // gets edge closer to center const dirToEdge = directionToEdge(from); + // all the commands needed to do the collision now + const setupCommands: ReversibleRobotCommand[] = []; + + // we should be moving a piece, obviously, else raise error if (mainPiece !== undefined) { console.log("main piece"); const to = move.to; + + let mainDrive: SequentialCommandGroup | DriveCommand + let mainTurn: ReversibleRobotCommand + if (collisionType === CollisionType.HORIZONTAL && numCollisions > 1) { + // y is like the distance we need to travel to get to that edge const y = dirToEdge[1] * 0.5; + + //NOTE: to get MIDDLe of tile, each tile is 1x1, so it's 0.5 + + //first, set position as same horizontal value, but now veritcally in the direction closer to center. const pos1 = new Position(from.i + 0.5, from.j + y + 0.5); + // then, move it horizontally to the right location const pos2 = new Position(to.i + 0.5, from.j + y + 0.5); + //then, just move it a bit down (or up) towards the center of the chosen tile const pos3 = new Position(to.i + 0.5, to.j + 0.5); console.log("from, to ========", from, " ", to); - const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); - const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); - const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); - const mainTurn1 = constructRotateCommand(mainPiece, pos1, null); - const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); - const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); - const setupCommands: ReversibleRobotCommand[] = []; - - const mainDrive: SequentialCommandGroup = - new SequentialCommandGroup([ - mainDrive1, - mainTurn2, - mainDrive2, - mainTurn3, - mainDrive3, - ]); - setupCommands.push(...rotateCommands, mainTurn1, ...driveCommands); - return new MovePiece(setupCommands, mainDrive); + // create the commands needed + + mainTurn = constructRotateCommand(mainPiece, pos1, null); + + // helper function to clean up the code, reudcing duplicated lines + mainDrive = getMoveSequence(mainPiece, pos1, pos2, pos3) } else if ( collisionType === CollisionType.VERTICAL && numCollisions > 1 ) { + //distance to get to the edge needed const x = dirToEdge[0] * 0.5; + // move horizontally to one of the edges on the from square const pos1 = new Position(from.i + x + 0.5, from.j + 0.5); + // move veritcally to the "to" square const pos2 = new Position(from.i + x + 0.5, to.j + 0.5); + // move back in place to the center of the square const pos3 = new Position(to.i + 0.5, to.j + 0.5); console.log("from, to ========", from, " ", to); - const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); - const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); - const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); - const mainTurn1 = constructRotateCommand(mainPiece, pos1, null); - const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); - const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); - const setupCommands: ReversibleRobotCommand[] = []; - - const mainDrive: SequentialCommandGroup = - new SequentialCommandGroup([ - mainDrive1, - mainTurn2, - mainDrive2, - mainTurn3, - mainDrive3, - ]); - setupCommands.push(...rotateCommands, mainTurn1, ...driveCommands); - return new MovePiece(setupCommands, mainDrive); - } else { + + mainTurn = constructRotateCommand(mainPiece, pos1, null); + + // helper function to clean up the code, reudcing duplicated lines + mainDrive = getMoveSequence(mainPiece, pos1, pos2, pos3) + } + //diagonal or knight option + else { const pos = new Position(to.i + 0.5, to.j + 0.5); - const mainDrive = constructDriveCommand(mainPiece, pos, null); - const mainTurn = constructRotateCommand(mainPiece, pos, null); - const setupCommands: ReversibleRobotCommand[] = []; - setupCommands.push(...rotateCommands, mainTurn, ...driveCommands); - return new MovePiece(setupCommands, mainDrive); + //just drive directly to the location in question + mainDrive = constructDriveCommand(mainPiece, pos, null); + mainTurn = constructRotateCommand(mainPiece, pos, null); } + + setupCommands.push(...rotateCommands, mainTurn, ...driveCommands); + return new MovePiece(setupCommands, mainDrive); } else { console.log("no main piece"); return new MovePiece(rotateCommands, new SequentialCommandGroup([])); @@ -419,13 +479,17 @@ export function moveMainPiece(move: GridMove): MovePiece { const driveCommands: DriveCommand[] = []; const rotateCommands: ReversibleRobotCommand[] = []; const collisionType = calcCollisionType(move); + // gets collisions const collisions: string[] = detectCollisions(move, collisionType); + //loop through the collisions. Find where they should shimmy, and push the appropriate drive and roate + // commands to get them to that location for (let i = 0; i < collisions.length; i++) { const pieceId = collisions[i]; const location = findShimmyLocation(pieceId, move, collisionType); driveCommands.push(constructDriveCommand(pieceId, location, null)); rotateCommands.push(constructRotateCommand(pieceId, location, null)); } + // with all the data now, create all the final commands needed to handle any collisions with this move return constructFinalCommand( move, driveCommands, @@ -456,6 +520,8 @@ function moveToDeadZone(origin: GridIndices): GridMove { to: new GridIndices(1, origin.j), //("a" + origin[1]) as Square, }; + // check if there's any collisions by doing this + const aboveCollision = detectCollisions( aboveMove, calcCollisionType(aboveMove), @@ -480,10 +546,13 @@ function moveToDeadZone(origin: GridIndices): GridMove { [leftMove, leftCollision], ]; + // sorts by which way has the least collisions, and then choose the one with the fewest collisions to return collisionTuple.sort((a, b) => a[1].length - b[1].length); return collisionTuple[0][0]; } +// based on where it wants to go, returns a vector of what edge it should go towards. The edge doesn't mean the board edge, but the square edge. +// Idea seems to be that its biased towards going to edges of the square closer to the center, it seems, to prevent moving a lot of pieces early on. function directionToEdge(position: GridIndices) { let x = 0; let y = 0; @@ -502,6 +571,7 @@ function directionToEdge(position: GridIndices) { return DirectionTuple; } +// given the array of grid indices, finds the specific grid index function findGridIndicesInArray( array: GridIndices[], obj: GridIndices, @@ -509,93 +579,89 @@ function findGridIndicesInArray( return array.findIndex((o) => o.i === obj.i && o.j === obj.j); } +function decreasingFunction(number : number) +{ + return Math.floor(number / 9) * 9 +} + +function increasingFunction(number : number) +{ + return Math.ceil(number / 9) * 9 +} + +//returns a piece back to its home position function returnToHome(from: GridIndices, id: string): SequentialCommandGroup { //const capturedPiece: GridIndices = GridIndices.squareToGrid(from); const home: GridIndices = robotManager.getRobot(id).homeIndices; const fastestMoveToDeadzone = moveToDeadZone(from); + //gets all the fun commands to move the piece to the deadzone const toDeadzone = moveMainPiece(fastestMoveToDeadzone); + //now that we're in teh deadzone, how we get back const startInDeadzone = fastestMoveToDeadzone.to; - let finalDestination: GridIndices | undefined; - const checkDirections: [number, number][] = [ - [0, 1], - [1, 0], - [-1, 0], - [0, -1], - ]; - - for (const direction of checkDirections) { - try { - const adjacentToHome = home.addTuple(direction); - if (arrayOfDeadzone.find((dz) => dz.equals(adjacentToHome))) { - finalDestination = adjacentToHome; - break; - } - } catch (e) { - // adjacentToHome is out of bounds, skip check - continue; - } - } - if (!finalDestination) { - throw new Error("WHERE THE HELL ARE YOU GOING"); // real - } - const startInArray = findGridIndicesInArray( + // finds the index values + const startArrayIndex = findGridIndicesInArray( arrayOfDeadzone, startInDeadzone, ); - const endInArray = findGridIndicesInArray( + const endArrayIndex = findGridIndicesInArray( arrayOfDeadzone, - finalDestination, + home, ); - let differenceOfIndex = endInArray - startInArray; + + // gets net distance of this + let differenceOfIndex = endArrayIndex - startArrayIndex; + // if we got a negative value, make it positive this way, basically meant that a wraparound was required to travel downwards from startArrayIndex if (differenceOfIndex < 0) { differenceOfIndex += 36; } + // if short distance, go in that direciton. Otherwise, go opposite way since its shorter const botDirectionToHome = differenceOfIndex < 18 ? 1 : -1; console.log( "deadzone array checker", - startInArray, - endInArray, + startArrayIndex, + endArrayIndex, botDirectionToHome, ); - let i = startInArray; + let i = startArrayIndex; const moveCommands: MoveCommand[] = []; - while (i !== endInArray) { - if (arrayOfCornersIndicies.includes(i)) { + // if already at the destination, don't run this + + const incrementalFunction : Function = botDirectionToHome == 1 ? increasingFunction : decreasingFunction; + + // until we've gotten to our destination do this + while (i !== endArrayIndex) { + if (Math.abs(i - endArrayIndex) < 9) + { + // now head to the final tile moveCommands.push( - new AbsoluteMoveCommand( - id, - new Position( - arrayOfDeadzone[i].i + 0.5, - arrayOfDeadzone[i].j + 0.5, - ), - ), + new AbsoluteMoveCommand(id, new Position(arrayOfDeadzone[endArrayIndex].i + 0.5, arrayOfDeadzone[endArrayIndex].j + 0.5)) ); + break; } - i += botDirectionToHome; - if (i < 0) i += 36; - if (i >= 36) i -= 36; - } - if (arrayOfDeadzone[endInArray]) { + i = incrementalFunction(i); + + let currentPushing = i + + //wrappign aroudn when we reach a bound + if(i === 36) + { + currentPushing = i = 0; + } + else if(i === 0) + { + i = 36; + } + // now head to the final tile moveCommands.push( - new AbsoluteMoveCommand( - id, - new Position( - arrayOfDeadzone[endInArray].i + 0.5, - arrayOfDeadzone[endInArray].j + 0.5, - ), - ), + new AbsoluteMoveCommand(id, new Position(arrayOfDeadzone[currentPushing].i + 0.5, arrayOfDeadzone[currentPushing].j + 0.5)) ); } - moveCommands.push( - new AbsoluteMoveCommand(id, new Position(home.i + 0.5, home.j + 0.5)), - ); - const goHome: SequentialCommandGroup = new SequentialCommandGroup([ toDeadzone, ...moveCommands, @@ -636,6 +702,8 @@ export function moveAllRobotsToDefaultPositions( // Sort robots: column by column (left to right, no skipping), then bottom to top within each column // This prevents collisions by ensuring robots in the same column don't interfere with each other + + // this current code only seems to sort by column? const sortedRobots = robotsToMove.sort((a, b) => { const aPos = GridIndices.fromPosition(a.position); const bPos = GridIndices.fromPosition(b.position); @@ -645,6 +713,7 @@ export function moveAllRobotsToDefaultPositions( const allCommands: Command[] = []; + // generates a path to the default square for (const robot of sortedRobots) { const robotCommands = generateRobotPathToDefault( robot, @@ -684,11 +753,16 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { const mainPieceTargets = new Map(); const pawnTargets = new Map(); - for (const robot of robotManager.idsToRobots.values()) { + // puts each piece into different targets based on the piece type + for (const robot of robotManager.idsToRobots.values()) + { const def = robot.defaultIndices; - if (def.j === 2 || def.j === 9) { + if (robot.pieceType !== "w_pawn" && robot.pieceType !== "b_pawn") + { mainPieceTargets.set(robot.id, def); - } else if (def.j === 3 || def.j === 8) { + } + else + { pawnTargets.set(robot.id, def); } } @@ -709,36 +783,31 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { // Home -> Deadzone entry on its side, Along deadzone to file aligned with its row, // Into its pawn row square. Repeat until all pawns are placed. type PawnInfo = { id: string; def: GridIndices; start: GridIndices }; - const leftWhite: PawnInfo[] = []; - const leftBlack: PawnInfo[] = []; - const rightWhite: PawnInfo[] = []; - const rightBlack: PawnInfo[] = []; + const leftWhite: (PawnInfo | null)[] = [null, null, null, null]; + const leftBlack: (PawnInfo | null)[] = [null, null, null, null]; + const rightWhite: (PawnInfo | null)[] = [null, null, null, null]; + const rightBlack: (PawnInfo | null)[] = [null, null, null, null]; + // group pawns into 4 types as mentioned above for (const [robotId, def] of pawnTargets) { - const start = robotManager.getRobot(robotId).homeIndices; - const isWhite = def.j === 3; + const robot = robotManager.getRobot(robotId); + const start = robot.homeIndices; + + const isWhite = robot.pieceType[0] === "w" const sideIsLeft = start.i === 0; + const info: PawnInfo = { id: robotId, def, start }; + // gets its index and palces it where ones closer to the center are farther x + const placementIndex = Math.abs(start.j - (isWhite ? Math.floor(5.5) : Math.ceil(5.5)) ) + let chosenList : (PawnInfo | null)[] = [null]; if (sideIsLeft) { - (isWhite ? leftWhite : leftBlack).push(info); + chosenList = isWhite ? leftWhite : leftBlack; } else { - (isWhite ? rightWhite : rightBlack).push(info); + chosenList = isWhite ? rightWhite : rightBlack; } + chosenList[placementIndex] = info } - // Sort each group center-out by file to funnel from middle outward - const centerOutSort = (a: PawnInfo, b: PawnInfo) => { - const center = 5.5; - const da = Math.abs(a.def.i - center); - const db = Math.abs(b.def.i - center); - if (da !== db) return da - db; - return a.def.i - b.def.i; - }; - leftWhite.sort(centerOutSort); - leftBlack.sort(centerOutSort); - rightWhite.sort(centerOutSort); - rightBlack.sort(centerOutSort); - const pawnBatches: ParallelCommandGroup[] = []; while ( leftWhite.length > 0 || @@ -747,7 +816,7 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { rightBlack.length > 0 ) { const batchSeqs: SequentialCommandGroup[] = []; - const pick = (arr: PawnInfo[] | undefined) => { + const pick = (arr: (PawnInfo | null)[] | undefined) => { if (!arr || arr.length === 0) return; const pawn = arr.shift()!; const dzStart = moveToDeadzoneFromHome(pawn.start); diff --git a/src/server/robot/robot-manager.ts b/src/server/robot/robot-manager.ts index 96e75df0..758e87eb 100644 --- a/src/server/robot/robot-manager.ts +++ b/src/server/robot/robot-manager.ts @@ -60,16 +60,18 @@ export class RobotManager { const robot = new Robot( robotId, new GridIndices( - config[robotId]?.homeIndices.x, - config[robotId]?.homeIndices.y, + robotConfig?.homeIndices.x, + robotConfig?.homeIndices.y, ), new GridIndices( - config[robotId]?.defaultIndices.x, - config[robotId]?.defaultIndices.y, + robotConfig?.defaultIndices.x, + robotConfig?.defaultIndices.y, ), - config[robotId]?.startHeadingRadians * DEGREE, + robotConfig?.startHeadingRadians * DEGREE, + robotConfig?.attributes.piece_type ); this.addRobot(robot); + console.log("We have the following:" + robotConfig?.defaultIndices) return robot; } @@ -125,12 +127,14 @@ export const robotManager = new RobotManager( new GridIndices(0, 5), new GridIndices(5, 3), 90 * DEGREE, + "w_pawn" ), new Robot( "robot-4", new GridIndices(5, 0), new GridIndices(5, 2), 90 * DEGREE, + "w_queen" ), ], ); diff --git a/src/server/robot/robot.ts b/src/server/robot/robot.ts index 5ce88f10..7c08499a 100644 --- a/src/server/robot/robot.ts +++ b/src/server/robot/robot.ts @@ -12,6 +12,7 @@ export class Robot { private _headingRadians: number; private _position: Position; protected tunnel: BotTunnel | null; + protected _pieceType: String; constructor( public readonly id: string, @@ -24,6 +25,7 @@ export class Robot { */ public readonly defaultIndices: GridIndices, public readonly startHeadingRadians: number = 0, + public readonly thePieceType: String, position?: Position, ) { if ( @@ -33,8 +35,9 @@ export class Robot { throw new Error("startHeadingRadians must be a number"); } this._headingRadians = startHeadingRadians; - this._position = position ?? Position.fromGridIndices(homeIndices); + this._position = position ?? process.env.START_ROBOTS_AT_DEFAULT ? Position.fromGridIndices(homeIndices) : Position.fromGridIndices(defaultIndices); this.tunnel = null; + this._pieceType = thePieceType; } public get position(): Position { @@ -53,6 +56,14 @@ export class Robot { this._headingRadians = headingRadians; } + public get pieceType(): String { + return this._pieceType; + } + + public set pieceType(thePieceType: String) { + this._pieceType = thePieceType; + } + /** * @param headingRadians - An absolute heading to turn to, in radians. 0 is up (from white to black). CW is positive. */ @@ -126,7 +137,7 @@ export class Robot { */ public async sendDrivePacket(tileDistance: number): Promise { console.log( - `Sending drive packet to robot ${this.id} with distance ${tileDistance}`, + `Sending drive packet to robot ${this.id} with distance ${tileDistance}, where the piece type is ${this.pieceType}`, ); await this.tunnel!.send({ type: PacketType.DRIVE_TILES, diff --git a/src/server/simulator.ts b/src/server/simulator.ts index 39892891..f781e79e 100644 --- a/src/server/simulator.ts +++ b/src/server/simulator.ts @@ -10,7 +10,6 @@ import { SimulatorUpdateMessage } from "../common/message/simulator-message"; import { socketManager } from "./api/managers"; import { randomUUID } from "node:crypto"; import { GridIndices } from "./robot/grid-indices"; -import { getStartHeading, Side } from "../common/game-types"; import { BotTunnel, type RobotEventEmitter } from "./api/bot-tunnel"; const srcDir = path.resolve(__dirname, "../"); @@ -225,8 +224,9 @@ export class VirtualRobot extends Robot { homeIndices: GridIndices, defaultIndices: GridIndices, headingRadians: number, + pieceType: String ) { - super(id, homeIndices, defaultIndices, headingRadians); + super(id, homeIndices, defaultIndices, headingRadians, pieceType); this.tunnel = new VirtualBotTunnel(id, headingRadians, this.position); } @@ -251,7 +251,7 @@ function createVirtualRobots() { .map((_, i) => `robot-${(i + 1).toString()}`); return new Map( - virtualBotIds.map((id, idx) => { + virtualBotIds.map((id) => { const realRobotConfig = config[id]; return [ id, @@ -265,7 +265,8 @@ function createVirtualRobots() { realRobotConfig.defaultIndices.x, realRobotConfig.defaultIndices.y, ), - getStartHeading(idx < 16 ? Side.WHITE : Side.BLACK), + realRobotConfig.startHeadingRadians, + realRobotConfig.attributes.piece_type ), ] as const; }), diff --git a/src/server/utils/env.ts b/src/server/utils/env.ts index 781ef4ad..ec58fb47 100644 --- a/src/server/utils/env.ts +++ b/src/server/utils/env.ts @@ -6,8 +6,7 @@ export const IS_DEVELOPMENT = process.env.NODE_ENV === "development"; export const IS_PRODUCTION = !IS_DEVELOPMENT; export const DO_SAVES = process.env.ENABLE_SAVES === "true"; export const USE_VIRTUAL_ROBOTS = process.env.VIRTUAL_ROBOTS === "true"; -export const START_ROBOTS_AT_DEFAULT = - process.env.START_ROBOTS_AT_DEFAULT === "true"; +export const START_ROBOTS_AT_DEFAULT = process.env.START_ROBOTS_AT_DEFAULT; export const PING_INTERVAL = 1000; export const PING_TIMEOUT = 100; From b847ac9902688396e05b5feede14e1f1de7e5219 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 16:47:50 -0600 Subject: [PATCH 05/18] got the FEIN algorithm done for dynamically determinign robot default pos, probably should've made this a separate task but something deep inside me compelled me to use the FEIN --- src/server/api/puzzles.ts | 141 +++++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 26 deletions(-) diff --git a/src/server/api/puzzles.ts b/src/server/api/puzzles.ts index 564673dc..6d50a2a1 100644 --- a/src/server/api/puzzles.ts +++ b/src/server/api/puzzles.ts @@ -1,5 +1,9 @@ import { type Square } from "chess.js"; import { type Move } from "../../common/game-types"; +import config from "./bot-server-config.json"; + + + export interface PuzzleComponents { fen: string; @@ -7,7 +11,110 @@ export interface PuzzleComponents { rating: number; tooltip: string; // the key is a physical robot id. value is the square where the robot should be at the start of the game. - robotDefaultPositions?: Record; + robotDefaultPositions: Record; +} + +const processFEINToDefaultPos = (fein) => { + const feinBoard = fein.split(" ")[0]; + const feinArr = feinBoard.split("/") + + const columnRows = ["a", "b", "c", "d", "e", "f", "g", "h"] + + // store the dict of piece type and position combos, so we can assign robots to them + const piecePositionCombo : Record = {} + + for (let row = 0; row < feinArr.length; row++) + { + let column = 1 + for (const char of feinArr[row]) + { + // if it's a number, skip ahead by that amount + if (/[0-9]/.test(char)) + { + column += Number(char) + } + // means we have a piece to place + else + { + let pieceType = "" + + const normalizedChar = char.toLowerCase(); + // check if its a black piece, or uppercase + if(char === normalizedChar) + { + pieceType = "b_" + } + else + { + pieceType = "w_" + } + + switch (normalizedChar) + { + case 'p': + pieceType += "pawn" + break + case 'r': + pieceType += "rook" + break + case 'b': + pieceType += "bishop" + break + case 'k': + pieceType += "king" + break + case 'q': + pieceType += "queen" + break + case 'n': + pieceType += "knight" + break + default: + pieceType += "none" + break + } + + + const position = (columnRows[column - 1] + (8 - row)) + if(pieceType in piecePositionCombo) + { + piecePositionCombo[pieceType].push(position as Square) + } + else + { + piecePositionCombo[pieceType] = [position as Square]; + } + + // now that you placed this piece, go up one to the right + column += 1 + } + } + } + + const newBoardConfig: Record = {} + + for(const [key, value] of Object.entries(config as any)) + { + if(key != "tcpServerPort" && key != "bots" && key != "botConfigSchema") + { + const v = value as any; + const botPieceType = v.attributes?.piece_type + // if its a piece we need to place, do that + if(botPieceType in piecePositionCombo) + { + newBoardConfig[key] = piecePositionCombo[botPieceType].pop()!; + if(piecePositionCombo[botPieceType].length == 0) + { + delete piecePositionCombo[botPieceType] + } + } + } + } + + // print for debugging + console.log(JSON.stringify(newBoardConfig, null, 2)); + + return newBoardConfig } @@ -18,27 +125,14 @@ export const puzzles: Record = { moves: [{ from: "f5", to: "g5" }], rating: 511, tooltip: "tooltip for puzzle 1", - robotDefaultPositions: { - "robot-1": "c2", - "robot-2": "b3", - "robot-3": "d3", - "robot-4": "a4", - "robot-5": "d4", - "robot-6": "e4", - "robot-7": "a5", - "robot-8": "c5", - "robot-9": "f5", - "robot-10": "g5", - "robot-11": "b7", - "robot-12": "f7", - "robot-13": "h7", - }, + robotDefaultPositions: processFEINToDefaultPos("8/1p3p1k/8/p1p2Kr1/P2pP3/1P1P4/2P5/8 w - - 0 1") }, "Puzzle 2": { fen: "5rk1/p5pp/4q3/8/1P1P4/2P4P/P2p1RP1/5RK1 w", moves: [{ from: "f2", to: "f8" }], rating: 514, tooltip: "tooltip for puzzle 2", + robotDefaultPositions: processFEINToDefaultPos("5rk1/p5pp/4q3/8/1P1P4/2P4P/P2p1RP1/5RK1 w") }, "Puzzle 3": { fen: "8/8/8/8/2Prk1p1/2K5/8/5R2 w - - 0 1", @@ -49,6 +143,7 @@ export const puzzles: Record = { ], rating: 1000, tooltip: "tooltip for puzzle 3", + robotDefaultPositions: processFEINToDefaultPos("8/8/8/8/2Prk1p1/2K5/8/5R2 w - - 0 1") }, "Puzzle 4": { fen: "1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1", @@ -59,6 +154,7 @@ export const puzzles: Record = { ], rating: 1000, tooltip: "tooltip", + robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") }, "Puzzle 5": { fen: "r1b3k1/1pq1b1r1/p2p3Q/3Pp3/3p1P2/P2B3P/1PP3P1/1R3RK1 w - - 0 1", @@ -73,6 +169,7 @@ export const puzzles: Record = { ], rating: 2915, tooltip: "tooltip for puzzle 5", + robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") }, "Puzzle 6": { fen: "4k3/8/4p3/8/8/4P3/8/4K3 w - - 0 1", @@ -85,12 +182,7 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 6", - robotDefaultPositions: { - "robot-4": "e3", - "robot-5": "e6", - "robot-12": "e1", - "robot-7": "e8", - }, + robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") }, "Puzzle 7": { fen: "8/8/3k4/8/8/3K4/8/3R4 w - - 0 1", @@ -101,9 +193,6 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 7", - robotDefaultPositions: { - "robot-4": "d3", - "robot-12": "d6", - }, + robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") }, }; From 4e9d5309e6fbbe8e40430b14e5c8dfbc35566cfe Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 17:35:16 -0600 Subject: [PATCH 06/18] fixed issue with board not being updated when default position was turned off. Now hopefully just need to check pausing + bot connection --- src/server/api/api.ts | 2 +- src/server/api/game-manager.ts | 3 + src/server/api/puzzles.ts | 7 +- src/server/robot/path-materializer.ts | 347 +++++++++++--------------- src/server/robot/robot.ts | 2 +- src/server/utils/env.ts | 4 +- 6 files changed, 150 insertions(+), 215 deletions(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 8e8dc72f..8fd9a99e 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -80,7 +80,7 @@ async function setupDefaultRobotPositions( moveAllRobotsToDefaultPositions(defaultPositionsMap); await executor.execute(command); } else { - moveAllRobotsToDefaultPositions(defaultPositionsMap); + setAllRobotsToDefaultPositions(defaultPositionsMap); } } else { if (isMoving) { diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index 7b6a9e61..68eaa487 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -357,6 +357,9 @@ export class PuzzleGameManager extends GameManager { id; if (message instanceof MoveMessage) { //if the move is correct + console.log("Move number is: ", this.moveNumber) + console.log("Number of moves is ", this.moves.length) + // console.log("") if ( this.moves[this.moveNumber].from === message.move.from && this.moves[this.moveNumber].to === message.move.to && diff --git a/src/server/api/puzzles.ts b/src/server/api/puzzles.ts index 6d50a2a1..4b795238 100644 --- a/src/server/api/puzzles.ts +++ b/src/server/api/puzzles.ts @@ -169,8 +169,9 @@ export const puzzles: Record = { ], rating: 2915, tooltip: "tooltip for puzzle 5", - robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") + robotDefaultPositions: processFEINToDefaultPos("r1b3k1/1pq1b1r1/p2p3Q/3Pp3/3p1P2/P2B3P/1PP3P1/1R3RK1 w - - 0 1") }, + //whoever made this puzzle is pretty stupid "Puzzle 6": { fen: "4k3/8/4p3/8/8/4P3/8/4K3 w - - 0 1", moves: [ @@ -182,7 +183,7 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 6", - robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") + robotDefaultPositions: processFEINToDefaultPos("4k3/8/4p3/8/8/4P3/8/4K3 w - - 0 1") }, "Puzzle 7": { fen: "8/8/3k4/8/8/3K4/8/3R4 w - - 0 1", @@ -193,6 +194,6 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 7", - robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") + robotDefaultPositions: processFEINToDefaultPos("8/8/3k4/8/8/3K4/8/3R4 w - - 0 1") }, }; diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index 48075000..a07e9958 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -32,9 +32,8 @@ enum CollisionType { HORSE = 3, } -// const arrayOfCornersIndicies = [0, 9, 18, 27]; +const arrayOfCornersIndicies = [0, 9, 18, 27]; -// creates a "Border" around the geamboard that serves as a deadzone const arrayOfDeadzone = [ new GridIndices(1, 1), new GridIndices(1, 2), @@ -74,7 +73,6 @@ const arrayOfDeadzone = [ new GridIndices(2, 1), ]; -// converts from the Move object to the GridMove object. function moveToGridMove(move: Move): GridMove { return { from: GridIndices.squareToGrid(move.from), @@ -82,7 +80,6 @@ function moveToGridMove(move: Move): GridMove { }; } -// detects what type of collision may be found when moving to a location function calcCollisionType(gridMove: GridMove): CollisionType { const from = gridMove.from; const to = gridMove.to; @@ -104,7 +101,6 @@ function calcCollisionType(gridMove: GridMove): CollisionType { } } -// takes in a coordinate, sees if a robot is there. If there is, add it to a list of collisions function addToCollisions(collisions: string[], x: number, y: number) { const square = new GridIndices(x, y); if (robotManager.isRobotAtIndices(square)) { @@ -112,7 +108,6 @@ function addToCollisions(collisions: string[], x: number, y: number) { } } -// detects collisions by cecking whats in the way depending on the collision type. Note we don't check the destination square function detectCollisions( gridMove: GridMove, collisionType: CollisionType, @@ -124,27 +119,23 @@ function detectCollisions( switch (collisionType) { // Horizontal case CollisionType.HORIZONTAL: { - // if we're moving left, check each index at left, seeing if there's any robots there if (to.i < from.i) { for (let i = from.i - 1; i > to.i; i--) { addToCollisions(collisions, i, from.j); } - //If we didn't have a collision, yay, we have a clear path. If we didn't though, we'll have to go left or right (the closer one to the midpoint), - //and check if we travel along there, what collisions we'll have. if (collisions.length > 0) { - for (let i = from.i; i > to.i; i--) { + addToCollisions(collisions, from.i, from.j + direction[1]); + for (let i = from.i - 1; i > to.i; i--) { addToCollisions(collisions, i, from.j + direction[1]); } } - } - // if we're moving right now, check each index as we move right - else { + } else { for (let i = from.i + 1; i < to.i; i++) { addToCollisions(collisions, i, from.j); } - //same thing; if we got collisiions, now go closer to midpoint and see if traveling there what collisions we'd have. if (collisions.length > 0) { - for (let i = from.i; i < to.i; i++) { + addToCollisions(collisions, from.i, from.j + direction[1]); + for (let i = from.i + 1; i < to.i; i++) { addToCollisions(collisions, i, from.j + direction[1]); } } @@ -153,21 +144,17 @@ function detectCollisions( } // Vertical case CollisionType.VERTICAL: { - //if we are moving downwards if (to.j < from.j) { for (let j = from.j - 1; j > to.j; j--) { addToCollisions(collisions, from.i, j); } - // try moving to column closer to middle, see collisions that way if (collisions.length > 0) { addToCollisions(collisions, from.i + direction[0], from.j); for (let j = from.j - 1; j > to.j; j--) { addToCollisions(collisions, from.i + direction[0], j); } } - } - //means we are moving upwards - else { + } else { for (let j = from.j + 1; j < to.j; j++) { addToCollisions(collisions, from.i, j); } @@ -182,13 +169,15 @@ function detectCollisions( } // Diagonal case CollisionType.DIAGONAL: { + // Will be either positive or negative depending on direction + const dx = to.i - from.i; + const dy = to.j - from.j; // For diagonal, x and y offset by the same amount (not including signs) // thus, absolute value of either will be the same - const dx = to.i - from.i; const distance = Math.abs(dx); // Normalized to 1 or -1 to get direction (dividing by absolute value of self) - const nx = (dx) / distance; - const ny = (to.j - from.j) / distance; + const nx = dx / distance; + const ny = dy / distance; // Loop through the tiles along the diagonal excluding beginning and end // (Beginning is the moving piece, and end is capture piece. Capture handled separately) @@ -199,7 +188,6 @@ function detectCollisions( // Above or below the tile, depends on direction const square1 = new GridIndices(midx, midy + ny); - // if robot there, add to collisions if (robotManager.isRobotAtIndices(square1)) { const piece: string = robotManager.getRobotAtIndices(square1).id; @@ -207,7 +195,6 @@ function detectCollisions( } // Left or right of tile, depends on direction const square2 = new GridIndices(midx + nx, midy); - // robot there, add to collisions if (robotManager.isRobotAtIndices(square2)) { const piece: string = robotManager.getRobotAtIndices(square2).id; @@ -224,12 +211,11 @@ function detectCollisions( // Normalized to 1 or -1 (can also be directly used to get first piece) const nx = dx / Math.abs(dx); const ny = dy / Math.abs(dy); - // Shifted to get second piece, shift direction based on sign. Reduces the distance - // in its direction by 1 + // Shifted to get second piece, shift direction based on sign const sx = dx - nx; const sy = dy - ny; - // Same-sign horse moves share this square. Will always be 1 diagonal + // Same sign horse moves share this square. Will always be 1 diagonal // of moving piece const square1 = new GridIndices(from.i + nx, from.j + ny); if (robotManager.isRobotAtIndices(square1)) { @@ -246,23 +232,16 @@ function detectCollisions( collisions.push(piece); } break; - - // do we not check the other bots in the way of the L-shaped movement? } } return collisions; } -// note to self: each "i" is a column, each "J" is a row - -// finds the location that a robot that would normally collide should shimmy towards. Move in this case -// is the movement of the roiginal robot that causes the collision function findShimmyLocation( pieceId: string, move: GridMove, collisionType: CollisionType, ): Position { - // get current position of robot that may shimmy const shimmyPos: Position = robotManager.getRobot(pieceId).position; const axisShimmyAmount: number = 1 / 3; switch (collisionType) { @@ -270,9 +249,6 @@ function findShimmyLocation( case CollisionType.HORIZONTAL: { const direction: [number, number] = directionToEdge(move.to); const gridY: number = Math.floor(shimmyPos.y); - // if the collision happened while the original robot was moving horizontally, and - // this robot is on the same row, then move away from the center of the board; otherwise, - // move closer to the center of the board if (gridY === move.to.j) { const augmentY: number = shimmyPos.y + direction[1] * -axisShimmyAmount; @@ -286,9 +262,7 @@ function findShimmyLocation( // Vertical case CollisionType.VERTICAL: { const direction: [number, number] = directionToEdge(move.to); - const gridX: number = Math.floor(shimmyPos.y); - // if vertical collision, and on same row, move away from center; otherwise, - // move closer to center. + const gridX: number = Math.floor(shimmyPos.x); if (gridX === move.to.i) { const augmentX: number = shimmyPos.x + direction[0] * -axisShimmyAmount; @@ -299,24 +273,16 @@ function findShimmyLocation( return new Position(augmentX, shimmyPos.y); } } - //if diagonal or horse, use same idea case CollisionType.DIAGONAL: case CollisionType.HORSE: { const moveDistance: number = 0.5; const signedDistX: number = move.to.i - move.from.i; const signedDistY: number = move.to.j - move.from.j; - // gets total distance of the moving bot that it has to travel const distHypot = Math.hypot(signedDistX, signedDistY); - // distance normalized to a unit vector holding direction const normalX: number = signedDistX / distHypot; const normalY: number = signedDistY / distHypot; - - // gets the vector perpendicular to the normal vector. These are the two options to take const orth1: Position = new Position(-normalY, normalX); const orth2: Position = new Position(normalY, -normalX); - - // adds orthogonal vector to final position, so orthPos1 is a point slightly close to one side - // facing away from the destination, while orthPos2 is the same but in the opposite side. const orthPos1: Position = orth1.add( Position.fromGridIndices(move.to), ); @@ -326,13 +292,10 @@ function findShimmyLocation( // distance calculations :) const val1: Position = shimmyPos.sub(orthPos1); - const dist1: number = Math.hypot(val1.x, val1.y); - const val2: Position = shimmyPos.sub(orthPos2); + const dist1: number = Math.hypot(val1.x, val1.y); const dist2: number = Math.hypot(val2.x, val2.y); - // between the two possible shimmy options, chooses the one that travels less distance, and move it in the direction - // of the orthogonal vector. Basically if there's a vector (from, to) of the Robot, this shimmy moves it away from that line. return dist1 < dist2 ? new Position( shimmyPos.x + orth1.x * moveDistance, @@ -347,8 +310,6 @@ function findShimmyLocation( return new Position(0, 0); } -// constructs the drive command, how much the robot should drive FORWARD from its -// urrent heading function constructDriveCommand( pieceId: string, endLocation: Position, @@ -360,7 +321,6 @@ function constructDriveCommand( return new DriveCommand(pieceId, distance); } -//constructs rotat command of how m uch the robot should rotate function constructRotateCommand( pieceId: string, location: Position, @@ -368,33 +328,11 @@ function constructRotateCommand( ): ReversibleRobotCommand { const robot = robotManager.getRobot(pieceId); const offset = location.sub(startLocation ?? robot.position); - // angle that the offset vector makes, so where to rotate towards const angle = Math.atan2(offset.y, offset.x); console.log("rotate cmd construct", robot.position, offset, angle); return new ReversibleAbsoluteRotateCommand(pieceId, () => angle); } -// takes in the 3 positions, main piece, and computes the sequential command sequence. Made to reduce duplicated code -function getMoveSequence(mainPiece : string, pos1 : Position, pos2 : Position, pos3 : Position) : SequentialCommandGroup -{ - const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); - const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); - const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); - - const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); - const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); - - return new SequentialCommandGroup([ - mainDrive1, - mainTurn2, - mainDrive2, - mainTurn3, - mainDrive3, - ]); -} - -// constructs final command for the robots -//move in this case is the move of the original robot function constructFinalCommand( move: GridMove, driveCommands: DriveCommand[], @@ -405,68 +343,70 @@ function constructFinalCommand( const from = move.from; const robotAtFrom = robotManager.getRobotAtIndices(from); const mainPiece = robotAtFrom.id; - // gets edge closer to center const dirToEdge = directionToEdge(from); - // all the commands needed to do the collision now - const setupCommands: ReversibleRobotCommand[] = []; - - // we should be moving a piece, obviously, else raise error if (mainPiece !== undefined) { console.log("main piece"); const to = move.to; - - let mainDrive: SequentialCommandGroup | DriveCommand - let mainTurn: ReversibleRobotCommand - if (collisionType === CollisionType.HORIZONTAL && numCollisions > 1) { - // y is like the distance we need to travel to get to that edge const y = dirToEdge[1] * 0.5; - - //NOTE: to get MIDDLe of tile, each tile is 1x1, so it's 0.5 - - //first, set position as same horizontal value, but now veritcally in the direction closer to center. const pos1 = new Position(from.i + 0.5, from.j + y + 0.5); - // then, move it horizontally to the right location const pos2 = new Position(to.i + 0.5, from.j + y + 0.5); - //then, just move it a bit down (or up) towards the center of the chosen tile const pos3 = new Position(to.i + 0.5, to.j + 0.5); console.log("from, to ========", from, " ", to); - // create the commands needed - - mainTurn = constructRotateCommand(mainPiece, pos1, null); - - // helper function to clean up the code, reudcing duplicated lines - mainDrive = getMoveSequence(mainPiece, pos1, pos2, pos3) + const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); + const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); + const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); + const mainTurn1 = constructRotateCommand(mainPiece, pos1, null); + const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); + const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); + const setupCommands: ReversibleRobotCommand[] = []; + + const mainDrive: SequentialCommandGroup = + new SequentialCommandGroup([ + mainDrive1, + mainTurn2, + mainDrive2, + mainTurn3, + mainDrive3, + ]); + setupCommands.push(...rotateCommands, mainTurn1, ...driveCommands); + return new MovePiece(setupCommands, mainDrive); } else if ( collisionType === CollisionType.VERTICAL && numCollisions > 1 ) { - //distance to get to the edge needed const x = dirToEdge[0] * 0.5; - // move horizontally to one of the edges on the from square const pos1 = new Position(from.i + x + 0.5, from.j + 0.5); - // move veritcally to the "to" square const pos2 = new Position(from.i + x + 0.5, to.j + 0.5); - // move back in place to the center of the square const pos3 = new Position(to.i + 0.5, to.j + 0.5); console.log("from, to ========", from, " ", to); - - mainTurn = constructRotateCommand(mainPiece, pos1, null); - - // helper function to clean up the code, reudcing duplicated lines - mainDrive = getMoveSequence(mainPiece, pos1, pos2, pos3) - } - //diagonal or knight option - else { + const mainDrive1 = constructDriveCommand(mainPiece, pos1, null); + const mainDrive2 = constructDriveCommand(mainPiece, pos2, pos1); + const mainDrive3 = constructDriveCommand(mainPiece, pos3, pos2); + const mainTurn1 = constructRotateCommand(mainPiece, pos1, null); + const mainTurn2 = constructRotateCommand(mainPiece, pos2, pos1); + const mainTurn3 = constructRotateCommand(mainPiece, pos3, pos2); + const setupCommands: ReversibleRobotCommand[] = []; + + const mainDrive: SequentialCommandGroup = + new SequentialCommandGroup([ + mainDrive1, + mainTurn2, + mainDrive2, + mainTurn3, + mainDrive3, + ]); + setupCommands.push(...rotateCommands, mainTurn1, ...driveCommands); + return new MovePiece(setupCommands, mainDrive); + } else { const pos = new Position(to.i + 0.5, to.j + 0.5); - //just drive directly to the location in question - mainDrive = constructDriveCommand(mainPiece, pos, null); - mainTurn = constructRotateCommand(mainPiece, pos, null); + const mainDrive = constructDriveCommand(mainPiece, pos, null); + const mainTurn = constructRotateCommand(mainPiece, pos, null); + const setupCommands: ReversibleRobotCommand[] = []; + setupCommands.push(...rotateCommands, mainTurn, ...driveCommands); + return new MovePiece(setupCommands, mainDrive); } - - setupCommands.push(...rotateCommands, mainTurn, ...driveCommands); - return new MovePiece(setupCommands, mainDrive); } else { console.log("no main piece"); return new MovePiece(rotateCommands, new SequentialCommandGroup([])); @@ -479,17 +419,13 @@ export function moveMainPiece(move: GridMove): MovePiece { const driveCommands: DriveCommand[] = []; const rotateCommands: ReversibleRobotCommand[] = []; const collisionType = calcCollisionType(move); - // gets collisions const collisions: string[] = detectCollisions(move, collisionType); - //loop through the collisions. Find where they should shimmy, and push the appropriate drive and roate - // commands to get them to that location for (let i = 0; i < collisions.length; i++) { const pieceId = collisions[i]; const location = findShimmyLocation(pieceId, move, collisionType); driveCommands.push(constructDriveCommand(pieceId, location, null)); rotateCommands.push(constructRotateCommand(pieceId, location, null)); } - // with all the data now, create all the final commands needed to handle any collisions with this move return constructFinalCommand( move, driveCommands, @@ -520,8 +456,6 @@ function moveToDeadZone(origin: GridIndices): GridMove { to: new GridIndices(1, origin.j), //("a" + origin[1]) as Square, }; - // check if there's any collisions by doing this - const aboveCollision = detectCollisions( aboveMove, calcCollisionType(aboveMove), @@ -546,13 +480,10 @@ function moveToDeadZone(origin: GridIndices): GridMove { [leftMove, leftCollision], ]; - // sorts by which way has the least collisions, and then choose the one with the fewest collisions to return collisionTuple.sort((a, b) => a[1].length - b[1].length); return collisionTuple[0][0]; } -// based on where it wants to go, returns a vector of what edge it should go towards. The edge doesn't mean the board edge, but the square edge. -// Idea seems to be that its biased towards going to edges of the square closer to the center, it seems, to prevent moving a lot of pieces early on. function directionToEdge(position: GridIndices) { let x = 0; let y = 0; @@ -571,7 +502,6 @@ function directionToEdge(position: GridIndices) { return DirectionTuple; } -// given the array of grid indices, finds the specific grid index function findGridIndicesInArray( array: GridIndices[], obj: GridIndices, @@ -579,89 +509,93 @@ function findGridIndicesInArray( return array.findIndex((o) => o.i === obj.i && o.j === obj.j); } -function decreasingFunction(number : number) -{ - return Math.floor(number / 9) * 9 -} - -function increasingFunction(number : number) -{ - return Math.ceil(number / 9) * 9 -} - -//returns a piece back to its home position function returnToHome(from: GridIndices, id: string): SequentialCommandGroup { //const capturedPiece: GridIndices = GridIndices.squareToGrid(from); const home: GridIndices = robotManager.getRobot(id).homeIndices; const fastestMoveToDeadzone = moveToDeadZone(from); - //gets all the fun commands to move the piece to the deadzone const toDeadzone = moveMainPiece(fastestMoveToDeadzone); - //now that we're in teh deadzone, how we get back const startInDeadzone = fastestMoveToDeadzone.to; + let finalDestination: GridIndices | undefined; - // finds the index values - const startArrayIndex = findGridIndicesInArray( + const checkDirections: [number, number][] = [ + [0, 1], + [1, 0], + [-1, 0], + [0, -1], + ]; + + for (const direction of checkDirections) { + try { + const adjacentToHome = home.addTuple(direction); + if (arrayOfDeadzone.find((dz) => dz.equals(adjacentToHome))) { + finalDestination = adjacentToHome; + break; + } + } catch (e) { + // adjacentToHome is out of bounds, skip check + continue; + } + } + if (!finalDestination) { + throw new Error("WHERE THE HELL ARE YOU GOING"); // real + } + const startInArray = findGridIndicesInArray( arrayOfDeadzone, startInDeadzone, ); - const endArrayIndex = findGridIndicesInArray( + const endInArray = findGridIndicesInArray( arrayOfDeadzone, - home, + finalDestination, ); - - // gets net distance of this - let differenceOfIndex = endArrayIndex - startArrayIndex; + let differenceOfIndex = endInArray - startInArray; - // if we got a negative value, make it positive this way, basically meant that a wraparound was required to travel downwards from startArrayIndex if (differenceOfIndex < 0) { differenceOfIndex += 36; } - // if short distance, go in that direciton. Otherwise, go opposite way since its shorter const botDirectionToHome = differenceOfIndex < 18 ? 1 : -1; console.log( "deadzone array checker", - startArrayIndex, - endArrayIndex, + startInArray, + endInArray, botDirectionToHome, ); - let i = startArrayIndex; + let i = startInArray; const moveCommands: MoveCommand[] = []; - // if already at the destination, don't run this - - const incrementalFunction : Function = botDirectionToHome == 1 ? increasingFunction : decreasingFunction; - - // until we've gotten to our destination do this - while (i !== endArrayIndex) { - if (Math.abs(i - endArrayIndex) < 9) - { - // now head to the final tile + while (i !== endInArray) { + if (arrayOfCornersIndicies.includes(i)) { moveCommands.push( - new AbsoluteMoveCommand(id, new Position(arrayOfDeadzone[endArrayIndex].i + 0.5, arrayOfDeadzone[endArrayIndex].j + 0.5)) + new AbsoluteMoveCommand( + id, + new Position( + arrayOfDeadzone[i].i + 0.5, + arrayOfDeadzone[i].j + 0.5, + ), + ), ); - break; - } - i = incrementalFunction(i); - - let currentPushing = i - - //wrappign aroudn when we reach a bound - if(i === 36) - { - currentPushing = i = 0; - } - else if(i === 0) - { - i = 36; } - // now head to the final tile + i += botDirectionToHome; + if (i < 0) i += 36; + if (i >= 36) i -= 36; + } + if (arrayOfDeadzone[endInArray]) { moveCommands.push( - new AbsoluteMoveCommand(id, new Position(arrayOfDeadzone[currentPushing].i + 0.5, arrayOfDeadzone[currentPushing].j + 0.5)) + new AbsoluteMoveCommand( + id, + new Position( + arrayOfDeadzone[endInArray].i + 0.5, + arrayOfDeadzone[endInArray].j + 0.5, + ), + ), ); } + moveCommands.push( + new AbsoluteMoveCommand(id, new Position(home.i + 0.5, home.j + 0.5)), + ); + const goHome: SequentialCommandGroup = new SequentialCommandGroup([ toDeadzone, ...moveCommands, @@ -702,8 +636,6 @@ export function moveAllRobotsToDefaultPositions( // Sort robots: column by column (left to right, no skipping), then bottom to top within each column // This prevents collisions by ensuring robots in the same column don't interfere with each other - - // this current code only seems to sort by column? const sortedRobots = robotsToMove.sort((a, b) => { const aPos = GridIndices.fromPosition(a.position); const bPos = GridIndices.fromPosition(b.position); @@ -713,7 +645,6 @@ export function moveAllRobotsToDefaultPositions( const allCommands: Command[] = []; - // generates a path to the default square for (const robot of sortedRobots) { const robotCommands = generateRobotPathToDefault( robot, @@ -753,16 +684,11 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { const mainPieceTargets = new Map(); const pawnTargets = new Map(); - // puts each piece into different targets based on the piece type - for (const robot of robotManager.idsToRobots.values()) - { + for (const robot of robotManager.idsToRobots.values()) { const def = robot.defaultIndices; - if (robot.pieceType !== "w_pawn" && robot.pieceType !== "b_pawn") - { + if (def.j === 2 || def.j === 9) { mainPieceTargets.set(robot.id, def); - } - else - { + } else if (def.j === 3 || def.j === 8) { pawnTargets.set(robot.id, def); } } @@ -783,31 +709,36 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { // Home -> Deadzone entry on its side, Along deadzone to file aligned with its row, // Into its pawn row square. Repeat until all pawns are placed. type PawnInfo = { id: string; def: GridIndices; start: GridIndices }; - const leftWhite: (PawnInfo | null)[] = [null, null, null, null]; - const leftBlack: (PawnInfo | null)[] = [null, null, null, null]; - const rightWhite: (PawnInfo | null)[] = [null, null, null, null]; - const rightBlack: (PawnInfo | null)[] = [null, null, null, null]; + const leftWhite: PawnInfo[] = []; + const leftBlack: PawnInfo[] = []; + const rightWhite: PawnInfo[] = []; + const rightBlack: PawnInfo[] = []; - // group pawns into 4 types as mentioned above for (const [robotId, def] of pawnTargets) { - const robot = robotManager.getRobot(robotId); - const start = robot.homeIndices; - - const isWhite = robot.pieceType[0] === "w" + const start = robotManager.getRobot(robotId).homeIndices; + const isWhite = def.j === 3; const sideIsLeft = start.i === 0; - const info: PawnInfo = { id: robotId, def, start }; - // gets its index and palces it where ones closer to the center are farther x - const placementIndex = Math.abs(start.j - (isWhite ? Math.floor(5.5) : Math.ceil(5.5)) ) - let chosenList : (PawnInfo | null)[] = [null]; if (sideIsLeft) { - chosenList = isWhite ? leftWhite : leftBlack; + (isWhite ? leftWhite : leftBlack).push(info); } else { - chosenList = isWhite ? rightWhite : rightBlack; + (isWhite ? rightWhite : rightBlack).push(info); } - chosenList[placementIndex] = info } + // Sort each group center-out by file to funnel from middle outward + const centerOutSort = (a: PawnInfo, b: PawnInfo) => { + const center = 5.5; + const da = Math.abs(a.def.i - center); + const db = Math.abs(b.def.i - center); + if (da !== db) return da - db; + return a.def.i - b.def.i; + }; + leftWhite.sort(centerOutSort); + leftBlack.sort(centerOutSort); + rightWhite.sort(centerOutSort); + rightBlack.sort(centerOutSort); + const pawnBatches: ParallelCommandGroup[] = []; while ( leftWhite.length > 0 || @@ -816,7 +747,7 @@ export function moveAllRobotsHomeToDefaultOptimized(): SequentialCommandGroup { rightBlack.length > 0 ) { const batchSeqs: SequentialCommandGroup[] = []; - const pick = (arr: (PawnInfo | null)[] | undefined) => { + const pick = (arr: PawnInfo[] | undefined) => { if (!arr || arr.length === 0) return; const pawn = arr.shift()!; const dzStart = moveToDeadzoneFromHome(pawn.start); @@ -1257,4 +1188,4 @@ export function materializePath(move: Move): Command { const command = moveMainPiece(gridMove); return command; } -} +} \ No newline at end of file diff --git a/src/server/robot/robot.ts b/src/server/robot/robot.ts index 7c08499a..ef45b4fd 100644 --- a/src/server/robot/robot.ts +++ b/src/server/robot/robot.ts @@ -35,7 +35,7 @@ export class Robot { throw new Error("startHeadingRadians must be a number"); } this._headingRadians = startHeadingRadians; - this._position = position ?? process.env.START_ROBOTS_AT_DEFAULT ? Position.fromGridIndices(homeIndices) : Position.fromGridIndices(defaultIndices); + this._position = position ?? Position.fromGridIndices(homeIndices); this.tunnel = null; this._pieceType = thePieceType; } diff --git a/src/server/utils/env.ts b/src/server/utils/env.ts index ec58fb47..55bfec3a 100644 --- a/src/server/utils/env.ts +++ b/src/server/utils/env.ts @@ -4,9 +4,9 @@ config(); export const IS_DEVELOPMENT = process.env.NODE_ENV === "development"; export const IS_PRODUCTION = !IS_DEVELOPMENT; -export const DO_SAVES = process.env.ENABLE_SAVES === "true"; +export const DO_SAVES = process.env.ENABLE_SAVES === "false"; export const USE_VIRTUAL_ROBOTS = process.env.VIRTUAL_ROBOTS === "true"; -export const START_ROBOTS_AT_DEFAULT = process.env.START_ROBOTS_AT_DEFAULT; +export const START_ROBOTS_AT_DEFAULT = process.env.START_ROBOTS_AT_DEFAULT === "true"; export const PING_INTERVAL = 1000; export const PING_TIMEOUT = 100; From 1e41d478a124d38e998700315d2a6e69f96a5eee Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 18:07:34 -0600 Subject: [PATCH 07/18] alright checked everything, this should be good to push now --- src/server/api/tcp-interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/tcp-interface.ts b/src/server/api/tcp-interface.ts index 04916e54..a2de91d3 100644 --- a/src/server/api/tcp-interface.ts +++ b/src/server/api/tcp-interface.ts @@ -307,11 +307,11 @@ export class TCPServer { unpauseGame(false); } } - tunnel.id = id; tunnel.address = mac; this.connections[id] = tunnel; this.robotManager.createRobotFromId(id).setTunnel(tunnel); + }).bind(this), ); From 4a0c992295818cc9c2bbc73f505d7957840608ff Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 15 Feb 2026 18:32:40 -0600 Subject: [PATCH 08/18] added comments and ran the format + link checker --- src/client/api.ts | 10 +- src/client/debug/simulator.tsx | 4 +- src/server/api/api.ts | 23 ++-- src/server/api/bot-server-config.json | 2 +- src/server/api/game-manager.ts | 4 +- src/server/api/puzzles.ts | 188 +++++++++++++++----------- src/server/api/socket-manager.ts | 4 +- src/server/api/tcp-interface.ts | 1 - src/server/robot/path-materializer.ts | 2 +- src/server/robot/robot-manager.ts | 19 +-- src/server/robot/robot.ts | 8 +- src/server/simulator.ts | 9 +- src/server/utils/env.ts | 3 +- 13 files changed, 155 insertions(+), 122 deletions(-) diff --git a/src/client/api.ts b/src/client/api.ts index b94ec6af..eaf24d27 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -33,10 +33,10 @@ export function useEffectQuery( */ const USE_SSL = window.location.protocol === "https:"; const WS_PROTOCOL = USE_SSL ? "wss" : "ws"; -const PAGE_LOCATION = window.location.pathname +// added info about this, important in specifying socket connections +const PAGE_LOCATION = window.location.pathname; const WEBSOCKET_URL = `${WS_PROTOCOL}://${new URL(window.location.href).host}/ws?page=${encodeURIComponent(PAGE_LOCATION)}`; - /** * A custom hook which allows using a websocket to connect to the server. * @@ -51,7 +51,7 @@ export function useSocket( // handle sending a message and opening it const { sendMessage } = useWebSocket(WEBSOCKET_URL, { onOpen: () => { - console.log(`SOCKET URL IS: ${WEBSOCKET_URL}`) + console.log(`SOCKET URL IS: ${WEBSOCKET_URL}`); console.log("Connection established"); sendMessage(new RegisterWebsocketMessage().toJson()); if (onOpen !== undefined) { @@ -69,8 +69,8 @@ export function useSocket( }, onClose: () => { - console.log("IT'S CLOSED BRO!") - } + console.log("IT'S CLOSED BRO!"); + }, }); // handle how a message is sent diff --git a/src/client/debug/simulator.tsx b/src/client/debug/simulator.tsx index f20c243b..90b7afb8 100644 --- a/src/client/debug/simulator.tsx +++ b/src/client/debug/simulator.tsx @@ -76,9 +76,9 @@ export function Simulator() { // update the simulator when a message comes in useSocket((message) => { - console.log("Any message received!") + console.log("Any message received!"); if (message instanceof SimulatorUpdateMessage) { - console.log("Updating bot!") + console.log("Updating bot!"); dispatch({ type: "UPDATE_ROBOT", payload: { robotId: message.robotId, state: message.location }, diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 8fd9a99e..28430f32 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -100,7 +100,7 @@ async function setupDefaultRobotPositions( export const websocketHandler: WebsocketRequestHandler = (ws, req) => { // on close, delete the cookie id ws.on("close", () => { - console.log("We closed the connection") + console.log("We closed the connection"); socketManager.handleSocketClosed(req.cookies.id); }); @@ -110,12 +110,13 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { console.log("Received message: " + message.toJson()); if (message instanceof RegisterWebsocketMessage) { - console.log(`Register a new socket with request ${req.url}`) + console.log(`Register a new socket with request ${req.url}`); + //find in the url where we specify the page const cutoffIndex = req.url.indexOf("page=") + 5; + // take out that page value, add a delimeter const pageValue = req.url.substring(cutoffIndex) + "|o|o|"; - console.log(req.cookies.id) - const finalSocketId = pageValue.concat(req.cookies.id) - + // add current page to the cookie id + const finalSocketId = pageValue.concat(req.cookies.id); socketManager.registerSocket(finalSocketId, ws); } else if ( @@ -292,7 +293,9 @@ apiRouter.post("/start-puzzle-game", async (req, res) => { const moves = puzzle.moves; const difficulty = puzzle.rating; - console.log(`Fein is ${fen}, moves are ${moves}, difficulty is ${difficulty}`) + console.log( + `Fein is ${fen}, moves are ${moves}, difficulty is ${difficulty}`, + ); if (puzzle.robotDefaultPositions) { // Convert puzzle.robotDefaultPositions from Record to Map @@ -323,10 +326,10 @@ apiRouter.post("/start-puzzle-game", async (req, res) => { !START_ROBOTS_AT_DEFAULT, defaultPositionsMap, ); - } - else - { - throw Error("Should have the default positions set up, but the config is missing.") + } else { + throw Error( + "Should have the default positions set up, but the config is missing.", + ); } setGameManager( new PuzzleGameManager( diff --git a/src/server/api/bot-server-config.json b/src/server/api/bot-server-config.json index f35d8c59..d930ca4a 100644 --- a/src/server/api/bot-server-config.json +++ b/src/server/api/bot-server-config.json @@ -536,4 +536,4 @@ "default_value": 4.375 } ] -} \ No newline at end of file +} diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index 68eaa487..dca8a13b 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -357,8 +357,8 @@ export class PuzzleGameManager extends GameManager { id; if (message instanceof MoveMessage) { //if the move is correct - console.log("Move number is: ", this.moveNumber) - console.log("Number of moves is ", this.moves.length) + console.log("Move number is: ", this.moveNumber); + console.log("Number of moves is ", this.moves.length); // console.log("") if ( this.moves[this.moveNumber].from === message.move.from && diff --git a/src/server/api/puzzles.ts b/src/server/api/puzzles.ts index 4b795238..d57d2b0b 100644 --- a/src/server/api/puzzles.ts +++ b/src/server/api/puzzles.ts @@ -2,9 +2,6 @@ import { type Square } from "chess.js"; import { type Move } from "../../common/game-types"; import config from "./bot-server-config.json"; - - - export interface PuzzleComponents { fen: string; moves: Move[]; @@ -14,98 +11,117 @@ export interface PuzzleComponents { robotDefaultPositions: Record; } +// i actually hate typescript bro +interface BotValue { + attributes?: { + piece_type?: string; + }; + [key: string]: unknown; // other possible keys +} + +// This function takes in the FEIN position recorder, and processes it into which robots should go where. Specifically also +// chooses the correct robot numbers based on their piece types, and does so in about O(n + m) time, where n is # of pieces in +// the FEIN record, and m is the # of chess pieces total const processFEINToDefaultPos = (fein) => { + // only want what comes before the space const feinBoard = fein.split(" ")[0]; - const feinArr = feinBoard.split("/") + // can split the rows by this as the slash is a delimeter in this type of position recorder + const feinArr = feinBoard.split("/"); - const columnRows = ["a", "b", "c", "d", "e", "f", "g", "h"] + // when goign through the columns, can encode them as letters by having this type of array + const columnRows = ["a", "b", "c", "d", "e", "f", "g", "h"]; // store the dict of piece type and position combos, so we can assign robots to them - const piecePositionCombo : Record = {} - - for (let row = 0; row < feinArr.length; row++) - { - let column = 1 - for (const char of feinArr[row]) - { + const piecePositionCombo: Record = {}; + + // loop through the rows + for (let row = 0; row < feinArr.length; row++) { + // for columns, chess doesn't use zero indexing + let column = 1; + // go through each char in the current row + for (const char of feinArr[row]) { // if it's a number, skip ahead by that amount - if (/[0-9]/.test(char)) - { - column += Number(char) + if (/[0-9]/.test(char)) { + column += Number(char); } // means we have a piece to place - else - { - let pieceType = "" + else { + let pieceType = ""; + // lowercase the character for easier checking and also to check if white or black piece const normalizedChar = char.toLowerCase(); // check if its a black piece, or uppercase - if(char === normalizedChar) - { - pieceType = "b_" - } - else - { - pieceType = "w_" + if (char === normalizedChar) { + pieceType = "b_"; + } else { + pieceType = "w_"; } - switch (normalizedChar) - { - case 'p': - pieceType += "pawn" - break - case 'r': - pieceType += "rook" - break - case 'b': - pieceType += "bishop" - break - case 'k': - pieceType += "king" - break - case 'q': - pieceType += "queen" - break - case 'n': - pieceType += "knight" - break + // obvio + switch (normalizedChar) { + case "p": + pieceType += "pawn"; + break; + case "r": + pieceType += "rook"; + break; + case "b": + pieceType += "bishop"; + break; + case "k": + pieceType += "king"; + break; + case "q": + pieceType += "queen"; + break; + case "n": + pieceType += "knight"; + break; default: - pieceType += "none" - break + pieceType += "none"; + break; } - - const position = (columnRows[column - 1] + (8 - row)) - if(pieceType in piecePositionCombo) - { - piecePositionCombo[pieceType].push(position as Square) + // subtract by 1 since arrays are indexed at 0 obviously, for the row that's 8 - row because FEIN lists + // rows top down, but standard chess format lists them bottom up + const position = columnRows[column - 1] + (8 - row); + // if the piece is already in the puzzle, add the position as an extra element so we remember + if (pieceType in piecePositionCombo) { + piecePositionCombo[pieceType].push(position as Square); } - else - { + // otherwise, create the initial array + else { piecePositionCombo[pieceType] = [position as Square]; } - // now that you placed this piece, go up one to the right - column += 1 + // now that you placed this piece, go one to the right + column += 1; } } } - const newBoardConfig: Record = {} - - for(const [key, value] of Object.entries(config as any)) - { - if(key != "tcpServerPort" && key != "bots" && key != "botConfigSchema") - { - const v = value as any; - const botPieceType = v.attributes?.piece_type + const newBoardConfig: Record = {}; + + // loop through all the robot configs to check their piece type + for (const [key, value] of Object.entries( + config as Record, + )) { + // these ones we ignore + if ( + key !== "tcpServerPort" && + key !== "bots" && + key !== "botConfigSchema" + ) { + // kinda needed as we don't know the type of "value"? + const v = value as BotValue; + const botPieceType = v.attributes?.piece_type; // if its a piece we need to place, do that - if(botPieceType in piecePositionCombo) - { + if (botPieceType && botPieceType in piecePositionCombo) { + // put it in the last position recorded, and remove that elemnt from the arr newBoardConfig[key] = piecePositionCombo[botPieceType].pop()!; - if(piecePositionCombo[botPieceType].length == 0) - { - delete piecePositionCombo[botPieceType] + // if array empty, no more positions of that type, so remove it from the dictionary + if (piecePositionCombo[botPieceType].length === 0) { + delete piecePositionCombo[botPieceType]; } } } @@ -114,10 +130,8 @@ const processFEINToDefaultPos = (fein) => { // print for debugging console.log(JSON.stringify(newBoardConfig, null, 2)); - return newBoardConfig -} - - + return newBoardConfig; +}; export const puzzles: Record = { "Puzzle 1": { @@ -125,14 +139,18 @@ export const puzzles: Record = { moves: [{ from: "f5", to: "g5" }], rating: 511, tooltip: "tooltip for puzzle 1", - robotDefaultPositions: processFEINToDefaultPos("8/1p3p1k/8/p1p2Kr1/P2pP3/1P1P4/2P5/8 w - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "8/1p3p1k/8/p1p2Kr1/P2pP3/1P1P4/2P5/8 w - - 0 1", + ), }, "Puzzle 2": { fen: "5rk1/p5pp/4q3/8/1P1P4/2P4P/P2p1RP1/5RK1 w", moves: [{ from: "f2", to: "f8" }], rating: 514, tooltip: "tooltip for puzzle 2", - robotDefaultPositions: processFEINToDefaultPos("5rk1/p5pp/4q3/8/1P1P4/2P4P/P2p1RP1/5RK1 w") + robotDefaultPositions: processFEINToDefaultPos( + "5rk1/p5pp/4q3/8/1P1P4/2P4P/P2p1RP1/5RK1 w", + ), }, "Puzzle 3": { fen: "8/8/8/8/2Prk1p1/2K5/8/5R2 w - - 0 1", @@ -143,7 +161,9 @@ export const puzzles: Record = { ], rating: 1000, tooltip: "tooltip for puzzle 3", - robotDefaultPositions: processFEINToDefaultPos("8/8/8/8/2Prk1p1/2K5/8/5R2 w - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "8/8/8/8/2Prk1p1/2K5/8/5R2 w - - 0 1", + ), }, "Puzzle 4": { fen: "1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1", @@ -154,7 +174,9 @@ export const puzzles: Record = { ], rating: 1000, tooltip: "tooltip", - robotDefaultPositions: processFEINToDefaultPos("1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "1r3k2/R4p2/5Kp1/1p1Pp3/2p1PbP1/2P2P2/4B3/8 b - - 0 1", + ), }, "Puzzle 5": { fen: "r1b3k1/1pq1b1r1/p2p3Q/3Pp3/3p1P2/P2B3P/1PP3P1/1R3RK1 w - - 0 1", @@ -169,7 +191,9 @@ export const puzzles: Record = { ], rating: 2915, tooltip: "tooltip for puzzle 5", - robotDefaultPositions: processFEINToDefaultPos("r1b3k1/1pq1b1r1/p2p3Q/3Pp3/3p1P2/P2B3P/1PP3P1/1R3RK1 w - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "r1b3k1/1pq1b1r1/p2p3Q/3Pp3/3p1P2/P2B3P/1PP3P1/1R3RK1 w - - 0 1", + ), }, //whoever made this puzzle is pretty stupid "Puzzle 6": { @@ -183,7 +207,9 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 6", - robotDefaultPositions: processFEINToDefaultPos("4k3/8/4p3/8/8/4P3/8/4K3 w - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "4k3/8/4p3/8/8/4P3/8/4K3 w - - 0 1", + ), }, "Puzzle 7": { fen: "8/8/3k4/8/8/3K4/8/3R4 w - - 0 1", @@ -194,6 +220,8 @@ export const puzzles: Record = { rating: 1000, tooltip: "tooltip for puzzle 7", - robotDefaultPositions: processFEINToDefaultPos("8/8/3k4/8/8/3K4/8/3R4 w - - 0 1") + robotDefaultPositions: processFEINToDefaultPos( + "8/8/3k4/8/8/3K4/8/3R4 w - - 0 1", + ), }, }; diff --git a/src/server/api/socket-manager.ts b/src/server/api/socket-manager.ts index 4b247e63..8f259766 100644 --- a/src/server/api/socket-manager.ts +++ b/src/server/api/socket-manager.ts @@ -8,7 +8,7 @@ export class SocketManager { constructor(private sockets: Record) {} public registerSocket(id: string, socket: WebSocket): void { - console.log(`Id is: ${id}`) + console.log(`Id is: ${id}`); this.sockets[id] = socket; } @@ -48,7 +48,7 @@ export class SocketManager { */ public sendToAll(message: Message): boolean { const sockets = Object.values(this.sockets); - console.log(`Current list of connections are: ${sockets.length}`) + console.log(`Current list of connections are: ${sockets.length}`); for (const socket of sockets) { socket.send(message.toJson()); } diff --git a/src/server/api/tcp-interface.ts b/src/server/api/tcp-interface.ts index a2de91d3..169a262d 100644 --- a/src/server/api/tcp-interface.ts +++ b/src/server/api/tcp-interface.ts @@ -311,7 +311,6 @@ export class TCPServer { tunnel.address = mac; this.connections[id] = tunnel; this.robotManager.createRobotFromId(id).setTunnel(tunnel); - }).bind(this), ); diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index a07e9958..8a93ea23 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -1188,4 +1188,4 @@ export function materializePath(move: Move): Command { const command = moveMainPiece(gridMove); return command; } -} \ No newline at end of file +} diff --git a/src/server/robot/robot-manager.ts b/src/server/robot/robot-manager.ts index 758e87eb..68dcfd06 100644 --- a/src/server/robot/robot-manager.ts +++ b/src/server/robot/robot-manager.ts @@ -68,10 +68,10 @@ export class RobotManager { robotConfig?.defaultIndices.y, ), robotConfig?.startHeadingRadians * DEGREE, - robotConfig?.attributes.piece_type + robotConfig?.attributes.piece_type, ); this.addRobot(robot); - console.log("We have the following:" + robotConfig?.defaultIndices) + console.log("We have the following:" + robotConfig?.defaultIndices); return robot; } @@ -90,10 +90,13 @@ export class RobotManager { getRobotAtIndices(indices: GridIndices): Robot { const indicesToIds = this.getIndicesToIds(); const robotId = indicesToIds.get(indices.toString()); - const theKeys = indicesToIds.keys() - const keyArr = [...theKeys] - console.log(`Indices is ${indices.toString()}, current id's are,`, keyArr); - + const theKeys = indicesToIds.keys(); + const keyArr = [...theKeys]; + console.log( + `Indices is ${indices.toString()}, current id's are,`, + keyArr, + ); + if (robotId === undefined) { throw new Error("Failed to find robot at indices " + indices); } @@ -127,14 +130,14 @@ export const robotManager = new RobotManager( new GridIndices(0, 5), new GridIndices(5, 3), 90 * DEGREE, - "w_pawn" + "w_pawn", ), new Robot( "robot-4", new GridIndices(5, 0), new GridIndices(5, 2), 90 * DEGREE, - "w_queen" + "w_queen", ), ], ); diff --git a/src/server/robot/robot.ts b/src/server/robot/robot.ts index ef45b4fd..5d1e3f3d 100644 --- a/src/server/robot/robot.ts +++ b/src/server/robot/robot.ts @@ -12,7 +12,7 @@ export class Robot { private _headingRadians: number; private _position: Position; protected tunnel: BotTunnel | null; - protected _pieceType: String; + protected _pieceType: string; constructor( public readonly id: string, @@ -25,7 +25,7 @@ export class Robot { */ public readonly defaultIndices: GridIndices, public readonly startHeadingRadians: number = 0, - public readonly thePieceType: String, + public readonly thePieceType: string, position?: Position, ) { if ( @@ -56,11 +56,11 @@ export class Robot { this._headingRadians = headingRadians; } - public get pieceType(): String { + public get pieceType(): string { return this._pieceType; } - public set pieceType(thePieceType: String) { + public set pieceType(thePieceType: string) { this._pieceType = thePieceType; } diff --git a/src/server/simulator.ts b/src/server/simulator.ts index f781e79e..0c88a6bd 100644 --- a/src/server/simulator.ts +++ b/src/server/simulator.ts @@ -149,7 +149,6 @@ export class VirtualBotTunnel extends BotTunnel { } async send(packet: Packet): Promise { - const packetId = randomUUID(); let stack: StackFrame[] = []; try { @@ -208,7 +207,7 @@ export class VirtualBotTunnel extends BotTunnel { { ...packet, packetId }, stack, ); - console.log("Pushing message!") + console.log("Pushing message!"); VirtualBotTunnel.messages.push({ ts: new Date(), message }); socketManager.sendToAll(message); }); @@ -224,7 +223,7 @@ export class VirtualRobot extends Robot { homeIndices: GridIndices, defaultIndices: GridIndices, headingRadians: number, - pieceType: String + pieceType: string, ) { super(id, homeIndices, defaultIndices, headingRadians, pieceType); this.tunnel = new VirtualBotTunnel(id, headingRadians, this.position); @@ -245,7 +244,7 @@ export class VirtualRobot extends Robot { export const virtualRobots = createVirtualRobots(); function createVirtualRobots() { - console.log("Creating virtual bots!") + console.log("Creating virtual bots!"); const virtualBotIds = Array(32) .fill(undefined) .map((_, i) => `robot-${(i + 1).toString()}`); @@ -266,7 +265,7 @@ function createVirtualRobots() { realRobotConfig.defaultIndices.y, ), realRobotConfig.startHeadingRadians, - realRobotConfig.attributes.piece_type + realRobotConfig.attributes.piece_type, ), ] as const; }), diff --git a/src/server/utils/env.ts b/src/server/utils/env.ts index 55bfec3a..6211b3ce 100644 --- a/src/server/utils/env.ts +++ b/src/server/utils/env.ts @@ -6,7 +6,8 @@ export const IS_DEVELOPMENT = process.env.NODE_ENV === "development"; export const IS_PRODUCTION = !IS_DEVELOPMENT; export const DO_SAVES = process.env.ENABLE_SAVES === "false"; export const USE_VIRTUAL_ROBOTS = process.env.VIRTUAL_ROBOTS === "true"; -export const START_ROBOTS_AT_DEFAULT = process.env.START_ROBOTS_AT_DEFAULT === "true"; +export const START_ROBOTS_AT_DEFAULT = + process.env.START_ROBOTS_AT_DEFAULT === "true"; export const PING_INTERVAL = 1000; export const PING_TIMEOUT = 100; From 1449c69a58e9189053986823208a4f6e4db12178 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Fri, 20 Feb 2026 18:15:55 -0600 Subject: [PATCH 09/18] finished making changes --- src/client/api.ts | 1 - src/client/debug/simulator.tsx | 1 - src/common/game-types.ts | 10 ---------- src/server/api/game-manager.ts | 3 --- src/server/simulator.ts | 1 - 5 files changed, 16 deletions(-) diff --git a/src/client/api.ts b/src/client/api.ts index eaf24d27..4ae4ecc1 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -51,7 +51,6 @@ export function useSocket( // handle sending a message and opening it const { sendMessage } = useWebSocket(WEBSOCKET_URL, { onOpen: () => { - console.log(`SOCKET URL IS: ${WEBSOCKET_URL}`); console.log("Connection established"); sendMessage(new RegisterWebsocketMessage().toJson()); if (onOpen !== undefined) { diff --git a/src/client/debug/simulator.tsx b/src/client/debug/simulator.tsx index 90b7afb8..65ca5c09 100644 --- a/src/client/debug/simulator.tsx +++ b/src/client/debug/simulator.tsx @@ -76,7 +76,6 @@ export function Simulator() { // update the simulator when a message comes in useSocket((message) => { - console.log("Any message received!"); if (message instanceof SimulatorUpdateMessage) { console.log("Updating bot!"); dispatch({ diff --git a/src/common/game-types.ts b/src/common/game-types.ts index 1d8eaaaa..2512cf73 100644 --- a/src/common/game-types.ts +++ b/src/common/game-types.ts @@ -33,16 +33,6 @@ export function oppositeSide(side: Side) { return side === Side.WHITE ? Side.BLACK : Side.WHITE; } -// I don't think we need this anymore. DIE! -// /** -// * get the robot's start heading based on the side it is on -// * @param side - the current side -// * @returns angle in radians -// */ -// export function getStartHeading(side: Side) { -// return side === Side.WHITE ? 90 * DEGREE : 270 * DEGREE; -// } - /** * holds the piece side, type, robot, and square */ diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index dca8a13b..7b6a9e61 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -357,9 +357,6 @@ export class PuzzleGameManager extends GameManager { id; if (message instanceof MoveMessage) { //if the move is correct - console.log("Move number is: ", this.moveNumber); - console.log("Number of moves is ", this.moves.length); - // console.log("") if ( this.moves[this.moveNumber].from === message.move.from && this.moves[this.moveNumber].to === message.move.to && diff --git a/src/server/simulator.ts b/src/server/simulator.ts index 0c88a6bd..7e41964d 100644 --- a/src/server/simulator.ts +++ b/src/server/simulator.ts @@ -207,7 +207,6 @@ export class VirtualBotTunnel extends BotTunnel { { ...packet, packetId }, stack, ); - console.log("Pushing message!"); VirtualBotTunnel.messages.push({ ts: new Date(), message }); socketManager.sendToAll(message); }); From 2da6c0e6e168809e60fed8c43250120ffe9e32b5 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sat, 21 Feb 2026 14:16:31 -0600 Subject: [PATCH 10/18] fixed two issues --- src/client/puzzle/setup-puzzle.tsx | 2 -- src/client/setup/setup-base.tsx | 2 +- src/server/api/api.ts | 35 +++++++++++++++----------- src/server/api/client-manager.ts | 33 +++++++++++++----------- src/server/api/game-manager.ts | 3 +++ src/server/api/socket-manager.ts | 40 ++++++++++++++++++++---------- 6 files changed, 71 insertions(+), 44 deletions(-) diff --git a/src/client/puzzle/setup-puzzle.tsx b/src/client/puzzle/setup-puzzle.tsx index 8355dbb4..7b563529 100644 --- a/src/client/puzzle/setup-puzzle.tsx +++ b/src/client/puzzle/setup-puzzle.tsx @@ -29,12 +29,10 @@ export function SetupPuzzle() { } return ( - - ); } diff --git a/src/client/setup/setup-base.tsx b/src/client/setup/setup-base.tsx index 3a5b396f..8e678dc1 100644 --- a/src/client/setup/setup-base.tsx +++ b/src/client/setup/setup-base.tsx @@ -46,7 +46,7 @@ export function SetupBase(props: SetupBaseProps): JSX.Element { - + ); } diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 2ae01704..615f36da 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -117,8 +117,8 @@ let canReloadQueue = true; export const websocketHandler: WebsocketRequestHandler = (ws, req) => { // on close, delete the cookie id ws.on("close", () => { - console.log("We closed the connection"); - socketManager.handleSocketClosed(req.cookies.id); + console.log(`We closed the connection of ${req.cookies.id}`); + socketManager.handleSocketClosed(req.cookies.id, ws); //if you reload and the game is over if (gameManager?.isGameEnded() && canReloadQueue) { @@ -172,7 +172,7 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { //wait in case the client is just reloading or disconnected instead of leaving setTimeout(() => { - if (socketManager.getSocket(req.cookies.id) === undefined) { + if (socketManager.getSockets(req.cookies.id) === undefined) { //remove the person from the queue to free up space queue.popInd(queue.find(req.cookies.id)); names.delete(req.cookies.id); @@ -228,16 +228,17 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { const message = parseMessage(data.toString()); console.log("Received message: " + message.toJson()); + //find in the url where we specify the page + const cutoffIndex = req.url.indexOf("page=") + 5; + // take out that page value, add a delimeter + const pageString = req.url.substring(cutoffIndex); + // // add current page to the cookie id + // const finalSocketId = pageValue.concat(req.cookies.id); + if (message instanceof RegisterWebsocketMessage) { - console.log(`Register a new socket with request ${req.url}`); - //find in the url where we specify the page - const cutoffIndex = req.url.indexOf("page=") + 5; - // take out that page value, add a delimeter - const pageValue = req.url.substring(cutoffIndex) + "|o|o|"; - // add current page to the cookie id - const finalSocketId = pageValue.concat(req.cookies.id); - - socketManager.registerSocket(finalSocketId, ws); + console.log(`Register a new socket with request ${req.cookies.id}`); + + socketManager.registerSocket(req.cookies.id, ws); } else if ( message instanceof GameInterruptedMessage || message instanceof MoveMessage || @@ -247,6 +248,10 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { message instanceof GameEndMessage ) { // TODO: Handle game manager not existing + if(gameManager == null) + { + console.log("BRUHHH") + } await gameManager?.handleMessage(message, req.cookies.id); } else if (message instanceof DriveRobotMessage) { await doDriveRobot(message); @@ -255,8 +260,9 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { } else if (message instanceof JoinQueue) { console.log("So we got the join message"); // this was initially !isPlayer, shouldn't it be isPlayer? - if (!clientManager.isPlayer(req.cookies.id)) { - if (queue.find(req.cookies.id) === undefined) { + if (pageString.indexOf("debug") != -1 && !clientManager.isPlayer(req.cookies.id)) { + if (queue.find(req.cookies.id) === undefined) + { queue.insert(req.cookies.id, 0); } names.set(req.cookies.id, message.playerName); @@ -373,6 +379,7 @@ apiRouter.get("/game-state", (req, res) => { * returns a success message */ apiRouter.post("/start-computer-game", async (req, res) => { + console.log("start comp game") canReloadQueue = true; const side = req.query.side as Side; const difficulty = parseInt(req.query.difficulty as string) as Difficulty; diff --git a/src/server/api/client-manager.ts b/src/server/api/client-manager.ts index b0892446..c688cc7f 100644 --- a/src/server/api/client-manager.ts +++ b/src/server/api/client-manager.ts @@ -20,9 +20,9 @@ export class ClientManager { * get the host's socket * @returns the host socket */ - public getHostSocket(): WebSocket | undefined { + public getHostSocket(): Set | undefined { if (this.hostId !== undefined) { - return this.socketManager.getSocket(this.hostId); + return this.socketManager.getSockets(this.hostId); } return undefined; } @@ -33,11 +33,11 @@ export class ClientManager { * @returns if the socket was found */ public sendToHost(message: Message): boolean { - const socket = this.getHostSocket(); - if (socket !== undefined) { - socket.send(message.toJson()); + const sockets = this.getHostSocket(); + if (sockets !== undefined) { + for (const socket of sockets) socket.send(message.toJson()); } - return socket !== undefined; + return sockets !== undefined; } /** @@ -46,11 +46,11 @@ export class ClientManager { * @returns if the socket was found */ public sendToClient(message: Message): boolean { - const socket = this.getClientSocket(); - if (socket !== undefined) { - socket.send(message.toJson()); + const sockets = this.getClientSocket(); + if (sockets !== undefined) { + for(const socket of sockets) socket.send(message.toJson()); } - return socket !== undefined; + return sockets !== undefined; } /** @@ -61,8 +61,13 @@ export class ClientManager { public sendToSpectators(message: Message): boolean { if (this.spectatorIds.size !== 0) { for (const item of this.spectatorIds) { - if (this.socketManager.getSocket(item)) - this.socketManager.getSocket(item).send(message.toJson()); + const potentialSocket = this.socketManager.getSockets(item) + if (potentialSocket != null) { + for (const socket of potentialSocket) + { + socket.send(message.toJson()); + } + } } return true; } @@ -73,9 +78,9 @@ export class ClientManager { * get the client socket * @returns the socket of the client */ - public getClientSocket(): WebSocket | undefined { + public getClientSocket(): Set | undefined { if (this.clientId !== undefined) { - return this.socketManager.getSocket(this.clientId); + return this.socketManager.getSockets(this.clientId); } return undefined; } diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index 084744a3..db5f83fc 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -119,6 +119,8 @@ export class HumanGameManager extends GameManager { * @param id - id of the sender */ public async handleMessage(message: Message, id: string): Promise { + console.log("handling message") + // check which type the id is const clientType = this.clientManager.getClientType(id); let sendToPlayer: SendMessage; @@ -148,6 +150,7 @@ export class HumanGameManager extends GameManager { const ids = this.clientManager.getIds(); const currentSave = SaveManager.loadGame(id); // update the internal chess object if it is a move massage and game not paused + console.log("up to here") if (message instanceof MoveMessage && !gamePaused) { // Call path materializer and send to bots const command = materializePath(message.move); diff --git a/src/server/api/socket-manager.ts b/src/server/api/socket-manager.ts index 8f259766..7276d90d 100644 --- a/src/server/api/socket-manager.ts +++ b/src/server/api/socket-manager.ts @@ -1,31 +1,41 @@ -import type WebSocket from "ws"; +import WebSocket from "ws"; import type { Message } from "../../common/message/message"; /** * A class which maps player client ids to their corresponding websocket (if any). */ export class SocketManager { - constructor(private sockets: Record) {} + constructor(private sockets: Record>) {} public registerSocket(id: string, socket: WebSocket): void { + if(this.sockets[id] == null) + { + this.sockets[id] = new Set() + } console.log(`Id is: ${id}`); - this.sockets[id] = socket; + this.sockets[id].add(socket); } /** * deletes the socket at the provided id * @param id - id to be deleted */ - public handleSocketClosed(id: string): void { - delete this.sockets[id]; + public handleSocketClosed(id: string, socket : WebSocket): void { + // find if the socket exists and is in the set for that user + if (this.sockets[id] && this.sockets[id].has(socket)) { + // if so, KILL IT! + this.sockets[id].delete(socket); + return; + } } /** - * gets the socket at the provided id + * gets the socket at the provided id, which may be multiple if they have multiple tabs open * @param id - id of the desired socket */ - public getSocket(id: string): WebSocket { - return this.sockets[id]; + public getSockets(id: string): Set | undefined { + if (this.sockets[id]) return this.sockets[id]; + return undefined; } /** @@ -33,9 +43,9 @@ export class SocketManager { * Returns true if the message was sent successfully, and false otherwise. */ public sendToSocket(id: string, message: Message): boolean { - const socket = this.getSocket(id); - if (socket !== undefined) { - socket.send(message.toJson()); + const sockets = this.getSockets(id); + if (sockets !== undefined) { + for (const socket of sockets) socket.send(message.toJson()); return true; } return false; @@ -49,8 +59,12 @@ export class SocketManager { public sendToAll(message: Message): boolean { const sockets = Object.values(this.sockets); console.log(`Current list of connections are: ${sockets.length}`); - for (const socket of sockets) { - socket.send(message.toJson()); + for (const socketSet of sockets) + { + for(const socket of socketSet ) + { + socket.send(message.toJson()); + } } return true; } From bf13bd49d3712ff838ac0de467509e5b3a94d7fe Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sat, 21 Feb 2026 14:19:57 -0600 Subject: [PATCH 11/18] queue works! --- src/server/api/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 615f36da..6a9d1ae6 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -260,7 +260,7 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { } else if (message instanceof JoinQueue) { console.log("So we got the join message"); // this was initially !isPlayer, shouldn't it be isPlayer? - if (pageString.indexOf("debug") != -1 && !clientManager.isPlayer(req.cookies.id)) { + if (pageString.indexOf("debug") == -1 && !clientManager.isPlayer(req.cookies.id)) { if (queue.find(req.cookies.id) === undefined) { queue.insert(req.cookies.id, 0); From 3ea21fce986971010003ecf63e094223bcb8f736 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sat, 21 Feb 2026 14:26:01 -0600 Subject: [PATCH 12/18] fixed lint and format error --- src/client/puzzle/setup-puzzle.tsx | 11 +++++------ src/client/setup/setup-base.tsx | 2 +- src/server/api/api.ts | 15 ++++++++------- src/server/api/client-manager.ts | 9 ++++----- src/server/api/game-manager.ts | 6 +++--- src/server/api/socket-manager.ts | 15 ++++++--------- 6 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/client/puzzle/setup-puzzle.tsx b/src/client/puzzle/setup-puzzle.tsx index 7b563529..d84c7da9 100644 --- a/src/client/puzzle/setup-puzzle.tsx +++ b/src/client/puzzle/setup-puzzle.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import { SetupBase } from "../setup/setup-base"; import { SelectPuzzle } from "./select-puzzle"; import { NonIdealState, Spinner } from "@blueprintjs/core"; import { get, useEffectQuery } from "../api"; @@ -29,10 +28,10 @@ export function SetupPuzzle() { } return ( - + ); } diff --git a/src/client/setup/setup-base.tsx b/src/client/setup/setup-base.tsx index 8e678dc1..3a5b396f 100644 --- a/src/client/setup/setup-base.tsx +++ b/src/client/setup/setup-base.tsx @@ -46,7 +46,7 @@ export function SetupBase(props: SetupBaseProps): JSX.Element { - + ); } diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 6a9d1ae6..5a416751 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -248,9 +248,8 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { message instanceof GameEndMessage ) { // TODO: Handle game manager not existing - if(gameManager == null) - { - console.log("BRUHHH") + if (gameManager === null) { + console.log("BRUHHH"); } await gameManager?.handleMessage(message, req.cookies.id); } else if (message instanceof DriveRobotMessage) { @@ -260,9 +259,11 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { } else if (message instanceof JoinQueue) { console.log("So we got the join message"); // this was initially !isPlayer, shouldn't it be isPlayer? - if (pageString.indexOf("debug") == -1 && !clientManager.isPlayer(req.cookies.id)) { - if (queue.find(req.cookies.id) === undefined) - { + if ( + pageString.indexOf("debug") === -1 && + !clientManager.isPlayer(req.cookies.id) + ) { + if (queue.find(req.cookies.id) === undefined) { queue.insert(req.cookies.id, 0); } names.set(req.cookies.id, message.playerName); @@ -379,7 +380,7 @@ apiRouter.get("/game-state", (req, res) => { * returns a success message */ apiRouter.post("/start-computer-game", async (req, res) => { - console.log("start comp game") + console.log("start comp game"); canReloadQueue = true; const side = req.query.side as Side; const difficulty = parseInt(req.query.difficulty as string) as Difficulty; diff --git a/src/server/api/client-manager.ts b/src/server/api/client-manager.ts index c688cc7f..89416bb4 100644 --- a/src/server/api/client-manager.ts +++ b/src/server/api/client-manager.ts @@ -48,7 +48,7 @@ export class ClientManager { public sendToClient(message: Message): boolean { const sockets = this.getClientSocket(); if (sockets !== undefined) { - for(const socket of sockets) socket.send(message.toJson()); + for (const socket of sockets) socket.send(message.toJson()); } return sockets !== undefined; } @@ -61,10 +61,9 @@ export class ClientManager { public sendToSpectators(message: Message): boolean { if (this.spectatorIds.size !== 0) { for (const item of this.spectatorIds) { - const potentialSocket = this.socketManager.getSockets(item) - if (potentialSocket != null) { - for (const socket of potentialSocket) - { + const potentialSocket = this.socketManager.getSockets(item); + if (potentialSocket !== null && potentialSocket !== undefined) { + for (const socket of potentialSocket) { socket.send(message.toJson()); } } diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index db5f83fc..697deed4 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -119,8 +119,8 @@ export class HumanGameManager extends GameManager { * @param id - id of the sender */ public async handleMessage(message: Message, id: string): Promise { - console.log("handling message") - + console.log("handling message"); + // check which type the id is const clientType = this.clientManager.getClientType(id); let sendToPlayer: SendMessage; @@ -150,7 +150,7 @@ export class HumanGameManager extends GameManager { const ids = this.clientManager.getIds(); const currentSave = SaveManager.loadGame(id); // update the internal chess object if it is a move massage and game not paused - console.log("up to here") + console.log("up to here"); if (message instanceof MoveMessage && !gamePaused) { // Call path materializer and send to bots const command = materializePath(message.move); diff --git a/src/server/api/socket-manager.ts b/src/server/api/socket-manager.ts index 7276d90d..84648998 100644 --- a/src/server/api/socket-manager.ts +++ b/src/server/api/socket-manager.ts @@ -1,4 +1,4 @@ -import WebSocket from "ws"; +import type WebSocket from "ws"; import type { Message } from "../../common/message/message"; /** @@ -8,9 +8,8 @@ export class SocketManager { constructor(private sockets: Record>) {} public registerSocket(id: string, socket: WebSocket): void { - if(this.sockets[id] == null) - { - this.sockets[id] = new Set() + if (this.sockets[id] === null) { + this.sockets[id] = new Set(); } console.log(`Id is: ${id}`); this.sockets[id].add(socket); @@ -20,7 +19,7 @@ export class SocketManager { * deletes the socket at the provided id * @param id - id to be deleted */ - public handleSocketClosed(id: string, socket : WebSocket): void { + public handleSocketClosed(id: string, socket: WebSocket): void { // find if the socket exists and is in the set for that user if (this.sockets[id] && this.sockets[id].has(socket)) { // if so, KILL IT! @@ -59,10 +58,8 @@ export class SocketManager { public sendToAll(message: Message): boolean { const sockets = Object.values(this.sockets); console.log(`Current list of connections are: ${sockets.length}`); - for (const socketSet of sockets) - { - for(const socket of socketSet ) - { + for (const socketSet of sockets) { + for (const socket of socketSet) { socket.send(message.toJson()); } } From e114312bcfe49429ffcbc51c4d06342d3a3cbdb8 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sat, 21 Feb 2026 14:28:42 -0600 Subject: [PATCH 13/18] ok now fixed --- src/server/api/api.ts | 4 ++-- src/server/api/socket-manager.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 5a416751..65e93395 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -248,7 +248,7 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { message instanceof GameEndMessage ) { // TODO: Handle game manager not existing - if (gameManager === null) { + if (gameManager === null || gameManager === undefined) { console.log("BRUHHH"); } await gameManager?.handleMessage(message, req.cookies.id); @@ -362,7 +362,7 @@ apiRouter.get("/client-information", async (req, res) => { * returns an object with the side, game pgn, and the game end reason */ apiRouter.get("/game-state", (req, res) => { - if (gameManager === null) { + if (gameManager === null || gameManager === undefined) { console.warn("Invalid attempt to fetch game state"); return res.status(400).send({ message: "No game is currently active" }); } diff --git a/src/server/api/socket-manager.ts b/src/server/api/socket-manager.ts index 84648998..302c6582 100644 --- a/src/server/api/socket-manager.ts +++ b/src/server/api/socket-manager.ts @@ -8,7 +8,7 @@ export class SocketManager { constructor(private sockets: Record>) {} public registerSocket(id: string, socket: WebSocket): void { - if (this.sockets[id] === null) { + if (this.sockets[id] === null || this.sockets[id] === undefined) { this.sockets[id] = new Set(); } console.log(`Id is: ${id}`); From 7075c51570c9c70d04eae3ebbac463ccffa47c1d Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 22 Feb 2026 22:40:26 -0600 Subject: [PATCH 14/18] found an error with black side queen castling lol. Fixed now. I really should get to working on embedded stuff --- src/server/robot/path-materializer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index f533363d..a9edce68 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -1168,11 +1168,13 @@ export function materializePath(move: Move): Command { rookPiece.id, Position.fromGridIndices(new GridIndices(7, 2)), ); - } else { + } + // black side castling + else { rookPiece = robotManager.getRobotAtIndices(new GridIndices(9, 9)); kingMove = new AbsoluteMoveCommand( robotManager.getRobotAtIndices(moveToGridMove(move).from).id, - Position.fromGridIndices(new GridIndices(9, 8)), + Position.fromGridIndices(new GridIndices(8, 9)), ); rookMove1 = new AbsoluteMoveCommand( rookPiece.id, From 0d39d95f89f66bd2d12122a3a5295af964393e2c Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 22 Feb 2026 23:38:19 -0600 Subject: [PATCH 15/18] a couple qualms about the shimmying algo, but it's not blatantly obvious the peces are slamming into each other or anything like that at least --- src/client/editor/editor.tsx | 2 +- src/server/api/game-manager.ts | 2 +- src/server/robot/path-materializer.ts | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/client/editor/editor.tsx b/src/client/editor/editor.tsx index fc9b2052..1bf63b4c 100644 --- a/src/client/editor/editor.tsx +++ b/src/client/editor/editor.tsx @@ -275,7 +275,7 @@ export function Editor() { startIdx, endIdx + 1, ); - console.log(selectedEvents); + // console.log(selectedEvents); await navigator.clipboard.writeText( JSON.stringify(selectedEvents), diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index 697deed4..baf3d522 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -158,7 +158,7 @@ export class HumanGameManager extends GameManager { this.chess.makeMove(message.move); console.log("running executor"); - console.dir(command, { depth: null }); + // console.dir(command, { depth: null }); await executor.execute(command).catch((reason) => { setPaused(true); console.log(reason); diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index a9edce68..0b62acdc 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -243,7 +243,7 @@ function findShimmyLocation( collisionType: CollisionType, ): Position { const shimmyPos: Position = robotManager.getRobot(pieceId).position; - const axisShimmyAmount: number = 1 / 3; + const axisShimmyAmount: number = 1 / 2; switch (collisionType) { // Horizontal case CollisionType.HORIZONTAL: { @@ -490,6 +490,8 @@ function moveToDeadZone(origin: GridIndices): GridMove { ]; collisionTuple.sort((a, b) => a[1].length - b[1].length); + console.log("Collision decision:") + console.log(collisionTuple[0]) return collisionTuple[0][0]; } @@ -522,7 +524,7 @@ function returnToHome(from: GridIndices, id: string): SequentialCommandGroup { //const capturedPiece: GridIndices = GridIndices.squareToGrid(from); const home: GridIndices = robotManager.getRobot(id).homeIndices; const fastestMoveToDeadzone = moveToDeadZone(from); - const toDeadzone = moveMainPiece(fastestMoveToDeadzone, true); + const toDeadzone = moveMainPiece(fastestMoveToDeadzone, false); const startInDeadzone = fastestMoveToDeadzone.to; let finalDestination: GridIndices | undefined; From 078db32161936385d5030ba6b973cda5a89acf68 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Sun, 22 Feb 2026 23:39:09 -0600 Subject: [PATCH 16/18] add formatting --- src/server/robot/path-materializer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index 0b62acdc..7b72cb0a 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -490,8 +490,8 @@ function moveToDeadZone(origin: GridIndices): GridMove { ]; collisionTuple.sort((a, b) => a[1].length - b[1].length); - console.log("Collision decision:") - console.log(collisionTuple[0]) + console.log("Collision decision:"); + console.log(collisionTuple[0]); return collisionTuple[0][0]; } @@ -1171,7 +1171,7 @@ export function materializePath(move: Move): Command { Position.fromGridIndices(new GridIndices(7, 2)), ); } - // black side castling + // black side castling else { rookPiece = robotManager.getRobotAtIndices(new GridIndices(9, 9)); kingMove = new AbsoluteMoveCommand( From c12d8ffd2b75e264df1b4fd638c4431e4bb77599 Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Mon, 23 Feb 2026 17:47:25 -0600 Subject: [PATCH 17/18] goo goo ga ga --- src/server/api/api.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 65e93395..412b7977 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -247,10 +247,6 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { message instanceof GameFinishedMessage || message instanceof GameEndMessage ) { - // TODO: Handle game manager not existing - if (gameManager === null || gameManager === undefined) { - console.log("BRUHHH"); - } await gameManager?.handleMessage(message, req.cookies.id); } else if (message instanceof DriveRobotMessage) { await doDriveRobot(message); From 4763e687fb4bda12c6d59b9a8e9d1d058c33b7ab Mon Sep 17 00:00:00 2001 From: MahdMalik Date: Mon, 23 Feb 2026 18:15:14 -0600 Subject: [PATCH 18/18] goo goo ga ga --- src/server/api/api.ts | 10 ++-------- src/server/api/game-manager.ts | 2 +- src/server/robot/path-materializer.ts | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/server/api/api.ts b/src/server/api/api.ts index 412b7977..82096c1a 100644 --- a/src/server/api/api.ts +++ b/src/server/api/api.ts @@ -228,10 +228,7 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { const message = parseMessage(data.toString()); console.log("Received message: " + message.toJson()); - //find in the url where we specify the page - const cutoffIndex = req.url.indexOf("page=") + 5; // take out that page value, add a delimeter - const pageString = req.url.substring(cutoffIndex); // // add current page to the cookie id // const finalSocketId = pageValue.concat(req.cookies.id); @@ -255,10 +252,7 @@ export const websocketHandler: WebsocketRequestHandler = (ws, req) => { } else if (message instanceof JoinQueue) { console.log("So we got the join message"); // this was initially !isPlayer, shouldn't it be isPlayer? - if ( - pageString.indexOf("debug") === -1 && - !clientManager.isPlayer(req.cookies.id) - ) { + if (!clientManager.isPlayer(req.cookies.id)) { if (queue.find(req.cookies.id) === undefined) { queue.insert(req.cookies.id, 0); } @@ -358,7 +352,7 @@ apiRouter.get("/client-information", async (req, res) => { * returns an object with the side, game pgn, and the game end reason */ apiRouter.get("/game-state", (req, res) => { - if (gameManager === null || gameManager === undefined) { + if (gameManager === null) { console.warn("Invalid attempt to fetch game state"); return res.status(400).send({ message: "No game is currently active" }); } diff --git a/src/server/api/game-manager.ts b/src/server/api/game-manager.ts index baf3d522..e72fb2f4 100644 --- a/src/server/api/game-manager.ts +++ b/src/server/api/game-manager.ts @@ -119,7 +119,7 @@ export class HumanGameManager extends GameManager { * @param id - id of the sender */ public async handleMessage(message: Message, id: string): Promise { - console.log("handling message"); + // console.log("handling message"); // check which type the id is const clientType = this.clientManager.getClientType(id); diff --git a/src/server/robot/path-materializer.ts b/src/server/robot/path-materializer.ts index 7b72cb0a..189d7ff5 100644 --- a/src/server/robot/path-materializer.ts +++ b/src/server/robot/path-materializer.ts @@ -524,7 +524,7 @@ function returnToHome(from: GridIndices, id: string): SequentialCommandGroup { //const capturedPiece: GridIndices = GridIndices.squareToGrid(from); const home: GridIndices = robotManager.getRobot(id).homeIndices; const fastestMoveToDeadzone = moveToDeadZone(from); - const toDeadzone = moveMainPiece(fastestMoveToDeadzone, false); + const toDeadzone = moveMainPiece(fastestMoveToDeadzone); const startInDeadzone = fastestMoveToDeadzone.to; let finalDestination: GridIndices | undefined;