The LeetCode for Game Developers.
Practice real engine math, AI, physics, pathfinding, and systems design — in C#, C++, Lua, and GDScript.
Live Demo · Documentation · Report a Bug · Request a Feature · Discord
GameCode is an open-source coding practice platform purpose-built for game developers. Think LeetCode — same problem-solving flow, same run/submit loop, same density — but every problem maps to a real system inside a game engine.
You are not memorizing API calls. You are learning why engines are built the way they are.
Browse Problems → Understand the engine concept → Code in C#, C++, or Lua → Run & Submit → Read the editorial
No streaks. No coins. No leaderboards. Just you and the compiler.
GameCode is in active development. We are currently in v0.3.0 (private beta). Join the waitlist
- Philosophy
- Features
- Tech Stack
- Architecture
- Getting Started
- Environment Variables
- Project Structure
- Problem Taxonomy
- Code Execution Engine
- API Reference
- Roadmap
- Contributing
- License
| Principle | What it means |
|---|---|
| Engine-agnostic concepts | We teach the math, algorithms, and patterns behind game engines — not engine-specific API calls. A vector lerp is a vector lerp whether you are in Unity or Unreal. |
| Zero gamification | No streaks, coins, badges, XP, or social feeds. We respect your time. |
| Frictionless practice | You should be writing code within 60 seconds of landing on the site. |
| Editorial depth | Every problem ships with an editorial explaining why the system exists in real engines, not just the algorithm. |
| Open source | Every line of code, every problem, and every editorial is open. Built in public, for the community. |
- Multi-Language Support — Write and execute in C# (.NET 8), C++ (g++ 13, -std=c++17), Lua (5.4), and GDScript (4.x).
- Sandboxed Execution — Each run gets an isolated Docker container. Network disabled, read-only filesystem, strict RAM and CPU caps. No escape.
- Intelligent Problem Browsing — Full-text search, tag filtering, difficulty filter, status filter, and language filter — all composable and URL-persisted.
- Monaco Editor — VS Code's editor, directly in the browser. Language-aware syntax highlighting and IntelliSense.
- Submission History — Full history with verdicts (AC, WA, TLE, MLE, RTE, CE), runtime, and memory per submission.
- Curated Learning Paths — "Beginner Game Dev", "Pathfinding Deep Dive", "Physics Systems" — structured problem sets for deliberate practice.
- Secure Authentication — GitHub OAuth, Google OAuth, and magic-link email. JWT-based with silent auto-refresh.
- Admin CMS — Markdown problem editor, test case manager, per-language starter code editor, and editorial editor. Full content workflow: Draft → Review → Published.
- Accessible — Keyboard navigable, WCAG AA color contrast, reduced motion respected.
| Technology | Purpose |
|---|---|
| Next.js 14 (App Router) | SSR for SEO on problem pages. CSR for the editor. |
| TypeScript strict | End-to-end type safety. No any. |
| Tailwind CSS v3 | Utility-first styling. |
| Monaco Editor | VS Code's editor engine. Loaded from CDN — not bundled. |
| TanStack Query v5 | Server state, submission polling, optimistic mutations. |
| react-resizable-panels | Three-panel solve layout with drag-to-resize. |
| ky | Lightweight fetch wrapper with automatic JWT refresh. |
| Technology | Purpose |
|---|---|
| Go 1.22+ | Compiled, fast, single binary. Goroutines make concurrency natural. |
| Echo v4 | High-performance HTTP framework. ~200k req/s per core. |
| PostgreSQL 15 | Primary database. Full-text search via pg_trgm. |
| sqlc | Type-safe SQL. Write SQL, get Go structs. Zero reflection, zero runtime panics from bad queries. |
| golang-migrate | Raw SQL migration files. Deterministic, reviewable. |
| asynq | Redis-backed job queue for code execution workers. |
| Redis 7 | Session cache, rate limiting, SSE pub/sub, job queue. |
| golang-jwt/jwt v5 | JWT access + refresh token pair. |
| Technology | Purpose |
|---|---|
| Docker | Containerized runner sandboxes per code execution. |
| Vercel | Next.js frontend with edge caching. |
| Railway | Go API binary deployment. |
| Neon | Serverless PostgreSQL with branch-per-PR previews. |
| Upstash | Serverless Redis. |
| GitHub Actions | CI/CD for both apps. |
graph TB
subgraph Browser["Browser"]
Next["Next.js 14 — SSR + CSR"]
Monaco["Monaco Editor — CSR only"]
end
subgraph API["Go REST API — Echo v4 — Port 8080"]
Auth["auth/*"]
Problems["problems/*"]
Submissions["submissions/*"]
Run["run/*"]
end
subgraph Workers["Execution Workers — asynq"]
RunWorker["Run Worker"]
SubWorker["Submit Worker"]
end
subgraph Containers["Runner Containers — Docker"]
CPP["gc-runner-cpp\ngcc:13"]
CS["gc-runner-csharp\ndotnet:8.0"]
Lua["gc-runner-lua\nlua:5.4"]
GDS["gc-runner-gdscript\ngodot:4.x"]
end
subgraph Data["Data Layer"]
PG[("PostgreSQL 15")]
Redis[("Redis 7")]
end
Browser --> |HTTPS + httpOnly Cookies| API
API --> |pgx v5| PG
API --> |go-redis| Redis
Redis --> |asynq queue| Workers
Workers --> |docker run| Containers
Workers --> |verdict stored| Redis
API --> |SSE stream| Browser
Run Code — fast path, ~2s:
POST /api/run
→ validate + rate-limit
→ enqueue asynq job (priority: high)
→ return { run_id }
Client opens SSE: GET /api/run/:runId/stream
→ worker picks job
→ spawns Docker container
→ captures stdout
→ stores result in Redis
→ SSE handler streams result to browser
Submit Code — full evaluation:
POST /api/submissions
→ create row (verdict = pending)
→ enqueue asynq job (priority: normal)
→ return { submission_id }
Client polls: GET /api/submissions/:id every 1.5s
→ worker fetches ALL test cases
→ runs each in Docker container sequentially
→ aggregates verdict
→ UPDATE submissions SET verdict = ...
→ client poll resolves with final verdict
docker run \
--rm \
--network none \ # No outbound network access
--memory 256m \ # Hard memory cap
--memory-swap 256m \ # No swap allowed
--cpus 1.0 \ # 1 CPU max
--pids-limit 64 \ # No fork bombs
--ulimit nofile=64:64 \ # No file descriptor exhaustion
--read-only \ # Immutable container filesystem
--tmpfs /tmp:size=32m,noexec \ # Writable /tmp, non-executable
--stop-timeout 5 \ # Hard kill after 5s
-v /tmp/gc-jobs/$JOB_ID:/workspace:ro \
gc-runner-$LANGUAGE:latest- Docker and Docker Compose
- Go 1.22+
- Node.js 20+ and
npm - golang-migrate CLI
- sqlc CLI
The fastest way to get the full stack running locally.
1. Clone the repository
git clone https://github.com/gc-platform/gamecode.git
cd gamecode2. Configure environment
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env.localEdit both files. The minimum required values are DATABASE_URL, REDIS_ADDR, JWT_ACCESS_SECRET, JWT_REFRESH_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and FRONTEND_URL.
3. Start all services
docker compose -f docker/docker-compose.dev.yml upThis starts PostgreSQL, Redis, the Go API (with hot-reload via air), and the Next.js dev server.
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Go API | http://localhost:8080 |
| PostgreSQL | localhost:5432 |
| Redis | localhost:6379 |
4. Run migrations and seed
make migrate-up # Apply all database migrations
make seed # Seed 10 sample problems, tags, and curated listsOpen http://localhost:3000.
1. Start PostgreSQL and Redis
docker run -d --name gc-pg \
-e POSTGRES_DB=gamecode_dev \
-e POSTGRES_USER=gc \
-e POSTGRES_PASSWORD=gc \
-p 5432:5432 postgres:15-alpine
docker run -d --name gc-redis \
-p 6379:6379 redis:7-alpine2. Backend
cd apps/api
cp .env.example .env
# Edit .env with your values
# Run database migrations
migrate -path internal/db/migrations \
-database "postgres://gc:gc@localhost:5432/gamecode_dev?sslmode=disable" up
# Generate sqlc types (only needed when .sql query files change)
sqlc generate
# Start the API server on port 8080
go run ./cmd/api/main.go3. Build runner images
Code execution requires pre-built Docker images for each supported language.
make build-runners
# Or individually:
docker build -t gc-runner-cpp:latest docker/runners/cpp/
docker build -t gc-runner-csharp:latest docker/runners/csharp/
docker build -t gc-runner-lua:latest docker/runners/lua/4. Frontend
cd apps/web
npm install
cp .env.example .env.local
# Edit .env.local — set NEXT_PUBLIC_API_URL=http://localhost:8080
npm run dev| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 8080 |
API server port |
ENVIRONMENT |
No | development |
development or production |
DATABASE_URL |
Yes | — | PostgreSQL connection string |
REDIS_ADDR |
Yes | — | Redis host:port |
REDIS_PASSWORD |
No | — | Redis password (if auth is enabled) |
JWT_ACCESS_SECRET |
Yes | — | Random 64-char string. Signs access tokens (15 min TTL). |
JWT_REFRESH_SECRET |
Yes | — | Different 64-char string. Signs refresh tokens (7 day TTL). |
ACCESS_TOKEN_TTL |
No | 15 |
Access token TTL in minutes |
REFRESH_TOKEN_TTL |
No | 7 |
Refresh token TTL in days |
GITHUB_CLIENT_ID |
Yes | — | GitHub OAuth App Client ID |
GITHUB_CLIENT_SECRET |
Yes | — | GitHub OAuth App Client Secret |
GOOGLE_CLIENT_ID |
Yes | — | Google OAuth Client ID |
GOOGLE_CLIENT_SECRET |
Yes | — | Google OAuth Client Secret |
FRONTEND_URL |
Yes | — | Frontend origin. Used for CORS and OAuth redirects. |
MAX_WORKERS |
No | 4 |
asynq worker concurrency (parallel executions) |
DOCKER_RUNNER_TAG |
No | latest |
Docker image tag for runner containers |
RESEND_API_KEY |
Yes | — | Resend API key for magic link emails |
EMAIL_FROM |
No | noreply@gamecode.dev |
Sender address for transactional emails |
Generate secrets:
openssl rand -hex 32 # Run twice — once per JWT secret| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_API_URL |
Yes | Go API public URL (e.g. http://localhost:8080) |
API_URL |
Yes | Go API URL for server-side fetches. Same as above in dev; internal URL in prod. |
NEXT_PUBLIC_ENV |
No | development or production |
gamecode/
│
├── apps/
│ ├── api/ # Go REST API
│ │ ├── cmd/
│ │ │ └── api/main.go # Entrypoint — wires all dependencies, starts server + workers
│ │ ├── internal/
│ │ │ ├── config/ # Viper config struct
│ │ │ ├── db/
│ │ │ │ ├── migrations/ # Raw SQL migrations (golang-migrate)
│ │ │ │ ├── queries/ # sqlc source query files (.sql)
│ │ │ │ └── sqlc/ # Auto-generated Go code — do not edit
│ │ │ ├── domain/ # Pure Go types + domain errors (no external dependencies)
│ │ │ ├── repository/ # Database access (implements domain interfaces)
│ │ │ ├── service/ # Business logic
│ │ │ ├── handler/ # Echo HTTP handlers (thin — bind, call service, respond)
│ │ │ ├── middleware/ # Auth, rate-limit, request logging, role enforcement
│ │ │ ├── executor/ # Code execution: Docker sandbox, asynq workers, language runners
│ │ │ └── cache/ # Typed Redis cache helper
│ │ ├── pkg/
│ │ │ ├── apierr/ # Typed HTTP error responses
│ │ │ ├── jwt/ # Token issue and validate
│ │ │ ├── oauth/ # GitHub + Google OAuth2 flows
│ │ │ └── pagination/ # Offset pagination helpers
│ │ ├── go.mod
│ │ └── sqlc.yaml
│ │
│ └── web/ # Next.js 14 frontend
│ ├── app/
│ │ ├── (auth)/login/ # Login page
│ │ ├── (main)/
│ │ │ ├── page.tsx # Home
│ │ │ ├── problems/ # Problem list + [slug] detail + editor
│ │ │ ├── submissions/ # Submission history
│ │ │ ├── lists/ # Curated and user lists
│ │ │ └── profile/ # User profiles
│ │ └── admin/ # CMS — problem authoring, test cases, editorials
│ ├── components/
│ │ ├── ui/ # Design system primitives (Button, Badge, Modal...)
│ │ ├── editor/ # Monaco wrapper, console panel, verdict panel
│ │ ├── problems/ # Problem table, filters, detail tabs
│ │ ├── submissions/ # Submission table and detail view
│ │ └── layout/ # TopNav, SolveLayout (three-panel resizable)
│ └── lib/
│ ├── api.ts # ky client with automatic token refresh
│ ├── auth.ts # Auth context
│ └── hooks/ # useProblems, useSubmission, useEditorCode, useFavorite
│
├── docker/
│ ├── docker-compose.dev.yml
│ └── runners/
│ ├── cpp/Dockerfile # gcc:13 — compiles and executes C++
│ ├── csharp/Dockerfile # dotnet/sdk:8.0 — builds and runs C#
│ └── lua/Dockerfile # lua:5.4-alpine — executes Lua directly
│
├── scripts/
│ └── seed/main.go # Seeds problems, tags, curated lists
│
└── Makefile
Problems are organized into 10 game-dev-native categories — not LeetCode topics rebranded. These are actual systems you build in a game engine.
| Category | Example Problems |
|---|---|
| Math and Vectors | Vector Lerp, Quaternion to Euler, Dot Product Angle |
| Movement and Physics | Verlet Integration, Rigidbody Step, Projectile Arc |
| AI and State Machines | Hierarchical FSM, Behavior Tree Tick, Blackboard Pattern |
| Pathfinding and Navigation | A* Grid, Jump Point Search, NavMesh Funnel Algorithm |
| Collision and Spatial | AABB Sweep, BVH Build, Spatial Hash Grid |
| Procedural Generation | BSP Dungeon, Voronoi Biomes, Wave Function Collapse |
| Rendering and Camera | View Frustum Cull, Camera Follow, Sprite Batching |
| ECS and Architecture | Archetype Chunk Layout, System Dependency Graph |
| Input and Gameplay | Input Buffer, Combo Recognizer, Cooldown System |
| Optimization | Object Pool, LOD System, Job Graph Scheduler |
GameCode runs submitted code inside isolated Docker containers. There is no shared process, no shared filesystem, and no network access.
| Language | Base Image | Compile Command | Default Time Limit |
|---|---|---|---|
| C++ | gcc:13-bookworm |
g++ -O2 -std=c++17 -o /tmp/sol |
2000ms |
| C# | mcr.microsoft.com/dotnet/sdk:8.0-alpine |
dotnet build |
3000ms |
| Lua | lua:5.4-alpine |
interpreted | 2000ms |
| GDScript | godot:4.x-headless |
interpreted | 3000ms |
| Exit Code | Cause | Verdict |
|---|---|---|
0 |
Success | Output compared — AC or WA |
1 |
Runtime crash | RUNTIME_ERROR |
2 |
Compile failure | COMPILE_ERROR |
124 |
Killed by timeout | TIME_LIMIT_EXCEEDED |
137 |
Killed by OOM | MEMORY_LIMIT_EXCEEDED |
- Create
docker/runners/<lang>/Dockerfilewith a non-rootrunneruser. - Create
docker/runners/<lang>/run.sh— compile if needed, then execute withstdin < /workspace/input.txt. - Add the language value to the
languageenum ininternal/db/migrations/000001_init.up.sql. - Add a case in
internal/executor/sandbox.go → languageCodeFile(). - Build the image:
docker build -t gc-runner-<lang>:latest docker/runners/<lang>/ - Open a pull request.
Full OpenAPI spec: docs.gamecode.dev/api
Quick reference:
GET /api/problems List problems — filterable by difficulty, tags, language, status
GET /api/problems/:slug Problem detail — description, examples, starter code, hints
POST /api/problems/:slug/favorite Toggle favorite (auth required)
POST /api/run Execute code against a test case — returns { run_id }
GET /api/run/:runId/stream SSE stream — delivers execution result
POST /api/submissions Submit code for full evaluation — returns { submission_id }
GET /api/submissions/:id Get submission status and results (poll until verdict != pending)
GET /api/submissions User submission history — filterable
GET /api/lists Browse problem lists
POST /api/lists Create a list (auth required)
POST /api/lists/:listId/items Add problem to list (auth required)
GET /api/users/:username User profile and stats
PATCH /api/users/me Update profile (auth required)
GET /api/auth/oauth/github Initiate GitHub OAuth
GET /api/auth/oauth/google Initiate Google OAuth
POST /api/auth/magic-link Send magic link email
POST /api/auth/refresh Refresh access token from refresh cookie
POST /api/auth/logout Revoke session and clear cookies
All authenticated requests require the gc_access_token httpOnly cookie, set automatically after OAuth or magic-link login. The frontend client refreshes the token transparently on 401.
Track progress on the public project board.
| Status | Item |
|---|---|
| Done | Go API, auth (GitHub / Google / magic-link), JWT |
| Done | Problem list with search, filtering, pagination |
| Done | Problem detail page (description, examples, hints) |
| Done | Monaco editor with localStorage persistence |
| Done | Run code via Docker sandbox and SSE stream |
| Done | Submit code — full test case evaluation and verdicts |
| In Progress | Admin CMS (problem authoring, editorial editor) |
| In Progress | Lists and curated learning paths |
| Planned | GDScript runner (Godot 4 headless) |
| Planned | Discuss — per-problem solution threads |
| Planned | Problem notes — personal markdown, autosaved |
| Planned | Mobile read mode — description only |
| Planned | OpenAPI spec and public API docs |
| Planned | CLI — gc submit solution.cpp |
| Will not do | Streaks, coins, badges, XP, social feed, contests |
We welcome contributions. GameCode is built to be worked on.
- Add a problem — The highest-value contribution. See the problem authoring guide.
- Fix a bug — Check issues labeled
good first issue. - Improve the execution engine — Better sandboxing, new language runners, faster evaluation.
- Frontend improvements — Accessibility, keyboard navigation, performance.
- Write tests — Go service and repository tests. Playwright E2E for critical flows.
1. Fork and clone
git clone https://github.com/YOUR_USERNAME/gamecode.git
cd gamecode2. Create a branch
git checkout -b feat/your-feature-name
git checkout -b fix/issue-number-description3. Run tests before committing
make test-api # Go unit and integration tests
make test-web # Frontend tests
make lint # golangci-lint4. Commit using Conventional Commits
feat: add GDScript runner Dockerfile
fix: clamp memory usage metric to 0 on parse error
docs: add problem authoring guide
refactor(executor): extract sandbox timeout logic
test: add handler test for POST /api/submissions
5. Open a pull request
Include a clear description of what changed and why. UI changes need screenshots. New Go service or repository code needs test coverage.
| Language | Formatter | Linter |
|---|---|---|
| Go | gofmt / goimports |
golangci-lint (see .golangci.yml) |
| TypeScript | prettier |
eslint (see apps/web/.eslintrc.json) |
A problem pull request must include all of the following:
- Problem description in markdown
- At least 3 visible examples with input, output, and explanation
- Constraints section
- At least 10 hidden test case pairs (
.txtfiles) - Starter code for at least 2 languages
- Editorial (explanation, complexity analysis, engine context)
- Tag assignments
| Metric | Target | Measured |
|---|---|---|
| Problem list LCP | < 1.5s | 0.9s (Vercel Edge) |
| Problem detail LCP | < 1.5s | 1.1s |
| Monaco editor time to interactive | < 3s | 2.3s |
| POST /api/run P90 | < 3s | 2.1s |
| Submit verdict P90 | < 8s | 5.8s |
| Go API P99 latency (read endpoints) | < 50ms | 18ms |
Measured on 2 vCPU / 2 GB Railway with Neon PostgreSQL and Upstash Redis.
The execution engine is the highest-risk surface. If you discover a vulnerability — especially in the sandbox — please report it privately.
- Report: Email
security@gamecode.dev. Do not open a public issue. - Scope: Code execution sandbox escape, authentication bypass, data exposure.
- Response: We aim to acknowledge within 24 hours and patch critical issues within 72 hours.
See SECURITY.md for the full disclosure policy.
GameCode is licensed under the MIT License. See LICENSE.
Problem content (descriptions, editorials, test cases) under data/problems/ is licensed under CC BY 4.0.
- LeetCode — for proving the problem-solving loop works at scale.
- Judge0 — open source reference for execution engine design.
- shadcn/ui — design system reference.
- cal.com — open-source monorepo architecture reference.
- Every game developer who ever wondered why
transform.position += velocity * Time.deltaTimeworks the way it does.
gamecode.dev · docs · discord · @gamecodehq
Built by game developers, for game developers. If this helps you ship better games, consider leaving a star.