socket connection established and chess logic imported from old codebase
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"typeinfo": "cpp"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ const cors = require("cors");
|
|||||||
const authRoutes = require("./routes/auth");
|
const authRoutes = require("./routes/auth");
|
||||||
const userRoutes = require("./routes/user");
|
const userRoutes = require("./routes/user");
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
mongoose
|
mongoose
|
||||||
.connect("mongodb://127.0.0.1:27017/test")
|
.connect("mongodb://127.0.0.1:27017/test")
|
||||||
@@ -14,27 +15,65 @@ const app = express();
|
|||||||
const http = require("http");
|
const http = require("http");
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const { Server } = require("socket.io");
|
const { Server } = require("socket.io");
|
||||||
|
const { sendEmail } = require("./mail");
|
||||||
|
const { User } = require("./models/user");
|
||||||
const io = new Server(server, { cors: { origin: "*" } });
|
const io = new Server(server, { cors: { origin: "*" } });
|
||||||
|
|
||||||
io.on("connection", (socket) => {
|
const activeRooms = new Map();
|
||||||
// console.log("Connected: ", socket.data);
|
const pendingChallenges = new Map();
|
||||||
socket.broadcast.emit("game", "emitting...");
|
|
||||||
socket.emit("game", "Welcome");
|
|
||||||
|
|
||||||
socket.on("join-room", async (data) => {
|
io.on("connection", (socket) => {
|
||||||
if (data?.roomid) await socket.join(roomid);
|
console.log("Client connected:", socket.id);
|
||||||
else socket.emit("error", "Room id not received");
|
|
||||||
|
// Handle join event
|
||||||
|
socket.on("join-room", async (roomID, playerUsername, challengedPlayerUsername) => {
|
||||||
|
// room exists
|
||||||
|
if (activeRooms.has(roomID)) {
|
||||||
|
socket.emit("room-full");
|
||||||
|
return;
|
||||||
|
} else if (pendingChallenges.has(roomID)) {
|
||||||
|
const challenge = pendingChallenges.get(roomID);
|
||||||
|
pendingChallenges.delete(roomID);
|
||||||
|
let newRoom = {
|
||||||
|
id: roomID,
|
||||||
|
players: {
|
||||||
|
challenger: {
|
||||||
|
id: challenge.challengerID,
|
||||||
|
name: challenge.challengerUsername,
|
||||||
|
},
|
||||||
|
challenged: {
|
||||||
|
id: socket.id,
|
||||||
|
name: playerUsername,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
activeRooms.set(roomID, newRoom);
|
||||||
|
io.to(roomID).emit("room-created");
|
||||||
|
} else {
|
||||||
|
// no room on pending challenges found
|
||||||
|
const challenge = {
|
||||||
|
roomID,
|
||||||
|
challengerID: socket.id,
|
||||||
|
challengerUsername: playerUsername,
|
||||||
|
challengedUsername: challengedPlayerUsername,
|
||||||
|
};
|
||||||
|
pendingChallenges.set(roomID, challenge);
|
||||||
|
|
||||||
|
console.log(challengedPlayerUsername);
|
||||||
|
// notify the challenged player
|
||||||
|
const email = (await User.findOne({ username: challengedPlayerUsername })).email;
|
||||||
|
sendEmail(
|
||||||
|
email,
|
||||||
|
`Challenge from ${playerUsername}`,
|
||||||
|
`To accept the challenge follow the link: http://localhost:5173/game/friend/${roomID}`
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.emit("challenge-pending");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("play", (data) => {
|
socket.on("move", (roomID, moveData) => {
|
||||||
console.log(data);
|
socket.to(roomID).emit("opponent-move", moveData);
|
||||||
let { fromRow, fromCol, toRow, toCol, room } = data;
|
|
||||||
fromRow = 7 - fromRow;
|
|
||||||
fromCol = 7 - fromCol;
|
|
||||||
toRow = 7 - toRow;
|
|
||||||
toCol = 7 - toCol;
|
|
||||||
socket.to(room).emit("play", { fromCol, fromRow, toCol, toRow });
|
|
||||||
socket.broadcast.emit("play", { fromCol, fromRow, toCol, toRow });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{"events":[{"title":"new event","image":"https://images.squarespace-cdn.com/content/v1/56d082a760b5e95c074d11a0/8ce30633-23f2-4dc9-b301-c251cbf26d02/Mercedes+Benz+Oscar+Party?format=2500w","date":"2023-05-03","description":"hello","id":"1f24eb47-5019-4a07-9c3c-1ba18b591800"},{"title":"concert","image":"https://business.twitter.com/content/dam/business-twitter/insights/may-2018/event-targeting.png.twimg.1920.png","date":"2023-05-04","description":"lorem","id":"2035d991-0e72-456c-b6df-1aa9774caaaa"}],"users":[{"email":"m@g.com","password":"$2a$12$.GW99UfbOfISG6mQHN.8MOXtYXePsWH592XudNWU9Df9tYEV.K.QG","id":"2a744c6d-da1a-4938-ab45-144b567d5187"},{"email":"moon@gmails.com","password":"$2a$12$o7p.OS1vA66yBlkOsh.xV.ifDsItbSeBOTaN5VgjYHIUVtDvP3nbu","id":"8f394056-592e-45fc-9808-e923e1a1d60a"},{"email":"moon@gmail.com","password":"$2a$12$3t8sENEvXNvC1XeKhMsJFOsaU0vVh27wnY4hHIPlXzzOIAwMiSSJC","id":"421e3728-ac58-4fc1-86d5-70b618d01f9b"},{"email":"moonpatel2003@gmail.com","password":"$2a$12$QxG6hh0vFudhlsX3MJvXLuVfnFKBAPd15Bei42MNB2v69BwwGT3oq","id":"d77a0a0b-2b82-489e-9782-ccac490db36d"}]}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
const nodemailer = require("nodemailer");
|
||||||
|
const transporter = nodemailer.createTransport({
|
||||||
|
service: "gmail",
|
||||||
|
auth: {
|
||||||
|
user: "chessroyalemail@gmail.com",
|
||||||
|
pass: process.env.MAIL_SERVER_PASSWORD,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendEmail = (receiverEmail, subject, data) => {
|
||||||
|
console.log(process.env.MAIL_SERVER_PASSWORD);
|
||||||
|
let mailDetails = {
|
||||||
|
from: "chessroyalemail@gmail.com",
|
||||||
|
to: receiverEmail,
|
||||||
|
subject: subject,
|
||||||
|
text: data,
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailDetails, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
} else {
|
||||||
|
console.log("Email sent successfully");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendEmail,
|
||||||
|
};
|
||||||
@@ -39,7 +39,7 @@ const userSchema = new Schema(
|
|||||||
methods: {
|
methods: {
|
||||||
async getFriends() {
|
async getFriends() {
|
||||||
await this.populate("friends", "username");
|
await this.populate("friends", "username");
|
||||||
console.log(this.friends);
|
// console.log(this.friends);
|
||||||
return this.friends.map(friend => friend.username);
|
return this.friends.map(friend => friend.username);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,9 +12,11 @@
|
|||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mongoose": "^7.2.1",
|
"mongoose": "^7.2.1",
|
||||||
|
"nodemailer": "^6.9.3",
|
||||||
"socket.io": "^4.6.1",
|
"socket.io": "^4.6.1",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
}
|
}
|
||||||
@@ -211,6 +213,17 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ecdsa-sig-formatter": {
|
"node_modules/ecdsa-sig-formatter": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||||
@@ -774,6 +787,14 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemailer": {
|
||||||
|
"version": "6.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.3.tgz",
|
||||||
|
"integrity": "sha512-fy9v3NgTzBngrMFkDsKEj0r02U7jm6XfC3b52eoNV+GCrGj+s8pt5OqhiJdWKuw51zCTdiNR/IUD1z33LIIGpg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
|||||||
@@ -14,9 +14,11 @@
|
|||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mongoose": "^7.2.1",
|
"mongoose": "^7.2.1",
|
||||||
|
"nodemailer": "^6.9.3",
|
||||||
"socket.io": "^4.6.1",
|
"socket.io": "^4.6.1",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
"@tabler/icons-react": "^2.23.0",
|
"@tabler/icons-react": "^2.23.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.14.0"
|
"react-router-dom": "^6.14.0",
|
||||||
|
"socket.io-client": "^4.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.0.37",
|
"@types/react": "^18.0.37",
|
||||||
@@ -1273,6 +1274,11 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
"node_modules/@tabler/icons": {
|
"node_modules/@tabler/icons": {
|
||||||
"version": "2.23.0",
|
"version": "2.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.23.0.tgz",
|
||||||
@@ -1704,7 +1710,6 @@
|
|||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
@@ -1762,6 +1767,26 @@
|
|||||||
"integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==",
|
"integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-hE5wKXH8Ru4L19MbM1GgYV/2Qo54JSMh1rlJbfpa40bEWkCKNo3ol2eOtGmowcr+ysgbI7+SGL+by42Q3pt/Ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.1.0",
|
||||||
|
"ws": "~8.11.0",
|
||||||
|
"xmlhttprequest-ssl": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/error-ex": {
|
"node_modules/error-ex": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||||
@@ -3016,8 +3041,7 @@
|
|||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.6",
|
"version": "3.3.6",
|
||||||
@@ -3687,6 +3711,32 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.1.tgz",
|
||||||
|
"integrity": "sha512-Qk3Xj8ekbnzKu3faejo4wk2MzXA029XppiXtTF/PkbTg+fcwaTw1PlDrTrrrU4mKoYC4dvlApOnSeyLCKwek2w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.5.1",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -4127,6 +4177,34 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
"@tabler/icons-react": "^2.23.0",
|
"@tabler/icons-react": "^2.23.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.14.0"
|
"react-router-dom": "^6.14.0",
|
||||||
|
"socket.io-client": "^4.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.0.37",
|
"@types/react": "^18.0.37",
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import Play from './pages/Play/Play'
|
|||||||
import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication'
|
import AuthenticationPage, { loginAction, signupAction } from './pages/Authentication/Authentication'
|
||||||
import { getAuthToken } from '../utils/auth'
|
import { getAuthToken } from '../utils/auth'
|
||||||
import { logoutAction } from './components/Logout'
|
import { logoutAction } from './components/Logout'
|
||||||
|
import ChallengeFriend, { playFriendAction } from './pages/Play/ChallengeFriend'
|
||||||
|
import ChessGame from './pages/Chess/ChessGame'
|
||||||
|
|
||||||
const router = createBrowserRouter([{
|
const router = createBrowserRouter([{
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -30,11 +30,13 @@ const router = createBrowserRouter([{
|
|||||||
{
|
{
|
||||||
path: 'play', element: <PlayLayout />, children: [
|
path: 'play', element: <PlayLayout />, children: [
|
||||||
{ index: true, element: <Play /> },
|
{ index: true, element: <Play /> },
|
||||||
|
{ path: 'friend/:friend_username', element: <ChallengeFriend />, action: playFriendAction },
|
||||||
{ path: 'friend', element: <PlayFriend /> },
|
{ path: 'friend', element: <PlayFriend /> },
|
||||||
{ path: 'computer', element: <div>Computer</div> },
|
{ path: 'computer', element: <div>Computer</div> },
|
||||||
{ path: 'online', element: <div>Online</div> }
|
{ path: 'online', element: <div>Online</div> }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{ path: "game/friend/:roomID", element: <ChessGame /> },
|
||||||
{
|
{
|
||||||
path: 'settings', element: <Settings />, children: [
|
path: 'settings', element: <Settings />, children: [
|
||||||
{ index: true, element: <Profile /> },
|
{ index: true, element: <Profile /> },
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Piece from './Piece';
|
||||||
|
import { socket } from '../socket';
|
||||||
|
import { Box, Flex } from '@mantine/core';
|
||||||
|
|
||||||
|
const Cell = ({ cellProps, selectedPiece, dispatch, myColor }) => {
|
||||||
|
const { row, col, piece, marked } = cellProps;
|
||||||
|
let bgColor = (selectedPiece?.row === row && selectedPiece?.col === col) ? 'bg-gray-100' : ((row + col) % 2 ? 'bg-stone-800' : 'bg-neutral-200');
|
||||||
|
bgColor = marked && piece ? `bg-red-300` : bgColor;
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
// if (!myTurn) return;
|
||||||
|
if (piece && piece.color === myColor) {
|
||||||
|
dispatch({ type: 'SELECT_PIECE', val: { row, col, color: piece.color } }) // select piece
|
||||||
|
}
|
||||||
|
else if (!piece && selectedPiece && marked) {
|
||||||
|
let payload = { fromRow: selectedPiece.row, fromCol: selectedPiece.col, toRow: row, toCol: col };
|
||||||
|
socket.emit('move', payload);
|
||||||
|
dispatch({ type: 'MOVE_PIECE', val: payload, }); // move piece
|
||||||
|
}
|
||||||
|
else if (piece && marked && piece.color !== myColor) {
|
||||||
|
let payload = { fromRow: selectedPiece.row, fromCol: selectedPiece.col, toRow: row, toCol: col, color: piece.color };
|
||||||
|
socket.emit('move', payload);
|
||||||
|
dispatch({ type: 'CAPTURE_PIECE', val: payload }) // capture piece
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = marked ? <Mark /> : <Piece piece={piece} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex onClick={handleClick} w="75px" h="75px" bg={(row + col) % 2 ? 'gray' : 'white'} className={`w-12 h-12 md:w-20 md:h-20 ${bgColor} flex justify-center items-center relative`}>
|
||||||
|
{content}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Mark = () => {
|
||||||
|
return (
|
||||||
|
<Box w="33%" h="33%" sx={{ backgroundColor: 'gray', borderRadius: '100%' }} m="auto"></Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default Cell
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Avatar, Flex, Image, Loader, NavLink, Text, Title } from '@mantine/core';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const FriendsList = () => {
|
||||||
|
const [friends, setFriends] = useState(null);
|
||||||
|
useEffect(() => {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
let response = null;
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/user1/friends`;
|
||||||
|
try {
|
||||||
|
response = await fetch(url, { signal: abortController.signal });
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
setFriends(data.friends);
|
||||||
|
} else {
|
||||||
|
throw data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message === 'The user aborted a request.');
|
||||||
|
else {
|
||||||
|
console.log('Error fetching data');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!response) {
|
||||||
|
abortController.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex sx={{ flexGrow: '1' }} height="100%" justify="start" my="md" align="start" direction="column">
|
||||||
|
<Title px="sm" pt="md" order={3}>Friends</Title>
|
||||||
|
|
||||||
|
{
|
||||||
|
friends ?
|
||||||
|
friends.map((friend, index) => <NavLink key={index} component={Link} to="/play/friend/moonpatel" p='5px' icon={<Avatar size='sm' color='blue' children="M" />} label={<Text fw={700}>{friend}</Text>} />)
|
||||||
|
:
|
||||||
|
<Loader m="20px" variant='dots' color='lime' />
|
||||||
|
}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FriendsList
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Image } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Piece = ({ piece }) => {
|
||||||
|
if (piece === null) return null;
|
||||||
|
const { type, color } = piece;
|
||||||
|
let logo;
|
||||||
|
switch (type) {
|
||||||
|
case 'P':
|
||||||
|
logo = color === 'W' ? 'pawn_white' : 'pawn_black';
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
logo = color === 'W' ? 'rook_white' : 'rook_black';
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
logo = color === 'W' ? 'knight_white' : 'knight_black';
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
logo = color === 'W' ? 'bishop_white' : 'bishop_black';
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
logo = color === 'W' ? 'queen_white' : 'queen_black';
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
logo = color === 'W' ? 'king_white' : 'king_black';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Image src={`/src/assets/${logo}.png`} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Piece
|
||||||
@@ -8,7 +8,7 @@ const AuthenticationPage = (props) => {
|
|||||||
const { isLogin } = props;
|
const { isLogin } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maw="100%" bg="gray" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
|
<Container maw="100%" sx={(theme) => ({ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', backgroundImage: theme.fn.gradient({ from: 'blue', to: 'teal' }) })}>
|
||||||
<Card shadow="md" p="lg" style={{ maxWidth: 400, width: '100%' }}>
|
<Card shadow="md" p="lg" style={{ maxWidth: 400, width: '100%' }}>
|
||||||
<Text align="center" variant="h4" style={{ marginBottom: 20 }}>
|
<Text align="center" variant="h4" style={{ marginBottom: 20 }}>
|
||||||
{isLogin ? 'Login' : 'Sign Up'}
|
{isLogin ? 'Login' : 'Sign Up'}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Avatar, Flex, Image, NavLink, Text, Title } from '@mantine/core'
|
||||||
|
import React from 'react'
|
||||||
|
import ChessBoard from '../Play/ChessBoard'
|
||||||
|
|
||||||
|
const ChessGame = () => {
|
||||||
|
return (
|
||||||
|
<Flex gap="xl" justify='center' align='center' wrap='nowrap' direction='row'>
|
||||||
|
<Flex gap="xs" justify='center' align='start' wrap='nowrap' direction='column' >
|
||||||
|
<NavLink
|
||||||
|
p="2px"
|
||||||
|
label={"username"}
|
||||||
|
icon={<Avatar radius="3px" />}
|
||||||
|
description={"description"}
|
||||||
|
/>
|
||||||
|
<ChessBoard />
|
||||||
|
<NavLink
|
||||||
|
p="2px"
|
||||||
|
label={"username"}
|
||||||
|
icon={<Avatar radius="3px" />}
|
||||||
|
description={"description"}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex w="450px" bg='gray' h="600px" sx={{ borderRadius: '10px' }}>
|
||||||
|
<Title>Game Data</Title>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChessGame
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { Avatar, Button, Card, Flex, Image, Select, Text, TextInput, Title } from '@mantine/core'
|
||||||
|
import React from 'react'
|
||||||
|
import FriendsList from '../../components/FriendsList'
|
||||||
|
import { IconSearch } from '@tabler/icons-react'
|
||||||
|
import { Form, Link, redirect, useParams, useSearchParams } from 'react-router-dom'
|
||||||
|
import { socket } from '../../socket'
|
||||||
|
|
||||||
|
const ChallengeFriend = () => {
|
||||||
|
const params = useParams();
|
||||||
|
let friend_username = params["friend_username"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
width: '450px',
|
||||||
|
height: '600px',
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex align="center" direction="column" justify="center" gap="xs" my="lg">
|
||||||
|
<Title order={2}>Play vs {friend_username}</Title>
|
||||||
|
<Avatar mt="lg" color='lime' size="100px">{friend_username[0].toUpperCase()}</Avatar>
|
||||||
|
<Text>{friend_username}</Text>
|
||||||
|
</Flex>
|
||||||
|
<Select label="Time limit" placeholder='Time limit' value='10' data={['5', '10', '15', '30']} />
|
||||||
|
<Select value='W' onChange={(evt) => setColor(evt.target.value)} my="20px" label={<Text mx="auto" order={3}>I play as</Text>} placeholder='choose your color' data={[
|
||||||
|
{ value: 'W', label: 'White' },
|
||||||
|
{ value: 'B', label: 'Black' },
|
||||||
|
{ value: 'RANDOM', label: 'Random' }
|
||||||
|
]} />
|
||||||
|
<Form action={`/play/friend/${friend_username}`} method='POST'>
|
||||||
|
<Button color='lime' type='submit' >Challenge</Button>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const playFriendAction = ({ request, params }) => {
|
||||||
|
let roomID = Math.floor(Math.random() * 1_000_000_000).toString();
|
||||||
|
// const req = new URL(request);
|
||||||
|
socket.connect();
|
||||||
|
socket.emit('join-room', roomID, 'user1', params.friend_username);
|
||||||
|
|
||||||
|
// socket.on('pending-challenge', () => {
|
||||||
|
// })
|
||||||
|
return redirect(`/game/friend/${roomID}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChallengeFriend;
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import React, { useEffect, useReducer, useRef } from 'react';
|
||||||
|
import { blackColor, chessBoardInit, getPieceHint, pieces, whiteColor } from '../../../utils/chess';
|
||||||
|
import Cell from '../../components/Cell';
|
||||||
|
import { socket } from '../../socket';
|
||||||
|
import { Flex } from '@mantine/core';
|
||||||
|
|
||||||
|
let myColor = 'W';
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
if (state.capturedPieces.length && state.capturedPieces.at(-1).type === pieces.king) return state;
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SELECT_PIECE':
|
||||||
|
{
|
||||||
|
let { row, col, color } = action.val;
|
||||||
|
let selectedPiece = { row, col, color };
|
||||||
|
let possibleMoves = getPieceHint(state.chessBoard, { row, col, color, type: state.chessBoard[row][col].type }, color).movePos;
|
||||||
|
return { ...state, selectedPiece, possibleMoves };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'MOVE_PIECE':
|
||||||
|
{
|
||||||
|
let { fromRow, fromCol, toRow, toCol } = action.val;
|
||||||
|
let newChessBoard = state.chessBoard.map(row => row.slice());
|
||||||
|
let piece = state.chessBoard[fromRow][fromCol];
|
||||||
|
newChessBoard[toRow][toCol] = piece;
|
||||||
|
newChessBoard[fromRow][fromCol] = null;
|
||||||
|
return { ...state, chessBoard: newChessBoard, possibleMoves: [], selectedPiece: null, myTurn: !state.myTurn };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'CAPTURE_PIECE':
|
||||||
|
{
|
||||||
|
let { fromRow, fromCol, toRow, toCol } = action.val;
|
||||||
|
let newChessBoard = state.chessBoard.map(row => row.slice());
|
||||||
|
let capturedPieces = [...state.capturedPieces, state.chessBoard[toRow][toCol]];
|
||||||
|
newChessBoard[toRow][toCol] = state.chessBoard[fromRow][fromCol];
|
||||||
|
newChessBoard[fromRow][fromCol] = null;
|
||||||
|
return { ...state, chessBoard: newChessBoard, capturedPieces, possibleMoves: [], selectedPiece: null, myTurn: !state.myTurn };
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChessBoard = () => {
|
||||||
|
const [gameState, dispatch] = useReducer(reducer, {
|
||||||
|
chessBoard: chessBoardInit(myColor), selectedPiece: null, possibleMoves: [], capturedPieces: [], myTurn: myColor === whiteColor
|
||||||
|
});
|
||||||
|
|
||||||
|
const chessBoardRef = useRef(gameState.chessBoard);
|
||||||
|
chessBoardRef.current = gameState.chessBoard;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleOpponentMove(data) {
|
||||||
|
let { fromCol, fromRow, toCol, toRow } = data;
|
||||||
|
if (chessBoardRef.current[toRow][toCol] === null) {
|
||||||
|
console.log('Moving piece: ', data)
|
||||||
|
dispatch({ type: 'MOVE_PIECE', val: { fromRow, fromCol, toRow, toCol } });
|
||||||
|
} else if (myColor === chessBoardRef.current[toRow][toCol].color) {
|
||||||
|
dispatch({ type: 'CAPTURE_PIECE', val: { fromRow, fromCol, toRow, toCol } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.on('move', handleOpponentMove)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off('move', handleOpponentMove);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Flex w="600px">
|
||||||
|
<div>
|
||||||
|
{gameState.chessBoard.map((line, row) => {
|
||||||
|
return (
|
||||||
|
<Flex className='flex' key={row * 2}>
|
||||||
|
{line.map((cell, col) => {
|
||||||
|
let marked = null;
|
||||||
|
for (let k = 0; k < gameState.possibleMoves.length; k++) {
|
||||||
|
if (gameState.possibleMoves[k].row === row && gameState.possibleMoves[k].col === col) {
|
||||||
|
marked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let piece = cell ? { type: cell.type, color: cell.color } : null;
|
||||||
|
return <Cell
|
||||||
|
key={col * 3 + 1}
|
||||||
|
selectedPiece={gameState.selectedPiece}
|
||||||
|
cellProps={{ row, col, piece, marked }}
|
||||||
|
dispatch={dispatch}
|
||||||
|
myColor={myColor}
|
||||||
|
myTurn={gameState.myTurn}
|
||||||
|
/>
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChessBoard
|
||||||
@@ -6,7 +6,7 @@ const Play = () => {
|
|||||||
return (
|
return (
|
||||||
<Card sx={{
|
<Card sx={{
|
||||||
width: '450px',
|
width: '450px',
|
||||||
height: '100%',
|
height: '600px',
|
||||||
textAlign: 'center'
|
textAlign: 'center'
|
||||||
}}>
|
}}>
|
||||||
<Flex gap="5px" px="20px" justify='center' align='center' wrap='nowrap' direction='column'>
|
<Flex gap="5px" px="20px" justify='center' align='center' wrap='nowrap' direction='column'>
|
||||||
@@ -28,7 +28,7 @@ const CardItem = ({ label, description, src, to }) => {
|
|||||||
label={label}
|
label={label}
|
||||||
icon={<Image src={src} width={50} />}
|
icon={<Image src={src} width={50} />}
|
||||||
description={description}
|
description={description}
|
||||||
sx={{ backgroundColor: 'gray', borderRadius: '5px' }}
|
sx={{ backgroundColor: 'black', borderRadius: '5px' }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Button, Card, CopyButton, Flex, Group, Image, Modal, NativeSelect, NavLink, Select, Text, TextInput, Title } from '@mantine/core'
|
import { Button, Card, CopyButton, Flex, Group, Image, Modal, NativeSelect, NavLink, Select, Text, TextInput, Title } from '@mantine/core'
|
||||||
import { useDisclosure } from '@mantine/hooks'
|
import { useDisclosure } from '@mantine/hooks'
|
||||||
import { IconSearch } from '@tabler/icons-react'
|
import { IconSearch, IconUserCircle } from '@tabler/icons-react'
|
||||||
import { PersonIcon } from '@radix-ui/react-icons'
|
import FriendsList from '../../components/FriendsList'
|
||||||
|
|
||||||
const createChallengeLink = (color) => {
|
const createChallengeLink = (color) => {
|
||||||
let challengeLink = Math.floor(Math.random() * 100_000_000).toString();
|
let challengeLink = Math.floor(Math.random() * 100_000_000).toString();
|
||||||
@@ -46,10 +46,7 @@ const PlayFriend = () => {
|
|||||||
<Title order={2}>Play a Friend</Title>
|
<Title order={2}>Play a Friend</Title>
|
||||||
</Flex>
|
</Flex>
|
||||||
<TextInput my="5px" placeholder="Search by email or username" icon={<IconSearch />} />
|
<TextInput my="5px" placeholder="Search by email or username" icon={<IconSearch />} />
|
||||||
<Flex sx={{ flexGrow: '1' }} height="100%" justify="start" align="start" direction="column">
|
<FriendsList />
|
||||||
<Title px="md" pt="md" order={3}>Friends</Title>
|
|
||||||
{friends.map((friend, index) => <NavLink key={index} icon={friend.avatar} label={<Text fw={700}>{friend.username}</Text>} />)}
|
|
||||||
</Flex>
|
|
||||||
<Flex direction='column' gap='10px'>
|
<Flex direction='column' gap='10px'>
|
||||||
<Button color='lime' onClick={open}>Create Challenge Link</Button>
|
<Button color='lime' onClick={open}>Create Challenge Link</Button>
|
||||||
<Button color='lime'>Join using Challenge Link</Button>
|
<Button color='lime'>Join using Challenge Link</Button>
|
||||||
@@ -60,10 +57,10 @@ const PlayFriend = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const friends = [
|
const friends = [
|
||||||
{ avatar: <PersonIcon />, username: "friend", rating: 100 },
|
{ avatar: <IconUserCircle />, username: "friend", rating: 100 },
|
||||||
{ avatar: <PersonIcon />, username: "friend", rating: 100 },
|
{ avatar: <IconUserCircle />, username: "friend", rating: 100 },
|
||||||
{ avatar: <PersonIcon />, username: "friend", rating: 100 },
|
{ avatar: <IconUserCircle />, username: "friend", rating: 100 },
|
||||||
{ avatar: <PersonIcon />, username: "friend", rating: 100 },
|
{ avatar: <IconUserCircle />, username: "friend", rating: 100 },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default PlayFriend
|
export default PlayFriend
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { io } from "socket.io-client";
|
||||||
|
const url = import.meta.env.VITE_BACKEND_HOST
|
||||||
|
export const socket = io(url, { autoConnect: false });
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
const pawn = "P",
|
||||||
|
rook = "R",
|
||||||
|
knight = "N",
|
||||||
|
bishop = "B",
|
||||||
|
queen = "Q",
|
||||||
|
king = "K";
|
||||||
|
|
||||||
|
export const pieces = {
|
||||||
|
pawn,
|
||||||
|
rook,
|
||||||
|
knight,
|
||||||
|
bishop,
|
||||||
|
queen,
|
||||||
|
king,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const whiteColor = "W",
|
||||||
|
blackColor = "B";
|
||||||
|
|
||||||
|
export function chessBoardInit(myColor) {
|
||||||
|
let opColor = myColor === whiteColor ? blackColor : whiteColor;
|
||||||
|
const chessBoardMatrix = [
|
||||||
|
[
|
||||||
|
{ color: opColor, type: rook },
|
||||||
|
{ color: opColor, type: knight },
|
||||||
|
{ color: opColor, type: bishop },
|
||||||
|
{ color: opColor, type: myColor === whiteColor ? queen : king },
|
||||||
|
{ color: opColor, type: myColor === whiteColor ? king : queen },
|
||||||
|
{ color: opColor, type: bishop },
|
||||||
|
{ color: opColor, type: knight },
|
||||||
|
{ color: opColor, type: rook },
|
||||||
|
],
|
||||||
|
Array(8).fill({ color: opColor, type: pawn }),
|
||||||
|
[null, null, null, null, null, null, null, null],
|
||||||
|
[null, null, null, null, null, null, null, null],
|
||||||
|
[null, null, null, null, null, null, null, null],
|
||||||
|
[null, null, null, null, null, null, null, null],
|
||||||
|
Array(8).fill({ color: myColor, type: pawn }),
|
||||||
|
[
|
||||||
|
{ color: myColor, type: rook },
|
||||||
|
{ color: myColor, type: knight },
|
||||||
|
{ color: myColor, type: bishop },
|
||||||
|
{ color: myColor, type: myColor === whiteColor ? queen : king },
|
||||||
|
{ color: myColor, type: myColor === whiteColor ? king : queen },
|
||||||
|
{ color: myColor, type: bishop },
|
||||||
|
{ color: myColor, type: knight },
|
||||||
|
{ color: myColor, type: rook },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
return chessBoardMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inBoard(i, j) {
|
||||||
|
if (i >= 0 && i < 8 && j >= 0 && j < 8) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBlocked(chessBoard, chessPiece, i, j) {
|
||||||
|
if (chessBoard[i][j] === null) return false;
|
||||||
|
else if (chessBoard[i][j].color === chessPiece.color) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAttacking(chessBoard, chessPiece, i, j) {
|
||||||
|
if (chessBoard[i][j] === null) return false;
|
||||||
|
else if (chessBoard[i][j].color !== chessPiece.color) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPawnHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
if (chessPiece.color === myColor) {
|
||||||
|
// for moving forward
|
||||||
|
if (inBoard(row - 1, col) && chessBoard[row - 1][col] === null && movePos.push({ row: row - 1, col })) {
|
||||||
|
chessPiece.row === 6 &&
|
||||||
|
chessBoard[row - 2][col] === null &&
|
||||||
|
inBoard(row - 2, col) &&
|
||||||
|
!isBlocked(chessBoard, chessPiece, row - 1, col) &&
|
||||||
|
movePos.push({ row: row - 2, col });
|
||||||
|
}
|
||||||
|
// for killing opponent piece
|
||||||
|
if (
|
||||||
|
inBoard(row - 1, col + 1) &&
|
||||||
|
chessBoard[row - 1][col + 1]?.type &&
|
||||||
|
chessBoard[row - 1][col + 1]?.color !== myColor
|
||||||
|
)
|
||||||
|
inBoard(row - 1, col + 1) && movePos.push({ row: row - 1, col: col + 1 });
|
||||||
|
if (
|
||||||
|
inBoard(row - 1, col - 1) &&
|
||||||
|
chessBoard[row - 1][col - 1]?.type &&
|
||||||
|
chessBoard[row - 1][col - 1]?.color !== myColor
|
||||||
|
)
|
||||||
|
inBoard(row - 1, col - 1) && movePos.push({ row: row - 1, col: col - 1 });
|
||||||
|
} else {
|
||||||
|
// for moving forward
|
||||||
|
if (inBoard(row + 1, col) && chessBoard[row + 1][col] === null && movePos.push({ row: row + 1, col })) {
|
||||||
|
chessPiece.row === 1 &&
|
||||||
|
chessBoard[row + 2][col] === null &&
|
||||||
|
inBoard(row + 2, col) &&
|
||||||
|
movePos.push({ row: row + 2, col });
|
||||||
|
}
|
||||||
|
// for killing opponent piece
|
||||||
|
if (
|
||||||
|
inBoard(row + 1, col + 1) &&
|
||||||
|
chessBoard[row + 1][col + 1]?.type &&
|
||||||
|
chessBoard[row + 1][col + 1]?.color === myColor
|
||||||
|
)
|
||||||
|
inBoard(row + 1, col + 1) && movePos.push({ row: row + 1, col: col + 1 });
|
||||||
|
if (
|
||||||
|
inBoard(row + 1, col - 1) &&
|
||||||
|
chessBoard[row + 1][col - 1]?.type &&
|
||||||
|
chessBoard[row + 1][col - 1]?.color === myColor
|
||||||
|
)
|
||||||
|
inBoard(row + 1, col - 1) && movePos.push({ row: row + 1, col: col - 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRookHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col, color } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
let i = row,
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
console.log(movePos);
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKnightHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col, color } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
|
||||||
|
if (inBoard(row + 2, col + 1) && !isBlocked(chessBoard, chessPiece, row + 2, col + 1))
|
||||||
|
movePos.push({ row: row + 2, col: col + 1 });
|
||||||
|
if (inBoard(row + 2, col - 1) && !isBlocked(chessBoard, chessPiece, row + 2, col - 1))
|
||||||
|
movePos.push({ row: row + 2, col: col - 1 });
|
||||||
|
if (inBoard(row - 2, col + 1) && !isBlocked(chessBoard, chessPiece, row - 2, col + 1))
|
||||||
|
movePos.push({ row: row - 2, col: col + 1 });
|
||||||
|
if (inBoard(row - 2, col - 1) && !isBlocked(chessBoard, chessPiece, row - 2, col - 1))
|
||||||
|
movePos.push({ row: row - 2, col: col - 1 });
|
||||||
|
if (inBoard(row + 1, col + 2) && !isBlocked(chessBoard, chessPiece, row + 1, col + 2))
|
||||||
|
movePos.push({ row: row + 1, col: col + 2 });
|
||||||
|
if (inBoard(row - 1, col + 2) && !isBlocked(chessBoard, chessPiece, row - 1, col + 2))
|
||||||
|
movePos.push({ row: row - 1, col: col + 2 });
|
||||||
|
if (inBoard(row + 1, col - 2) && !isBlocked(chessBoard, chessPiece, row + 1, col - 2))
|
||||||
|
movePos.push({ row: row + 1, col: col - 2 });
|
||||||
|
if (inBoard(row - 1, col - 2) && !isBlocked(chessBoard, chessPiece, row - 1, col - 2))
|
||||||
|
movePos.push({ row: row - 1, col: col - 2 });
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBishopHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col, color } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
|
||||||
|
let i = row,
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueenHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col, color } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
let i = row,
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(++i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, ++j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
i = row;
|
||||||
|
j = col;
|
||||||
|
while (inBoard(--i, --j) && !isBlocked(chessBoard, chessPiece, i, j)) {
|
||||||
|
movePos.push({ row: i, col: j });
|
||||||
|
if (isAttacking(chessBoard, chessPiece, i, j)) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKingHint(chessBoard, chessPiece, myColor) {
|
||||||
|
const { row, col } = chessPiece;
|
||||||
|
let movePos = [];
|
||||||
|
|
||||||
|
if (inBoard(row, col + 1) && !isBlocked(chessBoard, chessPiece, row, col + 1))
|
||||||
|
movePos.push({ row: row, col: col + 1 });
|
||||||
|
if (inBoard(row, col - 1) && !isBlocked(chessBoard, chessPiece, row, col - 1))
|
||||||
|
movePos.push({ row: row, col: col - 1 });
|
||||||
|
if (inBoard(row + 1, col + 1) && !isBlocked(chessBoard, chessPiece, row + 1, col + 1))
|
||||||
|
movePos.push({ row: row + 1, col: col + 1 });
|
||||||
|
if (inBoard(row + 1, col) && !isBlocked(chessBoard, chessPiece, row + 1, col))
|
||||||
|
movePos.push({ row: row + 1, col: col });
|
||||||
|
if (inBoard(row + 1, col - 1) && !isBlocked(chessBoard, chessPiece, row + 1, col - 1))
|
||||||
|
movePos.push({ row: row + 1, col: col - 1 });
|
||||||
|
if (inBoard(row - 1, col + 1) && !isBlocked(chessBoard, chessPiece, row - 1, col + 1))
|
||||||
|
movePos.push({ row: row - 1, col: col + 1 });
|
||||||
|
if (inBoard(row - 1, col) && !isBlocked(chessBoard, chessPiece, row - 1, col))
|
||||||
|
movePos.push({ row: row - 1, col: col });
|
||||||
|
if (inBoard(row - 1, col - 1) && !isBlocked(chessBoard, chessPiece, row - 1, col - 1))
|
||||||
|
movePos.push({ row: row - 1, col: col - 1 });
|
||||||
|
|
||||||
|
return { movePos };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPieceHint(chessBoard, chessPiece, myColor) {
|
||||||
|
switch (chessPiece.type) {
|
||||||
|
case pawn:
|
||||||
|
return getPawnHint(chessBoard, chessPiece, myColor);
|
||||||
|
case rook:
|
||||||
|
return getRookHint(chessBoard, chessPiece, myColor);
|
||||||
|
case knight:
|
||||||
|
return getKnightHint(chessBoard, chessPiece, myColor);
|
||||||
|
case bishop:
|
||||||
|
return getBishopHint(chessBoard, chessPiece, myColor);
|
||||||
|
case queen:
|
||||||
|
return getQueenHint(chessBoard, chessPiece, myColor);
|
||||||
|
case king:
|
||||||
|
return getKingHint(chessBoard, chessPiece, myColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||