• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
alexwein

alexwein

thinkalittlelonger

a little game where you think of big words
Public
Like
thinkalittlelonger
Home
Code
5
frontend
6
scripts
2
towniePrompts
3
README.md
H
index.ts
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
towniePrompts
/
done-2-leaderboard.md
Code
/
towniePrompts
/
done-2-leaderboard.md
Search
…
Viewing readonly version of main branch: v169
View latest version
done-2-leaderboard.md

club leaderboards and postGame

Don't make code changes outside of this file. Help me edit this prompt so that Townie with Claude Opus 4.5 gives best results.

I want to change the postgame workflow to support club leaderboards.

After the user gets their scored results,

  • header

  • resultsTable

  • clubNameInput

  • userNameInput

  • submitToLeaderboardButton

  • club names should be a string of only 26 letters in the alphabet. no digits or other characters or spaces.(ie, can be input using the keyboard component)

  • club names should be no more than 25 characters long

likewise for players:

  • player names should be a string of 26 letters in the alphabet. no digits or other characters or spaces.(ie, can be input using the keyboard component)
  • player names should be no more than 25 characters long

collecting game data

each submission gets a unique id, two players can submit with the same userName and clubName

Here's what I was thinking for data needed to be collected to support the leaderboard view. Although submitted at is not currently needed, it should be included. if this is insufficient, or could be handled better, make those changes.

  • submission_id

  • game_id

  • club_name

  • user_name

  • submitted_at: timestamp when the user submitted to finish the final round, ending the game.

  • total_points: sum of length of every valid submission, as displayed on the postGame resultsTable.

  • submissions: array of strings containing the words the user played, in order

  • submission_validity : array of booleans of same length of submission, true for valid words and false for invalid,

  • create a file scripts/initDbResults.ts to initialize a game_results table where these are stored.

leadboardBoard view

  • add leaderboard as a gameState.
  • the user transitions to the leaderboard state after they press the submitToLeaderboardButton
  • the leaderboard view displays a scrollable table with the results of every submission of to that club.
  • the results should be sorted by default from most total points to least
  • reuse the roundIndicator, but with an extra unconnected dot for total points, to allow the user to change the sort order to sort by round.
  • if two users have the same total points, use the username as a tiebreaker for sort order.
  • if two users have the same number of points for a round, use the submitted word as the first tiebreaker, followed by user_name.

routing

  • The club name should be autopopulated (but editable) if the user navigated to the app via a path with "/club/{clubname}/" (or "/club/{clubname}" without the terminal "/")
  • if a user inputs the url with a path "/club/{clubname}/leaderboard/" into their browser, that should take them to club leaderboard
  • if there are no player submissions with that club name, indicate "nobody in the club has played yet!"
  • when the user navigates from the postGame to leaderboard gameState, update the path in the browser as well.

REFINED PROMPTS FOR TOWNIE

Use these 5 prompts in sequence to deliver a leaderboard feature. Each builds on the previous one. Here is prompt 1


PROMPT 1: Database Schema

Goal: Create the database table for storing game results.

File to create: scripts/initDbResults.ts

Context:

  • Use import { sqlite } from "https://esm.town/v/std/sqlite"
  • This is a Val Town project using SQLite

Schema requirements:

CREATE TABLE IF NOT EXISTS game_results ( submission_id TEXT PRIMARY KEY, game_id TEXT NOT NULL, club_name TEXT NOT NULL, user_name TEXT NOT NULL, submitted_at TEXT NOT NULL, total_points INTEGER NOT NULL, submissions TEXT NOT NULL, submission_validity TEXT NOT NULL )

Also create an index on club_name for efficient leaderboard queries.

Notes:

  • submissions and submission_validity are JSON-stringified arrays
  • club_name and user_name should be stored uppercase for case-insensitive matching
  • submitted_at is an ISO 8601 timestamp
  • submission_id is a UUID generated at insert time
  • game_id is a UUID generated per game session (passed from frontend)

PROMPT 2: Backend API Endpoints

Goal: Add API endpoints for submitting results and fetching leaderboards.

File to modify: index.ts

Context:

  • Backend uses Hono framework
  • SQLite is already imported: import { sqlite } from "https://esm.town/v/std/sqlite"
  • The game_results table exists (from Prompt 1)

Endpoint 1: POST /api/submit-result

Request body:

{ game_id: string, // UUID from frontend club_name: string, // A-Z only, max 25 chars user_name: string, // A-Z only, max 25 chars total_points: number, submissions: string[], submission_validity: boolean[] }

Behavior:

  • Generate submission_id using crypto.randomUUID()
  • Generate submitted_at as new Date().toISOString()
  • Normalize club_name and user_name to uppercase
  • Store arrays as JSON strings
  • Return { submission_id } on success
  • Return 400 for missing required fields, 500 for DB errors

Endpoint 2: GET /api/leaderboard/:clubName

Behavior:

  • Normalize clubName param to uppercase
  • Query all results for that club, ordered by total_points DESC, user_name ASC
  • Parse JSON fields back to arrays
  • Return { club_name, entries: [...] }

Add routes for serving frontend at club URLs:

app.get("/club/:clubName", () => serveFile("/frontend/index.html")); app.get("/club/:clubName/", () => serveFile("/frontend/index.html")); app.get("/club/:clubName/leaderboard", () => serveFile("/frontend/index.html")); app.get("/club/:clubName/leaderboard/", () => serveFile("/frontend/index.html"));

PROMPT 3: PostGame UI - Club Submission Form

Goal: Add club name and player name inputs to the postGame view.

File to modify: frontend/components/App.tsx

Context:

  • Current GameState type is "preGame" | "inGame" | "postGame"
  • PostGame view currently shows: Header, "Good game." heading, ResultsTable, "Play Again" button
  • The results state contains: { round, word, isValid }[]

Changes needed:

  1. Add state variables:
const [clubName, setClubName] = useState(""); const [playerName, setPlayerName] = useState("");
  1. Add URL parsing on mount (useEffect with empty deps):

    • Check window.location.pathname
    • If matches /club/{name} or /club/{name}/, extract name and set clubName (uppercase)
    • If matches /club/{name}/leaderboard, also set gameState to "leaderboard"
  2. Update GameState type to include "leaderboard"

  3. In the postGame view, after ResultsTable, add:

    • "Submit to Club Leaderboard" heading
    • Club name text input (validate: A-Z only, max 25 chars, auto-uppercase)
    • Player name text input (same validation)
    • "Submit to Leaderboard" button (disabled unless both inputs valid)
  4. Add handleSubmitToLeaderboard function:

    • Calculate total_points from results (sum of word lengths where isValid is true)
    • Generate game_id with crypto.randomUUID()
    • POST to /api/submit-result
    • On success: set gameState to "leaderboard" and update URL with window.history.pushState

Input validation:

  • Only allow A-Z characters (use regex: /[^A-Z]/g to strip invalid chars)
  • Convert to uppercase automatically
  • Max 25 characters

Styling: Add appropriate CSS classes. Match the existing app style.


PROMPT 4: Leaderboard View

Goal: Create the leaderboard component and integrate it into App.

Files to create/modify:

  • Create: frontend/components/Leaderboard.tsx
  • Modify: frontend/components/App.tsx
  • Modify: frontend/components/RoundIndicator.tsx
  • Modify: frontend/style.css

Context:

  • Current RoundIndicator shows connected dots for rounds (see frontend/components/RoundIndicator.tsx)
  • There are 5 rounds in the game (defined in frontend/rounds.ts)

Leaderboard.tsx requirements:

Props: { clubName: string }

State:

  • entries: array of leaderboard entries from API
  • loading: boolean
  • sortByRound: number | null (null means sort by total, 0-4 means sort by that round)

On mount, fetch /api/leaderboard/{clubName}

Display:

  • Club name as heading
  • Modified RoundIndicator for sort selection (see below)
  • Scrollable table with columns: Rank, Player, Total Points, Words
  • Words should show each submission, styled as valid (normal) or invalid (strikethrough or muted)
  • If no entries: "Nobody in the club has played yet!"

Sorting logic:

  • Default (null): sort by total_points DESC, tiebreaker: user_name ASC
  • By round: sort by that round's points (word length if valid, 0 if invalid), tiebreaker: word alphabetically, then user_name

RoundIndicator.tsx modifications:

Add optional props:

showTotal?: boolean; // If true, add one extra dot (disconnected) for "Total" onRoundClick?: (roundIndex: number) => void; // Make dots clickable selectedRound?: number; // Which dot is selected (for leaderboard sorting)

When showTotal is true:

  • Render totalRounds + 1 dots
  • The last dot should be visually separated (small gap, no connecting line to it)
  • This extra dot represents "Total Points"

When onRoundClick is provided:

  • Make dots clickable (add cursor:pointer, hover state)
  • Call onRoundClick(index) when clicked
  • Use selectedRound to highlight which dot is currently selected

App.tsx integration:

Add leaderboard gameState handling:

if (gameState === "leaderboard") { return ( <div className="app-wrapper"> <div className="game-container"> <Header /> <div className="content-area"> <Leaderboard clubName={clubName} /> <button onClick={handleStartGame} className="start-button">Play Again</button> </div> </div> </div> ); }

CSS additions needed:

  • .leaderboard container
  • .leaderboard-table with scrollable container
  • Valid/invalid word styling in table
  • Clickable dot hover states for RoundIndicator

PROMPT 5: Testing & Polish

Goal: Test the full flow and fix any issues.

Test scenarios:

  1. Play a full game → postGame shows results and submission form
  2. Enter club name and player name → Submit button enables
  3. Submit → navigates to leaderboard with your entry
  4. Direct URL /club/TESTCLUB → Club name pre-filled, can start game
  5. Direct URL /club/TESTCLUB/leaderboard → Shows leaderboard directly
  6. Empty leaderboard → Shows "Nobody in the club has played yet!"
  7. Multiple submissions → Sorted correctly by total points
  8. Click different round dots → Re-sorts by that round's scores

Potential issues to check:

  • URL case sensitivity (should be case-insensitive)
  • Trailing slashes in URLs
  • Form validation edge cases (empty, special characters)
  • Loading states during API calls
  • Error handling for failed API requests
  • Mobile responsiveness of leaderboard table

Polish items:

  • Smooth transitions between game states
  • Clear visual feedback when submitting
  • Consistent styling with rest of app
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.