From f7235b26cba836e54672e703c3dd65a5787acf05 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 31 Jan 2023 22:47:45 +0000 Subject: [PATCH] sunrise-sunset: Streak Counting is Hard --- .../SunriseSunsetStreakCalculator.test.ts | 21 +++++-- .../SunriseSunsetStreakCalculator.ts | 57 +++++++++++++------ 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts index 36816e8..10971a0 100644 --- a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts +++ b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts @@ -27,7 +27,7 @@ describe('SunriseSunsetStreakCalculator', () => { expect(currentStreakLength).toBe(1); }); - it(`should return a two day stream if it's the most recent`, () => { + it(`should return a two day streak if it's the most recent`, () => { // GIVEN const correctDays = ['2023-01-29', '2023-01-28', '2023-01-26', '2023-01-25', '2023-01-24']; const today = '2023-01-29'; @@ -51,6 +51,17 @@ describe('SunriseSunsetStreakCalculator', () => { expect(currentStreakLength).toBe(1); }); + it(`should recognise a one-day streak with gaps`, () => { + // GIVEN + const correctDays = ['2023-01-26', '2023-01-27', '2023-01-31']; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator('2023-01-31').getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(1); + }); + it(`should recognise a two day streak`, () => { // GIVEN const correctDays = ['2023-01-28', '2023-01-27']; @@ -135,15 +146,15 @@ describe('SunriseSunsetStreakCalculator', () => { it(`should get a fully shareable streak`, () => { // GIVEN - const correctDays = ['2023-01-10', '2023-01-11', '2023-01-15', '2023-01-16']; - const incorrectDays = ['2023-01-12', '2023-01-13']; - const today = new Date('2023-01-20T21:52Z'); + const correctDays = ['2023-01-26', '2023-01-27', '2023-01-31']; + const incorrectDays = ['2023-01-28']; + const today = new Date('2023-01-31T21:52Z'); // WHEN const shareableStatement = calculator.getShareableStatement(correctDays, incorrectDays, today); // THEN - const expected = `Sunrise, Sunset?\n2023-01-20\nšŸŒžšŸŒžšŸ„·šŸŒššŸŒššŸŒžšŸŒž\nCurrent Streak: 2\nLongest Streak: 2`; + const expected = `Sunrise, Sunset?\n2023-01-31\nšŸŒžšŸ„·šŸ„·šŸŒššŸŒžšŸŒž\nCurrent Streak: 1\nLongest Streak: 2`; expect(shareableStatement).toStrictEqual(expected); }); }); diff --git a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts index 7d0ccf0..8d5b22f 100644 --- a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts +++ b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts @@ -78,31 +78,53 @@ class SunriseSunsetDayGuessSet { } class SunriseSunsetStreak { - private readonly longestStreak: number; + readonly longestStreak: number; private readonly allStreaks: number[]; readonly mostRecentStreak: number; constructor(correctDays: Date[]) { + if (correctDays.length === 0) { + this.longestStreak = 0; + this.mostRecentStreak = 0; + this.allStreaks = []; + return; + } + // A (reverse-order) list of streaks in the lis tof correct days. const sortedDays = correctDays.sort((a, b) => b.getTime() - a.getTime()); - const allStreaks: number[] = sortedDays.reduce((streaks: number[], day, index, days) => { - const currentStreakLength = streaks[0] ?? 1; - const daysBetween = differenceInCalendarDays(day, days[index + 1]); - if (daysBetween === 1) { - // The streak continues ! Add a day. - streaks[0] = currentStreakLength + 1; - } else { - // We've hit a gap, so start a new streak - streaks.unshift(1); - } + const allStreaks: number[] = sortedDays.reduce( + (streaks: number[], day, index, days) => { + const currentStreakLength = streaks.at(-1); - return streaks; - }, []); + const daysBetween = differenceInCalendarDays(day, days.at(index + 1)); + + console.log( + streaks, + `Day ${index}: ${formatDate( + day, + 'yyyy-MM-dd' + )}, streak length: ${currentStreakLength}, days between: ${daysBetween}` + ); + + if (isNaN(daysBetween)) { + // We're on the last day, so just return the streaks + return streaks; + } else if (daysBetween === 1) { + // Add one to the last item in the streaks array + return [...streaks.slice(0, -1), currentStreakLength + 1]; + } else { + // We've hit a gap, so start a new streak + return [...streaks, 1]; + } + }, + [1] + ); // The streaks are in reverse order, so the most recent streak is the last one. - this.mostRecentStreak = allStreaks[allStreaks.length - 1] ?? 0; - this.longestStreak = Math.max(...allStreaks); + console.log('allStreaks', allStreaks); + this.mostRecentStreak = allStreaks[0] ?? 0; + this.longestStreak = allStreaks.length > 0 ? Math.max(...allStreaks) : 0; this.allStreaks = allStreaks; } } @@ -139,11 +161,14 @@ export class SunriseSunsetStreakCalculator { todayFormatted, emoji, `Current Streak: ${streak.mostRecentStreak}`, - `Longest Streak: ${streak.mostRecentStreak}`, + `Longest Streak: ${streak.longestStreak}`, ].join(joiningString); } getStreakLength(correctDays: string[]): number { + console.log(`getStreakLength`); + console.log(correctDays); + if (correctDays.length === 0) { console.log(`No correct days, returning 0.`); return 0;