From ff228fb54785cc7955295e63edc64f46d2285ddc Mon Sep 17 00:00:00 2001 From: George Powell Date: Wed, 28 Jan 2026 23:35:33 -0500 Subject: [PATCH 1/6] Update package.json version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2bcf2a..b79e92a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bibdle", "private": true, - "version": "0.0.1", + "version": "2.5.0", "type": "module", "scripts": { "dev": "vite dev", From d797b980ead21ac41ab21c97799dfe76a5916084 Mon Sep 17 00:00:00 2001 From: George Powell Date: Mon, 2 Feb 2026 00:48:35 -0500 Subject: [PATCH 2/6] updated ranking formula --- .env.example | 21 +++++++++++++ analyze_top_users.sh | 31 +++++++++++++++++++ daily_completions_report.sh | 34 +++++++++++++++++++++ src/lib/server/db/schema.ts | 2 -- src/routes/+page.server.ts | 9 +++++- src/routes/api/submit-completion/+server.ts | 16 ++++------ 6 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 .env.example create mode 100755 analyze_top_users.sh create mode 100755 daily_completions_report.sh diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..093733d --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +DATABASE_URL=example.db + +# nodemailer +SMTP_USERNAME=email@example.com +SMTP_TOKEN=TOKEN +SMTP_SERVER=smtp.example.com +SMTP_PORT=port +# note from mail provider: Enable TLS or SSL on the external service if it is supported. + +# sign in with Discord + +# sign in with google + +# sign in with apple +AUTH_SECRET=your-random-secret-here +APPLE_ID=com.yourcompany.yourapp.client +APPLE_TEAM_ID=your-team-id +APPLE_KEY_ID=your-key-id +APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY----- +your-private-key-here +-----END PRIVATE KEY-----" diff --git a/analyze_top_users.sh b/analyze_top_users.sh new file mode 100755 index 0000000..1f33b81 --- /dev/null +++ b/analyze_top_users.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# analyze_top_users.sh +# Analyzes the daily_completions table to find the top 10 anonymous IDs by completion count + +# Set database path from argument or default to dev.db +DB_PATH="${1:-dev.db}" + +# Check if database file exists +if [ ! -f "$DB_PATH" ]; then + echo "Error: Database file not found: $DB_PATH" + echo "Usage: $0 [database_path]" + exit 1 +fi + +# Run the analysis query +sqlite3 "$DB_PATH" < c.guessCount < guessCount).length; const guessRank = betterGuesses + 1; + // Count ties: how many have the SAME guessCount (excluding self) + const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length; + // Average guesses const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0); const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10; + // Percentile: what percentage of people you beat (100 - your rank percentage) + const betterOrEqualCount = allCompletions.filter(c => c.guessCount <= guessCount).length; + const percentile = Math.round((betterOrEqualCount / totalSolves) * 100); + return { success: true, - stats: { solveRank, guessRank, totalSolves, averageGuesses } + stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount, percentile } }; } }; diff --git a/src/routes/api/submit-completion/+server.ts b/src/routes/api/submit-completion/+server.ts index 0b80d09..b4e6dd7 100644 --- a/src/routes/api/submit-completion/+server.ts +++ b/src/routes/api/submit-completion/+server.ts @@ -44,11 +44,9 @@ export const POST: RequestHandler = async ({ request }) => { // Solve rank: position in time-ordered list const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1; - // Guess rank: count how many DISTINCT guess counts are better (grouped ranking) - const uniqueBetterGuessCounts = new Set( - allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount) - ); - const guessRank = uniqueBetterGuessCounts.size + 1; + // Guess rank: count how many had FEWER guesses (ties get same rank) + const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length; + const guessRank = betterGuesses + 1; // Count ties: how many have the SAME guessCount (excluding self) const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length; @@ -110,11 +108,9 @@ export const GET: RequestHandler = async ({ url }) => { // Solve rank: position in time-ordered list const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1; - // Guess rank: count how many DISTINCT guess counts are better (grouped ranking) - const uniqueBetterGuessCounts = new Set( - allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount) - ); - const guessRank = uniqueBetterGuessCounts.size + 1; + // Guess rank: count how many had FEWER guesses (ties get same rank) + const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length; + const guessRank = betterGuesses + 1; // Count ties: how many have the SAME guessCount (excluding self) const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length; From f7ec0742e1ea61941976fd6a9dde46dcca50dd21 Mon Sep 17 00:00:00 2001 From: George Powell Date: Mon, 2 Feb 2026 01:27:12 -0500 Subject: [PATCH 3/6] fixed "first letter" clue edge cases --- clear-today-verse.sh | 20 ++++++++++++++++ src/lib/components/GuessesTable.svelte | 17 +++++++++---- src/routes/+page.svelte | 33 ++++++++++++++++++-------- 3 files changed, 56 insertions(+), 14 deletions(-) create mode 100755 clear-today-verse.sh diff --git a/clear-today-verse.sh b/clear-today-verse.sh new file mode 100755 index 0000000..7960f3e --- /dev/null +++ b/clear-today-verse.sh @@ -0,0 +1,20 @@ +#!/bin/zsh + +# Clear today's verse from daily_verses table +DB_PATH="dev.db" +TODAY=$(date +%Y-%m-%d) + +echo "Deleting verse for date: $TODAY" + +sqlite3 "$DB_PATH" "DELETE FROM daily_verses WHERE date = '$TODAY';" + +if [ $? -eq 0 ]; then + echo "✓ Successfully deleted verse for $TODAY" + + # Show remaining verses in table + COUNT=$(sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM daily_verses;") + echo "Remaining verses in database: $COUNT" +else + echo "✗ Failed to delete verse" + exit 1 +fi diff --git a/src/lib/components/GuessesTable.svelte b/src/lib/components/GuessesTable.svelte index e64446c..eacf874 100644 --- a/src/lib/components/GuessesTable.svelte +++ b/src/lib/components/GuessesTable.svelte @@ -28,6 +28,11 @@ return "bg-red-500 border-red-600"; } + function getFirstLetter(bookName: string): string { + const match = bookName.match(/[a-zA-Z]/); + return match ? match[0] : bookName[0]; + } + function getBoxContent( guess: Guess, column: "book" | "firstLetter" | "testament" | "section", @@ -44,16 +49,20 @@ (correctBook?.section === "Pauline Epistles" || correctBook?.section === "General Epistles") && correctBook.name[0] === "1"; - const guessStartsWithNumber = guess.book.name[0] === "1"; + const guessIsEpistlesWithNumber = + (guess.book.section === "Pauline Epistles" || + guess.book.section === "General Epistles") && + guess.book.name[0] === "1"; if ( correctIsEpistlesWithNumber && - guessStartsWithNumber && + guessIsEpistlesWithNumber && guess.firstLetterMatch ) { - return "Yes"; // Special wordplay case + const words = ["Exactly", "Right", "Yes", "Naturally"]; + return words[Math.floor(Math.random() * words.length)]; // Special wordplay case } - return guess.book.name[0]; // Normal case: just show the first letter + return getFirstLetter(guess.book.name); // Normal case: show first letter, ignoring numbers case "testament": return ( guess.book.testament.charAt(0).toUpperCase() + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 9fcaace..918d7d4 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -82,6 +82,11 @@ return !!(b1 && b2 && Math.abs(b1.order - b2.order) === 1); } + function getFirstLetter(bookName: string): string { + const match = bookName.match(/[a-zA-Z]/); + return match ? match[0] : bookName[0]; + } + function submitGuess(bookId: string) { if (guesses.some((g) => g.book.id === bookId)) return; @@ -98,15 +103,19 @@ // 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 === "Pauline Epistles" || + correctBook.section === "General Epistles") && correctBook.name[0] === "1"; - const guessStartsWithNumber = book.name[0] === "1"; + const guessIsEpistlesWithNumber = + (book.section === "Pauline Epistles" || + book.section === "General Epistles") && + book.name[0] === "1"; const firstLetterMatch = - correctIsEpistlesWithNumber && guessStartsWithNumber + correctIsEpistlesWithNumber && guessIsEpistlesWithNumber ? true - : book.name[0].toUpperCase() === - correctBook.name[0].toUpperCase(); + : getFirstLetter(book.name).toUpperCase() === + getFirstLetter(correctBook.name).toUpperCase(); console.log( `Guess: ${book.name} (order ${book.order}), Correct: ${correctBook.name} (order ${correctBook.order}), Adjacent: ${adjacent}`, @@ -206,15 +215,19 @@ // Apply same first letter logic as in submitGuess const correctIsEpistlesWithNumber = - correctBook.section === "Pauline Epistles" && + (correctBook.section === "Pauline Epistles" || + correctBook.section === "General Epistles") && correctBook.name[0] === "1"; - const guessStartsWithNumber = book.name[0] === "1"; + const guessIsEpistlesWithNumber = + (book.section === "Pauline Epistles" || + book.section === "General Epistles") && + book.name[0] === "1"; const firstLetterMatch = - correctIsEpistlesWithNumber && guessStartsWithNumber + correctIsEpistlesWithNumber && guessIsEpistlesWithNumber ? true - : book.name[0].toUpperCase() === - correctBook.name[0].toUpperCase(); + : getFirstLetter(book.name).toUpperCase() === + getFirstLetter(correctBook.name).toUpperCase(); return { book, From 5b9b2f76f488da3d1152d365b14660a8c13776df Mon Sep 17 00:00:00 2001 From: George Powell Date: Mon, 2 Feb 2026 01:32:17 -0500 Subject: [PATCH 4/6] added some more words to the "first letter" edge case --- src/lib/components/GuessesTable.svelte | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/lib/components/GuessesTable.svelte b/src/lib/components/GuessesTable.svelte index eacf874..c8a29b9 100644 --- a/src/lib/components/GuessesTable.svelte +++ b/src/lib/components/GuessesTable.svelte @@ -50,17 +50,24 @@ correctBook?.section === "General Epistles") && correctBook.name[0] === "1"; const guessIsEpistlesWithNumber = - (guess.book.section === "Pauline Epistles" || - guess.book.section === "General Epistles") && - guess.book.name[0] === "1"; + (guess.book.section === "Pauline Epistles" || + guess.book.section === "General Epistles") && + guess.book.name[0] === "1"; if ( correctIsEpistlesWithNumber && guessIsEpistlesWithNumber && guess.firstLetterMatch ) { - const words = ["Exactly", "Right", "Yes", "Naturally"]; - return words[Math.floor(Math.random() * words.length)]; // Special wordplay case + const words = [ + "Exactly", + "Right", + "Yes", + "Naturally", + "Of course", + "Sure", + ]; + return words[Math.floor(Math.random() * words.length)]; // Special wordplay case } return getFirstLetter(guess.book.name); // Normal case: show first letter, ignoring numbers case "testament": From 244113671ecb6adf7c904d626c4528f62ae97397 Mon Sep 17 00:00:00 2001 From: George Powell Date: Mon, 2 Feb 2026 02:07:12 -0500 Subject: [PATCH 5/6] created rss feed --- .env.example | 1 + .gitignore | 3 +- src/lib/components/Imposter.svelte | 406 ++++++++++++++--------------- src/routes/+layout.svelte | 1 + src/routes/feed.xml/+server.ts | 132 ++++++++++ 5 files changed, 339 insertions(+), 204 deletions(-) create mode 100644 src/routes/feed.xml/+server.ts diff --git a/.env.example b/.env.example index 093733d..a5eab9e 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ DATABASE_URL=example.db +PUBLIC_SITE_URL=https://bibdle.com # nodemailer SMTP_USERNAME=email@example.com diff --git a/.gitignore b/.gitignore index c6b584a..6415f92 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ vite.config.ts.timestamp-* llms-* embeddings* -*.xml \ No newline at end of file +*bible.xml +engwebu_usfx.xml diff --git a/src/lib/components/Imposter.svelte b/src/lib/components/Imposter.svelte index 6159e79..51f78e3 100644 --- a/src/lib/components/Imposter.svelte +++ b/src/lib/components/Imposter.svelte @@ -1,241 +1,241 @@
- {#if loading} -

Loading verses...

- {:else if error} -
-

Error: {error}

- -
- {:else if data} - -
- {#each data.verses as verse, i} -
- - {#if gameOver} -
{data.refs[i]}
- {/if} -
- {/each} -
- {#if gameOver} -
- -
- {/if} - {/if} +
+ {#each data.verses as verse, i} +
+ + {#if gameOver} +
{data.refs[i]}
+ {/if} +
+ {/each} +
+ {#if gameOver} +
+ +
+ {/if} + {/if}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 5d3de82..c720d34 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,6 +7,7 @@ + diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 918d7d4..919dcf4 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -437,7 +437,6 @@ - A daily bible game{isDev ? " (dev)" : ""}