Skip to content

Commit da5eaf5

Browse files
Merge pull request #107 from Guma15/feature/reaction_time_game
Reaction Time Game
2 parents 83e34c7 + 096acdc commit da5eaf5

File tree

3 files changed

+726
-0
lines changed

3 files changed

+726
-0
lines changed

src/data/content.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { RandomMeme } from "../pages/activities/RandomMemes";
1010
import { RandomJoke } from "../pages/activities/RandomJoke";
1111
import { RandomAnimeQuote } from "../pages/activities/RandomAnimeQuote";
1212
import { SimonSays } from "../pages/games/SimonSays";
13+
import { ReactionTime } from "../pages/games/ReactionTime";
1314
import MemeCaptionMaker from "../pages/games/MemeCaptionMaker";
1415
import meme from "../assets/activities/meme.jpg";
1516
import dog from "../assets/activities/dogimage.jpeg";
@@ -130,6 +131,13 @@ export const games = [
130131
urlTerm: "simon-says",
131132
element: <SimonSays />,
132133
},
134+
{
135+
title: "Reaction Time Test",
136+
description: "Test your reflexes - click as fast as you can!",
137+
icon: "https://cdn-icons-png.flaticon.com/512/2972/2972554.png",
138+
urlTerm: "reaction-time",
139+
element: <ReactionTime />,
140+
},
133141
{
134142
title: "Meme Caption Maker",
135143
description: "Create hilarious memes with custom captions",

src/pages/games/ReactionTime.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import React, { useState, useEffect, useRef } from 'react';
2+
import "../../styles/pages/games/ReactionTime.css";
3+
4+
export const ReactionTime = () => {
5+
const [gameState, setGameState] = useState('idle'); // idle, waiting, ready, result
6+
const [reactionTimes, setReactionTimes] = useState([]);
7+
const [currentAttempt, setCurrentAttempt] = useState(null);
8+
const [message, setMessage] = useState('Click to Start');
9+
const [stats, setStats] = useState({ average: 0, fastest: 0, slowest: 0 });
10+
const [tooEarly, setTooEarly] = useState(false);
11+
12+
const startTimeRef = useRef(null);
13+
const timeoutRef = useRef(null);
14+
15+
useEffect(() => {
16+
return () => {
17+
if (timeoutRef.current) {
18+
clearTimeout(timeoutRef.current);
19+
}
20+
};
21+
}, []);
22+
23+
useEffect(() => {
24+
if (reactionTimes.length > 0) {
25+
const avg = reactionTimes.reduce((a, b) => a + b, 0) / reactionTimes.length;
26+
const fastest = Math.min(...reactionTimes);
27+
const slowest = Math.max(...reactionTimes);
28+
setStats({
29+
average: Math.round(avg),
30+
fastest: fastest,
31+
slowest: slowest
32+
});
33+
}
34+
}, [reactionTimes]);
35+
36+
const startGame = () => {
37+
if (gameState === 'waiting' || gameState === 'ready') return;
38+
39+
setTooEarly(false);
40+
setGameState('waiting');
41+
setMessage('Wait for green...');
42+
setCurrentAttempt(null);
43+
44+
// Random delay between 1000ms and 5000ms
45+
const randomDelay = Math.floor(Math.random() * 4000) + 1000;
46+
47+
timeoutRef.current = setTimeout(() => {
48+
setGameState('ready');
49+
setMessage('CLICK NOW!');
50+
startTimeRef.current = Date.now();
51+
}, randomDelay);
52+
};
53+
54+
const handleClick = () => {
55+
if (gameState === 'idle' || gameState === 'result') {
56+
startGame();
57+
} else if (gameState === 'waiting') {
58+
// Clicked too early
59+
clearTimeout(timeoutRef.current);
60+
setGameState('result');
61+
setMessage('Too early! Click to try again');
62+
setTooEarly(true);
63+
setCurrentAttempt(null);
64+
} else if (gameState === 'ready') {
65+
// Calculate reaction time
66+
const reactionTime = Date.now() - startTimeRef.current;
67+
setCurrentAttempt(reactionTime);
68+
setReactionTimes([...reactionTimes, reactionTime]);
69+
setGameState('result');
70+
setMessage(`${reactionTime}ms - Click to try again`);
71+
setTooEarly(false);
72+
}
73+
};
74+
75+
const resetStats = () => {
76+
setReactionTimes([]);
77+
setStats({ average: 0, fastest: 0, slowest: 0 });
78+
setGameState('idle');
79+
setMessage('Click to Start');
80+
setCurrentAttempt(null);
81+
setTooEarly(false);
82+
};
83+
84+
const getPerformanceRating = (time) => {
85+
if (time < 200) return { text: 'Lightning Fast!', color: '#FFD700' };
86+
if (time < 250) return { text: 'Excellent!', color: '#00ff41' };
87+
if (time < 300) return { text: 'Great!', color: '#00d4ff' };
88+
if (time < 400) return { text: 'Good!', color: '#7fff00' };
89+
if (time < 500) return {text: 'Not bad!', color: '#ffaa00' };
90+
return { text: 'Keep trying!', color: '#ff6b6b' };
91+
};
92+
93+
const getStateClass = () => {
94+
if (gameState === 'waiting') return 'waiting';
95+
if (gameState === 'ready') return 'ready';
96+
if (tooEarly) return 'too-early';
97+
return '';
98+
};
99+
100+
return (
101+
<div className="reaction-time-container">
102+
<div className="reaction-header">
103+
<h1 className="reaction-title">⚡ Reaction Time Test</h1>
104+
<p className="reaction-subtitle">Test how fast your reflexes are!</p>
105+
</div>
106+
107+
<div className={`reaction-game-area ${getStateClass()}`} onClick={handleClick}>
108+
<div className="reaction-message">
109+
<span className="message-text">{message}</span>
110+
{currentAttempt && !tooEarly && (
111+
<div className="performance-badge" style={{ color: getPerformanceRating(currentAttempt).color }}>
112+
<span className="badge-emoji">{getPerformanceRating(currentAttempt).emoji}</span>
113+
<span className="badge-text">{getPerformanceRating(currentAttempt).text}</span>
114+
</div>
115+
)}
116+
{tooEarly && (
117+
<div className="too-early-message">
118+
<span className="error-emoji"></span>
119+
<span className="error-text">Wait for the green screen!</span>
120+
</div>
121+
)}
122+
</div>
123+
124+
{gameState === 'waiting' && (
125+
<div className="pulse-indicator">
126+
<div className="pulse-ring"></div>
127+
<div className="pulse-ring"></div>
128+
<div className="pulse-ring"></div>
129+
</div>
130+
)}
131+
</div>
132+
133+
<div className="reaction-instructions">
134+
<p>
135+
{gameState === 'idle' && '👆 Click the box above to start'}
136+
{gameState === 'waiting' && '⏳ Wait for the screen to turn green'}
137+
{gameState === 'ready' && '🎯 Click as fast as you can!'}
138+
{gameState === 'result' && '🔄 Click to try again'}
139+
</p>
140+
</div>
141+
142+
{reactionTimes.length > 0 && (
143+
<div className="reaction-stats">
144+
<h2 className="stats-title">📊 Your Statistics</h2>
145+
<div className="stats-grid">
146+
<div className="stat-card">
147+
<div className="stat-icon">📈</div>
148+
<div className="stat-label">Attempts</div>
149+
<div className="stat-value">{reactionTimes.length}</div>
150+
</div>
151+
<div className="stat-card">
152+
<div className="stat-icon"></div>
153+
<div className="stat-label">Average</div>
154+
<div className="stat-value">{stats.average}ms</div>
155+
</div>
156+
<div className="stat-card highlight">
157+
<div className="stat-icon">🏆</div>
158+
<div className="stat-label">Fastest</div>
159+
<div className="stat-value">{stats.fastest}ms</div>
160+
</div>
161+
<div className="stat-card">
162+
<div className="stat-icon">🐢</div>
163+
<div className="stat-label">Slowest</div>
164+
<div className="stat-value">{stats.slowest}ms</div>
165+
</div>
166+
</div>
167+
168+
<div className="attempts-history">
169+
<h3 className="history-title">Recent Attempts</h3>
170+
<div className="attempts-list">
171+
{reactionTimes.slice().reverse().map((time, index) => {
172+
const rating = getPerformanceRating(time);
173+
return (
174+
<div key={index} className="attempt-item">
175+
<span className="attempt-number">#{reactionTimes.length - index}</span>
176+
<span className="attempt-time">{time}ms</span>
177+
<span className="attempt-rating" style={{ color: rating.color }}>
178+
{rating.emoji} {rating.text}
179+
</span>
180+
</div>
181+
);
182+
})}
183+
</div>
184+
</div>
185+
186+
<button className="reset-button" onClick={resetStats}>
187+
🔄 Reset Statistics
188+
</button>
189+
</div>
190+
)}
191+
192+
<div className="reaction-info">
193+
<h3 className="info-title">💡 How It Works</h3>
194+
<ul className="info-list">
195+
<li>Click the box to start the test</li>
196+
<li>Wait for the screen to turn <strong>green</strong></li>
197+
<li>Click as fast as you can when it turns green</li>
198+
<li>Try multiple times to improve your average!</li>
199+
<li>Don't click too early or you'll have to start over</li>
200+
</ul>
201+
</div>
202+
</div>
203+
);
204+
};
205+

0 commit comments

Comments
 (0)