Chess on the ATmosphere checkmate.blue
chess
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update terminology to ATmosphere, fix mobile keyboard inputs, expand roadmap

- Replace "Bluesky handle" with "ATmosphere username" in UI labels and placeholders
- Add autocapitalize=none, autocorrect=off, spellcheck=false to handle inputs
- Fix "Atmosphere" capitalization to "ATmosphere" across nav, homepage, game page
- Restructure roadmap into high/medium/lower priority tiers with new features:
time controls, integrity checks, push notifications, chess variants, and more

+151 -13
+133 -3
docs/ROADMAP.md
··· 1 1 # checkmate.blue Roadmap 2 2 3 - ## Up Next 3 + ## High Priority 4 + 5 + ### Time Controls 6 + 7 + Chess clocks with configurable time + increment. The lexicon already has optional time control fields. 8 + 9 + - Bullet (1+0, 2+1), Blitz (3+0, 3+2, 5+0, 5+3), Rapid (10+0, 15+10), Classical (30+0) 10 + - Clock display on the game page with countdown timers for each player 11 + - Timeout detection: if a player's clock runs out, flag the game as lost on time 12 + - **Challenge:** No server to enforce clocks. Both clients track time independently using `lastMoveAt` timestamps from PDS records. Drift is possible but acceptable for casual play -- the bot (once deployed) could serve as an authoritative time arbiter for disputed games. 13 + 14 + ### Client-Side Integrity Checks 15 + 16 + Detect tampered or invalid game records from the opponent and halt the game gracefully. 17 + 18 + - When applying an opponent's PGN, verify the new move history starts with the exact sequence we already have (no rewritten history) 19 + - Reject PGNs with illegal moves (chess.js `loadPgn` failure) 20 + - On mismatch, show a "Game state mismatch detected" banner and disable further moves 21 + - No PDS writes about corruption -- purely client-side detection and UI 4 22 5 23 ### Homepage & Game Discovery (Phase 4) 6 24 ··· 25 43 - Filter out expired challenges client-side (older than 24h) 26 44 - **Same blocker as Phase 4** -- needs Constellation global query support 27 45 46 + --- 47 + 48 + ## Medium Priority 49 + 50 + ### Push Notifications 51 + 52 + Browser Notification API for turn alerts, especially useful when both players aren't staring at the screen. 53 + 54 + - Request notification permission on game join 55 + - Notify when it's your turn (opponent made a move) 56 + - Notify on rematch offers, challenge invitations 57 + - Respect user preference (opt-in, stored in `localStorage`) 58 + 59 + ### Keyboard Move Input 60 + 61 + Type moves in algebraic notation (e.g., "e4", "Nf3") instead of dragging pieces. 62 + 63 + - Input field on the game page, submit with Enter 64 + - Validate against legal moves via chess.js 65 + - Accessibility win -- screen reader friendly 66 + - Could support UCI notation too (e.g., "e2e4") 67 + 68 + ### Rematch Timeout 69 + 70 + Auto-cleanup when a rematch goes unanswered. 71 + 72 + - After creating a rematch game, start a 5-minute countdown 73 + - If no opponent joins within 5 minutes, delete the waiting record via `deleteRecord` and redirect to the homepage 74 + - Opponent's "Dismiss" hides the notification on their end (no protocol change needed) 75 + 76 + ### Orphaned Game Cleanup 77 + 78 + Let users clean up stale games that were never accepted or finished. 79 + 80 + - On the homepage or a "My Games" section, show the user's own records stuck in `waiting` status 81 + - Allow one-click deletion of orphaned records via `deleteRecord` 82 + - Consider auto-prompting on game pages: if a `waiting` game is older than 24 hours, offer to delete it 83 + 84 + ### Chess Variants 85 + 86 + Support for non-standard starting positions. Add a `variant` field to the `blue.checkmate.game` lexicon and store the starting FEN in the record. 87 + 88 + **Chess960 / Fischer Random:** Randomized back-rank starting positions (960 possible arrangements). Eliminates opening theory. chess.js supports it natively. Store position number (1-960) in the record. Castling rules differ slightly (king/rook end on standard squares regardless of start position). 89 + 90 + **Daily Really Bad Chess:** Each day, a deterministic seed generates a random board with non-standard piece composition (asymmetric -- each side gets different random pieces). All players get the same board, play against the bot, compare results. Wordle-style daily challenge. 91 + 92 + - chess.js handles arbitrary FENs and generates legal moves per standard piece movement rules 93 + - Stockfish plays from any FEN position -- evaluation is tuned for standard chess but move generation is correct at any depth 94 + - Disable castling in the FEN for non-standard boards (king/rooks won't be in expected squares) 95 + - Seed generation: hash the date string to get a deterministic board, store the FEN in the game record 96 + - Leaderboard: fewest moves to checkmate, or fastest time (requires time tracking) 97 + 98 + --- 99 + 100 + ## Lower Priority 101 + 102 + ### Board and Piece Themes 103 + 104 + Chessground supports multiple board and piece styles. Let players customize their experience. 105 + 106 + - Board themes: brown, blue, green, wood, marble, etc. 107 + - Piece sets: cburnett (current default), merida, alpha, Leipzig, etc. 108 + - Store preference in `localStorage` 109 + - Theme picker in a settings menu or on the game page 110 + 111 + ### Player Profiles 112 + 113 + `/profile/{handle}` page showing a player's game history and record. 114 + 115 + - List completed games with results, opponents, and links to view 116 + - Win/loss/draw stats (queried from the player's own PDS records) 117 + - Without the bot's index, limited to games discoverable from the player's own repo 118 + 119 + ### Follow-Graph Challenges 120 + 121 + Use the Bluesky social graph to suggest opponents. 122 + 123 + - "Challenge someone you follow" -- fetch the user's follow list, display as challenge targets 124 + - Shows who's online or recently active on checkmate.blue (if discoverable) 125 + - More social than typing in a handle manually 126 + 127 + ### Puzzles 128 + 129 + Daily tactics puzzles from a public database (Lichess has a CC-licensed set with millions of puzzles). 130 + 131 + - No opponent needed -- good for engagement when nobody's online 132 + - Random puzzle or daily puzzle (same for everyone) 133 + - Track solve streak in `localStorage` 134 + 135 + ### In-Game Chat 136 + 137 + Real-time messaging between players during a game. 138 + 139 + - **ATmosphere-native approach:** `blue.checkmate.message` record collection. Each player writes messages to their own PDS, opponent reads via Jetstream. Same pattern as moves. Each player owns their messages. 140 + - Downside: adds a new lexicon, more PDS writes, Jetstream latency 141 + - Alternative: link to a Bluesky thread as a public "game lobby" 142 + 143 + ### Premoves / Move List Panel / Takebacks 144 + 145 + Quality-of-life features for the playing experience. 146 + 147 + - **Premoves:** queue a move while waiting for opponent (chessground supports natively) 148 + - **Move list panel:** algebraic notation alongside the board, clickable to step through positions 149 + - **Takebacks:** same pattern as draw offers -- request/accept/decline via a record field 150 + 151 + ### Opening Name Display / Board Coordinates 152 + 153 + Minor polish. 154 + 155 + - Show opening name (e.g., "Sicilian Defense, Najdorf") from the ECO database 156 + - Toggle rank/file labels on the board (chessground supports this) 157 + 28 158 ### Dynamic OG Tags (Phase 2b) 29 159 30 160 Per-route Open Graph meta tags for richer link previews when sharing game URLs. ··· 47 177 48 178 ## Separate Project: checkmate.blue Bot 49 179 50 - **Not part of this repo.** A Jetstream listener service that watches `blue.checkmate.game` records and posts game results from a `@checkmate.blue` bot account. Separate repo, separate deployment, runs as a persistent process. 180 + **Not part of this repo.** A Jetstream listener service that watches `blue.checkmate.game` records and posts game results from a `@checkmate.blue` bot account. Separate repo, separate deployment, runs as a persistent process. See `specs/bot.md` for the full specification. 51 181 52 - Potential responsibilities: game result posts, abandoned game detection, global game indexing, rating calculation. 182 + Responsibilities: game result posts, playable chess engine (3 difficulty levels), global game indexing with REST API, result verification, abandoned game detection. 53 183 54 184 --- 55 185
+7 -3
src/lib/components/LoginButton.svelte
··· 22 22 <input 23 23 type="text" 24 24 bind:value={handleInput} 25 - placeholder="your-handle.bsky.social" 26 - aria-label="Bluesky handle" 25 + placeholder="username.bsky.social" 26 + aria-label="ATmosphere username" 27 + autocapitalize="none" 28 + autocorrect="off" 29 + autocomplete="username" 30 + spellcheck="false" 27 31 class="rounded-lg border border-border bg-bg-secondary px-4 py-2 text-text-primary placeholder:text-text-secondary focus:border-accent-blue focus:ring-2 focus:ring-accent-blue/50" 28 32 /> 29 33 <button ··· 31 35 disabled={isLoading || !handleInput.trim()} 32 36 class="rounded-lg bg-accent-blue px-4 py-2 font-semibold text-white transition-colors hover:bg-accent-blue-hover disabled:opacity-50" 33 37 > 34 - {isLoading ? 'Signing in...' : 'Sign in with Bluesky'} 38 + {isLoading ? 'Signing in...' : 'Sign in'} 35 39 </button> 36 40 {#if error} 37 41 <p class="text-sm text-danger">{error}</p>
+1 -1
src/routes/+layout.svelte
··· 29 29 <a href="/" class="logo text-2xl"> 30 30 <span class="logo-hash">#</span>checkmate 31 31 </a> 32 - <p class="text-xs text-text-secondary">Chess on the Atmosphere</p> 32 + <p class="text-xs text-text-secondary">Chess on the ATmosphere</p> 33 33 </div> 34 34 <div class="flex items-center gap-4"> 35 35 {#if auth.isLoggedIn}
+2 -2
src/routes/+page.svelte
··· 115 115 </script> 116 116 117 117 <div class="mx-auto max-w-2xl px-4 py-8"> 118 - <h1 class="sr-only">checkmate.blue - Chess on the Atmosphere</h1> 118 + <h1 class="sr-only">checkmate.blue - Chess on the ATmosphere</h1> 119 119 <div class="mb-8 flex justify-center gap-3"> 120 120 {#if auth.isLoggedIn} 121 121 <a ··· 173 173 174 174 <section> 175 175 <div class="mb-3 flex items-center gap-2"> 176 - <h2 class="text-lg font-semibold">Live on the Atmosphere</h2> 176 + <h2 class="text-lg font-semibold">Live on the ATmosphere</h2> 177 177 {#if liveConnected} 178 178 <span class="h-2 w-2 rounded-full bg-success"></span> 179 179 {/if}
+1 -1
src/routes/game/[did]/[rkey]/+page.svelte
··· 773 773 {#if isSpectator && isWaiting && !auth.isLoggedIn} 774 774 <div class="w-full max-w-md rounded-lg border border-accent-blue bg-bg-secondary p-6 text-center"> 775 775 <h2 class="text-lg font-semibold">You've been challenged!</h2> 776 - <p class="mt-2 text-sm text-text-secondary">Sign in with your Bluesky account to join this game</p> 776 + <p class="mt-2 text-sm text-text-secondary">Sign in with your ATmosphere account to join this game</p> 777 777 <div class="mt-4"> 778 778 <LoginButton /> 779 779 </div>
+7 -3
src/routes/play/+page.svelte
··· 30 30 if (opponentHandle.trim()) { 31 31 const profile = await resolveIdentity(opponentHandle.trim()); 32 32 if (!profile) { 33 - error = 'Could not resolve that handle'; 33 + error = 'Could not find that username'; 34 34 isCreating = false; 35 35 return; 36 36 } ··· 85 85 <input 86 86 type="text" 87 87 bind:value={opponentHandle} 88 - placeholder="Opponent handle (optional)" 89 - aria-label="Opponent handle (optional)" 88 + placeholder="Opponent username (optional)" 89 + aria-label="Opponent username (optional)" 90 + autocapitalize="none" 91 + autocorrect="off" 92 + autocomplete="off" 93 + spellcheck="false" 90 94 class="rounded-lg border border-border bg-bg-secondary px-4 py-2 text-text-primary placeholder:text-text-secondary focus:border-accent-blue focus:ring-2 focus:ring-accent-blue/50" 91 95 /> 92 96