import { bibleBooks, type BibleBook } from '$lib/types/bible'; export interface Guess { book: BibleBook; testamentMatch: boolean; sectionMatch: boolean; adjacent: boolean; firstLetterMatch: boolean; } export function getBookById(id: string): BibleBook | undefined { return bibleBooks.find((b) => b.id === id); } export function isAdjacent(id1: string, id2: string): boolean { const b1 = getBookById(id1); const b2 = getBookById(id2); return !!(b1 && b2 && Math.abs(b1.order - b2.order) === 1); } export function getFirstLetter(bookName: string): string { const match = bookName.match(/[a-zA-Z]/); return match ? match[0] : bookName[0]; } export function evaluateGuess(guessBookId: string, correctBookId: string): Guess | null { const book = getBookById(guessBookId); const correctBook = getBookById(correctBookId); if (!book || !correctBook) return null; const testamentMatch = book.testament === correctBook.testament; const sectionMatch = book.section === correctBook.section; const adjacent = isAdjacent(guessBookId, correctBookId); // Special case: if correct book is in the Epistles + starts with "1", // any guess starting with "1" counts as first letter match const correctIsEpistlesWithNumber = (correctBook.section === "Pauline Epistles" || correctBook.section === "General Epistles") && correctBook.name[0] === "1"; const guessIsEpistlesWithNumber = (book.section === "Pauline Epistles" || book.section === "General Epistles") && book.name[0] === "1"; const firstLetterMatch = correctIsEpistlesWithNumber && guessIsEpistlesWithNumber ? true : getFirstLetter(book.name).toUpperCase() === getFirstLetter(correctBook.name).toUpperCase(); return { book, testamentMatch, sectionMatch, adjacent, firstLetterMatch, }; } export function getGrade(numGuesses: number): string { if (numGuesses === 1) return "S+"; if (numGuesses === 2) return "A+"; if (numGuesses === 3) return "A"; if (numGuesses >= 4 && numGuesses <= 6) return "B+"; if (numGuesses >= 7 && numGuesses <= 10) return "B"; if (numGuesses >= 11 && numGuesses <= 15) return "C+"; return "C"; } export function getNextGradeMessage(numGuesses: number): string { if (numGuesses === 1) return ""; if (numGuesses === 2) return "Next grade: 1 guess or less"; if (numGuesses === 3) return "Next grade: 2 guesses or less"; if (numGuesses >= 4 && numGuesses <= 6) return "Next grade: 3 guesses or less"; if (numGuesses >= 7 && numGuesses <= 10) return "Next grade: 6 guesses or less"; if (numGuesses >= 11 && numGuesses <= 15) return "Next grade: 10 guesses or less"; return "Next grade: 15 guesses or less"; } export function toOrdinal(n: number): string { if (n % 100 >= 11 && n % 100 <= 13) { return `${n}th`; } const mod = n % 10; const suffix = mod === 1 ? "st" : mod === 2 ? "nd" : mod === 3 ? "rd" : "th"; return `${n}${suffix}`; } export function generateUUID(): string { if (typeof window !== 'undefined' && typeof (window as any).crypto?.randomUUID === "function") { return (window as any).crypto.randomUUID(); } // Fallback return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { const r = ((window as any).crypto.getRandomValues(new Uint8Array(1))[0] % 16) | 0; const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); }