Skip to content

Commit 6adef5d

Browse files
committed
new(apps): rock paper scissor
1 parent ad9e0fa commit 6adef5d

File tree

4 files changed

+173
-0
lines changed

4 files changed

+173
-0
lines changed

public/apps/apps.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@
7474
"title": "Memory Game",
7575
"description": "Test your memory by matching pairs of cards.",
7676
"icon": "BrainIcon"
77+
},
78+
{
79+
"slug": "rock-paper-scissors",
80+
"to": "/apps/rock-paper-scissors",
81+
"title": "Rock Paper Scissors",
82+
"description": "Play the classic game of Rock Paper Scissors against the computer.",
83+
"icon": "HandshakeIcon"
7784
}
7885
]
7986
},

src/components/AnimatedRoutes.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import JSONGeneratorPage from '../pages/apps/JSONGeneratorPage';
4747
import CardGamePage from '../pages/apps/CardGamePage';
4848
import SoccerPongPage from '../pages/apps/SoccerPongPage';
4949
import MemoryGamePage from '../pages/apps/MemoryGamePage'; // Import MemoryGamePage
50+
import RockPaperScissorsPage from '../pages/apps/RockPaperScissorsPage'; // Import RockPaperScissorsPage
5051
import SettingsPage from '../pages/SettingsPage';
5152

5253
import UsefulLinksPage from '../pages/UsefulLinksPage';
@@ -405,6 +406,7 @@ function AnimatedRoutes() {
405406
<Route path="/apps::card" element={<Navigate to="/apps/card-game" replace />} />
406407
<Route path="/apps::sp" element={<Navigate to="/apps/soccer-pong" replace />} />
407408
<Route path="/apps::mg" element={<Navigate to="/apps/memory-game" replace />} />
409+
<Route path="/apps::rps" element={<Navigate to="/apps/rock-paper-scissors" replace />} />
408410
{/* End of hardcoded redirects */}
409411
<Route
410412
path="/apps/ip"
@@ -420,6 +422,20 @@ function AnimatedRoutes() {
420422
</motion.div>
421423
}
422424
/>
425+
<Route
426+
path="/apps/rock-paper-scissors"
427+
element={
428+
<motion.div
429+
initial="initial"
430+
animate="in"
431+
exit="out"
432+
variants={pageVariants}
433+
transition={pageTransition}
434+
>
435+
<RockPaperScissorsPage />
436+
</motion.div>
437+
}
438+
/>
423439
<Route
424440
path="/apps/card-game"
425441
element={
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { ArrowLeft, Handshake } from '@phosphor-icons/react';
2+
import { useToast } from '../../hooks/useToast';
3+
import useSeo from "../../hooks/useSeo";
4+
import colors from '../../config/colors';
5+
import {useEffect, useState} from "react";
6+
import {Link} from "react-router-dom";
7+
8+
const choices = [
9+
{ name: 'Rock', emoji: '🪨' },
10+
{ name: 'Paper', emoji: '📄' },
11+
{ name: 'Scissors', emoji: '✂️' },
12+
];
13+
14+
const RockPaperScissorsPage = () => {
15+
useSeo({
16+
title: 'Rock Paper Scissors | Fezcodex',
17+
description: 'Play the classic game of Rock Paper Scissors against the computer.',
18+
keywords: ['Fezcodex', 'rock paper scissors', 'game', 'fun app'],
19+
ogTitle: 'Rock Paper Scissors | Fezcodex',
20+
ogDescription: 'Play the classic game of Rock Paper Scissors against the computer.',
21+
ogImage: 'https://fezcode.github.io/logo512.png',
22+
twitterCard: 'summary_large_image',
23+
twitterTitle: 'Rock Paper Scissors | Fezcodex',
24+
twitterDescription: 'Play the classic game of Rock Paper Scissors against the computer.',
25+
twitterImage: 'https://fezcode.github.io/logo512.png'
26+
});
27+
28+
const [playerChoice, setPlayerChoice] = useState(null);
29+
const [computerChoice, setComputerChoice] = useState(null);
30+
const [result, setResult] = useState('');
31+
const [playerScore, setPlayerScore] = useState(0);
32+
const [computerScore, setComputerScore] = useState(0);
33+
34+
useEffect(() => {
35+
if (playerChoice !== null) {
36+
const computerRandomChoice = choices[Math.floor(Math.random() * choices.length)];
37+
setComputerChoice(computerRandomChoice);
38+
determineWinner(playerChoice, computerRandomChoice);
39+
}
40+
}, [playerChoice]);
41+
42+
const determineWinner = (pChoice, cChoice) => {
43+
if (pChoice.name === cChoice.name) {
44+
setResult('It\'s a tie!');
45+
} else if (
46+
(pChoice.name === 'Rock' && cChoice.name === 'Scissors') ||
47+
(pChoice.name === 'Paper' && cChoice.name === 'Rock') ||
48+
(pChoice.name === 'Scissors' && cChoice.name === 'Paper')
49+
) {
50+
setResult('You win!');
51+
setPlayerScore(prevScore => prevScore + 1);
52+
} else {
53+
setResult('Computer wins!');
54+
setComputerScore(prevScore => prevScore + 1);
55+
}
56+
};
57+
58+
const handlePlayerChoice = (choice) => {
59+
setPlayerChoice(choice);
60+
};
61+
62+
const resetGame = () => {
63+
setPlayerChoice(null);
64+
setComputerChoice(null);
65+
setResult('');
66+
};
67+
68+
const { addToast } = useToast();
69+
70+
const cardStyle = {
71+
backgroundColor: colors['app-alpha-10'],
72+
borderColor: colors['app-alpha-50'],
73+
color: colors.app,
74+
};
75+
76+
return (
77+
<div className="py-16 sm:py-24">
78+
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
79+
<Link
80+
to="/apps"
81+
className="text-article hover:underline flex items-center justify-center gap-2 text-lg mb-4"
82+
>
83+
<ArrowLeft size={24} /> Back to Apps
84+
</Link>
85+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
86+
<span className="codex-color">fc</span>
87+
<span className="separator-color">::</span>
88+
<span className="apps-color">apps</span>
89+
<span className="separator-color">::</span>
90+
<span className="single-app-color">rps</span>
91+
</h1>
92+
<hr className="border-gray-700" />
93+
<div className="flex justify-center items-center mt-16">
94+
<div
95+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform transition-all duration-300 ease-in-out scale-105 overflow-hidden h-full w-full max-w-4xl"
96+
style={cardStyle}
97+
>
98+
<div
99+
className="absolute top-0 left-0 w-full h-full opacity-10"
100+
style={{
101+
backgroundImage:
102+
'radial-gradient(circle, white 1px, transparent 1px)',
103+
backgroundSize: '10px 10px',
104+
}}
105+
></div>
106+
<div className="relative z-10 p-1">
107+
<h1 className="text-3xl font-arvo font-normal mb-4 text-app"> Rock Paper Scissors </h1>
108+
<hr className="border-gray-700 mb-4" />
109+
<div className="flex flex-col items-center gap-8">
110+
<div className="flex justify-center space-x-4 mb-8">
111+
{choices.map((choice) => (
112+
<button
113+
key={choice.name}
114+
onClick={() => handlePlayerChoice(choice)}
115+
className="p-4 rounded-lg shadow-md bg-gray-700 hover:bg-gray-600 transition-colors duration-200 text-6xl"
116+
>
117+
{choice.emoji}
118+
</button>
119+
))}
120+
</div>
121+
122+
{playerChoice && computerChoice && (
123+
<div className="text-center mb-8">
124+
<p className="text-2xl mb-2">You chose: {playerChoice.emoji} {playerChoice.name}</p>
125+
<p className="text-2xl mb-4">Computer chose: {computerChoice.emoji} {computerChoice.name}</p>
126+
<p className="text-3xl font-semibold text-blue-400">{result}</p>
127+
<button
128+
onClick={resetGame}
129+
className="mt-4 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200"
130+
>
131+
Play Again
132+
</button>
133+
</div>
134+
)}
135+
136+
<div className="flex justify-center space-x-8 text-2xl font-medium">
137+
<p>Player Score: <span className="text-green-400">{playerScore}</span></p>
138+
<p>Computer Score: <span className="text-red-400">{computerScore}</span></p>
139+
</div>
140+
</div>
141+
</div>
142+
</div>
143+
</div>
144+
</div>
145+
</div>
146+
);
147+
};
148+
149+
export default RockPaperScissorsPage;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* No custom CSS needed for RockPaperScissorsPage yet, as it primarily uses Tailwind CSS. */

0 commit comments

Comments
 (0)