Reviewed-on: #17
🏃 Escape the Teacher
A server-authoritative 2D endless runner built with Go, Redis, and JavaScript.
📖 About the Project
"Escape the Teacher" is a high-performance web game developed as a school project. You play as a student caught cheating, running away from an angry teacher. The game features increasing difficulty, power-ups, boss phases, and a competitive global leaderboard.
Unlike typical browser games, this project implements anti-cheat architecture usually found in multiplayer RTS or FPS games. The browser does not decide if you survived; the server does.
✨ Features
🎮 Gameplay
- Endless Progression: The game speeds up over time.
- Controls:
- Jump: Space / Arrow Up / Tap / Left Click.
- Crouch: Arrow Down / Swipe Down (Mobile).
- Power-Ups:
- 🛡️ Godmode: Survives 3 hits.
- ⚾ Baseball Bat: Eliminates the next teacher obstacle.
- 👟 Jumpboots: Grants higher jumping power for a limited time.
- 💰 Coins: Bonus points (visual score only, does not affect game speed).
- Dynamic Backgrounds: Environment changes as you progress.
- Boss Phases: Every 1500 ticks, special boss enemies spawn.
📱 Mobile First
- Fully responsive Canvas rendering (Letterboxing).
- Touch gestures (Swipe to crouch).
- "Rotate Device" enforcement for optimal gameplay.
🛡️ Security & Admin
- Admin Panel: Password-protected (/admin) interface.
- Moderation: Review and delete leaderboard entries.
- Badword Filter: Automatic blocking of inappropriate names using Redis Sets.
- Proof System: Players receive an 8-character "Claim Code" to prove their high score to the teacher.
🏗️ Technical Architecture
The core philosophy is "Server-Authoritative, Client-Predicted".
- Frontend (JS): Captures inputs and renders the game state immediately (Client-Side Prediction) to ensure zero input lag. It sends input logs to the server in chunks.
- Backend (Go): Validates the inputs by re-simulating the game physics tick-by-tick.
- Database (Redis): Stores active sessions, RNG states, and leaderboards (Sorted Sets).
Tech Stack
- Backend: Go (Golang) 1.21+
- Frontend: Vanilla JavaScript (Canvas API), CSS3
- Database: Redis
- Containerization: Docker (Multi-Stage Build: Node Minifier -> Go Builder -> Alpine Runner)
- Orchestration: Kubernetes (Deployment, Service, Ingress)
🔧 Development Challenges & Solutions
Developing a cheat-proof game for the web came with significant technical hurdles. Here is how we solved them:
1. The "Butterfly Effect" (RNG Desynchronization)
- Problem: JavaScript's Math.random() and Go's rand implementations are different. Even if seeded with the same number, they produce different sequences. This caused "Ghost Objects" (Server spawns a teacher, Client spawns a coin).
- Solution: We implemented a custom Linear Congruential Generator (LCG) in both languages.
- The Tricky Part: JavaScript uses 64-bit Floating Point numbers for everything, while Go uses strict types. Math operations drifted apart after a few hundred iterations.
- Fix: We forced JavaScript to use BigInt to simulate 32-bit integer overflows exactly like Go's uint32.
2. Floating Point Drift & Spawning
- Problem: We initially spawned objects based on pixel positions (if x < 800). Due to floating point precision errors, the client sometimes calculated 799.99 (Spawn!) while the server calculated 800.01 (Wait!). This desynchronized the RNG state immediately.
- Solution: Tick-Based Spawning. We decoupled spawning from spatial positions. The game now decides: "The next object spawns at Tick 500" rather than "at Pixel 1200". Integers don't drift.
3. Logic-Score Coupling
- Problem: Initially, game speed and boss phases depended on the Score. When we added Coins (+2000 Points), the client's score jumped instantly, speeding up the game on the client side before the server acknowledged the coin pickup. This caused massive desyncs.
- Solution: We decoupled logic from the visual score. Game difficulty now depends strictly on "Ticks Alive" (Time), which cannot be manipulated by picking up items.
4. Physics Tunneling (High Speed Collisions)
- Problem: At high speeds, the player could move 20 pixels per frame. If an obstacle was only 15 pixels wide, the player effectively "teleported" through it without triggering a collision.
- Solution: Continuous Collision Detection (CCD). We extend the hitbox of obstacles dynamically based on their current speed (width + speed).
5. "Ghost Hits" (The CCD Side Effect)
- Problem: The CCD fix caused a new issue where the extended hitbox would hit the player after they had already jumped over the obstacle (hitting them from behind).
- Solution: A "Passed Check". Before checking collisions, we verify if the obstacle's right edge is already physically behind the player's left edge. If so, collision is ignored.
🚀 Getting Started
Using Docker (Recommended)
This project includes a production-ready Dockerfile and docker-compose.yml.
-
Clone the repository:
git clone [https://git.zb-server.de/ZB-Server/it232Abschied.git](https://git.zb-server.de/ZB-Server/it232Abschied.git)
cd it232Abschied -
Configure Environment (Optional):
Edit docker-compose.yml to set your admin credentials:
environment:
- ADMIN_USER=teacher
- ADMIN_PASS=secret123 -
Run:
docker-compose up --build -d -
Play: Open http://localhost:8080
Local Development (Go & Redis)
-
Start Redis:
docker run -d -p 6379:6379 redis:alpine -
Start the Server:
go run .(Note: Use go run . to include all files, not just main.go)
📂 Project Structure
.
├── k8s/ # Kubernetes manifests
├── static/ # Frontend files
│ ├── assets/ # Images & Sprites
│ ├── fonts/ # Local GDPR-compliant fonts
│ ├── js/ # Modular Game Engine
│ │ ├── config.js # Constants
│ │ ├── logic.js # Physics & Collision
│ │ ├── network.js # Server-Sync
│ │ └── ...
│ ├── index.html # Entry Point
│ └── style.css # Retro Design
├── secure/ # Protected Admin Files
├── main.go # Go Server Entrypoint
├── simulation.go # Server-side Physics Engine
├── rng.go # Deterministic RNG
├── types.go # Data Structures
├── Dockerfile # Multi-Stage Build
└── ...
📜 Legal
This is a non-commercial educational project.
- Privacy: No tracking cookies are used. Highscores are stored in Redis; local scores in LocalStorage.
- Assets: Font "Press Start 2P" is hosted locally.
Good luck escaping! 🏃💨