Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added playble tic tac toe game in Board game section #401

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,19 @@ We extend our heartfelt gratitude to all the amazing contributors who have made
</a>
</td>
<td align="center">
<a href="https://github.com/alo7lika">
<img src="https://avatars.githubusercontent.com/u/152315710?v=4" width="100;" alt="alo7lika"/>
<a href="https://github.com/Ashwinib26">
<img src="https://avatars.githubusercontent.com/u/149402720?v=4" width="100;" alt="Ashwinib26"/>
<br />
<sub><b>alolika bhowmik</b></sub>
<sub><b>Ashwini_ab</b></sub>
Comment on lines +277 to +280
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix indentation using spaces instead of tabs.

The indentation in these sections uses hard tabs. For consistency with the rest of the file and to follow markdown best practices, replace tabs with spaces.

Apply this change to fix the indentation:

-                <a href="https://github.com/Ashwinib26">
-                    <img src="https://avatars.githubusercontent.com/u/149402720?v=4" width="100;" alt="Ashwinib26"/>
-                    <br />
-                    <sub><b>Ashwini_ab</b></sub>
+                <a href="https://github.com/Ashwinib26">
+                    <img src="https://avatars.githubusercontent.com/u/149402720?v=4" width="100;" alt="Ashwinib26"/>
+                    <br />
+                    <sub><b>Ashwini_ab</b></sub>

Also applies to: 286-289

</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://github.com/Ashwinib26">
<img src="https://avatars.githubusercontent.com/u/149402720?v=4" width="100;" alt="Ashwinib26"/>
<a href="https://github.com/alo7lika">
<img src="https://avatars.githubusercontent.com/u/152315710?v=4" width="100;" alt="alo7lika"/>
<br />
<sub><b>Ashwini_ab</b></sub>
<sub><b>alolika bhowmik</b></sub>
</a>
</td>
<td align="center">
Expand Down
152 changes: 79 additions & 73 deletions frontend/src/components/Pages/Boardgame.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState,useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { Splide, SplideSlide } from '@splidejs/react-splide';
import { AutoScroll } from '@splidejs/splide-extension-auto-scroll';
import '@splidejs/react-splide/css';
Expand All @@ -12,16 +12,17 @@ import board6 from '../../assets/Boardgames/board6.png';
import board7 from '../../assets/Boardgames/board7.png';
import board8 from '../../assets/Boardgames/board8.png';
import board10 from '../../assets/Boardgames/board10.png';
import ludo from "../../assets/Boardgames/ludo.jpg";
import snake from "../../assets/Boardgames/snake.jpg";
import tic from "../../assets/Boardgames/tic.png";
import uno from "../../assets/Boardgames/uno.jpg";
import word from "../../assets/Boardgames/word.jpg";
import war from "../../assets/Boardgames/war.jpg";
import ludo from '../../assets/Boardgames/ludo.jpg';
import snake from '../../assets/Boardgames/snake.jpg';
import tic from '../../assets/Boardgames/tic.png';
import uno from '../../assets/Boardgames/uno.jpg';
import word from '../../assets/Boardgames/word.jpg';
import war from '../../assets/Boardgames/war.jpg';
import bg from '../../assets/Boardgames/bg.jpg';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';
import MainHOC from '../MainHOC';
import { Link } from 'react-router-dom';
export default MainHOC(Boardgame);
function Boardgame() {
const [selectedBoard, setSelectedBoard] = useState(null);
Expand Down Expand Up @@ -219,76 +220,79 @@ function Boardgame() {
],
},
{
"src": ludo,
"title": "Ludo",
"description": "A classic board game where players race their tokens from start to finish based on dice rolls.",
"instructions": [
"Players take turns rolling a single die to move their tokens around the board.",
"To enter a token onto the board, players must roll a 6. Tokens move based on the number rolled.",
src: ludo,
title: 'Ludo',
description:
'A classic board game where players race their tokens from start to finish based on dice rolls.',
instructions: [
'Players take turns rolling a single die to move their tokens around the board.',
'To enter a token onto the board, players must roll a 6. Tokens move based on the number rolled.',
"Players can send opponents' tokens back to start by landing on the same space.",
"The first player to move all their tokens into the home area wins the game."
]
'The first player to move all their tokens into the home area wins the game.',
],
},
{
"src": snake,
"title": "Snake and Ladders",
"description": "A race game where players climb ladders and avoid snakes to reach the end.",
"instructions": [
"Players take turns rolling a die to advance their token along the numbered board.",
"If a player lands at the base of a ladder, they climb to the top.",
"If a player lands on the head of a snake, they slide down to its tail.",
"The first player to reach the last square wins the game."
]
src: snake,
title: 'Snake and Ladders',
description:
'A race game where players climb ladders and avoid snakes to reach the end.',
instructions: [
'Players take turns rolling a die to advance their token along the numbered board.',
'If a player lands at the base of a ladder, they climb to the top.',
'If a player lands on the head of a snake, they slide down to its tail.',
'The first player to reach the last square wins the game.',
],
},
{
"src": tic,
"title": "Tic-Tac-Toe",
"description": "A simple two-player game where players try to get three of their symbols in a row.",
"instructions": [
"Players take turns placing their symbol (X or O) in an empty square on a 3x3 grid.",
"The first player to align three symbols vertically, horizontally, or diagonally wins.",
"If all squares are filled without a winner, the game ends in a draw."
]
src: tic,
title: 'Tic-Tac-Toe',
description:
'A simple two-player game where players try to get three of their symbols in a row.',
instructions: [
'Players take turns placing their symbol (X or O) in an empty square on a 3x3 grid.',
'The first player to align three symbols vertically, horizontally, or diagonally wins.',
'If all squares are filled without a winner, the game ends in a draw.',
],
},
{
"src": uno,
"title": "Uno",
"description": "A popular card game where players try to be the first to play all their cards.",
"instructions": [
"Players take turns matching a card from their hand to the top card of the discard pile by color or number.",
"Special action cards can change the gameplay, like skipping a turn or reversing play direction.",
src: uno,
title: 'Uno',
description:
'A popular card game where players try to be the first to play all their cards.',
instructions: [
'Players take turns matching a card from their hand to the top card of the discard pile by color or number.',
'Special action cards can change the gameplay, like skipping a turn or reversing play direction.',
"When a player has one card left, they must yell 'Uno!' to warn others.",
"The first player to play all their cards wins the game."
]
'The first player to play all their cards wins the game.',
],
},
{
"src": word,
"title": "Word Finder",
"description": "A fun puzzle game where players create words from a set of letters and fit them into designated boxes.",
"instructions": [
"Players are given a list of words to find, each composed of letters that can be rearranged.",
"A grid of boxes is provided, where players must fit the words either horizontally, vertically, or diagonally.",
"Each word must be filled in completely, and letters cannot be reused for different words.",
"The goal is to fill in all the boxes with the given words as quickly as possible."
]
src: word,
title: 'Word Finder',
description:
'A fun puzzle game where players create words from a set of letters and fit them into designated boxes.',
instructions: [
'Players are given a list of words to find, each composed of letters that can be rearranged.',
'A grid of boxes is provided, where players must fit the words either horizontally, vertically, or diagonally.',
'Each word must be filled in completely, and letters cannot be reused for different words.',
'The goal is to fill in all the boxes with the given words as quickly as possible.',
],
},
{
"src": war,
"title": "War",
"description": "A simple two-player card game where players compete to win all the cards.",
"instructions": [
"The deck is shuffled and split evenly between the two players.",
"Each player reveals the top card of their stack at the same time.",
"The player with the higher card wins both cards and adds them to the bottom of their stack.",
src: war,
title: 'War',
description:
'A simple two-player card game where players compete to win all the cards.',
instructions: [
'The deck is shuffled and split evenly between the two players.',
'Each player reveals the top card of their stack at the same time.',
'The player with the higher card wins both cards and adds them to the bottom of their stack.',
"In case of a tie, a 'war' occurs: each player places three cards face down and reveals the next card. The higher card wins all the cards on the table.",
"The game continues until one player has all the cards or until players decide to stop."
]
}


'The game continues until one player has all the cards or until players decide to stop.',
],
},
];


useEffect(() => {
window.scrollTo(0, 0);
}, []);
Expand Down Expand Up @@ -317,9 +321,9 @@ function Boardgame() {
autoplay: true,
lazyLoad: 'sequential',
autoScroll: {
speed: 0.2,
pauseOnHover:true
}
speed: 0.2,
pauseOnHover: true,
},
}}
className="mx-auto w-full rounded-t-xl object-cover object-center shadow-2xl"
>
Expand All @@ -329,7 +333,7 @@ function Boardgame() {
effect="blur"
src="https://images.unsplash.com/photo-1656686631034-e88d4fbde1e3?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
className="object-cover w-full"
style={{height : "500px" ,width:"2000px"}}
style={{ height: '500px', width: '2000px' }}
/>
</SplideSlide>
<SplideSlide>
Expand All @@ -338,7 +342,7 @@ function Boardgame() {
effect="blur"
src="https://images.unsplash.com/photo-1681402720847-961bb1aab8d8?q=80&w=2071&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
className="object-cover w-full"
style={{height : "500px",width:"2000px"}}
style={{ height: '500px', width: '2000px' }}
/>
</SplideSlide>
<SplideSlide>
Expand All @@ -347,7 +351,7 @@ function Boardgame() {
effect="blur"
src="https://images.unsplash.com/photo-1609818698346-8cb3be6e0bc0?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
className="object-cover"
style={{height : "500px",width:"2000px"}}
style={{ height: '500px', width: '2000px' }}
/>
</SplideSlide>
<SplideSlide>
Expand All @@ -356,7 +360,7 @@ function Boardgame() {
effect="blur"
src="https://images.unsplash.com/photo-1659480142923-0cd01191e0e9?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
className="object-cover"
style={{height : "500px",width:"2000px"}}
style={{ height: '500px', width: '2000px' }}
/>
</SplideSlide>
{/* Add more slides as necessary */}
Expand Down Expand Up @@ -392,12 +396,14 @@ function Boardgame() {
>
See Instructions
</button>
<button
onClick={() => handleInstantPlay(board)}
className="px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
{board.title === "Tic-Tac-Toe" && (
<Link
to="/Boardgame/TicTacToe"
className="inline-block px-4 py-2 text-white bg-blue-500 rounded-lg opacity-0 transition-opacity duration-700 delay-300 group-hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Instant Play
</button>
</Link>
)}
</div>
</div>
</div>
Expand Down
86 changes: 86 additions & 0 deletions frontend/src/components/Pages/Games/TicTacToe.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState } from 'react';

function TicTacToe() {
const [board, setBoard] = useState(Array(9).fill(null));
const [isXNext, setIsXNext] = useState(true);
const winner = calculateWinner(board);
Comment on lines +1 to +6
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider enhancing type safety and accessibility

While the basic setup is correct, consider these improvements:

  1. Add PropTypes or TypeScript for better type safety
  2. Consider extracting game logic into a custom hook for better separation of concerns

Example implementation of a custom hook:

function useGameState() {
  const [board, setBoard] = useState(Array(9).fill(null));
  const [isXNext, setIsXNext] = useState(true);
  const winner = calculateWinner(board);
  
  // ... game logic here
  
  return { board, isXNext, winner, handleClick, resetGame };
}


const handleClick = (index) => {
if (board[index] || winner) return;
const newBoard = board.slice();
newBoard[index] = isXNext ? 'X' : 'O';
setBoard(newBoard);
setIsXNext(!isXNext);
};
Comment on lines +8 to +14
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add input validation and state update safety

The handleClick function should validate the input index and use the functional update form of setState to handle potential race conditions.

  const handleClick = (index) => {
+   if (typeof index !== 'number' || index < 0 || index > 8) return;
    if (board[index] || winner) return;
-   const newBoard = board.slice();
-   newBoard[index] = isXNext ? 'X' : 'O';
-   setBoard(newBoard);
+   setBoard(currentBoard => {
+     if (currentBoard[index]) return currentBoard;
+     const newBoard = currentBoard.slice();
+     newBoard[index] = isXNext ? 'X' : 'O';
+     return newBoard;
+   });
    setIsXNext(!isXNext);
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleClick = (index) => {
if (board[index] || winner) return;
const newBoard = board.slice();
newBoard[index] = isXNext ? 'X' : 'O';
setBoard(newBoard);
setIsXNext(!isXNext);
};
const handleClick = (index) => {
if (typeof index !== 'number' || index < 0 || index > 8) return;
if (board[index] || winner) return;
setBoard(currentBoard => {
if (currentBoard[index]) return currentBoard;
const newBoard = currentBoard.slice();
newBoard[index] = isXNext ? 'X' : 'O';
return newBoard;
});
setIsXNext(!isXNext);
};


const resetGame = () => {
setBoard(Array(9).fill(null));
setIsXNext(true);
};

return (
<div className="flex flex-col items-center my-20">
<h1 className="text-6xl font-bold tracking-tighter text-center mb-5">
Tic Tac Toe
</h1>
<div className="grid grid-cols-3 gap-2 w-64">
{board.map((value, index) => (
<button
key={index}
onClick={() => handleClick(index)}
aria-label={`Cell ${index + 1}, ${value || 'empty'}`}
tabIndex={0}
disabled={board[index] || winner}
className="w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix template string syntax in className

The className contains an improperly formatted template string that won't work as expected.

- className="w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500  ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md"
+ className={`w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md`}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md"
className={`w-20 h-20 bg-amber-200 flex items-center justify-center text-3xl font-semibold text-black-800 border-2 border-amber-500 ${!board[index] && !winner ? 'hover:bg-amber-300' : 'cursor-not-allowed opacity-75'} transition-all duration-200 rounded-md`}

>
{value}
</button>
))}
</div>
<div className="mt-5 content-center ">
{winner ? (
<h2 className="text-2xl font-semibold text-green-600 text-center">
{winner} Wins!
</h2>
) : board.every((cell) => cell) ? (
<h2 className="text-2xl font-semibold text-red-600 text-center">
It's a Draw!
</h2>
) : (
<h2 className="text-xl font-semibold text-gray-700 text-center">
Next Player: {isXNext ? 'X' : 'O'}
</h2>
)}
<button
onClick={resetGame}
className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200"
>
Reset Game
</button>
</div>
Comment on lines +54 to +60
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add keyboard shortcut for game reset

Consider adding a keyboard shortcut (e.g., 'R' key) for resetting the game to improve user experience.

+ useEffect(() => {
+   const handleKeyPress = (e) => {
+     if (e.key.toLowerCase() === 'r') {
+       resetGame();
+     }
+   };
+   window.addEventListener('keydown', handleKeyPress);
+   return () => window.removeEventListener('keydown', handleKeyPress);
+ }, []);

  <button
    onClick={resetGame}
+   title="Press 'R' to reset game"
    className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200"
  >
    Reset Game
  </button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
onClick={resetGame}
className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200"
>
Reset Game
</button>
</div>
useEffect(() => {
const handleKeyPress = (e) => {
if (e.key.toLowerCase() === 'r') {
resetGame();
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, []);
<button
onClick={resetGame}
title="Press 'R' to reset game"
className="ml-1 mt-4 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 hover:text-white transition-all duration-200"
>
Reset Game
</button>
</div>

</div>
);
}

function calculateWinner(board) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];

for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
return board[a];
}
}
return null;
}
Comment on lines +65 to +84
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize winner calculation with memoization

The winner calculation could be optimized to prevent unnecessary recalculations.

Consider using useMemo:

+ import React, { useState, useMemo } from 'react';

  function TicTacToe() {
-   const winner = calculateWinner(board);
+   const winner = useMemo(() => calculateWinner(board), [board]);

Committable suggestion was skipped due to low confidence.


export default TicTacToe;
4 changes: 4 additions & 0 deletions frontend/src/router/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import Admin from '../components/Pages/Admin';
import VerifyOtp from '../components/Pages/VerifyOtp';
import EmailVerify from '../components/Pages/EmailVerify';
import Membership from '../components/Membership';
import HelpAndSupport from '../components/Pages/HelpAndSupport';
import TicTacToe from '../components/Pages/Games/TicTacToe';
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<App />}>
<Route index={true} path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/menu" element={<Menu />} />
<Route path="/boardgame" element={<Boardgame />} />
<Route path="/boardgame/TicTacToe" element={<TicTacToe />} />
<Route path="/events" element={<Event />} />
<Route path="/book" element={<MyBook />} />
<Route path="/reservation" element={<Register />} />
Expand All @@ -40,6 +43,7 @@ const router = createBrowserRouter(
<Route path="/email-verify" element={<EmailVerify />} />
<Route path="/membership" element={<Membership />} />


</Route>
)
);
Expand Down
Loading