Learn the handful of Teamfight Tactics decisions that get you to top 4.
Live site · Learn · Arena · Solver · API health
LineLab is an independent, beginner-focused tool for learning TFT fundamentals — the durable, patch-agnostic habits (economy, leveling, rolling, items, positioning, reading the lobby) that separate a bleeding-out new player from a reliable top-4 finisher. It teaches by giving you one clear move at a time, then lets you drill it against a live coach.
It uses no Riot Games assets, artwork, champion data, or live-client access, and is not affiliated with or endorsed by Riot Games. The Arena and Solver run on an original Monte Carlo simulation model.
- Learn (
/learn) — 14 ranked fundamentals in 5 modules, with a "must-know 6" shown first and a "Go deeper" reveal. Each card is a single actionable rule plus the common mistake it fixes. - Arena (
/play) — a playable auto-battler with a live coach that calls one move at a time, so you drill the habits instead of reading about them. Includes a post-game review that grades every macro decision. - Solver (
/solver) — describe any spot and get one recommended move (ROLL / LEVEL / SAVE / STABILIZE) with a plain-English why. The full expected-value table, decision tree, and scenario builder live behind an Advanced toggle.
The coach never asks you to rate your own board — it reads health, gold, level, and board strength for you, runs thousands of fast simulated games, and collapses the result into a single verb.
| Layer | Stack |
|---|---|
| Frontend | Next.js 14 (App Router), TypeScript, Tailwind CSS |
| Backend | FastAPI + Python, a Monte Carlo expected-value engine |
| Database | Supabase Postgres (saved scenarios); falls back to a local JSON store |
| Optional | AI chat-coach via the Anthropic SDK, off unless ANTHROPIC_API_KEY is set |
backend/ FastAPI app + Monte Carlo solver
app/
main.py FastAPI app + endpoints
models.py Pydantic wire models
solver/ the engine (economy, leveling, shop, rollout, evaluator, …)
chat.py optional Anthropic-backed chat-coach
api/index.py serverless entrypoint (re-exports the ASGI app)
tests/ pytest suite
frontend/ Next.js app
app/ landing, learn, play (Arena), solver, compliance, terms, privacy
lib/game/ client-side Arena engine
lib/coach/ one-verb coach (ROLL/LEVEL/SAVE/STABILIZE)
lib/learn/ fundamentals curriculum content
components/ UI (Nav, Footer, play/*, solver/*, learn/*, coach/*)
docs/ model notes + build specs (see docs/README.md)
You need two processes: the Python backend (the simulation engine) and the Next.js frontend (the UI).
cd backend
python -m venv .venv
. .venv/Scripts/activate # macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt # drop the second file for runtime only
cp .env.example .env # optional: fill in for Supabase / chat-coach
uvicorn app.main:app --port 8000The backend runs fine with an empty .env — it falls back to a local JSON scenario
store and leaves the AI chat-coach disabled. GET /api/health reports which stores
are active. Interactive API docs are at /docs.
cd frontend
npm install
cp .env.local.example .env.local # set NEXT_PUBLIC_API_URL if not localhost:8000
npm run devOpen http://localhost:3000/learn to start with the fundamentals, or http://localhost:3000/play to practice in the Arena. If the backend is unreachable, the Solver falls back to an embedded sample (with a banner) so the site stays reviewable offline.
Real secrets live only in gitignored .env files (never committed). See
backend/.env.example and frontend/.env.local.example. Key ones:
SUPABASE_DB_URL— Postgres session-pooler connection string (optional; enables the Supabase scenario store)ANTHROPIC_API_KEY— enables the AI chat-coach (optional)NEXT_PUBLIC_API_URL— where the frontend finds the backend (defaults tohttp://localhost:8000)LINELAB_DATA_DIR— where the JSON fallback store keeps saved scenarios (defaults tobackend/data). Point it at a writable path on hosts that mount the app directory read-only.LINELAB_CORS_ORIGINS/LINELAB_CORS_ORIGIN_REGEX— who may call the API from a browser (defaults to localhost)
GET /api/health reports scenario_store and scenario_store_writable, so you
can tell at a glance whether saving is actually going to work.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health |
liveness + which stores/features are active |
| GET | /api/config |
option vocabulary for the scenario builder |
| POST | /api/compare |
compare candidate lines for a game state |
| POST | /api/project |
project a chosen line forward (HP/placement/survival per stage) |
| POST | /api/review |
grade a sequence of past decisions |
| POST | /api/coach/chat |
streaming AI chat-coach (requires ANTHROPIC_API_KEY) |
| GET/POST/DELETE | /api/scenarios |
built-in + saved teaching spots |
curl -s localhost:8000/api/compare -H 'content-type: application/json' -d '{
"state": {"stage":"midgame","hp":58,"gold":42,"level":6,
"board_strength":"weak","bench_value":"medium","pairs":2,
"items":"medium","lobby_tempo":"high","goal":"top4"},
"n_rollouts": 2000
}'LineLab teaches universal TFT concepts using the game's own vocabulary (gold,
interest, levels, rolls, traits, augments) but ships no Riot artwork, icons,
champion data, or patch tables, and never connects to the live game — no overlay,
real-opponent scouting, in-client "do this now" prescriptions, automation, or memory
reading. The Arena's opponents are simulated. See
/compliance for the full statement.
Both halves run on Vercel as two projects off this one repo, each with its own
root directory. Pushing to main redeploys both.
| Project | Root dir | Config | URL |
|---|---|---|---|
linelab |
frontend/ |
frontend/vercel.json |
https://linelab-yougijain.vercel.app |
linelab-api |
backend/ |
backend/vercel.json |
https://linelab-api-yougijain.vercel.app |
Non-secret wiring lives in those two vercel.json files rather than in dashboard
settings, so the deployment is reproducible from the repo:
- the frontend's
NEXT_PUBLIC_API_URLandNEXT_PUBLIC_SITE_URL; - the backend's
LINELAB_CORS_ORIGINS, plusLINELAB_CORS_ORIGIN_REGEXso per-branch preview deployments are allowed without a redeploy.
Two things are worth knowing if you fork this:
- The backend routes through
backend/api/index.py, which just re-exports the ASGI app. It usesroutesrather thanrewrites— Vercel hands a rewritten request the destination path, which would make every route arrive at FastAPI as/api/index. - Solver calls are CPU-bound Monte Carlo runs (~3s at 2 000 rollouts, the
frontend's ceiling is 4 500), so the function is given
maxDuration: 60.
The backend also sets LINELAB_DATA_DIR=/tmp/linelab, because the application
directory is read-only on the serverless runtime. /tmp is per-instance and
ephemeral, so saved scenarios survive only within a warm instance — set
SUPABASE_DB_URL for real persistence.
Secrets (SUPABASE_DB_URL, ANTHROPIC_API_KEY) are not in vercel.json —
set those as environment variables in the Vercel dashboard. Without them the
backend still runs: it falls back to the local JSON scenario store and leaves the
chat-coach off.
Setting SUPABASE_DB_URL switches the API off the JSON fallback. Use the
Session pooler string from the dashboard's Connect dialog — it suits a
long-lived ASGI process. The transaction pooler (6543) also works, because
db.py disables prepared statements.
The app creates its own table on first connect (ensure_ready, idempotent) and
seeds the built-in teaching spots, so there is no migration step to run.
public.scenarios has RLS enabled with no policies, deliberately. The API is
the table's only client and connects as postgres, which bypasses RLS; nothing
reaches it through PostgREST. Leaving RLS off would make the table readable and
writable by anyone holding the project's publishable key, which is public by
design. Don't add permissive policies unless a client starts talking to Supabase
directly.
Self-hosting instead of Vercel works unchanged — the backend is a plain
uvicorn app.main:app ASGI app, so Render / Fly.io / Railway need no extra
config beyond the environment variables above.
MIT © 2026 yougijain
Teamfight Tactics and TFT are trademarks of Riot Games, Inc., referenced here nominatively for identification and educational purposes only. LineLab is not affiliated with, endorsed by, or sponsored by Riot Games.