Skip to content

Conversation

@Chiranthdv
Copy link

@Chiranthdv Chiranthdv commented Dec 10, 2025

Description

This PR adds a fully functional Multiplayer Memory Game along with an improved local 2-player Memory Game. The implementation introduces real-time synchronization using Socket.io, enhanced UI styling, correct card-flip behavior, and stable turn-based gameplay.

The feature includes:

  • Full multiplayer support (P1 vs P2) with synchronized game state
  • Local 2-player mode for offline play
  • Accurate player-role loading (P1 or P2) via localStorage
  • Real-time turn switching and match updates through WebSocket events
  • A UI rendering fix that ensures both flipped cards appear correctly before match checking
  • Grid loading fix for Player 2 by triggering state initialization on mount
  • Modern UI with Tailwind styling for the grid, cards, scoreboard, and turn highlights
  • Smooth animations for flips, matches, and turn transitions
  • Winner calculation and display for both game modes

This PR significantly enhances gameplay consistency, ensures correct state synchronization between players, and improves the overall user experience.


Key Features Added

🎮 Multiplayer Mode (Socket.io)

  • Real-time synchronized memory game
  • Flip events broadcast to opponent
  • Matches update scores live for both players
  • Server-managed turn switching
  • Full-state sync when a player joins late
  • Reset functionality over sockets

👥 Local 2-Player Mode

  • Turn-based card flipping
  • Accurate mismatch/match handling
  • Correct flip timing so the second card always shows
  • Independent score tracking
  • Clean visual feedback

🎨 UI & UX Improvements

  • Tailwind-based polished layout
  • Responsive grid size scaling
  • Styled scoreboard and turn indicators
  • Matched cards highlighted with animations
  • Loading state for when Player 2 waits for server sync

🛠️ Fixes Applied

  • Second card now always reveals before checking match (timing fix)
  • Player 2 grid not showing — fixed by forcing initialization on mount
  • Proper role assignment using localStorage
  • Consistent flip/match logic across both modes

Motivation & Context

This feature significantly increases the interactivity and playability of the app by offering both online multiplayer and local two-player gameplay. It also fixes several core visual and logic issues, such as:

  • Second card not showing before match evaluation
  • Player 2 not receiving initialized board
  • Turn desynchronization issues
  • UI inconsistencies between different grid sizes

The update creates a stable, enjoyable, and visually appealing memory game experience.

Fixes # (issue)


Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • UI improvement (non-breaking styling enhancement)
  • Breaking change (feature affecting existing functionality)
  • Documentation update

Additional Notes

Future enhancements may include:

  • Flip animations using Framer Motion
  • Sound effects for matches and flips
  • Animated winner screen
  • Spectator mode for multiplayer
  • Real-time chat inside the game room

This PR lays the foundation for all future multiplayer expansions.

@Chiranthdv
Copy link
Author

@microsoft-github-policy-service agree

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a multiplayer memory game feature with both online (Socket.io-based) and local 2-player modes. The implementation includes a Node.js backend server for real-time game synchronization, enhanced UI with Tailwind CSS, and comprehensive game state management across multiple players.

Key Changes:

  • Full multiplayer support with Socket.io for real-time game synchronization between two players
  • Local 2-player mode with turn-based gameplay and independent scoring
  • Backend server implementation for managing game state, room creation, and player synchronization

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
memory-game/package.json Adds socket.io-client dependency for frontend WebSocket communication
memory-game/package-lock.json Lockfile updates for new socket.io-client and related dependencies
memory-game/components/socket.js Socket.io client configuration with connection event handlers
memory-game/components/MemoryGameMultiplayer.jsx Multiplayer game component with real-time state synchronization
memory-game/components/MemoryGame.jsx Enhanced local 2-player mode with scoring and turn management
memory-game/backend/server.js Express + Socket.io server for managing multiplayer game rooms and state
memory-game/backend/package.json Backend dependencies including socket.io, express, and cors
memory-game/backend/package-lock.json Backend dependency lockfile
memory-game/app/room/[roomId]/page.jsx Dynamic route for multiplayer game rooms
memory-game/app/page.js Updated home page with navigation to multiplayer lobby
memory-game/app/lobby/page.jsx Lobby interface for creating/joining multiplayer rooms
memory-game/app/layout.js Simplified root layout with updated metadata
Files not reviewed (2)
  • memory-game/backend/package-lock.json: Language not supported
  • memory-game/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +64
socket.on("join_room", ({ roomId }) => {
console.log("👥 join_room:", roomId);
const room = gameState[roomId];
if (!room) {
console.log("⚠ join_room: room not found", roomId);
return;
}

socket.join(roomId);
socket.emit("sync_full_state", room);
});
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server lacks input validation for room IDs and other user inputs. Malicious users could send invalid data or excessively large room IDs. Add validation to check that roomId exists, has a reasonable length, and matches expected format before processing socket events.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +12
// import MemoryGame from '@/components/MemoryGame'
// import React from 'react'

const page = () => {
// const page = () => {
// return (
// <div>
// <MemoryGame />
// </div>
// )
// }

// export default page
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out code sections should be removed. Keep only the active implementation to improve code readability.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +11
// "use client";
// import { useParams } from "next/navigation";
// import MemoryGameMultiplayer from "../../../components/MemoryGameMultiplayer";

// export default function RoomPage() {
// const { roomId } = useParams();

// return <MemoryGameMultiplayer roomId={roomId} />;
// }

// app/room/[roomId]/page.jsx
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out code in this file should be removed for better maintainability.

Suggested change
// "use client";
// import { useParams } from "next/navigation";
// import MemoryGameMultiplayer from "../../../components/MemoryGameMultiplayer";
// export default function RoomPage() {
// const { roomId } = useParams();
// return <MemoryGameMultiplayer roomId={roomId} />;
// }
// app/room/[roomId]/page.jsx

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +106
socket.on("match", ({ roomId, pair, scorer }) => {
const room = gameState[roomId];
if (!room) return;

room.matched.push(...pair);

if (!room.scores) {
room.scores = { P1: 0, P2: 0 };
}
if (scorer === "P1" || scorer === "P2") {
room.scores[scorer] += 1;
}

console.log(
"✅ match in room",
roomId,
"matched:",
room.matched,
"scores:",
room.scores
);

io.to(roomId).emit("match_update", {
matched: room.matched,
scores: room.scores,
});
});
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The match event handler doesn't validate that the pair array contains exactly 2 elements or that the cardId values are within valid bounds. A malicious client could send invalid data causing the matched array to contain incorrect values. Add validation to check the pair is an array of length 2 with valid card IDs.

Copilot uses AI. Check for mistakes.
Comment on lines +195 to +202
localStorage.setItem("playerRole", "P1");
socket.emit("create_room", { roomId: id, gridSize });
router.push(`/room/${id}`);
};

const joinRoom = () => {
if (!roomId) return;
localStorage.setItem("playerRole", "P2");
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The localStorage access in the lobby assumes the operation will succeed, but localStorage can throw exceptions if it's disabled, full, or in private browsing mode. Wrap localStorage calls in try-catch blocks to handle potential errors gracefully and provide user feedback if the operation fails.

Suggested change
localStorage.setItem("playerRole", "P1");
socket.emit("create_room", { roomId: id, gridSize });
router.push(`/room/${id}`);
};
const joinRoom = () => {
if (!roomId) return;
localStorage.setItem("playerRole", "P2");
try {
if (typeof window !== "undefined" && window.localStorage) {
window.localStorage.setItem("playerRole", "P1");
}
} catch (err) {
console.error("Failed to access localStorage when creating room:", err);
alert(
"We couldn't save your player role due to your browser's storage settings. You can still join the room, but your role might not be remembered."
);
}
socket.emit("create_room", { roomId: id, gridSize });
router.push(`/room/${id}`);
};
const joinRoom = () => {
if (!roomId) return;
try {
if (typeof window !== "undefined" && window.localStorage) {
window.localStorage.setItem("playerRole", "P2");
}
} catch (err) {
console.error("Failed to access localStorage when joining room:", err);
alert(
"We couldn't save your player role due to your browser's storage settings. You can still join the room, but your role might not be remembered."
);
}

Copilot uses AI. Check for mistakes.
const router = useRouter();

const createRoom = () => {
const id = Math.floor(1000 + Math.random() * 9000).toString();
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The room ID generation uses a simple 4-digit random number, which has only 9000 possible values. This creates a high collision probability and makes rooms easy to guess. Consider using a more robust ID generation method like UUID or a longer random string to ensure uniqueness and improve security.

Suggested change
const id = Math.floor(1000 + Math.random() * 9000).toString();
const id =
typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
? crypto.randomUUID()
: Math.random().toString(36).slice(2, 10);

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +12
const app = express();
app.use(cors());
const server = http.createServer(app);

const io = new Server(server, {
cors: {
origin: "*",
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CORS configuration allows all origins ("*"), which is a security risk in production. This should be restricted to only allow requests from your application's domain. Consider using environment variables to configure allowed origins based on the deployment environment.

Suggested change
const app = express();
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: "*",
const allowedOrigins = process.env.ALLOWED_ORIGINS
? process.env.ALLOWED_ORIGINS.split(",").map((origin) => origin.trim())
: ["http://localhost:3000"];
const app = express();
app.use(
cors({
origin: allowedOrigins,
})
);
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: allowedOrigins,

Copilot uses AI. Check for mistakes.
});

// In-memory game state
const gameState = {};
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gameState object grows unbounded as rooms are created but never cleaned up when rooms are abandoned or games end. This will cause a memory leak over time. Consider implementing a cleanup mechanism to remove inactive rooms after a timeout period or when both players disconnect.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +514
// "use client";
// import React, { useEffect, useState, useCallback } from "react";
// // Fisher-Yates shuffle for unbiased randomization
// function fisherYatesShuffle(array) {
// const arr = array.slice();
// for (let i = arr.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [arr[i], arr[j]] = [arr[j], arr[i]];
// }
// return arr;
// }
// const MemoryGame = () => {
// const [gridSize, setGridSize] = useState(2);
// const [array, setArray] = useState([]);
// const [flipped, setFlipped] = useState([]);
// const [selectedPairs, setSelectedPairs] = useState([]);
// const [disabled, setDisabled] = useState(false);
// const [error, setError] = useState("");
// const [won, setWon] = useState(false);
// const handleGridSize = (e) => {
// const size = parseInt(e.target.value);
// if (2 <= size && size <= 10 && (size % 2 === 0)) {
// setGridSize(size);
// setError("");
// } else {
// setError("Please enter a grid size where size is even (e.g., 2, 4, 6, 8, 10)");
// }
// };
// const initializeGame = useCallback(() => {
// const totalCards = gridSize * gridSize;
// const pairCount = totalCards / 2;
// const numbers = [...Array(pairCount).keys()].map((n) => n + 1);
// const cardNumbers = [...numbers, ...numbers];
// const shuffledCardNumbers = fisherYatesShuffle(cardNumbers);
// const shuffledCards = shuffledCardNumbers.map((number, index) => ({
// id: index,
// number,
// }));
// setArray(shuffledCards);
// setFlipped([]);
// setSelectedPairs([]);
// setDisabled(false);
// setWon(false);
// }, [gridSize]);
// useEffect(() => {
// initializeGame();
// }, [initializeGame]);
// const handleMatch = (secondId) => {
// const [firstId] = flipped;
// if (array[firstId].number === array[secondId].number) {
// setSelectedPairs([...selectedPairs, firstId, secondId]);
// setFlipped([]);
// setDisabled(false);
// } else {
// setTimeout(() => {
// setDisabled(false);
// setFlipped([]);
// }, 1000);
// }
// };
// const handleClick = (id) => {
// if (disabled || won) return;

// if (flipped.length === 0) {
// setFlipped([id]);
// return;
// }

// if (flipped.length === 1) {
// setDisabled(true);
// if (id !== flipped[0]) {
// setFlipped([...flipped, id]);
// handleMatch(id);
// } else {
// setFlipped([]);
// setDisabled(false);
// }
// }
// };
// const isFlipped = (id) => flipped.includes(id) || selectedPairs.includes(id);
// const isSelectedPairs = (id) => selectedPairs.includes(id);
// useEffect(() => {
// if (selectedPairs.length === array.length && array.length > 0) {
// setWon(true);
// }
// }, [selectedPairs, array]);
// return (
// <div className="h-screen flex flex-col justify-center items-center p-4 bg-gray-100 ">
// {/* Heading */}
// <h1 className="text-3xl font-bold mb-6">Memory Game</h1>
// {/* Grid Size */}
// <div className="mb-4">
// <label htmlFor="gridSize">Grid Size: (max 10)</label>
// <input
// type="number"
// className="w-[50px] ml-3 rounded border-2 px-1.5 py-1"
// min="2"
// max="10"
// value={gridSize}
// onChange={handleGridSize}
// />
// {error && (
// <div className="text-sm text-red-500 mt-2">{error}</div>
// )}
// </div>
// {/* Cards */}
// <div
// className="grid gap-2 mb-4"
// style={{
// gridTemplateColumns: `repeat(${gridSize}, minmax(0,1fr))`,
// width: `min(100%,${gridSize * 5.5}rem)`,
// }}
// >
// {array.map((card) => (
// <div
// key={card.id}
// onClick={() => handleClick(card.id)}
// className={`aspect-square flex items-center justify-center text-xl transition-all duration-300 font-bold rounded-lg cursor-pointer ${
// isFlipped(card.id)
// ? isSelectedPairs(card.id)
// ? "bg-green-500 text-white"
// : "bg-blue-500 text-white"
// : "bg-gray-300 text-gray-400"
// }`}
// >
// {isFlipped(card.id) ? card.number : "?"}
// </div>
// ))}
// </div>
// {/* Result */}
// <div className="text-2xl text-green-500 font-bold">
// {won ? "You Won!" : ""}
// </div>

// {/* Reset Button */}
// <button
// className="px-5 py-2 bg-green-500 rounded text-white mt-5"
// onClick={initializeGame}
// >
// Reset
// </button>
// </div>
// );
// };
// export default MemoryGame;

// "use client";
// import React, { useEffect, useState, useCallback } from "react";
// // Fisher-Yates Shuffle
// function fisherYatesShuffle(array) {
// const arr = array.slice();
// for (let i = arr.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [arr[i], arr[j]] = [arr[j], arr[i]];
// }
// return arr;
// }
// const MemoryGame = () => {
// const [gridSize, setGridSize] = useState(4);
// const [inputSize, setInputSize] = useState("4"); // FIXED input handling
// const [cards, setCards] = useState([]);
// const [flipped, setFlipped] = useState([]);
// const [matched, setMatched] = useState([]);
// const [disabled, setDisabled] = useState(false);
// const [error, setError] = useState("");
// const [won, setWon] = useState(false);
// // Handle user typing
// const handleGridSizeInput = (e) => {
// const value = e.target.value;
// setInputSize(value);
// const size = Number(value);
// if (size >= 2 && size <= 10 && size % 2 === 0) {
// setGridSize(size);
// setError("");
// } else {
// setError("Even numbers only (2,4,6,8,10)");
// }
// };
// // Initialize game
// const initializeGame = useCallback(() => {
// const total = gridSize * gridSize;
// const pairCount = total / 2;
// const nums = [...Array(pairCount).keys()].map((n) => n + 1);
// const shuffled = fisherYatesShuffle([...nums, ...nums]).map(
// (num, index) => ({
// id: index,
// number: num,
// })
// );
// setCards(shuffled);
// setFlipped([]);
// setMatched([]);
// setDisabled(false);
// setWon(false);
// }, [gridSize]);
// useEffect(() => initializeGame(), [initializeGame]);
// // Card click logic
// const handleClick = (id) => {
// if (disabled || flipped.includes(id) || matched.includes(id)) return;
// const newFlipped = [...flipped, id];
// setFlipped(newFlipped);
// if (newFlipped.length === 2) {
// setDisabled(true);
// const [a, b] = newFlipped;
// if (cards[a].number === cards[b].number) {
// setMatched((prev) => [...prev, a, b]);
// setFlipped([]);
// setDisabled(false);
// } else {
// setTimeout(() => {
// setFlipped([]);
// setDisabled(false);
// }, 800);
// }
// }
// };
// // Win checker
// useEffect(() => {
// if (matched.length === cards.length && cards.length !== 0) {
// setWon(true);
// }
// }, [matched, cards]);
// return (
// <div className="min-h-screen flex flex-col items-center py-10 bg-gradient-to-br from-gray-100 to-gray-300">
// <h1 className="text-4xl font-extrabold mb-6 text-slate-800 tracking-wide">
// Memory Match
// </h1>
// {/* Grid Size Input */}
// <div className="mb-4 text-lg">
// <label className="font-semibold text-slate-700">Grid Size:</label>
// <input
// type="number"
// value={inputSize}
// onChange={handleGridSizeInput}
// min="2"
// max="10"
// className="ml-3 w-20 px-3 py-1 border rounded-lg shadow bg-white"
// />
// {error && <p className="mt-2 text-red-600 text-sm">{error}</p>}
// </div>
// {/* Game Grid */}
// <div
// className="grid gap-3 mx-auto transition-all duration-300"
// style={{
// gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
// width: `${gridSize * 4.2}rem`,
// maxWidth: "95vw",
// }}
// >
// {cards.map((card) => {
// const open = flipped.includes(card.id) || matched.includes(card.id);
// return (
// <div
// key={card.id}
// onClick={() => handleClick(card.id)}
// className={`
// aspect-square flex items-center justify-center rounded-xl
// text-xl font-bold shadow-md cursor-pointer transition-all
// ${
// matched.includes(card.id)
// ? "bg-emerald-500 text-white scale-105"
// : open
// ? "bg-blue-600 text-white"
// : "bg-slate-300 text-slate-500 hover:bg-slate-400"
// }
// `}
// >
// {open ? card.number : "?"}
// </div>
// );
// })}
// </div>
// {/* Win message */}
// {won && (
// <p className="text-3xl mt-6 font-bold text-emerald-600 animate-bounce">
// 🎉 You Won! 🎉
// </p>
// )}
// {/* Reset */}
// <button
// onClick={initializeGame}
// className="mt-6 px-6 py-2 text-lg bg-blue-700 text-white rounded-lg shadow hover:bg-blue-800"
// >
// Reset Game
// </button>
// </div>
// );
// };

// export default MemoryGame;

// "use client";
// import React, { useEffect, useState, useCallback } from "react";

// // Fisher-Yates shuffle
// function fisherYatesShuffle(array) {
// const arr = array.slice();
// for (let i = arr.length - 1; i > 0; i--) {
// const j = Math.floor(Math.random() * (i + 1));
// [arr[i], arr[j]] = [arr[j], arr[i]];
// }
// return arr;
// }

// const MemoryGame = () => {
// const [gridSize, setGridSize] = useState(4);
// const [inputSize, setInputSize] = useState("4");
// const [cards, setCards] = useState([]);
// const [flipped, setFlipped] = useState([]);
// const [matched, setMatched] = useState([]);
// const [disabled, setDisabled] = useState(false);
// const [error, setError] = useState("");
// const [won, setWon] = useState(false);

// // 🔹 NEW: multiplayer state
// const [currentPlayer, setCurrentPlayer] = useState(1); // 1 or 2
// const [scores, setScores] = useState({ 1: 0, 2: 0 });
// const [winnerText, setWinnerText] = useState("");

// // Handle grid size input (same as before, but stored separately)
// const handleGridSizeInput = (e) => {
// const value = e.target.value;
// setInputSize(value);

// const size = Number(value);
// if (size >= 2 && size <= 10 && size % 2 === 0) {
// setGridSize(size);
// setError("");
// } else {
// setError("Even numbers only (2,4,6,8,10)");
// }
// };

// // Initialize / reset game
// const initializeGame = useCallback(() => {
// const total = gridSize * gridSize;
// const pairCount = total / 2;

// const nums = [...Array(pairCount).keys()].map((n) => n + 1);
// const shuffled = fisherYatesShuffle([...nums, ...nums]).map(
// (num, index) => ({
// id: index,
// number: num,
// })
// );

// setCards(shuffled);
// setFlipped([]);
// setMatched([]);
// setDisabled(false);
// setWon(false);

// // reset multiplayer stuff
// setCurrentPlayer(1);
// setScores({ 1: 0, 2: 0 });
// setWinnerText("");
// }, [gridSize]);

// useEffect(() => {
// initializeGame();
// }, [initializeGame]);

// // Handle card click
// const handleClick = (id) => {
// if (disabled) return;
// if (flipped.includes(id) || matched.includes(id)) return;

// const newFlipped = [...flipped, id];
// setFlipped(newFlipped);

// if (newFlipped.length === 2) {
// setDisabled(true);
// const [a, b] = newFlipped;
// const isMatch = cards[a].number === cards[b].number;

// if (isMatch) {
// // Add to matched
// setMatched((prev) => {
// const updated = [...prev, a, b];

// // update score for current player
// setScores((prevScores) => ({
// ...prevScores,
// [currentPlayer]: prevScores[currentPlayer] + 1,
// }));

// return updated;
// });

// // Clear flipped and allow same player to continue
// setTimeout(() => {
// setFlipped([]);
// setDisabled(false);
// }, 500);
// } else {
// // No match: switch to other player after small delay
// setTimeout(() => {
// setFlipped([]);
// setDisabled(false);
// setCurrentPlayer((p) => (p === 1 ? 2 : 1));
// }, 800);
// }
// }
// };

// // Win + winner calculation
// useEffect(() => {
// if (matched.length === cards.length && cards.length !== 0) {
// setWon(true);
// if (scores[1] > scores[2]) {
// setWinnerText("Player 1 Wins! 🎉");
// } else if (scores[2] > scores[1]) {
// setWinnerText("Player 2 Wins! 🎉");
// } else {
// setWinnerText("It's a Tie! 🤝");
// }
// }
// }, [matched, cards.length, scores]);

// const isOpen = (id) => flipped.includes(id) || matched.includes(id);

// return (
// <div className="min-h-screen flex flex-col items-center py-10 bg-gradient-to-br from-gray-100 to-gray-300">

// <h1 className="text-4xl font-extrabold mb-4 text-slate-800 tracking-wide">
// Memory Match – 2 Player
// </h1>

// {/* Scores + current player */}
// <div className="flex gap-6 mb-4 text-lg">
// <div className={`px-4 py-2 rounded-lg shadow bg-white`}>
// <span className="font-semibold">Player 1:</span>{" "}
// <span className="font-bold text-blue-700">{scores[1]}</span>
// </div>
// <div className={`px-4 py-2 rounded-lg shadow bg-white`}>
// <span className="font-semibold">Player 2:</span>{" "}
// <span className="font-bold text-emerald-700">{scores[2]}</span>
// </div>
// </div>

// <div className="mb-4 text-lg">
// <span className="font-semibold text-slate-700">Current Turn: </span>
// <span
// className={`font-bold ${
// currentPlayer === 1 ? "text-blue-700" : "text-emerald-700"
// }`}
// >
// Player {currentPlayer}
// </span>
// </div>

// {/* Grid Size Input */}
// <div className="mb-4 text-lg">
// <label className="font-semibold text-slate-700">Grid Size:</label>
// <input
// type="number"
// value={inputSize}
// onChange={handleGridSizeInput}
// min="2"
// max="10"
// className="ml-3 w-20 px-3 py-1 border rounded-lg shadow bg-white"
// />
// {error && <p className="mt-2 text-red-600 text-sm">{error}</p>}
// </div>

// {/* Game Grid */}
// <div
// className="grid gap-3 mx-auto transition-all duration-300"
// style={{
// gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
// width: `${gridSize * 4.2}rem`,
// maxWidth: "95vw",
// }}
// >
// {cards.map((card) => (
// <div
// key={card.id}
// onClick={() => handleClick(card.id)}
// className={`
// aspect-square flex items-center justify-center rounded-xl
// text-xl font-bold shadow-md cursor-pointer transition-all
// ${
// matched.includes(card.id)
// ? "bg-emerald-500 text-white scale-105"
// : isOpen(card.id)
// ? "bg-blue-600 text-white"
// : "bg-slate-300 text-slate-500 hover:bg-slate-400"
// }
// `}
// >
// {isOpen(card.id) ? card.number : "?"}
// </div>
// ))}
// </div>

// {/* Win / Result */}
// {won && (
// <p className="text-2xl mt-6 font-bold text-emerald-700">
// {winnerText}
// </p>
// )}

// {/* Reset */}
// <button
// onClick={initializeGame}
// className="mt-6 px-6 py-2 text-lg bg-blue-700 text-white rounded-lg shadow hover:bg-blue-800"
// >
// Reset Game
// </button>
// </div>
// );
// };

// export default MemoryGame;
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains over 500 lines of commented-out code (lines 1-514). Commented code should be removed from the codebase as it clutters the file, makes it harder to maintain, and can be recovered from version control history if needed. Consider removing all the commented sections and keeping only the active implementation.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +29
// import { Geist, Geist_Mono } from "next/font/google";
// import "./globals.css";

// const geistSans = Geist({
// variable: "--font-geist-sans",
// subsets: ["latin"],
// });

// const geistMono = Geist_Mono({
// variable: "--font-geist-mono",
// subsets: ["latin"],
// });

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
// export const metadata = {
// title: "Memory Game",
// description: "A fun and challenging memory game built with Next.js.",
// };

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
// export default function RootLayout({ children }) {
// return (
// <html lang="en">
// <body
// className={`${geistSans.variable} ${geistMono.variable} antialiased`}
// >
// {children}
// </body>
// </html>
// );
// }
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains 30 lines of commented-out code including imports and component logic. Remove these commented sections to maintain code clarity.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! I've cleaned up the commented code and added basic input validation. Please let me know if further improvements are needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant