A web-based, turn-based mathematical deduction puzzle ("Function Wordle"). You play a
Function Detective trying to identify a secret integer-coefficient polynomial f(x) in
6 moves, using point evaluations (val x), slope queries (is_inc x), and final guesses
(target …). It borrows Wordle's structure — 6 attempts, precise feedback, minimalist dark UI —
and moves the problem from words to algebra.
- Frontend: Vue 3 + Vite + Pinia + Tailwind + Chart.js
- Backend: Node + Express + SQLite (
better-sqlite3) + a pure math engine - Language: TypeScript (strict) end-to-end, with shared types/validation in
shared/
Full design is in
docs/specifications.md; the build roadmap and progress are indocs/workflow.md.
- Node.js ≥ 20.17 (Node 22 LTS works too) and npm.
- A C/C++ toolchain is not usually needed —
better-sqlite3andbcryptship prebuilt binaries for common platforms.
The app is two separate npm packages — backend/ and frontend/ — each installed and
run on its own.
From the repository root:
# 1) Install dependencies (two packages)
cd backend && npm install
cd ../frontend && npm installThe backend reads its config from backend/.env. A ready-to-use dev file is already
provided (it is gitignored). It sets:
| Variable | Purpose | Dev value |
|---|---|---|
PORT |
Backend HTTP port | 3000 |
DB_PATH |
SQLite file path (created on first run) | ./data/funcle.db |
JWT_SECRET |
Signs admin tokens | a dev placeholder |
ADMIN_PASSWORD |
bcrypt hash of the admin password | hash of funcle-admin |
Dev admin password:
funcle-admin(see the Admin section below). To start fresh, copybackend/.env.example→backend/.envand set your own values. Generate a new admin hash with:cd backend node -e "console.log(require('bcrypt').hashSync('your-password', 10))"
Open two terminals:
# Terminal 1 — backend API on http://localhost:3000
cd backend && npm run dev
# Terminal 2 — frontend on http://localhost:5173
cd frontend && npm run devThen open:
- Play the game: http://localhost:5173/
- Admin (schedule puzzles): http://localhost:5173/admin
The Vite dev server proxies /api/* to the backend on port 3000, so you only need to open the
frontend URL.
You have 6 turns to identify the secret polynomial f(x) (degree 1–3, integer coefficients
from −10 to 10). Each turn you pick a command and submit it:
| Command | What it does | Feedback |
|---|---|---|
val x |
Evaluate f(x) at a number x (integers or decimals) |
🟩 returns the exact value |
is_inc x |
Check the slope f'(x) at x |
🟨 Increasing / Decreasing / Stationary |
target … |
Submit your guess for f(x) as an expression |
🎯 win if equivalent |
Notes:
- Every command uses one turn — including
val/is_inc. Use them to gather clues, then commit atarget. - Invalid
x(non-numeric) returns an error and does not cost a turn. targetaccepts any equivalent form —x^2 - 4,-4 + x^2, and(x-2)(x+2)all match. (You may use+ - * / ^, parentheses, and the variablexonly.)- You win when your
targetmatches; you lose if 6 turns pass without a correct guess. - When the game ends, the secret is revealed along with a graph of
f(x)(toggleable) and a Share button that copies a spoiler-free emoji grid.
- Daily — everyone gets the same puzzle each calendar day; your progress resumes if you reload. (Falls back to an auto-generated puzzle if none was scheduled.)
- Free Play — a fresh random puzzle every game; replay as much as you like.
Switch modes with the Daily / Free Play tabs near the top of the screen.
The mathematician schedules upcoming daily puzzles through a simple, password-protected page. It is not linked from the game UI.
- Go to http://localhost:5173/admin
- Log in with the admin password (dev default:
funcle-admin). - Schedule a puzzle:
- Date — must be today or in the future.
- Answer — the secret as an expression, e.g.
x^2 - 4. It is validated against the rules: degree 1–3 and integer coefficients between −10 and 10. Invalid answers are rejected with a reason. - Note (optional) — a private label for your own reference (never shown to players).
- The table lists scheduled puzzles. Future puzzles can be edited or deleted; today's and past puzzles are locked to keep the game fair.
If no puzzle is scheduled for a given day, the backend deterministically generates one from the date, so the Daily game always works.
Security: the admin password is stored only as a bcrypt hash in
ADMIN_PASSWORD; login is rate-limited; all admin API calls require a signed token. The secret polynomial is never sent to the browser during play.
# Backend (Vitest): math engine, routes, DB
cd backend && npm test
# Frontend (Vitest + @vue/test-utils): stores, components
cd frontend && npm testOther useful scripts (in each package):
npm run build # backend: tsc -> dist/ ; frontend: type-check + vite build
npm run lint # ESLint
npm run format # Prettier# Backend
cd backend && npm run build && npm start # runs node dist/backend/server.js
# Frontend
cd frontend && npm run build # static assets in frontend/dist/Deploy on a container / VPS with a persistent disk (Fly.io, Railway, Render, a small VPS) —
not edge/serverless: better-sqlite3 is a native module and SQLite needs persistent storage.
Set ADMIN_PASSWORD (bcrypt hash), JWT_SECRET, and DB_PATH via real secrets, and mount the DB
file on a persistent volume. You can also serve frontend/dist/ as static files from Express for a
single-origin deployment. See docs/specifications.md §15.
Funcle/
├── shared/ # Domain types + zod API schemas (one source of truth for both apps)
├── backend/
│ ├── server.ts # Express app + entry point
│ ├── engine/ # Pure math: polynomial, derivative, parser (no I/O)
│ ├── routes/ # session, game, stats, admin
│ ├── middleware/ # admin JWT auth
│ ├── db/ # schema.sql + SQLite helpers
│ └── config.ts # admin/auth config from env
├── frontend/
│ └── src/
│ ├── views/ # GameView, AdminView
│ ├── components/ # game/, input/, graph/, layout/
│ ├── stores/ # gameStore, settingsStore, adminStore (Pinia)
│ ├── api/ # typed fetch client
│ └── lib/ # curve, share, anon-id helpers
├── design/ # UI blueprint (CSS reference for the main screen)
├── math/ # Confirmed math rules
└── docs/ # specifications.md (source of truth), workflow.md (roadmap/status)
| Variable | Required | Notes |
|---|---|---|
PORT |
no (default 3000) | Backend HTTP port |
DB_PATH |
no (default ./data/funcle.db) |
SQLite file; parent dir is created automatically |
JWT_SECRET |
yes (prod) | Long random string used to sign admin tokens |
ADMIN_PASSWORD |
yes (for admin) | bcrypt hash of the admin password — never plaintext |
Implemented and tested end-to-end: the full game (Daily + Free Play, 6-turn loop, win/lose
overlay, graph reveal, share) and the password-protected admin puzzle scheduler. Still planned:
optional player accounts, persistent stats with a Stats screen, Help/Settings overlays, and the
final responsive/deploy polish (Phases 7–8 in docs/workflow.md).