diff --git a/api/src/party/__tests__/question-utils.test.ts b/api/src/party/__tests__/question-utils.test.ts new file mode 100644 index 0000000..d20cd0f --- /dev/null +++ b/api/src/party/__tests__/question-utils.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { getQuestionRange, getReleaseYearRange } from "../question-utils"; + +describe("question range helpers", () => { + it("normalizes inverted generic ranges", () => { + const range = getQuestionRange(10, 5); + expect(range.min).toBeLessThanOrEqual(range.max); + expect(range.min).toBe(5); + expect(range.max).toBe(15); + }); + + it("caps release-year ranges to the current year", () => { + const range = getReleaseYearRange(2026, 2026); + expect(range.max).toBeLessThanOrEqual(2026); + expect(range.min).toBeLessThan(range.max); + expect(range.max - range.min).toBeGreaterThanOrEqual(4); + expect(range.min).toBeLessThanOrEqual(2026); + }); + + it("keeps release year inside the range", () => { + const range = getReleaseYearRange(2020, 2026); + expect(range.min).toBeLessThanOrEqual(2020); + expect(range.max).toBeGreaterThanOrEqual(2020); + expect(range.max).toBeLessThanOrEqual(2026); + }); +}); diff --git a/api/src/party/numeric-question-generator.ts b/api/src/party/numeric-question-generator.ts index aeaf547..4f8723a 100644 --- a/api/src/party/numeric-question-generator.ts +++ b/api/src/party/numeric-question-generator.ts @@ -7,7 +7,7 @@ import { import type { Question } from "../party-types"; import { buildQuestionWindow, - getQuestionRange, + getReleaseYearRange, isUsableText, type PartyAnalytics, type PartyQuestionMember, @@ -52,7 +52,7 @@ async function getAlbumReleaseYear({ type: "numeric", text: `What's the release year of ${subject}?`, correct, - range: getQuestionRange(correct, 10, 3), + range: getReleaseYearRange(correct), points: 10, song: song ?? undefined, }; diff --git a/api/src/party/question-utils.ts b/api/src/party/question-utils.ts index aa70709..14fe0d8 100644 --- a/api/src/party/question-utils.ts +++ b/api/src/party/question-utils.ts @@ -87,15 +87,24 @@ export function getQuestionDistanceScore( export function getQuestionRange( correctValue: number, tolerance: number, - randomness: number, ): { min: number; max: number } { const min = Math.floor(correctValue - tolerance); const max = Math.ceil(correctValue + tolerance); - const range = max - min; - return { - min: min + Math.floor(Math.random() * range * randomness), - max: max - Math.floor(Math.random() * range * randomness), - }; + return normalizeRange(min, max); +} + +export function getReleaseYearRange( + releaseYear: number, + currentYear = new Date().getFullYear(), + tolerance = 10, + minSpan = 4, +): { min: number; max: number } { + const cappedMax = Math.min(currentYear, Math.ceil(releaseYear + tolerance)); + const rawMin = Math.floor(releaseYear - tolerance); + const minimumSpan = Math.max(1, minSpan); + const expandedMin = Math.max(0, cappedMax - minimumSpan); + const min = Math.min(rawMin, expandedMin, releaseYear); + return normalizeRange(min, cappedMax); } export function getMostSharedGenreNames(analytics: PartyAnalytics): string[] { @@ -311,6 +320,14 @@ function uniqueStrings(values: string[]): string[] { return values.filter((value, index, list) => list.indexOf(value) === index); } +function normalizeRange( + min: number, + max: number, +): { min: number; max: number } { + if (min <= max) return { min, max }; + return { min: max, max: min }; +} + function fallbackPlayerNames(count: number): string[] { return Array.from( { length: count },