sunrise-sunset: Update Streak Calculator if a Streak has just ended
This commit is contained in:
parent
89df3538b4
commit
daf73c797e
6 changed files with 109 additions and 90 deletions
5
src/routes/sunrise-sunset/GuessType.ts
Normal file
5
src/routes/sunrise-sunset/GuessType.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export enum GuessType {
|
||||
correct = 'correct',
|
||||
incorrect = 'incorrect',
|
||||
missing = 'missing',
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
export let currentStreakLength: number;
|
||||
|
||||
const todayAsString = formatDate(new Date(), "yyyy-MM-dd");
|
||||
$: totalGuessCount = incorrectGuessDays.length + correctGuessDays.length;
|
||||
const calculator = new SunriseSunsetStreakCalculator(todayAsString);
|
||||
let hasTextBeenCopied = false;
|
||||
|
||||
|
|
|
|||
23
src/routes/sunrise-sunset/SunriseSunsetDayGuess.ts
Normal file
23
src/routes/sunrise-sunset/SunriseSunsetDayGuess.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { parse } from 'date-fns';
|
||||
import { GuessType } from './GuessType.js';
|
||||
|
||||
export class SunriseSunsetDayGuess {
|
||||
readonly day: Date;
|
||||
readonly emoji: string;
|
||||
|
||||
private static getEmojiForGuessType(guessType: GuessType): string {
|
||||
switch (guessType) {
|
||||
case GuessType.correct:
|
||||
return '🎉';
|
||||
case GuessType.incorrect:
|
||||
return '💔';
|
||||
case GuessType.missing:
|
||||
return '🥷';
|
||||
}
|
||||
}
|
||||
|
||||
constructor(dayString: string, guessType: GuessType) {
|
||||
this.day = parse(dayString, 'yyyy-MM-dd', new Date());
|
||||
this.emoji = SunriseSunsetDayGuess.getEmojiForGuessType(guessType);
|
||||
}
|
||||
}
|
||||
53
src/routes/sunrise-sunset/SunriseSunsetGuessSet.ts
Normal file
53
src/routes/sunrise-sunset/SunriseSunsetGuessSet.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { isSameDay, isBefore, addDays, format as formatDate } from 'date-fns';
|
||||
import { SunriseSunsetDayGuess } from './SunriseSunsetDayGuess.js';
|
||||
import { GuessType } from './GuessType.js';
|
||||
|
||||
export class SunriseSunsetDayGuessSet {
|
||||
private readonly sortedGuesses: SunriseSunsetDayGuess[];
|
||||
private readonly sortedGuessesWithMissingDays: SunriseSunsetDayGuess[];
|
||||
|
||||
constructor(guesses: SunriseSunsetDayGuess[]) {
|
||||
this.sortedGuesses = guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
|
||||
const missingGuesses = this.getGuessesForMissingDays();
|
||||
this.sortedGuessesWithMissingDays = SunriseSunsetDayGuessSet.sortGuesses([...guesses, ...missingGuesses]);
|
||||
}
|
||||
|
||||
private static sortGuesses(guesses: SunriseSunsetDayGuess[]): SunriseSunsetDayGuess[] {
|
||||
return guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
|
||||
}
|
||||
|
||||
private isGuessPresentForDay(date: Date): boolean {
|
||||
return this.sortedGuesses.some((guess) => isSameDay(guess.day, date));
|
||||
}
|
||||
|
||||
private getGuessesForMissingDays(): SunriseSunsetDayGuess[] {
|
||||
const earliestDate: Date | undefined = this.sortedGuesses[this.sortedGuesses.length - 1]?.day;
|
||||
|
||||
if (!earliestDate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const latestDate = this.sortedGuesses[0].day;
|
||||
let guessesForMissingDays: SunriseSunsetDayGuess[] = [];
|
||||
|
||||
let currentDay = earliestDate;
|
||||
|
||||
while (isBefore(currentDay, latestDate)) {
|
||||
const isPresent = this.isGuessPresentForDay(currentDay);
|
||||
|
||||
if (!isPresent) {
|
||||
guessesForMissingDays = [
|
||||
...guessesForMissingDays,
|
||||
new SunriseSunsetDayGuess(formatDate(currentDay, 'yyyy-MM-dd'), GuessType.missing),
|
||||
];
|
||||
}
|
||||
currentDay = addDays(currentDay, 1);
|
||||
}
|
||||
|
||||
return guessesForMissingDays;
|
||||
}
|
||||
|
||||
getLastDays(maxDays = 10): SunriseSunsetDayGuess[] {
|
||||
return this.sortedGuessesWithMissingDays.slice(0, maxDays);
|
||||
}
|
||||
}
|
||||
|
|
@ -88,10 +88,10 @@ describe('SunriseSunsetStreakCalculator', () => {
|
|||
});
|
||||
|
||||
describe(`Streak Visualisation`, () => {
|
||||
let calculator: SunriseSunsetStreakCalculator;
|
||||
let streakCalculator: SunriseSunsetStreakCalculator;
|
||||
|
||||
beforeAll(() => {
|
||||
calculator = new SunriseSunsetStreakCalculator('2023-01-29');
|
||||
streakCalculator = new SunriseSunsetStreakCalculator('2023-01-29');
|
||||
});
|
||||
|
||||
it(`Sound visualise an empty streak`, () => {
|
||||
|
|
@ -100,7 +100,7 @@ describe('SunriseSunsetStreakCalculator', () => {
|
|||
const incorrectDays = [];
|
||||
|
||||
// WHEN
|
||||
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrectDays);
|
||||
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrectDays);
|
||||
|
||||
// THEN
|
||||
expect(emojiVisualisation).toBe('');
|
||||
|
|
@ -112,19 +112,32 @@ describe('SunriseSunsetStreakCalculator', () => {
|
|||
const incorrectDays = ['2023-01-19'];
|
||||
|
||||
// WHEN
|
||||
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrectDays);
|
||||
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrectDays);
|
||||
|
||||
// THEN
|
||||
expect(emojiVisualisation).toBe('🎉💔');
|
||||
});
|
||||
|
||||
it(`should recognise a streak which has just ended`, () => {
|
||||
// GIVEN
|
||||
const today = new Date('2023-02-01');
|
||||
const correctDays = ['2023-01-31', '2023-01-30'];
|
||||
const calculator = new SunriseSunsetStreakCalculator('2023-02-01');
|
||||
|
||||
// WHEN
|
||||
const statement = calculator.getShareableStatement(correctDays, [], today);
|
||||
|
||||
// THEN
|
||||
expect(statement).toContain(`Current Streak: 0`);
|
||||
});
|
||||
|
||||
it(`should handle a missing day`, () => {
|
||||
// GIVEN
|
||||
const correctDays = ['2023-01-20', '2023-01-21'];
|
||||
const incorrect = ['2023-01-18', '2023-01-22'];
|
||||
|
||||
// WHEN
|
||||
const emojiVisualisation = calculator.getEmojiForHistory(correctDays, incorrect);
|
||||
const emojiVisualisation = streakCalculator.getEmojiForHistory(correctDays, incorrect);
|
||||
|
||||
// THEN
|
||||
expect(emojiVisualisation).toBe('💔🎉🎉🥷💔');
|
||||
|
|
@ -137,7 +150,7 @@ describe('SunriseSunsetStreakCalculator', () => {
|
|||
const today = new Date('2023-01-20T21:52Z');
|
||||
|
||||
// WHEN
|
||||
const shareableStatement = calculator.getShareableStatement(correctDays, incorrectDays, today);
|
||||
const shareableStatement = streakCalculator.getShareableStatement(correctDays, incorrectDays, today);
|
||||
|
||||
// THEN
|
||||
const expected = `Sunrise, Sunset?\n2023-01-20\n\nCurrent Streak: 0\nLongest Streak: 0`;
|
||||
|
|
@ -150,6 +163,8 @@ describe('SunriseSunsetStreakCalculator', () => {
|
|||
const incorrectDays = ['2023-01-28'];
|
||||
const today = new Date('2023-01-31T21:52Z');
|
||||
|
||||
const calculator = new SunriseSunsetStreakCalculator('2023-01-31');
|
||||
|
||||
// WHEN
|
||||
const shareableStatement = calculator.getShareableStatement(correctDays, incorrectDays, today);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,88 +1,14 @@
|
|||
import { parse, format as formatDate, isAfter, isBefore, isSameDay, addDays, differenceInCalendarDays } from 'date-fns';
|
||||
|
||||
enum GuessType {
|
||||
correct = 'correct',
|
||||
incorrect = 'incorrect',
|
||||
missing = 'missing',
|
||||
}
|
||||
|
||||
class SunriseSunsetDayGuess {
|
||||
readonly day: Date;
|
||||
readonly emoji: string;
|
||||
|
||||
private static getEmojiForGuessType(guessType: GuessType): string {
|
||||
switch (guessType) {
|
||||
case GuessType.correct:
|
||||
return '🎉';
|
||||
case GuessType.incorrect:
|
||||
return '💔';
|
||||
case GuessType.missing:
|
||||
return '🥷';
|
||||
}
|
||||
}
|
||||
|
||||
constructor(dayString: string, private readonly guessType: GuessType) {
|
||||
this.day = parse(dayString, 'yyyy-MM-dd', new Date());
|
||||
this.emoji = SunriseSunsetDayGuess.getEmojiForGuessType(guessType);
|
||||
}
|
||||
}
|
||||
|
||||
class SunriseSunsetDayGuessSet {
|
||||
private readonly sortedGuesses: SunriseSunsetDayGuess[];
|
||||
private readonly sortedGuessesWithMissingDays: SunriseSunsetDayGuess[];
|
||||
|
||||
constructor(guesses: SunriseSunsetDayGuess[]) {
|
||||
this.sortedGuesses = guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
|
||||
const missingGuesses = this.getGuessesForMissingDays();
|
||||
this.sortedGuessesWithMissingDays = SunriseSunsetDayGuessSet.sortGuesses([...guesses, ...missingGuesses]);
|
||||
}
|
||||
|
||||
private static sortGuesses(guesses: SunriseSunsetDayGuess[]): SunriseSunsetDayGuess[] {
|
||||
return guesses.sort((a, b) => b.day.getTime() - a.day.getTime());
|
||||
}
|
||||
|
||||
private isGuessPresentForDay(date: Date): boolean {
|
||||
return this.sortedGuesses.some((guess) => isSameDay(guess.day, date));
|
||||
}
|
||||
|
||||
private getGuessesForMissingDays(): SunriseSunsetDayGuess[] {
|
||||
const earliestDate: Date | undefined = this.sortedGuesses[this.sortedGuesses.length - 1]?.day;
|
||||
|
||||
if (!earliestDate) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const latestDate = this.sortedGuesses[0].day;
|
||||
let guessesForMissingDays: SunriseSunsetDayGuess[] = [];
|
||||
|
||||
let currentDay = earliestDate;
|
||||
|
||||
while (isBefore(currentDay, latestDate)) {
|
||||
const isPresent = this.isGuessPresentForDay(currentDay);
|
||||
|
||||
if (!isPresent) {
|
||||
guessesForMissingDays = [
|
||||
...guessesForMissingDays,
|
||||
new SunriseSunsetDayGuess(formatDate(currentDay, 'yyyy-MM-dd'), GuessType.missing),
|
||||
];
|
||||
}
|
||||
currentDay = addDays(currentDay, 1);
|
||||
}
|
||||
|
||||
return guessesForMissingDays;
|
||||
}
|
||||
|
||||
getLastDays(maxDays = 10): SunriseSunsetDayGuess[] {
|
||||
return this.sortedGuessesWithMissingDays.slice(0, maxDays);
|
||||
}
|
||||
}
|
||||
import { parse, format as formatDate, differenceInCalendarDays } from 'date-fns';
|
||||
import { SunriseSunsetDayGuess } from './SunriseSunsetDayGuess.js';
|
||||
import { SunriseSunsetDayGuessSet } from './SunriseSunsetGuessSet.js';
|
||||
import { GuessType } from './GuessType.js';
|
||||
|
||||
class SunriseSunsetStreak {
|
||||
readonly longestStreak: number;
|
||||
private readonly allStreaks: number[];
|
||||
readonly mostRecentStreak: number;
|
||||
|
||||
constructor(correctDays: Date[]) {
|
||||
constructor(correctDays: Date[], today = new Date()) {
|
||||
if (correctDays.length === 0) {
|
||||
this.longestStreak = 0;
|
||||
this.mostRecentStreak = 0;
|
||||
|
|
@ -155,20 +81,18 @@ export class SunriseSunsetStreakCalculator {
|
|||
const emoji = this.getEmojiForHistory(correctDays, incorrectDays);
|
||||
const todayFormatted = formatDate(today, 'yyyy-MM-dd');
|
||||
const streak = new SunriseSunsetStreak(this.daysAsDates(correctDays));
|
||||
const currentStreak = this.getStreakLength(correctDays);
|
||||
|
||||
return [
|
||||
`Sunrise, Sunset?`,
|
||||
todayFormatted,
|
||||
emoji,
|
||||
`Current Streak: ${streak.mostRecentStreak}`,
|
||||
`Current Streak: ${currentStreak}`,
|
||||
`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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue