From 188a101388b32e0084116638fb735981e00ea84f Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:47:17 +0100 Subject: [PATCH 01/15] feat(difficulty): add difficulty constants --- frontend/src/constants/difficulty.ts | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 frontend/src/constants/difficulty.ts diff --git a/frontend/src/constants/difficulty.ts b/frontend/src/constants/difficulty.ts new file mode 100644 index 0000000..09a8996 --- /dev/null +++ b/frontend/src/constants/difficulty.ts @@ -0,0 +1,62 @@ +export type DifficultyLevel = 'easy' | 'medium' | 'hard' | 'expert'; + +export interface DifficultyConfig { + level: DifficultyLevel; + name: string; + description: string; + icon: string; + color: string; + pointMultiplier: number; + timeLimit: number; + unlockRequirement?: number; +} + +export const DIFFICULTY_LEVELS: DifficultyConfig[] = [ + { + level: 'easy', + name: 'Easy', + description: 'Perfect for beginners', + icon: '🌱', + color: '#10B981', + pointMultiplier: 1.0, + timeLimit: 60, + }, + { + level: 'medium', + name: 'Medium', + description: 'A balanced challenge', + icon: '⚡', + color: '#F59E0B', + pointMultiplier: 1.5, + timeLimit: 45, + unlockRequirement: 1000, + }, + { + level: 'hard', + name: 'Hard', + description: 'Test your knowledge', + icon: '🔥', + color: '#EF4444', + pointMultiplier: 2.0, + timeLimit: 30, + unlockRequirement: 5000, + }, + { + level: 'expert', + name: 'Expert', + description: 'Only for masters', + icon: '💎', + color: '#8B5CF6', + pointMultiplier: 3.0, + timeLimit: 20, + unlockRequirement: 15000, + }, +]; + +export const getDifficultyByLevel = (level: DifficultyLevel) => + DIFFICULTY_LEVELS.find(d => d.level === level); + +export const calculatePoints = (basePoints: number, difficulty: DifficultyLevel) => { + const config = getDifficultyByLevel(difficulty); + return Math.round(basePoints * (config?.pointMultiplier || 1)); +}; From 37a39179c293e0b6d2930188a8ced88635e5a06e Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:47:38 +0100 Subject: [PATCH 02/15] feat(difficulty): add DifficultyCard --- .../components/difficulty/DifficultyCard.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 frontend/src/components/difficulty/DifficultyCard.tsx diff --git a/frontend/src/components/difficulty/DifficultyCard.tsx b/frontend/src/components/difficulty/DifficultyCard.tsx new file mode 100644 index 0000000..cece33e --- /dev/null +++ b/frontend/src/components/difficulty/DifficultyCard.tsx @@ -0,0 +1,37 @@ +import { DifficultyLevel, DifficultyConfig, DIFFICULTY_LEVELS } from '@/constants/difficulty'; + +interface DifficultyCardProps { + difficulty: DifficultyConfig; + isSelected: boolean; + isUnlocked: boolean; + onSelect: () => void; +} + +export const DifficultyCard: React.FC = ({ + difficulty, + isSelected, + isUnlocked, + onSelect, +}) => { + return ( + + ); +}; From da61d1a5608508a2c8dee1ede769d265dfad1e92 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:48:00 +0100 Subject: [PATCH 03/15] feat(difficulty): add DifficultySelector --- .../difficulty/DifficultySelector.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 frontend/src/components/difficulty/DifficultySelector.tsx diff --git a/frontend/src/components/difficulty/DifficultySelector.tsx b/frontend/src/components/difficulty/DifficultySelector.tsx new file mode 100644 index 0000000..8383b8b --- /dev/null +++ b/frontend/src/components/difficulty/DifficultySelector.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { DifficultyCard } from './DifficultyCard'; +import { DifficultyLevel, DIFFICULTY_LEVELS } from '@/constants/difficulty'; + +interface DifficultySelectorProps { + selected: DifficultyLevel; + onSelect: (level: DifficultyLevel) => void; + unlockedLevels: DifficultyLevel[]; +} + +export const DifficultySelector: React.FC = ({ + selected, + onSelect, + unlockedLevels, +}) => { + return ( +
+ {DIFFICULTY_LEVELS.map((difficulty) => ( + onSelect(difficulty.level)} + /> + ))} +
+ ); +}; From 84f0b2957de5dbe966228cdd88e13facad715e54 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:48:16 +0100 Subject: [PATCH 04/15] feat(difficulty): add barrel exports --- frontend/src/components/difficulty/index.ts | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 frontend/src/components/difficulty/index.ts diff --git a/frontend/src/components/difficulty/index.ts b/frontend/src/components/difficulty/index.ts new file mode 100644 index 0000000..a4e98ea --- /dev/null +++ b/frontend/src/components/difficulty/index.ts @@ -0,0 +1,2 @@ +export { DifficultyCard } from './DifficultyCard'; +export { DifficultySelector } from './DifficultySelector'; From c0adf685df977938ce8364bcb9a4ec4e02d79eb5 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:48:33 +0100 Subject: [PATCH 05/15] feat(difficulty): add useDifficulty hook --- frontend/src/hooks/useDifficulty.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 frontend/src/hooks/useDifficulty.ts diff --git a/frontend/src/hooks/useDifficulty.ts b/frontend/src/hooks/useDifficulty.ts new file mode 100644 index 0000000..d034c7e --- /dev/null +++ b/frontend/src/hooks/useDifficulty.ts @@ -0,0 +1,29 @@ +import { useState, useCallback, useEffect } from 'react'; +import { DifficultyLevel } from '@/constants/difficulty'; + +export const useDifficulty = (initialLevel: DifficultyLevel = 'easy') => { + const [selectedDifficulty, setSelectedDifficulty] = useState(initialLevel); + const [unlockedDifficulties, setUnlockedDifficulties] = useState(['easy']); + + const unlockDifficulty = useCallback((level: DifficultyLevel) => { + setUnlockedDifficulties((prev) => { + if (!prev.includes(level)) { + return [...prev, level]; + } + return prev; + }); + }, []); + + const selectDifficulty = useCallback((level: DifficultyLevel) => { + if (unlockedDifficulties.includes(level)) { + setSelectedDifficulty(level); + } + }, [unlockedDifficulties]); + + return { + selectedDifficulty, + unlockedDifficulties, + selectDifficulty, + unlockDifficulty, + }; +}; From a656b78d7c6790a3354edaffeb2f187070514ec7 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:48:49 +0100 Subject: [PATCH 06/15] feat(difficulty): add difficulty page --- frontend/src/app/difficulty/page.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 frontend/src/app/difficulty/page.tsx diff --git a/frontend/src/app/difficulty/page.tsx b/frontend/src/app/difficulty/page.tsx new file mode 100644 index 0000000..2a7ed14 --- /dev/null +++ b/frontend/src/app/difficulty/page.tsx @@ -0,0 +1,24 @@ +'use client'; + +import React from 'react'; +import { DifficultySelector } from '@/components/difficulty'; +import { useDifficulty } from '@/hooks/useDifficulty'; + +export default function DifficultyPage() { + const { selectedDifficulty, unlockedDifficulties, selectDifficulty } = useDifficulty(); + + return ( +
+
+

+ Select Difficulty +

+ +
+
+ ); +} From 2407ec8d8900d0fdf5147d179f381318979c5263 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:49:04 +0100 Subject: [PATCH 07/15] feat(difficulty): add layout --- frontend/src/app/difficulty/layout.tsx | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 frontend/src/app/difficulty/layout.tsx diff --git a/frontend/src/app/difficulty/layout.tsx b/frontend/src/app/difficulty/layout.tsx new file mode 100644 index 0000000..5031780 --- /dev/null +++ b/frontend/src/app/difficulty/layout.tsx @@ -0,0 +1,4 @@ +import { ReactNode } from 'react'; +export default function DifficultyLayout({ children }: { children: ReactNode }) { + return <>{children}; +} From d0162386c7d53af0c6150b68dc04edee3d22e466 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:49:21 +0100 Subject: [PATCH 08/15] feat(difficulty): add stats types --- frontend/src/types/stats.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 frontend/src/types/stats.ts diff --git a/frontend/src/types/stats.ts b/frontend/src/types/stats.ts new file mode 100644 index 0000000..7eac6a4 --- /dev/null +++ b/frontend/src/types/stats.ts @@ -0,0 +1,9 @@ +export interface GameStats { + totalGamesPlayed: number; + totalQuestionsAnswered: number; + correctAnswers: number; + totalPointsEarned: number; + highestScore: number; + currentStreak: number; + longestStreak: number; +} From cb1c7d7a74b961dc34cc27e4c174aef13bad2220 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:49:36 +0100 Subject: [PATCH 09/15] feat(difficulty): add useGameStats hook --- frontend/src/hooks/useGameStats.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 frontend/src/hooks/useGameStats.ts diff --git a/frontend/src/hooks/useGameStats.ts b/frontend/src/hooks/useGameStats.ts new file mode 100644 index 0000000..d270aa5 --- /dev/null +++ b/frontend/src/hooks/useGameStats.ts @@ -0,0 +1,26 @@ +import { useState, useEffect } from 'react'; +import { GameStats } from '@/types/stats'; + +export const useGameStats = () => { + const [stats, setStats] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchStats = async () => { + await new Promise((r) => setTimeout(r, 300)); + setStats({ + totalGamesPlayed: 50, + totalQuestionsAnswered: 500, + correctAnswers: 420, + totalPointsEarned: 25000, + highestScore: 9500, + currentStreak: 7, + longestStreak: 15, + }); + setIsLoading(false); + }; + fetchStats(); + }, []); + + return { stats, isLoading }; +}; From 95476a912855c4fd380a858efeeea43c9e031883 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:49:50 +0100 Subject: [PATCH 10/15] feat(difficulty): add stats utils --- frontend/src/utils/statsUtils.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 frontend/src/utils/statsUtils.ts diff --git a/frontend/src/utils/statsUtils.ts b/frontend/src/utils/statsUtils.ts new file mode 100644 index 0000000..af8ab8d --- /dev/null +++ b/frontend/src/utils/statsUtils.ts @@ -0,0 +1,11 @@ +export const calculateAccuracy = (correct: number, total: number): number => { + if (total === 0) return 0; + return Math.round((correct / total) * 100); +}; + +export const formatPoints = (points: number): string => { + if (points >= 1000) { + return `${(points / 1000).toFixed(1)}k`; + } + return points.toString(); +}; From 4cf5b7a6bb446bac71f1481f7571d487a1074fcf Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:50:07 +0100 Subject: [PATCH 11/15] feat(difficulty): add leaderboard types --- frontend/src/types/leaderboard.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 frontend/src/types/leaderboard.ts diff --git a/frontend/src/types/leaderboard.ts b/frontend/src/types/leaderboard.ts new file mode 100644 index 0000000..b245219 --- /dev/null +++ b/frontend/src/types/leaderboard.ts @@ -0,0 +1,7 @@ +export interface LeaderboardEntry { + rank: number; + address: string; + username: string; + score: number; + gamesPlayed: number; +} From 03221998280094c34f829aecf14362a6da24c6e1 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:50:26 +0100 Subject: [PATCH 12/15] feat(difficulty): add useLeaderboard hook --- frontend/src/hooks/useLeaderboard.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/src/hooks/useLeaderboard.ts diff --git a/frontend/src/hooks/useLeaderboard.ts b/frontend/src/hooks/useLeaderboard.ts new file mode 100644 index 0000000..eb491ee --- /dev/null +++ b/frontend/src/hooks/useLeaderboard.ts @@ -0,0 +1,25 @@ +import { useState, useEffect } from 'react'; +import { LeaderboardEntry } from '@/types/leaderboard'; + +export const useLeaderboard = (limit = 10) => { + const [entries, setEntries] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchLeaderboard = async () => { + await new Promise((r) => setTimeout(r, 300)); + const mockEntries: LeaderboardEntry[] = Array.from({ length: limit }, (_, i) => ({ + rank: i + 1, + address: `0x${(i * 1111).toString(16)}...${(i * 9999).toString(16).slice(-4)}`, + username: `Player${i + 1}`, + score: 10000 - i * 500, + gamesPlayed: 50 - i * 2, + })); + setEntries(mockEntries); + setIsLoading(false); + }; + fetchLeaderboard(); + }, [limit]); + + return { entries, isLoading }; +}; From b18c612b8e94b306e6a15cfbbf9ad63420e520b3 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:50:50 +0100 Subject: [PATCH 13/15] feat(difficulty): add LeaderboardList --- .../leaderboard/LeaderboardList.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 frontend/src/components/leaderboard/LeaderboardList.tsx diff --git a/frontend/src/components/leaderboard/LeaderboardList.tsx b/frontend/src/components/leaderboard/LeaderboardList.tsx new file mode 100644 index 0000000..80879fd --- /dev/null +++ b/frontend/src/components/leaderboard/LeaderboardList.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { LeaderboardEntry } from '@/types/leaderboard'; + +interface LeaderboardListProps { + entries: LeaderboardEntry[]; +} + +export const LeaderboardList: React.FC = ({ entries }) => { + return ( +
+ {entries.map((entry) => ( +
+
+ + {entry.rank === 1 ? '🥇' : entry.rank === 2 ? '🥈' : entry.rank === 3 ? '🥉' : `#${entry.rank}`} + +
+

{entry.username}

+

{entry.address}

+
+
+
+

{entry.score}

+

{entry.gamesPlayed} games

+
+
+ ))} +
+ ); +}; From 927f5c55d3dcafe6cac7bac1047234dbac4d7e30 Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:51:04 +0100 Subject: [PATCH 14/15] feat(difficulty): add leaderboard exports --- frontend/src/components/leaderboard/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 frontend/src/components/leaderboard/index.ts diff --git a/frontend/src/components/leaderboard/index.ts b/frontend/src/components/leaderboard/index.ts new file mode 100644 index 0000000..3a67b0c --- /dev/null +++ b/frontend/src/components/leaderboard/index.ts @@ -0,0 +1 @@ +export { LeaderboardList } from './LeaderboardList'; From a0a7d350ddb0fa049b17d5e3caae9e721987c6cb Mon Sep 17 00:00:00 2001 From: diiabblo Date: Fri, 13 Feb 2026 02:51:22 +0100 Subject: [PATCH 15/15] docs(difficulty): add documentation --- README_DIFFICULTY.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 README_DIFFICULTY.md diff --git a/README_DIFFICULTY.md b/README_DIFFICULTY.md new file mode 100644 index 0000000..fb70b1c --- /dev/null +++ b/README_DIFFICULTY.md @@ -0,0 +1,19 @@ +# Difficulty Levels + +Four difficulty levels with increasing challenges and rewards. + +## Levels + +| Level | Multiplier | Time | Unlock | +|-------|------------|------|--------| +| Easy 🌱 | 1.0x | 60s | Default | +| Medium ⚡ | 1.5x | 45s | 1000 pts | +| Hard 🔥 | 2.0x | 30s | 5000 pts | +| Expert 💎 | 3.0x | 20s | 15000 pts | + +## Features + +- Progressive difficulty unlock system +- Point multipliers for harder difficulties +- Visual indicators for locked levels +- Time-based challenges