diff --git a/src/lib/components/ChapterGuess.svelte b/src/lib/components/ChapterGuess.svelte index aa15a9d..992ae0e 100644 --- a/src/lib/components/ChapterGuess.svelte +++ b/src/lib/components/ChapterGuess.svelte @@ -1,216 +1,222 @@ -
-

Bonus Challenge

-

- Guess the chapter for an even higher grade -

+
+

Bonus Challenge

+

+ Guess the chapter for an even higher grade +

-
- {#each chapterOptions as chapter} - - {/each} -
+ > + {chapter} + + {/each} +
- {#if hasAnswered} -

- {isCorrect ? "✓ Correct!" : "✗ Incorrect"} -

-

- The verse is from chapter {correctChapter} -

- {#if isCorrect} -

Grade: S++

- {/if} - {/if} -
+ {#if hasAnswered} +

+ {isCorrect ? "✓ Correct!" : "✗ Incorrect"} +

+

+ The verse is from chapter {correctChapter} +

+ {#if isCorrect} +

Grade: S++

+ {/if} + {/if} +
diff --git a/src/lib/components/WinScreen.svelte b/src/lib/components/WinScreen.svelte index 6258f64..86f8c47 100644 --- a/src/lib/components/WinScreen.svelte +++ b/src/lib/components/WinScreen.svelte @@ -16,6 +16,7 @@ totalSolves: number; averageGuesses: number; tiedCount: number; + percentile: number; } interface WeightedMessage { @@ -169,7 +170,9 @@ >
-
+
#{statsData.solveRank}
@@ -180,7 +183,9 @@
-
+
{toOrdinal(statsData.guessRank)}
@@ -189,13 +194,20 @@ ? "solve" : "solves"}{statsData.tiedCount > 0 ? `, tied with ${statsData.tiedCount} ${statsData.tiedCount === 1 ? "other" : "others"}` - : ""} + : ""}.
+ {#if statsData.percentile <= 25} + + (Top {statsData.percentile}%) + + {/if}
-
+
{statsData.averageGuesses}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a2e7a2a..9fcaace 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -43,6 +43,7 @@ totalSolves: number; averageGuesses: number; tiedCount: number; + percentile: number; } | null>(null); let guessedIds = $derived(new Set(guesses.map((g) => g.book.id))); diff --git a/src/routes/api/submit-completion/+server.ts b/src/routes/api/submit-completion/+server.ts index 8039910..0b80d09 100644 --- a/src/routes/api/submit-completion/+server.ts +++ b/src/routes/api/submit-completion/+server.ts @@ -44,9 +44,11 @@ export const POST: RequestHandler = async ({ request }) => { // Solve rank: position in time-ordered list const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1; - // Guess rank: count how many had FEWER guesses (ties get same rank) - const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length; - const guessRank = betterGuesses + 1; + // Guess rank: count how many DISTINCT guess counts are better (grouped ranking) + const uniqueBetterGuessCounts = new Set( + allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount) + ); + const guessRank = uniqueBetterGuessCounts.size + 1; // Count ties: how many have the SAME guessCount (excluding self) const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length; @@ -55,9 +57,13 @@ export const POST: RequestHandler = async ({ request }) => { const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0); const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10; + // Percentile: what percentage of people you beat (100 - your rank percentage) + const betterOrEqualCount = allCompletions.filter(c => c.guessCount <= guessCount).length; + const percentile = Math.round((betterOrEqualCount / totalSolves) * 100); + return json({ success: true, - stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount } + stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount, percentile } }); } catch (err) { console.error('Error submitting completion:', err); @@ -104,9 +110,11 @@ export const GET: RequestHandler = async ({ url }) => { // Solve rank: position in time-ordered list const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1; - // Guess rank: count how many had FEWER guesses (ties get same rank) - const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length; - const guessRank = betterGuesses + 1; + // Guess rank: count how many DISTINCT guess counts are better (grouped ranking) + const uniqueBetterGuessCounts = new Set( + allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount) + ); + const guessRank = uniqueBetterGuessCounts.size + 1; // Count ties: how many have the SAME guessCount (excluding self) const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length; @@ -115,9 +123,13 @@ export const GET: RequestHandler = async ({ url }) => { const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0); const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10; + // Percentile: what percentage of people you beat (100 - your rank percentage) + const betterOrEqualCount = allCompletions.filter(c => c.guessCount <= guessCount).length; + const percentile = Math.round((betterOrEqualCount / totalSolves) * 100); + return json({ success: true, - stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount } + stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount, percentile } }); } catch (err) { console.error('Error fetching stats:', err);