GridLock — Hackathon Project Story
Inspiration
When I was a kid my parents got me a sliding-block puzzle board game the kind where you move cars on a grid to free the “goal” car. I wanted to bring that experience to Reddit so people could play quick, satisfying puzzles without leaving the app. The idea of a “traffic jam” you unblock with your finger felt like a perfect fit for mobile and the Devvit platform. We were also inspired by the chance to let players design and share their own levels as Reddit posts, turning the game into a small community of puzzle creators and solvers.
What it does
GridLock is an unblocker puzzle game that runs inside Reddit. Players slide cars (2- and 3-cell blocks) on a (6 \times 6) grid to clear a path for the red goal car to exit to the right. The game includes 40 built-in levels with increasing difficulty, plus:
- Per-level leaderboards — Best solve times are stored and shown so players can compete.
- Custom level creator — Players can build levels by placing goal and obstacle cars, then test and publish them.
- User-created levels — Published levels become Reddit posts; others can play “random user-created level” from the main menu.
- Timer & high scores — Each level tracks your best time; sound effects (engine, honk, idle) add feedback while you play.
- Level select — A level grid shows progress (locked / next / completed) and lets you jump to any unlocked level.
Everything works in the Reddit mobile app and in-browser via Devvit’s WebView.
How we built it
We built GridLock on Reddit’s Devvit stack so it could run natively in Reddit (posts, subreddits, and user identity). The project is structured as:
Client (TypeScript + Vite)
The puzzle UI is custom HTML/CSS/TypeScript. We defined level data (block positions, orientations, goal car, exit direction) in shared TypeScript and implemented:- Drag-to-move with collision detection and movement bounds so cars only slide along their axis.
- A grid occupancy model to know which cells are free: for each block we compute min/max row or column so movement is constrained correctly (e.g. horizontal blocks only move left/right).
- Canvas-based rendering for car sprites (2-long sedans, 3-long trucks/bus, goal car) with rotation and cache-busting for assets.
- Screens for: level select, in-game HUD (timer, reset, next), level creator (place goal, 2-cell, 3-cell; rotate; clear; test; publish), and post-solve (congrats, leaderboard, next level / create / open post).
Server (Express + Devvit)
The backend uses Devvit’s server APIs and Redis for persistence:- Init Resolves current Reddit user and post context.
- Level solve Submit solve time for a level; we store per-user best time and update a sorted leaderboard per level (Redis sorted sets).
- Level leaderboard Return top N times plus the current user’s rank and time.
- Custom levels Publish level payload → create a Reddit post and store level data keyed by post ID; index of custom levels for “random user level”; fetch level by post ID when opening a shared link.
- State User preferences (e.g. sound on/off) and any extra state stored in Redis keyed by post + username.
Shared types & level data
All level definitions live inshared/unblock.ts: block type (id, row, col, width, height, orientation, isGoal, exitDirection), level type, and the array of 40 built-in levels. The client and server both use these types and the same level IDs for consistency.GameMaker
The repo is set up so a GameMaker Reddit/WASM build can be dropped into the client (e.g. splash or extra content). The main puzzle experience is the custom TypeScript unblock implementation; GameMaker is used where we want its tooling.Assets & UX
Car images (goal car, sedans, trucks, bus) are served from/assets; we use engine/honk/idle SFX and a simple sound toggle that persists via the state API. The UI is responsive and works in Reddit’s embedded WebView and in normal browsers.
Challenges we ran into
- Movement and collision Making drag feel right and keeping blocks on-grid and non-overlapping was tricky. We had to carefully compute valid ranges (min/max row or column) from the current grid state excluding the moving block, then snap drag deltas to cell steps and clamp to those ranges. Handling the “blocked” feel (e.g. playing a honk when the player tries to move into another car) required tracking intended vs. allowed position.
- Reddit/Devvit context The game sometimes loads in an iframe or as a post attachment; we had to handle
postIdand username from Devvit’s context, and sometimes open “open in Reddit” links vianavigateTovswindow.opendepending on in-app vs browser. - Custom level publishing Turning a level into a Reddit post and then having that post load the same level when someone opens it required a clear contract: post ID ↔ stored level payload, and an index of custom levels for the “random user level” feature. We also had to handle “post not ready yet” right after creation (eventual consistency) with a short retry loop.
- Level design Designing 40 levels that are solvable and progressively harder took iteration; we have a few levels with intentional typos (e.g. duplicate block IDs or overlapping blocks in data) that we fixed during testing. Keeping the data format consistent (horizontal vs vertical, exit direction, board size) avoided bugs in the solver logic.
- Audio Autoplay and “idle vs engine vs collision” behavior had to respect user interaction and Reddit’s autoplay rules; we gate all sound on a first user interaction and persist the sound toggle in the backend.
Accomplishments that we're proud of
- Full unblock experience on Reddit 40 levels, smooth drag-to-move, timer, and per-level leaderboards, all inside Reddit.
- Custom level creation and sharing Players can build, test, and publish levels that others can play via “random user level” or by opening the Reddit post; levels are stored and keyed by post ID.
- Clean separation of levels and logic All level data lives in one shared module; the same types drive the client renderer and the server’s custom-level publish/fetch, which made it easy to add levels and fix data.
- Polished UX Level select grid (locked/next/complete), high scores panel, level leaderboard after solve, sound effects and toggle, and a level creator that feels part of the same app.
- Devvit + Redis Using Devvit’s APIs and Redis for state, leaderboards, and custom level index gave us a real “app” feel (persistent times, user-created content) without managing our own infra.
What we learned
- Grid-based movement Implementing axis-aligned sliding with collision and bounds (compute valid range from grid state, then clamp drag) is a small set of ideas that scales to many levels once the data format is right.
- Devvit’s model How post context, Reddit identity, and Redis fit together; how to serve static assets and API routes from the same app; and how to open Reddit posts from inside the game (in-app vs new tab).
- TypeScript across client and server Sharing level and API types between frontend and backend caught mismatches early and made the custom-level pipeline (create → publish → fetch by post) straightforward.
- Level design Even with a simple rule set (slide cars to free the goal), designing levels that are fun and not broken required playtesting and a few data fixes (duplicate IDs, invalid positions).
What's next for GridLock
- Better UI Since we only had a couple weeks we will focus on making the game more beautiful.
- More levels and difficulty tiers Add more built-in levels and optional “hard” or “daily” sets.
- Level validation When publishing a custom level, check solvability (e.g. simple BFS) so we don’t allow unsolvable puzzles.
- Themes and accessibility Alternative color themes and clearer focus/aria for screen readers and keyboard play.
- Analytics and moderation Lightweight stats (plays per level, completion rates) and moderation tools for user-created levels (report, hide) so the custom level catalog stays fun and safe.
Built With
- css
- devvit/reddit-apis
- express.js
- gamemaker
- html
- reddit-devvit
- redis
- typescript
- vite
Log in or sign up for Devpost to join the conversation.