Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chessServer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 50 additions & 6 deletions chessServer/src/managers/EventHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ const registerSocketHandlers = (socket, io) => {
socket.on("newPuzzle", (msg) => {
try {
const parsed = JSON.parse(msg);
console.log('data',parsed, msg);
// create the new puzzle
gameManager.createOrJoinPuzzle({
student: parsed.student,
mentor: parsed.mentor,
role: parsed.role,
socketId: socket.id
socketId: socket.id,
credentials: parsed.credentials,
}, io);
}
catch (err) {
Expand All @@ -63,17 +65,59 @@ const registerSocketHandlers = (socket, io) => {
* Handles player move request
* Expected payload: { from, to }
*/
socket.on("move", (msg) => {
socket.on("move", async (msg) => {
try {
const { from, to } = JSON.parse(msg);
const result = gameManager.makeMove(socket.id, from, to);
gameManager.broadcastBoardState(result, io);
const { from, to, computerMove, username, credentials } = JSON.parse(msg);
const res = await gameManager.makeMove(socket.id, from, to);
const state = res.result;
gameManager.broadcastBoardState(res.result, io);
console.log('Move: ', res);
if(!computerMove && credentials) {
const activityEvents = res.activityEvents;
if (activityEvents && activityEvents.length > 0) {
const studentId = state.studentId;
const payload = {
activities: activityEvents,
lastMove: { from, to, san: state.move?.san }
};
console.log('Payload', payload);
const studentSocket = io.sockets.sockets.get(studentId);
//console.log('student socket', studentSocket);
if (studentSocket) {
try {
console.log('route:', `${process.env.MIDDLEWARE_URL}/activities/${username}/activity`);
const response = await fetch(`${process.env.MIDDLEWARE_URL}/activities/${username}/activity`, {
method: "PUT",
headers: {
'Content-Type': 'application/json',
'Authentication' : `Bearer ${credentials}`,
},
body: JSON.stringify({
activityName: payload.activities[0].name,
})
});
console.log('response',response);
socket.emit("completeActivity");
} catch (e) {
console.log('Error: ', e);
}
}
}

}

/*

*/
}
catch (err) {
socket.emit("error", err.message);
console.log("move error")
console.log('error thrown', err)
}
});
socket.on("completeActivity", () => {
console.log('activity completed');
});

/**
* Handles undo move request
Expand Down
63 changes: 54 additions & 9 deletions chessServer/src/managers/GameManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class GameManager {
* @param {Object} param0 - Contains student, mentor, role, socketId
* @returns {Object} Game object, assigned color, and new game status
*/
createOrJoinPuzzle({ student, mentor, role, socketId }, io) {
createOrJoinPuzzle({ student, mentor, role, socketId, credentials }, io) {
let game = this.ongoingGames.find(
(g) => g.student.username === student || g.mentor.username === mentor
);
Expand Down Expand Up @@ -131,7 +131,8 @@ class GameManager {
student: {
username: student,
id: role === "student" ? socketId : null,
color: studentColor
color: studentColor,
credentials: credentials,
},
mentor: {
username: mentor,
Expand Down Expand Up @@ -170,8 +171,10 @@ class GameManager {
}

const board = game.boardState;

const moveResult = board.move({ from: moveFrom, to: moveTo });
const move = {from: moveFrom, to: moveTo};
//console.log(move, typeof(move), typeof(move)==='object');
const moveResult = board.move(move);
console.log(moveResult);

if (!moveResult) {
throw new Error("Invalid move!");
Expand All @@ -180,11 +183,53 @@ class GameManager {
// Save board state
game.pastStates.push(board.fen())

return {
boardState: board.fen(),
move: moveResult,
studentId: game.student.id,
mentorId: game.mentor.id
const flags = moveResult.flags || ""; // e.g., 'c' capture, 'k'/'q' castle, 'e' en passant, 'p' promotion
const activityEvents = [];

const captureMap = {
q: "captureQueen",
r: "captureRook",
n: "captureKnight",
b: "captureBishop",
p: "capturePawn"
};

// Capture (including en passant)
if (flags.includes("c") || flags.includes("e")) {
const capLetter = moveResult.captured; // 'q','r','n','b','p'
const name = capLetter ? captureMap[capLetter] : null;
if (name) {
activityEvents.push({
name,
meta: {
from: moveResult.from,
to: moveResult.to,
san: moveResult.san
},
at: Date.now()
});
}
}

// Castling
if (flags.includes("k") || flags.includes("q")) {
activityEvents.push({
name: "performCastle",
meta: { san: moveResult.san },
at: Date.now()
});
}
//console.log(activityEvents);
//console.log('student info',game.student);
return {
result: {
boardState: board.fen(),
move: moveResult,
studentId: game.student.id,
mentorId: game.mentor.id,
studentUsername: game.student.username,
},
activityEvents: activityEvents
};
}

Expand Down
4 changes: 2 additions & 2 deletions chessServer/src/tests/GameManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ describe('GameManager', () => {
game.mentor.id = 'socket2';
const moveResult = gameManager.makeMove('socket1', 'e2', 'e4');

expect(moveResult.move.from).toBe('e2');
expect(moveResult.move.to).toBe('e4');
expect(moveResult.result.move.from).toBe('e2');
expect(moveResult.result.move.to).toBe('e4');
});

test('throws error for invalid move', () => {
Expand Down
1 change: 1 addition & 0 deletions middlewareNode/src/config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const passport = require("passport");
const config = require("config");
const users = require("../models/users.js")

/**
* Serializes user for session storage
Expand Down
60 changes: 24 additions & 36 deletions middlewareNode/src/routes/activities.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ async function getUserId(db, username) {
{ username },
);
if(!currentUser) {
return res.status(404).json({error: "User not found!"});
}
return;
}
const userId = currentUser._id;
return userId;
}
Expand All @@ -53,14 +53,14 @@ async function getUserId(db, username) {
* GET /activities
* Retrieves all daily activities for a user
*/
router.get("/", async (req, res) => {
router.get("/:username", async (req, res) => {
try {
const db = await getDb();
const { username } = req.params;
if(!username) {
return res.status(401).json({error:'Authentication required'});
const userId = await getUserId(db, username);
if(!userId) {
return res.status(404).json({error:'User not found'});
}
const userId = getUserId(db, username);
const activities = db.collection("activities");
const userActivities = await activities.findOne(
{ userId }, { projection: {activities: 1, _id: 0}}
Expand All @@ -73,14 +73,14 @@ router.get("/", async (req, res) => {
}
})

router.get("/dates", async (req, res) => {
router.get("/:username/dates", async (req, res) => {
try {
const db = await getDb();
const { username } = req.params;
if(!username) {
return res.status(401).json({error: "User not found"});
const userId = await getUserId(db, username);
if(!userId) {
return res.status(404).json({error: "User not found"});
}
const userId = getUserId(db, username);
const activities = db.collection("activities");
const completedDates = await activities.findOne(
{ userId }, {projection: {_id: 0, completedDates: 1}}
Expand All @@ -93,45 +93,33 @@ router.get("/dates", async (req, res) => {
});


router.put("/activity/:activityName", async (req, res) => {
router.put("/:username/activity", async (req, res) => {
try {
const db = await getDb();
const { username, activityName } = req.params;
if(!username) {
return res.status(401).json({error:'Authentication required'});
const { username } = req.params;
const { activityName } = req.body;
const userId = await getUserId(db, username);
if(!userId) {
return res.status(404).json({error:'User not found'});
}
const userId = getUserId(db, username);
const activities = db.collection("activities");
const activityIncomplete = await activities.findOne(
{ userId, "activities.name": activityName },
{ activities: {$elemMatch: { name: activityName }}, _id:0},
);
if(activityIncomplete) {
console.log('incomplete activity: ', activityName);
}
await activities.updateOne(
{ userId, "activities.name": activityName },
{ $set: { "activities.$.completed": true } }
);
return res.status(200);
return res.status(200).json({message:'success'});
} catch (err) {
console.error('Error updating activities: ', err);
return res.status(500).json({error: 'Server error'});
}
})

router.put("/activity/check", async (req, res) => {
try {
const db = await getDb();
const { username } = req.params;
const { moveData } = req.body;
if(!username) {
return res.status(401).json({error:'Authentication required'});
}
const userId = getUserId(db, username);
const activities = db.collection("activities");
const userActivities = await activities.findOne(
{ userId }, { projection: {activities: 1, _id: 0}}
);
//compare move data with activities
}
catch (err) {
console.error('Error checking move: ', err);
res.status(500).json({error: 'Server error'});
}
})

module.exports = router;
2 changes: 1 addition & 1 deletion middlewareNode/src/routes/badges.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

const express = require("express");
const { BADGE_CATALOG } = require("../badges/catalog");
const { getEarned, awardIfEligible } = require("../Badges/service");
const { getEarned, awardIfEligible } = require("../badges/service");

const router = express.Router();

Expand Down
2 changes: 1 addition & 1 deletion middlewareNode/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ app.use("/auth", require("./routes/auth"));
app.use("/timeTracking", require("./routes/timeTracking"));
app.use("/puzzles", require("./routes/puzzles"));
app.use("/lessons", require("./routes/lessons"));
app.use("/activities/:username", require("./routes/activities"));
app.use("/activities", require("./routes/activities"));
app.use('/streak', streakRoutes);
app.use("/badges", require("./routes/badges"));

Expand Down
3 changes: 3 additions & 0 deletions react-ystemandchess/src/core/types/chess.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export interface Move {
piece?: string;
captured?: string;
flags?: string;
computerMove?: boolean;
username?: string;
credentials?: string;
}

export interface GameConfig {
Expand Down
8 changes: 6 additions & 2 deletions react-ystemandchess/src/core/utils/activityNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ const activityNameMap: Record<string, string> = {
* @param {Array<Activity>} names - Array of activity objects
* @returns {Array<string>} Array of display names for the activities
*/
export const parseActivities = (names: Array<Activity>): Array<string> => {
const namesArray = names.map((activity) => activityNameMap[activity.name] || activity.name);
export const parseActivities = (names: Array<Activity>): Array<Activity> => {
const namesArray = names.map((activity) => ({
name: activityNameMap[activity.name] || activity.name,
type: activity.type,
completed: activity.completed,
}));
return namesArray;
// TODO: Consider making API call to fetch display names dynamically
}
20 changes: 10 additions & 10 deletions react-ystemandchess/src/environments/environment.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export const environment = {
production: false,
agora: {
appId: '6b7772f2a76f406192d8167460181be0',
},
urls: {
middlewareURL: 'http://localhost:8000',
stockfishServerURL: 'http://localhost:8080',
chessServerURL: 'http://localhost:3001',
},
productionType: 'development',
production: false,
agora: {
appId: '6b7772f2a76f406192d8167460181be0',
},
urls: {
middlewareURL: 'http://localhost:8000',
stockfishServerURL: 'http://localhost:8080', // unified naming
chessServerURL: 'http://localhost:3001', // removed trailing slash for consistency
},
productionType: 'development',
};
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ export const useChessSocket = ({
from: move.from,
to: move.to,
promotion: move.promotion,
computerMove: move.computerMove,
username: move.username,
credentials: move.credentials,
};
console.log("Sending move:", data);
socketRef.current?.emit("move", JSON.stringify(data));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ const Puzzles: React.FC<PuzzlesProps> = ({
if (newFen) {
setCurrentFEN(newFen);
}

move.username = username;
move.credentials = cookies.login;
socket.sendMove(move);
socket.sendLastMove(move.from, move.to);

Expand Down
Loading