From 35b9f9403a4d09cdb151413178077548a6203b6b Mon Sep 17 00:00:00 2001 From: realACO Date: Thu, 12 Mar 2026 08:12:03 +0530 Subject: [PATCH] PYTHON TO JS --- .gitconfig | 4 +- backend/.env | 2 - backend/requirements.txt | 25 -- backend/server.py | 75 ----- frontend/.env | 1 - frontend/jsconfig.json | 5 +- frontend/public/index.html | 164 +--------- frontend/src/games/Checkers.js | 363 +++++++++++++-------- frontend/src/games/Chess.js | 490 +++++++++++++++++++---------- frontend/src/games/FlappyBird.css | 4 + frontend/src/games/FlappyBird.js | 97 +++--- frontend/src/games/Minesweeper.css | 3 + frontend/src/games/Minesweeper.js | 139 ++++---- frontend/src/games/Snake.css | 5 + frontend/src/games/Snake.js | 124 +++++--- frontend/src/games/TicTacToe.js | 107 ++++--- package.json | 11 + tests/__init__.py | 0 18 files changed, 862 insertions(+), 757 deletions(-) delete mode 100644 backend/.env delete mode 100644 backend/requirements.txt delete mode 100644 backend/server.py create mode 100644 frontend/src/games/FlappyBird.css create mode 100644 frontend/src/games/Minesweeper.css create mode 100644 frontend/src/games/Snake.css create mode 100644 package.json delete mode 100644 tests/__init__.py 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/backend/server.py b/backend/server.py deleted file mode 100644 index 05856c5..0000000 --- a/backend/server.py +++ /dev/null @@ -1,75 +0,0 @@ -from fastapi import FastAPI, APIRouter -from dotenv import load_dotenv -from starlette.middleware.cors import CORSMiddleware -from motor.motor_asyncio import AsyncIOMotorClient -import os -import logging -from pathlib import Path -from pydantic import BaseModel, Field -from typing import List -import uuid -from datetime import datetime - - -ROOT_DIR = Path(__file__).parent -load_dotenv(ROOT_DIR / '.env') - -# MongoDB connection -mongo_url = os.environ['MONGO_URL'] -client = AsyncIOMotorClient(mongo_url) -db = client[os.environ['DB_NAME']] - -# Create the main app without a prefix -app = FastAPI() - -# Create a router with the /api prefix -api_router = APIRouter(prefix="/api") - - -# Define Models -class StatusCheck(BaseModel): - id: str = Field(default_factory=lambda: str(uuid.uuid4())) - client_name: str - timestamp: datetime = Field(default_factory=datetime.utcnow) - -class StatusCheckCreate(BaseModel): - client_name: str - -# Add your routes to the router instead of directly to app -@api_router.get("/") -async def root(): - return {"message": "Hello World"} - -@api_router.post("/status", response_model=StatusCheck) -async def create_status_check(input: StatusCheckCreate): - status_dict = input.dict() - status_obj = StatusCheck(**status_dict) - _ = await db.status_checks.insert_one(status_obj.dict()) - return status_obj - -@api_router.get("/status", response_model=List[StatusCheck]) -async def get_status_checks(): - status_checks = await db.status_checks.find().to_list(1000) - return [StatusCheck(**status_check) for status_check in status_checks] - -# Include the router in the main app -app.include_router(api_router) - -app.add_middleware( - CORSMiddleware, - allow_credentials=True, - allow_origins=["*"], - allow_methods=["*"], - allow_headers=["*"], -) - -# Configure logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) -logger = logging.getLogger(__name__) - -@app.on_event("shutdown") -async def shutdown_db_client(): - client.close() 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 822d8e4..7bd4fe7 100644 --- a/frontend/jsconfig.json +++ b/frontend/jsconfig.json @@ -3,7 +3,8 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"] - } + }, + "ignoreDeprecations": "6.0" }, "include": ["src"] -} \ No newline at end of file +} diff --git a/frontend/public/index.html b/frontend/public/index.html index 64b1ff7..cd3b419 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -1,151 +1,17 @@ - + - - - - - - - - Emergent | Fullstack App - - - -
- - -
- -

- Made with Emergent -

-
-
- - + + + + + + GameHub + + + +
+ diff --git a/frontend/src/games/Checkers.js b/frontend/src/games/Checkers.js index 6ce172f..1f006c7 100644 --- a/frontend/src/games/Checkers.js +++ b/frontend/src/games/Checkers.js @@ -1,143 +1,181 @@ -import React, { useState, useCallback } from 'react'; -import { RotateCcw, Crown, Trophy } from 'lucide-react'; +import React, { useState, useCallback } from "react"; +import { RotateCcw, Crown, Trophy } from "lucide-react"; const BOARD_SIZE = 8; const Checkers = () => { const [board, setBoard] = useState(() => { - const initialBoard = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(null)); - + const initialBoard = Array(BOARD_SIZE) + .fill() + .map(() => Array(BOARD_SIZE).fill(null)); + // Place black pieces (top) for (let row = 0; row < 3; row++) { for (let col = 0; col < BOARD_SIZE; col++) { if ((row + col) % 2 === 1) { - initialBoard[row][col] = { color: 'black', isKing: false }; + initialBoard[row][col] = { color: "black", isKing: false }; } } } - + // Place red pieces (bottom) for (let row = 5; row < BOARD_SIZE; row++) { for (let col = 0; col < BOARD_SIZE; col++) { if ((row + col) % 2 === 1) { - initialBoard[row][col] = { color: 'red', isKing: false }; + initialBoard[row][col] = { color: "red", isKing: false }; } } } - + return initialBoard; }); const [selectedSquare, setSelectedSquare] = useState(null); - const [currentPlayer, setCurrentPlayer] = useState('red'); + const [currentPlayer, setCurrentPlayer] = useState("red"); const [capturedPieces, setCapturedPieces] = useState({ red: 0, black: 0 }); const [mustCapture, setMustCapture] = useState(false); - const [gameStatus, setGameStatus] = useState('playing'); // playing, red_wins, black_wins + const [gameStatus, setGameStatus] = useState("playing"); // playing, red_wins, black_wins const isValidSquare = (row, col) => { return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE; }; - const getValidMoves = useCallback((row, col, piece, boardState = board, checkCaptures = true) => { - const moves = []; - const captures = []; - - if (!piece) return { moves, captures }; - - const directions = piece.isKing - ? [[-1, -1], [-1, 1], [1, -1], [1, 1]] // Kings can move in all directions - : piece.color === 'red' - ? [[-1, -1], [-1, 1]] // Red moves up - : [[1, -1], [1, 1]]; // Black moves down - - for (const [dRow, dCol] of directions) { - const newRow = row + dRow; - const newCol = col + dCol; - - if (!isValidSquare(newRow, newCol)) continue; - - if (!boardState[newRow][newCol]) { - // Regular move to empty square - if (!checkCaptures) { - moves.push({ row: newRow, col: newCol, type: 'move' }); - } - } else if (boardState[newRow][newCol].color !== piece.color) { - // Potential capture - const jumpRow = newRow + dRow; - const jumpCol = newCol + dCol; - - if (isValidSquare(jumpRow, jumpCol) && !boardState[jumpRow][jumpCol]) { - captures.push({ - row: jumpRow, - col: jumpCol, - type: 'capture', - capturedRow: newRow, - capturedCol: newCol - }); + const getValidMoves = useCallback( + (row, col, piece, boardState = board, checkCaptures = true) => { + const moves = []; + const captures = []; + + if (!piece) return { moves, captures }; + + const directions = piece.isKing + ? [ + [-1, -1], + [-1, 1], + [1, -1], + [1, 1], + ] // Kings can move in all directions + : piece.color === "red" + ? [ + [-1, -1], + [-1, 1], + ] // Red moves up + : [ + [1, -1], + [1, 1], + ]; // Black moves down + + for (const [dRow, dCol] of directions) { + const newRow = row + dRow; + const newCol = col + dCol; + + if (!isValidSquare(newRow, newCol)) continue; + + if (!boardState[newRow][newCol]) { + // Regular move to empty square + moves.push({ row: newRow, col: newCol, type: "move" }); + } else if (boardState[newRow][newCol].color !== piece.color) { + // Potential capture + const jumpRow = newRow + dRow; + const jumpCol = newCol + dCol; + + if ( + isValidSquare(jumpRow, jumpCol) && + !boardState[jumpRow][jumpCol] + ) { + captures.push({ + row: jumpRow, + col: jumpCol, + type: "capture", + capturedRow: newRow, + capturedCol: newCol, + }); + } } } - } - return { moves: checkCaptures && captures.length > 0 ? [] : moves, captures }; - }, [board]); + return { + moves: checkCaptures && captures.length > 0 ? [] : moves, + captures, + }; + }, + [board], + ); - const getAllCaptures = useCallback((color, boardState = board) => { - const allCaptures = []; - - for (let row = 0; row < BOARD_SIZE; row++) { - for (let col = 0; col < BOARD_SIZE; col++) { - const piece = boardState[row][col]; - if (piece && piece.color === color) { - const { captures } = getValidMoves(row, col, piece, boardState); - if (captures.length > 0) { - allCaptures.push({ row, col, captures }); + const getAllCaptures = useCallback( + (color, boardState = board) => { + const allCaptures = []; + + for (let row = 0; row < BOARD_SIZE; row++) { + for (let col = 0; col < BOARD_SIZE; col++) { + const piece = boardState[row][col]; + if (piece && piece.color === color) { + const { captures } = getValidMoves(row, col, piece, boardState); + if (captures.length > 0) { + allCaptures.push({ row, col, captures }); + } } } } - } - - return allCaptures; - }, [getValidMoves]); - const makeMove = (fromRow, fromCol, toRow, toCol, capturedRow = null, capturedCol = null) => { - const newBoard = board.map(row => [...row]); + return allCaptures; + }, + [getValidMoves], + ); + + const makeMove = ( + fromRow, + fromCol, + toRow, + toCol, + capturedRow = null, + capturedCol = null, + ) => { + const newBoard = board.map((row) => [...row]); const piece = newBoard[fromRow][fromCol]; - + // Move piece newBoard[toRow][toCol] = { ...piece }; newBoard[fromRow][fromCol] = null; - + // Handle capture if (capturedRow !== null && capturedCol !== null) { const capturedPiece = newBoard[capturedRow][capturedCol]; newBoard[capturedRow][capturedCol] = null; - setCapturedPieces(prev => ({ + setCapturedPieces((prev) => ({ ...prev, - [capturedPiece.color]: prev[capturedPiece.color] + 1 + [capturedPiece.color]: prev[capturedPiece.color] + 1, })); } - + // Check for king promotion if (!piece.isKing) { - if ((piece.color === 'red' && toRow === 0) || (piece.color === 'black' && toRow === BOARD_SIZE - 1)) { + if ( + (piece.color === "red" && toRow === 0) || + (piece.color === "black" && toRow === BOARD_SIZE - 1) + ) { newBoard[toRow][toCol].isKing = true; } } - + setBoard(newBoard); - + // Check for additional captures - const { captures } = getValidMoves(toRow, toCol, newBoard[toRow][toCol], newBoard); - if (captures.length > 0 && (capturedRow !== null && capturedCol !== null)) { + const { captures } = getValidMoves( + toRow, + toCol, + newBoard[toRow][toCol], + newBoard, + ); + if (captures.length > 0 && capturedRow !== null && capturedCol !== null) { // Player can capture again setSelectedSquare([toRow, toCol]); setMustCapture(true); } else { // End turn setSelectedSquare(null); - setCurrentPlayer(currentPlayer === 'red' ? 'black' : 'red'); + setCurrentPlayer((prev) => (prev === "red" ? "black" : "red")); setMustCapture(false); - + // Check for win conditions checkGameEnd(newBoard); } @@ -148,20 +186,32 @@ const Checkers = () => { let blackPieces = 0; let redCanMove = false; let blackCanMove = false; - + for (let row = 0; row < BOARD_SIZE; row++) { for (let col = 0; col < BOARD_SIZE; col++) { const piece = boardState[row][col]; if (piece) { - if (piece.color === 'red') { + if (piece.color === "red") { redPieces++; - const { moves, captures } = getValidMoves(row, col, piece, boardState, false); + const { moves, captures } = getValidMoves( + row, + col, + piece, + boardState, + false, + ); if (moves.length > 0 || captures.length > 0) { redCanMove = true; } } else { blackPieces++; - const { moves, captures } = getValidMoves(row, col, piece, boardState, false); + const { moves, captures } = getValidMoves( + row, + col, + piece, + boardState, + false, + ); if (moves.length > 0 || captures.length > 0) { blackCanMove = true; } @@ -169,16 +219,16 @@ const Checkers = () => { } } } - + if (redPieces === 0 || !redCanMove) { - setGameStatus('black_wins'); + setGameStatus("black_wins"); } else if (blackPieces === 0 || !blackCanMove) { - setGameStatus('red_wins'); + setGameStatus("red_wins"); } }; const handleSquareClick = (row, col) => { - if (gameStatus !== 'playing') return; + if (gameStatus !== "playing") return; const piece = board[row][col]; const allCaptures = getAllCaptures(currentPlayer); @@ -187,7 +237,7 @@ const Checkers = () => { if (selectedSquare) { const [selectedRow, selectedCol] = selectedSquare; const selectedPiece = board[selectedRow][selectedCol]; - + if (selectedRow === row && selectedCol === col) { // Deselect setSelectedSquare(null); @@ -195,23 +245,29 @@ const Checkers = () => { return; } - const { moves, captures } = getValidMoves(selectedRow, selectedCol, selectedPiece); + const { moves, captures } = getValidMoves( + selectedRow, + selectedCol, + selectedPiece, + ); const allMoves = [...moves, ...captures]; - const validMove = allMoves.find(move => move.row === row && move.col === col); + const validMove = allMoves.find( + (move) => move.row === row && move.col === col, + ); if (validMove) { - if (hasCaptures && validMove.type !== 'capture') { + if (hasCaptures && validMove.type !== "capture") { // Must capture if captures are available return; } - + makeMove( - selectedRow, - selectedCol, - row, - col, - validMove.capturedRow, - validMove.capturedCol + selectedRow, + selectedCol, + row, + col, + validMove.capturedRow, + validMove.capturedCol, ); } else if (piece && piece.color === currentPlayer && !mustCapture) { // Select new piece @@ -228,45 +284,51 @@ const Checkers = () => { }; const resetGame = () => { - const initialBoard = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(null)); - + const initialBoard = Array(BOARD_SIZE) + .fill() + .map(() => Array(BOARD_SIZE).fill(null)); + // Place black pieces (top) for (let row = 0; row < 3; row++) { for (let col = 0; col < BOARD_SIZE; col++) { if ((row + col) % 2 === 1) { - initialBoard[row][col] = { color: 'black', isKing: false }; + initialBoard[row][col] = { color: "black", isKing: false }; } } } - + // Place red pieces (bottom) for (let row = 5; row < BOARD_SIZE; row++) { for (let col = 0; col < BOARD_SIZE; col++) { if ((row + col) % 2 === 1) { - initialBoard[row][col] = { color: 'red', isKing: false }; + initialBoard[row][col] = { color: "red", isKing: false }; } } } - + setBoard(initialBoard); setSelectedSquare(null); - setCurrentPlayer('red'); + setCurrentPlayer("red"); setCapturedPieces({ red: 0, black: 0 }); setMustCapture(false); - setGameStatus('playing'); + setGameStatus("playing"); }; const getSquareClassName = (row, col) => { const isLight = (row + col) % 2 === 0; let baseClass = `w-16 h-16 flex items-center justify-center cursor-pointer transition-all duration-200 hover:scale-105 `; - + if (isLight) { baseClass += "bg-amber-100 "; } else { baseClass += "bg-amber-800 "; } - if (selectedSquare && selectedSquare[0] === row && selectedSquare[1] === col) { + if ( + selectedSquare && + selectedSquare[0] === row && + selectedSquare[1] === col + ) { baseClass += "ring-4 ring-blue-500 "; } @@ -275,13 +337,22 @@ const Checkers = () => { const [selectedRow, selectedCol] = selectedSquare; const selectedPiece = board[selectedRow][selectedCol]; if (selectedPiece) { - const { moves, captures } = getValidMoves(selectedRow, selectedCol, selectedPiece); + const { moves, captures } = getValidMoves( + selectedRow, + selectedCol, + selectedPiece, + ); const allMoves = [...moves, ...captures]; - if (allMoves.some(move => move.row === row && move.col === col)) { - const moveType = captures.some(move => move.row === row && move.col === col) ? 'capture' : 'move'; - baseClass += moveType === 'capture' - ? "ring-2 ring-red-400 bg-red-200 hover:bg-red-300 " - : "ring-2 ring-green-400 bg-green-200 hover:bg-green-300 "; + if (allMoves.some((move) => move.row === row && move.col === col)) { + const moveType = captures.some( + (move) => move.row === row && move.col === col, + ) + ? "capture" + : "move"; + baseClass += + moveType === "capture" + ? "ring-2 ring-red-400 bg-red-200 hover:bg-red-300 " + : "ring-2 ring-green-400 bg-green-200 hover:bg-green-300 "; } } } @@ -291,12 +362,14 @@ const Checkers = () => { const renderPiece = (piece) => { if (!piece) return null; - - const baseClass = "w-12 h-12 rounded-full border-4 flex items-center justify-center shadow-lg transition-all duration-200 "; - const colorClass = piece.color === 'red' - ? "bg-red-500 border-red-700 " - : "bg-gray-800 border-gray-900 "; - + + const baseClass = + "w-12 h-12 rounded-full border-4 flex items-center justify-center shadow-lg transition-all duration-200 "; + const colorClass = + piece.color === "red" + ? "bg-red-500 border-red-700 " + : "bg-gray-800 border-gray-900 "; + return (
{piece.isKing && } @@ -321,17 +394,21 @@ const Checkers = () => { {/* Game Board */}
-
-
- {currentPlayer === 'red' ? 'Red' : 'Black'} to move +
+
+ {currentPlayer === "red" ? "Red" : "Black"} to move
- +
{/* Game Status */} - {gameStatus !== 'playing' && ( + {gameStatus !== "playing" && (
- {gameStatus === 'red_wins' ? 'Red Wins!' : 'Black Wins!'} + + {gameStatus === "red_wins" ? "Red Wins!" : "Black Wins!"} +
)} @@ -370,7 +449,7 @@ const Checkers = () => { > {renderPiece(piece)} - )) + )), )}
@@ -380,30 +459,38 @@ const Checkers = () => {
{/* Score */}
-

Captured Pieces

- +

+ Captured Pieces +

+
Red captured:
- {capturedPieces.red} + + {capturedPieces.red} +
- +
Black captured:
- {capturedPieces.black} + + {capturedPieces.black} +
{/* Game Rules */}
-

How to Play

+

+ How to Play +

  • • Click a piece to select it
  • • Move diagonally to empty dark squares
  • @@ -421,4 +508,4 @@ const Checkers = () => { ); }; -export default Checkers; \ No newline at end of file +export default Checkers; 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 444b5b5..c2e1391 100644 --- a/frontend/src/games/Snake.js +++ b/frontend/src/games/Snake.js @@ -1,5 +1,6 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { RotateCcw, Trophy, Zap, ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from 'lucide-react'; +import React, { useState, useEffect, useCallback } from "react"; +import { RotateCcw, Trophy, Zap } from "lucide-react"; +import "./Snake.css"; const GRID_SIZE = 20; const INITIAL_SNAKE = [{ x: 10, y: 10 }]; @@ -10,7 +11,7 @@ const Snake = () => { const [snake, setSnake] = useState(INITIAL_SNAKE); const [food, setFood] = useState({ x: 15, y: 15 }); const [direction, setDirection] = useState(INITIAL_DIRECTION); - const [gameStatus, setGameStatus] = useState('paused'); // paused, playing, gameOver + const [gameStatus, setGameStatus] = useState("paused"); // paused, playing, gameOver const [score, setScore] = useState(0); const generateFood = useCallback((currentSnake) => { @@ -18,10 +19,14 @@ const Snake = () => { do { newFood = { x: Math.floor(Math.random() * GRID_SIZE), - y: Math.floor(Math.random() * GRID_SIZE) + y: Math.floor(Math.random() * GRID_SIZE), }; - } while (currentSnake.some(segment => segment.x === newFood.x && segment.y === newFood.y)); - + } while ( + currentSnake.some( + (segment) => segment.x === newFood.x && segment.y === newFood.y, + ) + ); + return newFood; }, []); @@ -29,33 +34,40 @@ const Snake = () => { setSnake(INITIAL_SNAKE); setFood({ x: 15, y: 15 }); setDirection(INITIAL_DIRECTION); - setGameStatus('paused'); + setGameStatus("paused"); setScore(0); }; const startGame = () => { - setGameStatus('playing'); + setGameStatus("playing"); }; const moveSnake = useCallback(() => { - if (gameStatus !== 'playing') return; + if (gameStatus !== "playing") return; - setSnake(currentSnake => { + setSnake((currentSnake) => { const newSnake = [...currentSnake]; const head = { ...newSnake[0] }; - + head.x += direction.x; head.y += direction.y; // Check wall collision - if (head.x < 0 || head.x >= GRID_SIZE || head.y < 0 || head.y >= GRID_SIZE) { - setGameStatus('gameOver'); + if ( + head.x < 0 || + head.x >= GRID_SIZE || + head.y < 0 || + head.y >= GRID_SIZE + ) { + setGameStatus("gameOver"); return currentSnake; } // Check self collision - if (newSnake.some(segment => segment.x === head.x && segment.y === head.y)) { - setGameStatus('gameOver'); + if ( + newSnake.some((segment) => segment.x === head.x && segment.y === head.y) + ) { + setGameStatus("gameOver"); return currentSnake; } @@ -63,7 +75,7 @@ const Snake = () => { // Check food collision if (head.x === food.x && head.y === food.y) { - setScore(prev => prev + 10); + setScore((prev) => prev + 10); setFood(generateFood(newSnake)); } else { newSnake.pop(); @@ -80,26 +92,45 @@ const Snake = () => { useEffect(() => { const handleKeyPress = (e) => { - if (gameStatus === 'paused') { - if (e.code === 'Space') { + 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 }); + startGame(); return; } } - - if (gameStatus !== 'playing') return; - switch (e.key) { - case 'ArrowUp': + if (gameStatus !== "playing") return; + + switch (key) { + case "w": + e.preventDefault(); if (direction.y === 0) setDirection({ x: 0, y: -1 }); break; - case 'ArrowDown': + case "s": + e.preventDefault(); if (direction.y === 0) setDirection({ x: 0, y: 1 }); break; - case 'ArrowLeft': + case "a": + e.preventDefault(); if (direction.x === 0) setDirection({ x: -1, y: 0 }); break; - case 'ArrowRight': + case "d": + e.preventDefault(); if (direction.x === 0) setDirection({ x: 1, y: 0 }); break; default: @@ -107,13 +138,15 @@ const Snake = () => { } }; - window.addEventListener('keydown', handleKeyPress); - return () => window.removeEventListener('keydown', handleKeyPress); + window.addEventListener("keydown", handleKeyPress); + return () => window.removeEventListener("keydown", handleKeyPress); }, [direction, gameStatus]); const getCellContent = (x, y) => { const isSnakeHead = snake[0]?.x === x && snake[0]?.y === y; - const isSnakeBody = snake.slice(1).some(segment => segment.x === x && segment.y === y); + const isSnakeBody = snake + .slice(1) + .some((segment) => segment.x === x && segment.y === y); const isFood = food.x === x && food.y === y; if (isSnakeHead) { @@ -129,7 +162,9 @@ const Snake = () => { } if (isFood) { - return
; + return ( +
+ ); } return null; @@ -165,15 +200,15 @@ const Snake = () => {
{/* Game Status */} - {gameStatus === 'paused' && ( + {gameStatus === "paused" && (
- Press SPACE to Start + Press Enter or W/A/S/D to Start
)} - {gameStatus === 'gameOver' && ( + {gameStatus === "gameOver" && (
@@ -185,14 +220,7 @@ const Snake = () => { {/* Game Grid */}
-
+
{Array.from({ length: GRID_SIZE * GRID_SIZE }).map((_, index) => { const x = index % GRID_SIZE; const y = Math.floor(index / GRID_SIZE); @@ -212,25 +240,27 @@ const Snake = () => { {/* Controls */}
-

Controls

+

+ Controls +

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

@@ -239,4 +269,4 @@ const Snake = () => { ); }; -export default Snake; \ No newline at end of file +export default 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 - +