TL;DR
I built Santra, a Wordle-style daily puzzle game for Turkish football fans. Every day, everyone gets the same Super Lig player to guess, drawn from a static dataset of 198 players across 19 clubs. Picking "today's player" never touches a database: the puzzle is generated deterministically from the date itself, so the core game logic stays completely stateless.
A puzzle that doesn't need a database
Most daily puzzle games store "today's answer" in a database row that a cron job writes at midnight. Santra skips that step entirely. The player index for any given date comes from a seed built out of the date itself (year, month, day), run through a small deterministic hashing function, then taken modulo 198. Feed in the same date and you get the same number, every time, on every server, with no shared state. That means the puzzle can be computed on any request, cached aggressively, and it never drifts out of sync even if I redeploy at 11:59 PM.
Guessing in Turkish is harder than it looks
Turkish football surnames are full of i, s, g, o, u, c with dots, cedillas, and umlauts (Cakir, Muslera, Calhanoglu). A naive string comparison punishes a fan for a missing cedilla the same way it punishes them for guessing the wrong player entirely. Every guess and every player name in Santra gets run through a normalization pass before comparison: Turkish-specific case folding (the dotted and dotless "i" problem breaks the standard lowercase function in most languages) plus diacritic stripping, so "calhanoglu" and "Calhanoglu" match cleanly.
Two modes, one dataset
The core game runs once a day, same puzzle for everyone, which is the whole point of a shared daily ritual. But some players want to grind. A separate /antrenman (practice) route reuses the same 198-player, 19-club dataset but picks a random index on every visit instead of the date-derived one, so it stays unlimited without ever touching the daily puzzle's determinism.
Testing a puzzle you can't screenshot
Wordle-style games are deceptively easy to break: off-by-one errors in attribute matching, case-sensitivity bugs, normalization edge cases that only show up on one specific surname. Santra's guess-comparison logic is covered by Vitest unit tests that pin down exact matches, partial matches (right club, wrong player, for example), and misses, so a refactor of the normalization function can't silently break the game for someone mid-streak.
The stack
Next.js 14, TypeScript, Prisma, Tailwind CSS, Vitest for testing, deployed on Vercel.
Try it
Santra is free, no account needed. Live at santra-theta.vercel.app, source on GitHub: akincskn/Santra.
I'm Akin Coskun, a full-stack developer from Turkey building production SaaS tools with zero-cost infrastructure. More projects on my portfolio: akin-coskun.web.app.
Top comments (0)