From e5d97486abe25b70087b49fbf91f0574d01905b2 Mon Sep 17 00:00:00 2001 From: Karn-2906 Date: Mon, 7 Oct 2024 12:41:51 +0530 Subject: [PATCH 1/2] frontend changes --- paras/src/App.css | 92 ++++++++++++++++++++++++++++++++++++++++++++--- paras/src/App.js | 15 +++++--- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/paras/src/App.css b/paras/src/App.css index 5048246..0b2adfe 100644 --- a/paras/src/App.css +++ b/paras/src/App.css @@ -25,7 +25,7 @@ grid-template-columns: repeat(3, 1fr); grid-gap: 10px; max-width: 300px; - margin: 20px auto; + margin: 0px auto; padding: 20px; border-radius: 15px; background: linear-gradient(120deg, #0e2e4e, #1f4679); @@ -116,11 +116,10 @@ } .toggle { - margin-left: auto; + position: relative; display: inline-block; width: 60px; height: 34px; - position: relative; } .toggle input { @@ -161,6 +160,89 @@ input:checked + .slider:before { transform: translateX(26px); } +.icon { + position: absolute; + top: 50%; + transform: translateY(-50%); + font-size: 18px; + transition: opacity 0.3s ease; +} + +.sun { + left: 7px; + top: 19px; + font-size: 18px; + font-weight: bold; + opacity: 1; + color: #f1c40f; /* Sunny yellow color */ +} + +.moon { + right: 5px; + top: 19px; + font-size: 18px; + font-weight: bold; + opacity: 0; + color: #f1c40f; /* Same color as sun for consistency */ +} + +input:checked + .slider .sun { + opacity: 0; +} + +input:checked + .slider .moon { + opacity: 1; +} + +/* Ensure contrast in both light and dark modes */ +.dark .toggle .moon, +.dark .toggle .sun { + color: #f1c40f; /* Maintain yellow color in dark mode */ +} + +.restart-button{ + background-color: #f44336; + color: white; + border: none; + border-radius: 5px; + padding: 10px 20px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: background-color 0.3s ease, transform 0.2s ease; +} + +.restart-button:hover { + background-color: #2f8cd3; + transform: scale(1.1); +} + +.winner-message{ + font-size: 24px; + font-weight: bold; + margin-top: 20px; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:checked + .slider:before { + transform: translateX(26px); +} + .winning-cell { animation: pulse 1s infinite; } @@ -224,7 +306,7 @@ input:checked + .slider:before { .rules-card h2 { text-align: center; /* Center align the title */ - margin-bottom: 20px; /* Space below the title */ + margin-bottom: 10px; /* Space below the title */ font-size: 28px; /* Title font size */ color: inherit; /* Use inherited color */ font-weight: bold; /* Bold title */ @@ -237,7 +319,7 @@ input:checked + .slider:before { } .rules-card li { - margin: 15px 0; /* Space between list items */ + margin: 5px 0; /* Space between list items */ padding: 10px; /* Padding inside list items */ background-color: #e0e0e0; /* Light gray background for list items in light theme */ border-radius: 8px; /* Rounded corners for list items */ diff --git a/paras/src/App.js b/paras/src/App.js index 569fba1..7aee8d8 100644 --- a/paras/src/App.js +++ b/paras/src/App.js @@ -79,7 +79,12 @@ const App = () => { @@ -89,14 +94,14 @@ const App = () => { {winner && (
-

Player {winner} wins!

- +

Player {winner} wins!

+
)} {draw && (
-

It's a draw!

- +

It's a draw!

+
)} From 5c89b57da7e5905ad5e5973fe3a49d19e992abb4 Mon Sep 17 00:00:00 2001 From: Karn-2906 Date: Mon, 7 Oct 2024 13:34:38 +0530 Subject: [PATCH 2/2] computer player added --- paras/src/App.css | 124 ++++++++++++++++++++++++++++++++++++++++++++++ paras/src/App.js | 113 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 227 insertions(+), 10 deletions(-) diff --git a/paras/src/App.css b/paras/src/App.css index 0b2adfe..8457321 100644 --- a/paras/src/App.css +++ b/paras/src/App.css @@ -370,3 +370,127 @@ input:checked + .slider:before { text-decoration: underline; /* Underline on hover */ } +.computer-toggle { + margin-left: 10px; + margin-bottom: 10px; + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.game-mode{ + display: flex; + align-items: center; + justify-content: center; +} + +.game-mode-text{ + font-size: 20px; + font-weight: bold; + margin-left: 10px; +} + +.current-player{ + font-size: 20px; + font-weight: 400; + margin-left: 10px; +} + +.computer-toggle input { + opacity: 0; + width: 0; + height: 0; +} + +.computer-toggle .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: .4s; + border-radius: 34px; +} + +.computer-toggle .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: .4s; + border-radius: 50%; +} + +.computer-toggle input:checked + .slider { + background-color: #2196F3; +} + +.computer-toggle input:checked + .slider:before { + transform: translateX(26px); +} + +.computer-toggle .icon { + position: absolute; + top: 50%; + transform: translateY(-50%); + font-size: 16px; + transition: opacity 0.3s ease; +} + +.computer-toggle .human { + left: 7px; + opacity: 1; + color: #333; +} + +.computer-toggle .computer { + right: 7px; + opacity: 0; + color: #fff; +} + +.computer-toggle input:checked + .slider .human { + opacity: 0; +} + +.computer-toggle input:checked + .slider .computer { + opacity: 1; +} + +/* AI difficulty indicator */ +.ai-difficulty { + display: inline-block; + margin-left: 10px; + font-size: 14px; + font-weight: bold; + color: #2196F3; + opacity: 0; + transition: opacity 0.3s ease; +} + +.computer-toggle input:checked ~ .ai-difficulty { + opacity: 1; +} + +/* Pulsating effect for AI mode */ +@keyframes ai-pulse { + 0% { + box-shadow: 0 0 0 0 rgba(33, 150, 243, 0.4); + } + 70% { + box-shadow: 0 0 0 10px rgba(33, 150, 243, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(33, 150, 243, 0); + } +} + +.computer-toggle input:checked + .slider { + animation: ai-pulse 2s infinite; +} \ No newline at end of file diff --git a/paras/src/App.js b/paras/src/App.js index 7aee8d8..3eebb80 100644 --- a/paras/src/App.js +++ b/paras/src/App.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import './App.css'; import Sparkle from './Sparkle'; // Import the Sparkle component @@ -11,14 +11,82 @@ const App = () => { const [draw, setDraw] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); + const [isComputerMode, setIsComputerMode] = useState(false); - const handleCellClick = (index) => { + const handleCellClick = useCallback((index) => { if (board[index] || winner || draw) return; const newBoard = [...board]; newBoard[index] = currentPlayer; setBoard(newBoard); checkWinner(newBoard, currentPlayer); setCurrentPlayer(currentPlayer === 'X' ? 'O' : 'X'); + }, [board, currentPlayer, winner, draw]); + + useEffect(() => { + if (isComputerMode && currentPlayer === 'O' && !winner && !draw) { + const timer = setTimeout(() => { + makeComputerMove(); + }, 500); + return () => clearTimeout(timer); + } + }, [currentPlayer, isComputerMode, winner, draw]); + + const makeComputerMove = () => { + // Check for winning move + const winningMove = findWinningMove(board, 'O'); + if (winningMove !== -1) { + handleCellClick(winningMove); + return; + } + + // Check for blocking move + const blockingMove = findWinningMove(board, 'X'); + if (blockingMove !== -1) { + handleCellClick(blockingMove); + return; + } + + // Choose center if available + if (board[4] === null) { + handleCellClick(4); + return; + } + + // Choose corners + const corners = [0, 2, 6, 8]; + const availableCorners = corners.filter(corner => board[corner] === null); + if (availableCorners.length > 0) { + const randomCorner = availableCorners[Math.floor(Math.random() * availableCorners.length)]; + handleCellClick(randomCorner); + return; + } + + // Choose any available cell + const emptyCells = board.reduce((acc, cell, index) => { + if (cell === null) acc.push(index); + return acc; + }, []); + if (emptyCells.length > 0) { + const randomIndex = emptyCells[Math.floor(Math.random() * emptyCells.length)]; + handleCellClick(randomIndex); + } + }; + + // Helper function to find winning move + const findWinningMove = (board, player) => { + const winningCombinations = [ + [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 + ]; + + for (let combination of winningCombinations) { + const [a, b, c] = combination; + if (board[a] === player && board[b] === player && board[c] === null) return c; + if (board[a] === player && board[c] === player && board[b] === null) return b; + if (board[b] === player && board[c] === player && board[a] === null) return a; + } + return -1; }; const checkWinner = (board, player) => { @@ -47,6 +115,11 @@ const App = () => { setDraw(false); }; + const toggleComputerMode = () => { + setIsComputerMode((prevMode) => !prevMode); + resetGame(); + }; + const renderCell = (index) => { const value = board[index]; return ( @@ -76,25 +149,44 @@ const App = () => {

Tic Tac Toe

-
- +
+ +
+ {isComputerMode ? 'vs Computer' : 'vs Player 2'} +
+
+
{board.map((cell, index) => renderCell(index))}
- +
+ Current Player: {currentPlayer === 'X' ? 'You' : (isComputerMode ? 'Computer' : 'Player 2')} +
{winner && (
-

Player {winner} wins!

+

+ {winner === 'X' ? 'You win!' : (isComputerMode ? 'Computer wins!' : 'Player 2 wins!')} +

)} @@ -111,6 +203,7 @@ const App = () => {
  • Two players take turns marking cells in a 3x3 grid.
  • The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row wins the game.
  • If all cells are filled and no player has three marks in a row, the game is a draw.
  • +
  • Toggle the computer mode to play against the computer (you'll be X).
  • @@ -124,4 +217,4 @@ const App = () => { ); }; -export default App; +export default App; \ No newline at end of file