This commit is contained in:
2026-10-21 14:02:50 -04:00
parent 69d49073f0
commit 613647cb50
6 changed files with 580 additions and 22 deletions
+16 -1
View File
@@ -1,6 +1,6 @@
# The Button
A web game of patience and nerve. A 24-hour countdown resets every time any player presses the button. If the countdown ever reaches zero, the game is over forever, and the final global stats are published.
A web game of patience and nerve. A 24-hour countdown resets every time any player presses the button. If it reaches zero, that season's game ends and final stats are published. Each equinox and solstice brings a fresh season.
Inspired by Reddit's [The Button](https://en.wikipedia.org/wiki/The_Button_(Reddit)) experiment.
@@ -31,6 +31,20 @@ The Chat section is a simple public chatroom. Logged-in players can post plain-t
Every successful button press also posts an automatic chat announcement with the player's name, exact time remaining, and points earned. These announcements are saved with the press and do not count toward the player's chat cooldown.
## Seasons and champions
Seasons begin at **00:00 UTC on the UTC date of each astronomical equinox or solstice**, not at the event's exact hour and not on fixed March/June/September/December dates. They are displayed as **Season I, Season II, Season III**, etc., including in the subtitle under **THE BUTTON**. Numbering begins at the earliest saved season (including existing archives on upgrade); its origin is persisted so numbering remains stable across restarts. Each astronomical boundary advances the number, including seasons missed during downtime. Date-based internal IDs and start/end dates remain unchanged. The built-in Meeus equinox/solstice calculation (with periodic and Delta-T corrections) supports 2000–2100 without network access or dependencies. The 2026 boundaries are March 20, June 21, September 23, and December 21; the December 2027 boundary is December 22. Calendar tests check these against USNO data. The far-future calendar is an astronomical estimate; extend/revalidate it before 2101.
At each boundary the server archives the old season and starts a fresh countdown anchored to that midnight. Seasonal points, press history, personal bests, achievements, and milestone progress reset. Accounts, password hashes, login sessions, mailing-list subscriptions, and chat are preserved. Previous press histories remain stored in the season archive; public archives expose only score summaries, not private per-player histories. Old milestone announcements remain visible in chat.
A season finishes when the countdown expires **or** the next seasonal boundary is reached. Pressing cannot extend the timer beyond that boundary. If the timer expires early, pressing and new registrations remain unavailable until the next season; existing players can still log in and chat. The watcher continues running and also catches up boundaries after downtime. A missed season starts at its scheduled midnight, not when the server restarts: without presses its countdown may already be over. Missed seasons have no invented winners.
The **Seasons** section shows the current season and next start, a champions table, and expandable final standings. Each result shows the winner(s), points per winner, and how long play lasted from actual game start to timeout/boundary (not the calendar season's full length). Equal top scores share the win; alphabetical ordering only orders the display. A player must have pressed at least once to win; a zero-point press qualifies, but an idle account does not. A season without presses has no winner.
**Upgrade behavior:** the existing JSON/SQLite game is adopted unchanged into the calendar season containing the upgrade time, including all old scores and presses. Its actual original start is retained for duration reporting, even if it predates that calendar season. A brand-new installation similarly starts a partial first season immediately rather than fabricating past play. All subsequent seasons begin at the scheduled UTC midnight. Archives and current-season metadata are added automatically in SQLite; no manual conversion is needed. Keep a database backup before deployment. The first watcher/state request archives a game that was already ended; historical seasons are not reconstructed from an undivided pre-season game.
Public `GET /api/seasons` returns `{current, seasons}` with current boundary metadata/standings and immutable finished results. `GET /api/state` includes `season` metadata. The main leaderboard, achievements, rivals, and final statistics refer to the current season. Client views refresh across seasonal boundaries without a reload; local alert preferences remain enabled but alerts are re-baselined for the new season. One-life mode is still separate and unimplemented.
## Engagement roadmap
The goal is to make waiting social and consequential without rewarding button spam or changing the current game's rules. Planned delivery order:
@@ -169,6 +183,7 @@ Tip: `BUTTON_DURATION_SECONDS=60` makes for quick testing.
| POST | `/api/press` | ✓ | Press the button |
| GET | `/api/me` | ✓ | Your score, zone counts, and press history |
| GET | `/api/leaderboard` | | Public player names, total points, and press counts |
| GET | `/api/seasons` | | Current season and archived champions, scores, durations, and standings |
| GET | `/api/chat` | | Latest 100 public chat messages |
| POST | `/api/chat` | ✓ | `{message}` → saved message; 200-character limit |
| GET | `/api/presence` | | Opted-in online count and chat display names |