From bd36f2941926c12edaf676c17be099d648008f4d Mon Sep 17 00:00:00 2001 From: George Powell Date: Sun, 22 Feb 2026 00:25:08 -0500 Subject: [PATCH] Updated streak-percentile to count all players from the last 30 days (or all active streaks if players have a greater than 30 day streak) --- src/lib/components/WinScreen.svelte | 3 +- src/routes/api/streak-percentile/+server.ts | 51 +++++++++++++++++---- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/lib/components/WinScreen.svelte b/src/lib/components/WinScreen.svelte index fb6cbb9..c980833 100644 --- a/src/lib/components/WinScreen.svelte +++ b/src/lib/components/WinScreen.svelte @@ -119,8 +119,7 @@ {/if} {#if streakPercentile !== null}

- Only {streakPercentile}% of players have a streak of {streak} - or greater. + {streakPercentile <= 50 ? "Only " : ""}{streakPercentile}% of players have a streak of {streak} or greater.

{/if} {/if} diff --git a/src/routes/api/streak-percentile/+server.ts b/src/routes/api/streak-percentile/+server.ts index f793792..daf8c69 100644 --- a/src/routes/api/streak-percentile/+server.ts +++ b/src/routes/api/streak-percentile/+server.ts @@ -38,13 +38,30 @@ export const GET: RequestHandler = async ({ url }) => { } } - // Calculate the current streak for each user - const streaks: number[] = []; + // Calculate the current streak for each user. + // Start from today; if the user hasn't played today yet, try yesterday so + // that streaks aren't zeroed out mid-day before the player has had a chance + // to complete today's puzzle. + const yesterday = new Date(`${localDate}T00:00:00`); + yesterday.setDate(yesterday.getDate() - 1); + const yesterdayStr = yesterday.toLocaleDateString('en-CA'); + + const thirtyDaysAgo = new Date(`${localDate}T00:00:00`); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); + const thirtyDaysAgoStr = thirtyDaysAgo.toLocaleDateString('en-CA'); + + // For each user, compute their current streak and whether they've played + // within the last 30 days. "Eligible players" = active streak OR recent play. + const userStats: { streak: number; isEligible: boolean }[] = []; for (const [, dates] of byUser) { // dates are already desc-sorted const dateSet = new Set(dates); + + // Pick the most recent anchor: today if played, otherwise yesterday + const anchor = dateSet.has(localDate) ? localDate : yesterdayStr; + let streak = 0; - let cursor = new Date(`${localDate}T00:00:00`); + let cursor = new Date(`${anchor}T00:00:00`); while (true) { const dateStr = cursor.toLocaleDateString('en-CA'); @@ -53,20 +70,34 @@ export const GET: RequestHandler = async ({ url }) => { cursor.setDate(cursor.getDate() - 1); } - streaks.push(streak); + const hasRecentPlay = dates.some((d) => d >= thirtyDaysAgoStr); + userStats.push({ streak, isEligible: streak >= 1 || hasRecentPlay }); } - // Only count users who have an active streak (streak >= 1) - const activeStreaks = streaks.filter((s) => s >= 1); + const eligiblePlayers = userStats.filter((u) => u.isEligible); - if (activeStreaks.length === 0) { + if (eligiblePlayers.length === 0) { + console.log('[streak-percentile] No eligible players found, returning 100th percentile'); return json({ percentile: 100 }); } - // Percentage of active-streak users who have a streak >= targetStreak - const atOrAbove = activeStreaks.filter((s) => s >= targetStreak).length; - const raw = (atOrAbove / activeStreaks.length) * 100; + // Percentage of eligible players who have a streak >= targetStreak + const atOrAbove = eligiblePlayers.filter((u) => u.streak >= targetStreak).length; + const raw = (atOrAbove / eligiblePlayers.length) * 100; const percentile = raw < 1 ? Math.round(raw * 100) / 100 : Math.round(raw); + console.log('[streak-percentile]', { + localDate, + targetStreak, + totalUsers: byUser.size, + totalRows: rows.length, + eligiblePlayers: eligiblePlayers.length, + activeStreaks: userStats.filter((u) => u.streak >= 1).length, + recentPlayers: userStats.filter((u) => u.isEligible).length, + atOrAbove, + raw, + percentile, + }); + return json({ percentile }); };