diff --git a/.gitconfig b/.gitconfig index 3219448..618e049 100644 --- a/.gitconfig +++ b/.gitconfig @@ -1,3 +1,3 @@ [user] - email = e1@emergent.sh - name = E1 + email = + name = diff --git a/backend/.env b/backend/.env deleted file mode 100644 index 0f4322c..0000000 --- a/backend/.env +++ /dev/null @@ -1,2 +0,0 @@ -MONGO_URL="mongodb://localhost:27017" -DB_NAME="test_database" \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt deleted file mode 100644 index 1e0e438..0000000 --- a/backend/requirements.txt +++ /dev/null @@ -1,25 +0,0 @@ -fastapi==0.110.1 -uvicorn==0.25.0 -boto3>=1.34.129 -requests-oauthlib>=2.0.0 -cryptography>=42.0.8 -python-dotenv>=1.0.1 -pymongo==4.5.0 -pydantic>=2.6.4 -email-validator>=2.2.0 -pyjwt>=2.10.1 -passlib>=1.7.4 -tzdata>=2024.2 -motor==3.3.1 -pytest>=8.0.0 -black>=24.1.1 -isort>=5.13.2 -flake8>=7.0.0 -mypy>=1.8.0 -python-jose>=3.3.0 -requests>=2.31.0 -pandas>=2.2.0 -numpy>=1.26.0 -python-multipart>=0.0.9 -jq>=1.6.0 -typer>=0.9.0 diff --git a/frontend/.env b/frontend/.env index 3b7ee31..6210cb8 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,2 +1 @@ -REACT_APP_BACKEND_URL=https://42902f61-f81d-4f93-8ed1-4a8abb3a8b3b.preview.emergentagent.com WDS_SOCKET_PORT=443 \ No newline at end of file diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json index aaa3fb7..8b12642 100644 --- a/frontend/jsconfig.json +++ b/frontend/jsconfig.json @@ -4,7 +4,8 @@ "ignoreDeprecations": "6.0", "paths": { "@/*": ["src/*"] - } + }, + "ignoreDeprecations": "6.0" }, "include": ["src"] } diff --git a/frontend/public/index.html b/frontend/public/index.html index d1c3594..a6c7dd9 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -1,5 +1,21 @@ - + +<<<<<<< HEAD + + + + + + GameHub + + + +
+ +======= @@ -11,4 +27,5 @@
+>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 diff --git a/frontend/src/games/Checkers.js b/frontend/src/games/Checkers.js index df6a1ee..58f3318 100644 --- a/frontend/src/games/Checkers.js +++ b/frontend/src/games/Checkers.js @@ -71,8 +71,15 @@ const Checkers = () => { if (!isValidSquare(newRow, newCol)) continue; if (!boardState[newRow][newCol]) { +<<<<<<< HEAD + // Regular move to empty square moves.push({ row: newRow, col: newCol, type: "move" }); } else if (boardState[newRow][newCol].color !== piece.color) { + // Potential capture +======= + moves.push({ row: newRow, col: newCol, type: "move" }); + } else if (boardState[newRow][newCol].color !== piece.color) { +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 const jumpRow = newRow + dRow; const jumpCol = newCol + dCol; @@ -128,9 +135,13 @@ const Checkers = () => { capturedRow = null, capturedCol = null, ) => { +<<<<<<< HEAD + const newBoard = board.map((row) => [...row]); +======= const newBoard = board.map((row) => row.map((cell) => (cell ? { ...cell } : null)), ); +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 const piece = newBoard[fromRow][fromCol]; // Move piece @@ -173,7 +184,11 @@ const Checkers = () => { } else { // End turn setSelectedSquare(null); +<<<<<<< HEAD + setCurrentPlayer((prev) => (prev === "red" ? "black" : "red")); +======= setCurrentPlayer(currentPlayer === "red" ? "black" : "red"); +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 setMustCapture(false); // Check for win conditions diff --git a/frontend/src/games/Chess.js b/frontend/src/games/Chess.js index bf1c5bf..da25078 100644 --- a/frontend/src/games/Chess.js +++ b/frontend/src/games/Chess.js @@ -1,33 +1,56 @@ -import React, { useState, useCallback } from 'react'; -import { RotateCcw, Crown, AlertCircle } from 'lucide-react'; +import React, { useState, useCallback } from "react"; +import { RotateCcw, Crown, AlertCircle } from "lucide-react"; const INITIAL_BOARD = [ - ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'], - ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'], + ["r", "n", "b", "q", "k", "b", "n", "r"], + ["p", "p", "p", "p", "p", "p", "p", "p"], [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], - ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'], - ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'] + ["P", "P", "P", "P", "P", "P", "P", "P"], + ["R", "N", "B", "Q", "K", "B", "N", "R"], ]; const PIECE_SYMBOLS = { - 'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙', - 'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟' + K: "♔", + Q: "♕", + R: "♖", + B: "♗", + N: "♘", + P: "♙", + k: "♚", + q: "♛", + r: "♜", + b: "♝", + n: "♞", + p: "♟", }; const PIECE_NAMES = { - 'K': 'King', 'Q': 'Queen', 'R': 'Rook', 'B': 'Bishop', 'N': 'Knight', 'P': 'Pawn', - 'k': 'King', 'q': 'Queen', 'r': 'Rook', 'b': 'Bishop', 'n': 'Knight', 'p': 'Pawn' + K: "King", + Q: "Queen", + R: "Rook", + B: "Bishop", + N: "Knight", + P: "Pawn", + k: "King", + q: "Queen", + r: "Rook", + b: "Bishop", + n: "Knight", + p: "Pawn", }; const Chess = () => { - const [board, setBoard] = useState(INITIAL_BOARD.map(row => [...row])); + const [board, setBoard] = useState(INITIAL_BOARD.map((row) => [...row])); const [selectedSquare, setSelectedSquare] = useState(null); - const [currentPlayer, setCurrentPlayer] = useState('white'); + const [currentPlayer, setCurrentPlayer] = useState("white"); const [moveHistory, setMoveHistory] = useState([]); - const [capturedPieces, setCapturedPieces] = useState({ white: [], black: [] }); + const [capturedPieces, setCapturedPieces] = useState({ + white: [], + black: [], + }); const isValidSquare = (row, col) => { return row >= 0 && row < 8 && col >= 0 && col < 8; @@ -42,140 +65,206 @@ const Chess = () => { }; const isCurrentPlayerPiece = (piece) => { - return currentPlayer === 'white' ? isWhitePiece(piece) : isBlackPiece(piece); + return currentPlayer === "white" + ? isWhitePiece(piece) + : isBlackPiece(piece); }; const isOpponentPiece = (piece) => { - return currentPlayer === 'white' ? isBlackPiece(piece) : isWhitePiece(piece); + return currentPlayer === "white" + ? isBlackPiece(piece) + : isWhitePiece(piece); }; - const getValidMoves = useCallback((row, col, piece, boardState = board) => { - const moves = []; - const pieceType = piece.toLowerCase(); - - switch (pieceType) { - case 'p': // Pawn - const direction = isWhitePiece(piece) ? -1 : 1; - const startRow = isWhitePiece(piece) ? 6 : 1; - - // Move forward - if (isValidSquare(row + direction, col) && !boardState[row + direction][col]) { - moves.push([row + direction, col]); - - // Initial two-square move - if (row === startRow && !boardState[row + 2 * direction][col]) { - moves.push([row + 2 * direction, col]); - } - } - - // Capture diagonally - for (const colOffset of [-1, 1]) { - const newRow = row + direction; - const newCol = col + colOffset; - if (isValidSquare(newRow, newCol) && boardState[newRow][newCol] && - isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol])) { - moves.push([newRow, newCol]); + const getValidMoves = useCallback( + (row, col, piece, boardState = board) => { + const moves = []; + const pieceType = piece.toLowerCase(); + + switch (pieceType) { + case "p": // Pawn + const direction = isWhitePiece(piece) ? -1 : 1; + const startRow = isWhitePiece(piece) ? 6 : 1; + + // Move forward + if ( + isValidSquare(row + direction, col) && + !boardState[row + direction][col] + ) { + moves.push([row + direction, col]); + + // Initial two-square move + if (row === startRow && !boardState[row + 2 * direction][col]) { + moves.push([row + 2 * direction, col]); + } } - } - break; - - case 'r': // Rook - const rookDirections = [[0, 1], [0, -1], [1, 0], [-1, 0]]; - for (const [dRow, dCol] of rookDirections) { - for (let i = 1; i < 8; i++) { - const newRow = row + i * dRow; - const newCol = col + i * dCol; - - if (!isValidSquare(newRow, newCol)) break; - - if (!boardState[newRow][newCol]) { + + // Capture diagonally + for (const colOffset of [-1, 1]) { + const newRow = row + direction; + const newCol = col + colOffset; + if ( + isValidSquare(newRow, newCol) && + boardState[newRow][newCol] && + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol]) + ) { moves.push([newRow, newCol]); - } else { - if (isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol])) { + } + } + break; + + case "r": // Rook + const rookDirections = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + ]; + for (const [dRow, dCol] of rookDirections) { + for (let i = 1; i < 8; i++) { + const newRow = row + i * dRow; + const newCol = col + i * dCol; + + if (!isValidSquare(newRow, newCol)) break; + + if (!boardState[newRow][newCol]) { moves.push([newRow, newCol]); + } else { + if ( + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol]) + ) { + moves.push([newRow, newCol]); + } + break; } - break; } } - } - break; - - case 'n': // Knight - const knightMoves = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]]; - for (const [dRow, dCol] of knightMoves) { - const newRow = row + dRow; - const newCol = col + dCol; - - if (isValidSquare(newRow, newCol) && - (!boardState[newRow][newCol] || - isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol]))) { - moves.push([newRow, newCol]); - } - } - break; - - case 'b': // Bishop - const bishopDirections = [[1, 1], [1, -1], [-1, 1], [-1, -1]]; - for (const [dRow, dCol] of bishopDirections) { - for (let i = 1; i < 8; i++) { - const newRow = row + i * dRow; - const newCol = col + i * dCol; - - if (!isValidSquare(newRow, newCol)) break; - - if (!boardState[newRow][newCol]) { + break; + + case "n": // Knight + const knightMoves = [ + [-2, -1], + [-2, 1], + [-1, -2], + [-1, 2], + [1, -2], + [1, 2], + [2, -1], + [2, 1], + ]; + for (const [dRow, dCol] of knightMoves) { + const newRow = row + dRow; + const newCol = col + dCol; + + if ( + isValidSquare(newRow, newCol) && + (!boardState[newRow][newCol] || + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol])) + ) { moves.push([newRow, newCol]); - } else { - if (isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol])) { + } + } + break; + + case "b": // Bishop + const bishopDirections = [ + [1, 1], + [1, -1], + [-1, 1], + [-1, -1], + ]; + for (const [dRow, dCol] of bishopDirections) { + for (let i = 1; i < 8; i++) { + const newRow = row + i * dRow; + const newCol = col + i * dCol; + + if (!isValidSquare(newRow, newCol)) break; + + if (!boardState[newRow][newCol]) { moves.push([newRow, newCol]); + } else { + if ( + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol]) + ) { + moves.push([newRow, newCol]); + } + break; } - break; } } - } - break; - - case 'q': // Queen - const queenDirections = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; - for (const [dRow, dCol] of queenDirections) { - for (let i = 1; i < 8; i++) { - const newRow = row + i * dRow; - const newCol = col + i * dCol; - - if (!isValidSquare(newRow, newCol)) break; - - if (!boardState[newRow][newCol]) { - moves.push([newRow, newCol]); - } else { - if (isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol])) { + break; + + case "q": // Queen + const queenDirections = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + [1, 1], + [1, -1], + [-1, 1], + [-1, -1], + ]; + for (const [dRow, dCol] of queenDirections) { + for (let i = 1; i < 8; i++) { + const newRow = row + i * dRow; + const newCol = col + i * dCol; + + if (!isValidSquare(newRow, newCol)) break; + + if (!boardState[newRow][newCol]) { moves.push([newRow, newCol]); + } else { + if ( + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol]) + ) { + moves.push([newRow, newCol]); + } + break; } - break; } } - } - break; - - case 'k': // King - const kingMoves = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [1, -1], [-1, 1], [-1, -1]]; - for (const [dRow, dCol] of kingMoves) { - const newRow = row + dRow; - const newCol = col + dCol; - - if (isValidSquare(newRow, newCol) && - (!boardState[newRow][newCol] || - isCurrentPlayerPiece(piece) !== isCurrentPlayerPiece(boardState[newRow][newCol]))) { - moves.push([newRow, newCol]); + break; + + case "k": // King + const kingMoves = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + [1, 1], + [1, -1], + [-1, 1], + [-1, -1], + ]; + for (const [dRow, dCol] of kingMoves) { + const newRow = row + dRow; + const newCol = col + dCol; + + if ( + isValidSquare(newRow, newCol) && + (!boardState[newRow][newCol] || + isCurrentPlayerPiece(piece) !== + isCurrentPlayerPiece(boardState[newRow][newCol])) + ) { + moves.push([newRow, newCol]); + } } - } - break; + break; - default: - break; - } + default: + break; + } - return moves; - }, [board]); + return moves; + }, + [board, currentPlayer], + ); const handleSquareClick = (row, col) => { const piece = board[row][col]; @@ -183,7 +272,7 @@ const Chess = () => { if (selectedSquare) { const [selectedRow, selectedCol] = selectedSquare; const selectedPiece = board[selectedRow][selectedCol]; - + if (selectedRow === row && selectedCol === col) { // Deselect setSelectedSquare(null); @@ -191,33 +280,38 @@ const Chess = () => { } const validMoves = getValidMoves(selectedRow, selectedCol, selectedPiece); - const isValidMove = validMoves.some(([moveRow, moveCol]) => moveRow === row && moveCol === col); + const isValidMove = validMoves.some( + ([moveRow, moveCol]) => moveRow === row && moveCol === col, + ); if (isValidMove) { // Make the move - const newBoard = board.map(r => [...r]); + const newBoard = board.map((r) => [...r]); const capturedPiece = newBoard[row][col]; - + newBoard[row][col] = selectedPiece; newBoard[selectedRow][selectedCol] = null; // Handle captures if (capturedPiece) { - const captureColor = isWhitePiece(capturedPiece) ? 'white' : 'black'; - setCapturedPieces(prev => ({ + const captureColor = isWhitePiece(capturedPiece) ? "white" : "black"; + setCapturedPieces((prev) => ({ ...prev, - [captureColor]: [...prev[captureColor], capturedPiece] + [captureColor]: [...prev[captureColor], capturedPiece], })); } setBoard(newBoard); - setMoveHistory(prev => [...prev, { - from: [selectedRow, selectedCol], - to: [row, col], - piece: selectedPiece, - captured: capturedPiece - }]); - setCurrentPlayer(currentPlayer === 'white' ? 'black' : 'white'); + setMoveHistory((prev) => [ + ...prev, + { + from: [selectedRow, selectedCol], + to: [row, col], + piece: selectedPiece, + captured: capturedPiece, + }, + ]); + setCurrentPlayer(currentPlayer === "white" ? "black" : "white"); setSelectedSquare(null); } else if (piece && isCurrentPlayerPiece(piece)) { // Select new piece @@ -233,9 +327,9 @@ const Chess = () => { }; const resetGame = () => { - setBoard(INITIAL_BOARD.map(row => [...row])); + setBoard(INITIAL_BOARD.map((row) => [...row])); setSelectedSquare(null); - setCurrentPlayer('white'); + setCurrentPlayer("white"); setMoveHistory([]); setCapturedPieces({ white: [], black: [] }); }; @@ -243,14 +337,18 @@ const Chess = () => { const getSquareClassName = (row, col) => { const isLight = (row + col) % 2 === 0; let baseClass = `w-16 h-16 flex items-center justify-center text-4xl cursor-pointer transition-all duration-200 hover:scale-105 `; - + if (isLight) { baseClass += "bg-amber-100 hover:bg-amber-200 "; } else { baseClass += "bg-amber-800 hover:bg-amber-700 "; } - if (selectedSquare && selectedSquare[0] === row && selectedSquare[1] === col) { + if ( + selectedSquare && + selectedSquare[0] === row && + selectedSquare[1] === col + ) { baseClass += "ring-4 ring-blue-500 "; } @@ -259,8 +357,16 @@ const Chess = () => { const [selectedRow, selectedCol] = selectedSquare; const selectedPiece = board[selectedRow][selectedCol]; if (selectedPiece) { - const validMoves = getValidMoves(selectedRow, selectedCol, selectedPiece); - if (validMoves.some(([moveRow, moveCol]) => moveRow === row && moveCol === col)) { + const validMoves = getValidMoves( + selectedRow, + selectedCol, + selectedPiece, + ); + if ( + validMoves.some( + ([moveRow, moveCol]) => moveRow === row && moveCol === col, + ) + ) { baseClass += "ring-2 ring-green-400 bg-green-200 hover:bg-green-300 "; } } @@ -286,15 +392,17 @@ const Chess = () => { {/* Game Board */}
-
+
- {currentPlayer === 'white' ? 'White' : 'Black'} to move + {currentPlayer === "white" ? "White" : "Black"} to move
- + - )) + )), )}
- + {/* Board coordinates */}
- {['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].map(letter => ( - {letter} + {["a", "b", "c", "d", "e", "f", "g", "h"].map((letter) => ( + + {letter} + ))}
@@ -336,11 +455,15 @@ const Chess = () => {
{/* Captured Pieces */}
-

Captured Pieces

- +

+ Captured Pieces +

+
-

Black captured:

+

+ Black captured: +

{capturedPieces.black.map((piece, index) => ( @@ -349,12 +472,17 @@ const Chess = () => { ))}
- +
-

White captured:

+

+ White captured: +

{capturedPieces.white.map((piece, index) => ( - + {PIECE_SYMBOLS[piece]} ))} @@ -365,14 +493,31 @@ const Chess = () => { {/* Move History */}
-

Recent Moves

+

+ Recent Moves +

- {moveHistory.slice(-5).reverse().map((move, index) => ( -
- {PIECE_NAMES[move.piece]} {String.fromCharCode(97 + move.from[1])}{8 - move.from[0]} → {String.fromCharCode(97 + move.to[1])}{8 - move.to[0]} - {move.captured && (captured {PIECE_NAMES[move.captured]})} -
- ))} + {moveHistory + .slice(-5) + .reverse() + .map((move, index) => ( +
+ {PIECE_NAMES[move.piece]}{" "} + {String.fromCharCode(97 + move.from[1])} + {8 - move.from[0]} →{" "} + {String.fromCharCode(97 + move.to[1])} + {8 - move.to[0]} + {move.captured && ( + + {" "} + (captured {PIECE_NAMES[move.captured]}) + + )} +
+ ))}
@@ -387,7 +532,10 @@ const Chess = () => {
  • • Valid moves are highlighted in green
  • • Click a highlighted square to move
  • • Capture opponent pieces to gain advantage
  • -
  • • This is a simplified version - no castling, en passant, or check detection
  • +
  • + • This is a simplified version - no castling, en passant, or + check detection +
  • @@ -397,4 +545,4 @@ const Chess = () => { ); }; -export default Chess; \ No newline at end of file +export default Chess; diff --git a/frontend/src/games/FlappyBird.css b/frontend/src/games/FlappyBird.css new file mode 100644 index 0000000..05caacf --- /dev/null +++ b/frontend/src/games/FlappyBird.css @@ -0,0 +1,4 @@ +.flappy-game-area { + width: 400px; + height: 600px; +} diff --git a/frontend/src/games/FlappyBird.js b/frontend/src/games/FlappyBird.js index dd94940..b6c6a10 100644 --- a/frontend/src/games/FlappyBird.js +++ b/frontend/src/games/FlappyBird.js @@ -1,5 +1,6 @@ -import React, { useState, useEffect, useCallback, useRef } from 'react'; -import { RotateCcw, Trophy, Bird, Play, Pause } from 'lucide-react'; +import React, { useState, useEffect, useCallback, useRef } from "react"; +import { RotateCcw, Trophy, Bird, Play, Pause } from "lucide-react"; +import "./FlappyBird.css"; const GAME_HEIGHT = 600; const GAME_WIDTH = 400; @@ -13,13 +14,13 @@ const PIPE_SPEED = 3; const FlappyBird = () => { const [bird, setBird] = useState({ y: GAME_HEIGHT / 2, velocity: 0 }); const [pipes, setPipes] = useState([]); - const [gameStatus, setGameStatus] = useState('ready'); // ready, playing, gameOver + const [gameStatus, setGameStatus] = useState("ready"); // ready, playing, gameOver const [score, setScore] = useState(0); const [highScore, setHighScore] = useState(() => { - const saved = localStorage.getItem('flappyBirdHighScore'); + const saved = localStorage.getItem("flappyBirdHighScore"); return saved ? parseInt(saved) : 0; }); - + const gameLoopRef = useRef(); const pipeIdRef = useRef(0); @@ -30,7 +31,7 @@ const FlappyBird = () => { x: GAME_WIDTH, topHeight: pipeHeight, bottomY: pipeHeight + PIPE_GAP, - passed: false + passed: false, }; }, []); @@ -38,17 +39,17 @@ const FlappyBird = () => { setBird({ y: GAME_HEIGHT / 2, velocity: 0 }); setPipes([]); setScore(0); - setGameStatus('ready'); + setGameStatus("ready"); pipeIdRef.current = 0; }; const jump = useCallback(() => { - if (gameStatus === 'ready') { - setGameStatus('playing'); + if (gameStatus === "ready") { + setGameStatus("playing"); } - - if (gameStatus !== 'gameOver') { - setBird(prev => ({ ...prev, velocity: JUMP_FORCE })); + + if (gameStatus !== "gameOver") { + setBird((prev) => ({ ...prev, velocity: JUMP_FORCE })); } }, [gameStatus]); @@ -73,39 +74,42 @@ const FlappyBird = () => { }, []); const gameLoop = useCallback(() => { - if (gameStatus !== 'playing') return; + if (gameStatus !== "playing") return; - setBird(prev => { + setBird((prev) => { const newVelocity = prev.velocity + GRAVITY; const newY = prev.y + newVelocity; return { y: newY, velocity: newVelocity }; }); - setPipes(prev => { - let newPipes = prev.map(pipe => ({ ...pipe, x: pipe.x - PIPE_SPEED })); - + setPipes((prev) => { + let newPipes = prev.map((pipe) => ({ ...pipe, x: pipe.x - PIPE_SPEED })); + // Remove pipes that are off screen - newPipes = newPipes.filter(pipe => pipe.x + PIPE_WIDTH > -100); - + newPipes = newPipes.filter((pipe) => pipe.x + PIPE_WIDTH > -100); + // Add new pipe if needed - if (newPipes.length === 0 || newPipes[newPipes.length - 1].x < GAME_WIDTH - 200) { + if ( + newPipes.length === 0 || + newPipes[newPipes.length - 1].x < GAME_WIDTH - 200 + ) { newPipes.push(createPipe()); } - + // Check for score updates let newScore = score; - newPipes.forEach(pipe => { + newPipes.forEach((pipe) => { if (!pipe.passed && pipe.x + PIPE_WIDTH < 50) { pipe.passed = true; newScore++; } }); - + if (newScore !== score) { setScore(newScore); if (newScore > highScore) { setHighScore(newScore); - localStorage.setItem('flappyBirdHighScore', newScore.toString()); + localStorage.setItem("flappyBirdHighScore", newScore.toString()); } } @@ -115,17 +119,17 @@ const FlappyBird = () => { // Check collision useEffect(() => { - if (gameStatus === 'playing') { + if (gameStatus === "playing") { const collision = checkCollision(bird.y, pipes); if (collision) { - setGameStatus('gameOver'); + setGameStatus("gameOver"); } } }, [bird.y, pipes, gameStatus, checkCollision]); // Game loop useEffect(() => { - if (gameStatus === 'playing') { + if (gameStatus === "playing") { gameLoopRef.current = setInterval(gameLoop, 1000 / 60); // 60 FPS } else { clearInterval(gameLoopRef.current); @@ -137,14 +141,14 @@ const FlappyBird = () => { // Handle keyboard input useEffect(() => { const handleKeyPress = (e) => { - if (e.code === 'Space' || e.key === 'ArrowUp') { + if (e.key.toLowerCase() === "w") { e.preventDefault(); jump(); } }; - window.addEventListener('keydown', handleKeyPress); - return () => window.removeEventListener('keydown', handleKeyPress); + window.addEventListener("keydown", handleKeyPress); + return () => window.removeEventListener("keydown", handleKeyPress); }, [jump]); const renderPipe = (pipe) => ( @@ -190,14 +194,20 @@ const FlappyBird = () => {
    {score}
    -
    Score
    +
    + Score +
    - +
    -
    {highScore}
    -
    Best
    +
    + {highScore} +
    +
    + Best +
    @@ -211,16 +221,16 @@ const FlappyBird = () => {
    {/* Game Status */} - {gameStatus === 'ready' && ( + {gameStatus === "ready" && (
    - Press SPACE or Click to Start! + Press W or Click to Start!
    )} - {gameStatus === 'gameOver' && ( + {gameStatus === "gameOver" && (
    @@ -233,8 +243,7 @@ const FlappyBird = () => {
    {/* Background elements */} @@ -243,7 +252,7 @@ const FlappyBird = () => {
    - + {/* Ground */}
    @@ -282,9 +291,11 @@ const FlappyBird = () => { {/* Instructions */}
    -

    How to Play

    +

    + How to Play +

      -
    • • Press SPACE or click to make the bird fly
    • +
    • • Press W or click to make the bird fly
    • • Avoid hitting the pipes or ground
    • • Each pipe you pass gives you 1 point
    • • Try to beat your high score!
    • @@ -296,4 +307,4 @@ const FlappyBird = () => { ); }; -export default FlappyBird; \ No newline at end of file +export default FlappyBird; diff --git a/frontend/src/games/Minesweeper.css b/frontend/src/games/Minesweeper.css new file mode 100644 index 0000000..774df35 --- /dev/null +++ b/frontend/src/games/Minesweeper.css @@ -0,0 +1,3 @@ +.minesweeper-grid { + grid-template-columns: repeat(10, 1fr); +} diff --git a/frontend/src/games/Minesweeper.js b/frontend/src/games/Minesweeper.js index b004f45..4ee3e69 100644 --- a/frontend/src/games/Minesweeper.js +++ b/frontend/src/games/Minesweeper.js @@ -1,32 +1,37 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { Bomb, Flag, RotateCcw, Trophy, Clock, Zap } from 'lucide-react'; +import React, { useState, useEffect, useCallback } from "react"; +import { Bomb, Flag, RotateCcw, Trophy, Clock, Zap } from "lucide-react"; +import "./Minesweeper.css"; const GRID_SIZE = 10; const MINE_COUNT = 15; const Minesweeper = () => { const [grid, setGrid] = useState([]); - const [gameStatus, setGameStatus] = useState('playing'); // playing, won, lost + const [gameStatus, setGameStatus] = useState("playing"); // playing, won, lost const [flagCount, setFlagCount] = useState(MINE_COUNT); const [time, setTime] = useState(0); const [timerActive, setTimerActive] = useState(false); const initializeGrid = useCallback(() => { - const newGrid = Array(GRID_SIZE).fill().map(() => - Array(GRID_SIZE).fill().map(() => ({ - isMine: false, - isRevealed: false, - isFlagged: false, - neighborMines: 0 - })) - ); + const newGrid = Array(GRID_SIZE) + .fill() + .map(() => + Array(GRID_SIZE) + .fill() + .map(() => ({ + isMine: false, + isRevealed: false, + isFlagged: false, + neighborMines: 0, + })), + ); // Place mines randomly let minesPlaced = 0; while (minesPlaced < MINE_COUNT) { const row = Math.floor(Math.random() * GRID_SIZE); const col = Math.floor(Math.random() * GRID_SIZE); - + if (!newGrid[row][col].isMine) { newGrid[row][col].isMine = true; minesPlaced++; @@ -43,8 +48,10 @@ const Minesweeper = () => { const newRow = row + i; const newCol = col + j; if ( - newRow >= 0 && newRow < GRID_SIZE && - newCol >= 0 && newCol < GRID_SIZE && + newRow >= 0 && + newRow < GRID_SIZE && + newCol >= 0 && + newCol < GRID_SIZE && newGrid[newRow][newCol].isMine ) { count++; @@ -61,7 +68,7 @@ const Minesweeper = () => { const resetGame = () => { setGrid(initializeGrid()); - setGameStatus('playing'); + setGameStatus("playing"); setFlagCount(MINE_COUNT); setTime(0); setTimerActive(false); @@ -73,16 +80,20 @@ const Minesweeper = () => { useEffect(() => { let interval; - if (timerActive && gameStatus === 'playing') { + if (timerActive && gameStatus === "playing") { interval = setInterval(() => { - setTime(time => time + 1); + setTime((time) => time + 1); }, 1000); } return () => clearInterval(interval); }, [timerActive, gameStatus]); const revealCell = (row, col) => { - if (gameStatus !== 'playing' || grid[row][col].isRevealed || grid[row][col].isFlagged) { + if ( + gameStatus !== "playing" || + grid[row][col].isRevealed || + grid[row][col].isFlagged + ) { return; } @@ -91,7 +102,7 @@ const Minesweeper = () => { } const newGrid = [...grid]; - + if (newGrid[row][col].isMine) { // Game over - reveal all mines for (let r = 0; r < GRID_SIZE; r++) { @@ -101,7 +112,7 @@ const Minesweeper = () => { } } } - setGameStatus('lost'); + setGameStatus("lost"); setTimerActive(false); } else { // Reveal cells using flood fill @@ -111,13 +122,15 @@ const Minesweeper = () => { while (queue.length > 0) { const [currentRow, currentCol] = queue.shift(); const key = `${currentRow}-${currentCol}`; - + if (visited.has(key)) continue; visited.add(key); if ( - currentRow < 0 || currentRow >= GRID_SIZE || - currentCol < 0 || currentCol >= GRID_SIZE || + currentRow < 0 || + currentRow >= GRID_SIZE || + currentCol < 0 || + currentCol >= GRID_SIZE || newGrid[currentRow][currentCol].isRevealed || newGrid[currentRow][currentCol].isFlagged || newGrid[currentRow][currentCol].isMine @@ -147,7 +160,7 @@ const Minesweeper = () => { } if (revealedCount === GRID_SIZE * GRID_SIZE - MINE_COUNT) { - setGameStatus('won'); + setGameStatus("won"); setTimerActive(false); } } @@ -157,15 +170,15 @@ const Minesweeper = () => { const toggleFlag = (e, row, col) => { e.preventDefault(); - - if (gameStatus !== 'playing' || grid[row][col].isRevealed) { + + if (gameStatus !== "playing" || grid[row][col].isRevealed) { return; } const newGrid = [...grid]; newGrid[row][col].isFlagged = !newGrid[row][col].isFlagged; - - setFlagCount(prev => newGrid[row][col].isFlagged ? prev - 1 : prev + 1); + + setFlagCount((prev) => (newGrid[row][col].isFlagged ? prev - 1 : prev + 1)); setGrid(newGrid); }; @@ -173,37 +186,48 @@ const Minesweeper = () => { if (cell.isFlagged) { return ; } - + if (!cell.isRevealed) { return null; } - + if (cell.isMine) { return ; } - + if (cell.neighborMines > 0) { return ( - + {cell.neighborMines} ); } - + return null; }; const getCellClassName = (cell) => { - let baseClass = "w-8 h-8 border border-gray-400 flex items-center justify-center text-xs font-bold transition-all duration-200 hover:scale-105 "; - + let baseClass = + "w-8 h-8 border border-gray-400 flex items-center justify-center text-xs font-bold transition-all duration-200 hover:scale-105 "; + if (cell.isRevealed) { if (cell.isMine) { baseClass += "bg-red-500 hover:bg-red-600"; @@ -211,9 +235,10 @@ const Minesweeper = () => { baseClass += "bg-gray-200 dark:bg-gray-600"; } } else { - baseClass += "bg-gray-300 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 cursor-pointer"; + baseClass += + "bg-gray-300 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 cursor-pointer"; } - + return baseClass; }; @@ -236,7 +261,7 @@ const Minesweeper = () => { {flagCount}
    - +
    {time}s @@ -252,12 +277,14 @@ const Minesweeper = () => {
    {/* Game Status */} - {gameStatus !== 'playing' && ( + {gameStatus !== "playing" && (
    -
    - {gameStatus === 'won' ? ( +
    + {gameStatus === "won" ? ( <> You Won! @@ -275,7 +302,7 @@ const Minesweeper = () => { {/* Game Grid */}
    -
    +
    {grid.map((row, rowIndex) => row.map((cell, colIndex) => ( - )) + )), )}
    - +
    Left click to reveal • Right click to flag
    @@ -301,4 +328,4 @@ const Minesweeper = () => { ); }; -export default Minesweeper; \ No newline at end of file +export default Minesweeper; diff --git a/frontend/src/games/Snake.css b/frontend/src/games/Snake.css new file mode 100644 index 0000000..68bb773 --- /dev/null +++ b/frontend/src/games/Snake.css @@ -0,0 +1,5 @@ +.snake-grid { + grid-template-columns: repeat(20, 1fr); + width: 400px; + height: 400px; +} diff --git a/frontend/src/games/Snake.js b/frontend/src/games/Snake.js index 697af62..90b4e2c 100644 --- a/frontend/src/games/Snake.js +++ b/frontend/src/games/Snake.js @@ -1,4 +1,8 @@ import React, { useState, useEffect, useCallback } from "react"; +<<<<<<< HEAD +import { RotateCcw, Trophy, Zap } from "lucide-react"; +import "./Snake.css"; +======= import { RotateCcw, Trophy, @@ -8,6 +12,7 @@ import { ArrowLeft, ArrowRight, } from "lucide-react"; +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 const GRID_SIZE = 20; const INITIAL_SNAKE = [{ x: 10, y: 10 }]; @@ -99,6 +104,25 @@ const Snake = () => { useEffect(() => { const handleKeyPress = (e) => { +<<<<<<< HEAD + const key = e.key.toLowerCase(); + + if (gameStatus === "paused") { + if (e.key === "Enter") { + e.preventDefault(); + startGame(); + return; + } + + if (["w", "a", "s", "d"].includes(key)) { + e.preventDefault(); + + if (key === "w") setDirection({ x: 0, y: -1 }); + if (key === "s") setDirection({ x: 0, y: 1 }); + if (key === "a") setDirection({ x: -1, y: 0 }); + if (key === "d") setDirection({ x: 1, y: 0 }); + +======= const blockedKeys = [ "ArrowUp", "ArrowDown", @@ -121,10 +145,31 @@ const Snake = () => { if (gameStatus === "paused") { if (e.key === "Enter" || e.code === "NumpadEnter") { +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 startGame(); return; } } +<<<<<<< HEAD + + if (gameStatus !== "playing") return; + + switch (key) { + case "w": + e.preventDefault(); + if (direction.y === 0) setDirection({ x: 0, y: -1 }); + break; + case "s": + e.preventDefault(); + if (direction.y === 0) setDirection({ x: 0, y: 1 }); + break; + case "a": + e.preventDefault(); + if (direction.x === 0) setDirection({ x: -1, y: 0 }); + break; + case "d": + e.preventDefault(); +======= if (gameStatus !== "playing") return; @@ -139,6 +184,7 @@ const Snake = () => { if (direction.x === 0) setDirection({ x: -1, y: 0 }); break; case "ArrowRight": +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 if (direction.x === 0) setDirection({ x: 1, y: 0 }); break; default: @@ -146,10 +192,15 @@ const Snake = () => { } }; +<<<<<<< HEAD + window.addEventListener("keydown", handleKeyPress); + return () => window.removeEventListener("keydown", handleKeyPress); +======= const listenerOptions = { capture: true, passive: false }; window.addEventListener("keydown", handleKeyPress, listenerOptions); return () => window.removeEventListener("keydown", handleKeyPress, listenerOptions); +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 }, [direction, gameStatus]); const getCellContent = (x, y) => { @@ -213,7 +264,11 @@ const Snake = () => { {gameStatus === "paused" && (
    +<<<<<<< HEAD + Press Enter or W/A/S/D to Start +======= Press ENTER to Start +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858
    )} @@ -230,6 +285,9 @@ const Snake = () => { {/* Game Grid */}
    +<<<<<<< HEAD +
    +=======
    { height: "400px", }} > +>>>>>>> 5ba5d7a24af347ce938db3c693acb141f5a12858 {Array.from({ length: GRID_SIZE * GRID_SIZE }).map((_, index) => { const x = index % GRID_SIZE; const y = Math.floor(index / GRID_SIZE); @@ -263,21 +322,21 @@ const Snake = () => {

    - Use arrow keys to control the snake + Use W/A/S/D keys to control the snake

    diff --git a/frontend/src/games/TicTacToe.js b/frontend/src/games/TicTacToe.js index d1d2b12..c23193b 100644 --- a/frontend/src/games/TicTacToe.js +++ b/frontend/src/games/TicTacToe.js @@ -1,5 +1,26 @@ -import React, { useState, useEffect } from 'react'; -import { RotateCcw, Trophy, X, Circle } from 'lucide-react'; +import React, { useState, useEffect } from "react"; +import { RotateCcw, Trophy, X, Circle } from "lucide-react"; + +const WINNING_PATTERNS = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], + [0, 4, 8], + [2, 4, 6], +]; + +const calculateWinner = (squares) => { + for (let i = 0; i < WINNING_PATTERNS.length; i++) { + const [a, b, c] = WINNING_PATTERNS[i]; + if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { + return { winner: squares[a], line: [a, b, c] }; + } + } + return null; +}; const TicTacToe = () => { const [board, setBoard] = useState(Array(9).fill(null)); @@ -8,36 +29,20 @@ const TicTacToe = () => { const [winningLine, setWinningLine] = useState([]); const [scores, setScores] = useState({ X: 0, O: 0, draws: 0 }); - const winningPatterns = [ - [0, 1, 2], [3, 4, 5], [6, 7, 8], // rows - [0, 3, 6], [1, 4, 7], [2, 5, 8], // columns - [0, 4, 8], [2, 4, 6] // diagonals - ]; - - const calculateWinner = (squares) => { - for (let i = 0; i < winningPatterns.length; i++) { - const [a, b, c] = winningPatterns[i]; - if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { - return { winner: squares[a], line: [a, b, c] }; - } - } - return null; - }; - useEffect(() => { const result = calculateWinner(board); if (result) { setWinner(result.winner); setWinningLine(result.line); - setScores(prev => ({ + setScores((prev) => ({ ...prev, - [result.winner]: prev[result.winner] + 1 + [result.winner]: prev[result.winner] + 1, })); - } else if (board.every(square => square !== null)) { - setWinner('draw'); - setScores(prev => ({ + } else if (board.every((square) => square !== null)) { + setWinner("draw"); + setScores((prev) => ({ ...prev, - draws: prev.draws + 1 + draws: prev.draws + 1, })); } }, [board]); @@ -48,7 +53,7 @@ const TicTacToe = () => { } const newBoard = [...board]; - newBoard[index] = isXNext ? 'X' : 'O'; + newBoard[index] = isXNext ? "X" : "O"; setBoard(newBoard); setIsXNext(!isXNext); }; @@ -66,16 +71,18 @@ const TicTacToe = () => { }; const getSquareClassName = (index) => { - let baseClass = "w-24 h-24 bg-white dark:bg-gray-800 border-2 border-gray-300 dark:border-gray-600 flex items-center justify-center text-4xl font-bold transition-all duration-200 hover:scale-105 "; - + let baseClass = + "w-24 h-24 bg-white dark:bg-gray-800 border-2 border-gray-300 dark:border-gray-600 flex items-center justify-center text-4xl font-bold transition-all duration-200 hover:scale-105 "; + if (winningLine.includes(index)) { baseClass += "bg-green-200 dark:bg-green-700 border-green-500 "; } - + if (!board[index] && !winner) { - baseClass += "hover:bg-purple-50 dark:hover:bg-purple-900 cursor-pointer "; + baseClass += + "hover:bg-purple-50 dark:hover:bg-purple-900 cursor-pointer "; } - + return baseClass; }; @@ -88,19 +95,19 @@ const TicTacToe = () => { onClick={() => handleClick(index)} disabled={!!winner || !!board[index]} > - {value === 'X' && } - {value === 'O' && } + {value === "X" && } + {value === "O" && } ); }; const getStatusMessage = () => { - if (winner === 'draw') { + if (winner === "draw") { return "It's a draw!"; } else if (winner) { return `Player ${winner} wins!`; } else { - return `Player ${isXNext ? 'X' : 'O'}'s turn`; + return `Player ${isXNext ? "X" : "O"}'s turn`; } }; @@ -126,14 +133,14 @@ const TicTacToe = () => {
    {scores.X}
    - +
    Draws
    {scores.draws}
    - +
    @@ -152,7 +159,7 @@ const TicTacToe = () => { New Game - +