ARTEMIS: NINJA HUNTER — A turn-based tactical fighter built for Reddit
Inspiration
The hackathon brief was "Games with a Hook" — something that pulls Redditors back, day after day. Most fighting games reward reflexes. We wanted the opposite: a fighter about reading your opponent's mind, where the whole match turns on whether you guessed where they'd move. Rock–paper–scissors with the depth of chess.
We also had history here. One of us had an older Phaser prototype of a deterministic tactical fighter sitting on a shelf — good combat, but chained to a stateful WebSocket relay and a Supabase backend that made it a pain to host and share. Reddit's Devvit Web platform turned out to be the perfect new home: identity, distribution, and a built-in comment thread for trash talk, all for free. The game that was hard to deploy anywhere suddenly had somewhere it belonged.
The hook
The Daily Duel. Everyone in the subreddit gets the same tactical puzzle each day (seeded by the UTC date), and races for th leaderboard. Because the whole game is deterministic, the puzzle is provably identical for everyone — and your run is verifiable. On top of that we layered live Online PvP: ranked Quick Match 1v1, private "challenge a friend" duels, and 2–3 player rooms where the host can fill empty never stalls.
How it plays
A duel is fought by 2–3 ninjas on a 27-hex arena. There are 7 fighters, each with
3 signature attacks (and no mirror matches online — every fighter is unique to one
player per duel). Each round is commit–reveal:
actions** — MOVE, ATTACK (spends energy, hits a pattern of hexes in your
facing), BLOCK, CHARGE (gain energy), or NONE — then everyone reveals and
the round resolves simultaneously in a fixed order:
$$ \text{BLOCK} \;\rightarrow\; \text{MOVE} / \text{CH \text{ATTACK (by lowest HP first)}. $$
A whiffed attack wastes the turn; a well-read one connects. Last ninja standing wins; everyone falling the same round is a draw. Th simultaneity: because both players commit blind, each turn is really a payoff matrix you're solving under uncertainty. If each of the 3 action slots has on the order of $k$ meaningful choices, your per-turn plan large enough that the right move is rarely a single best action but a read on what your opponent is likely to do.
How we built it
- From the Artemis prototype we ported the deterministic combat core into
src/shared/core— round resolution, the hex grid, a seeded CPU (Mulberry32 PRNG), state hashing, and the gam patterned attacks, arenas). The hard rule: zeroMath.random().
We then replaced the prototype's stateful WebSocket + Supabase backend with
Devvit's serverless model: request/response over Hoe
of truth, and Devvit's realtime channels for live matches. The client is
Phaser 3.90 running inside the Devvit webview, with scenes flowing
Boot → Preloader → Menu → Game → Result, plus Lo
WaitingRoom, andLobbyBrowser` for online play.
The math that makes it fair
Determinism is the whole game's backbone. Given a daily seed $s$ and a player's action sequence $A = (a_1, a_2, \dots, a_n)$, the o:
$$ \text{outcome} = f(s, A), \qquad \text{score} = M\big(f(s, A)\big). $$
The client may claim a score, but the server re-simulates $f(s, A)$ from scratch and trusts only the recomputed $M$. That gives us anti-cheat almost for free — there's no game state to forge, only an action log to replay. The same property powers fair leaderboards and future replays/spectating. And per-round state hashing,
$$ h_t = H(\text{state}_t), $$
lets two clients in a live match detect desync the instant their hashes diverge — which is what makes trustworthy PvP possible without a stateful authoritative server. We carried over the prototype's golden Vitest suite as a safety net so the core stays bit-for-bit stable.
Challenges we ran into
- **Real-time multiplayer on a serverless platform persistent socket relay; Devvit has no long-lived sockets. We rebuilt PvP on Devvit's realtime messaging with Redis as the sode bots that fill empty seats so a room never hangs waiting for humans.
- The 28 MB audio problem. The original soundtrack was a hard bundle blocker.
We stripped it down, compressed, and lazy-loaded
cache.audio.existsguards so the game is playable long before sound finishes loading. - Landscape on a portrait-locked phone. Reddit
portrait, but a tactical board wants landscape. Instead of fighting Phaser's
Scale.FIT, we rotate the canvas with CSS and size it manually withScale.NONE. - Shrinking a "full match" into something bite-sized. A complete tactical duel is long; the Daily Duel reframes it as one sharp, shareable puzzle with a short, concrete objective.
- Freezing the core. Once the game is public, changing the deterministic core
would invalidate every saved score and replay. So
shared/coreandLB_VERSIONare frozen, while art and audio stae between "logic you can't touch" and "polish you can."
What we learned
- Determinism is a superpower on Reddit. Replays, fair leaderboards, and anti-cheat all fall out of one discipline — *no R rather than being three separate features to build.
- You can ship real-time-feeling multiplayer on serverless if Redis is your single source of truth and the simulation is dete
- Reusing a validated shell beat any single feature. Standing on granny-run's proven Devvit foundation de-risked the platform so we could spend our time on the game.
- Mind-game depth doesn't need fancy tech. Simultaneous commit–reveal on a small hex board produces startling depth from a h
Built With
- aseprite
- css3
- devvit-web
- eslint
- hono
- html5
- node.js
- phaser.js
- pixellab
- prettier
- reddit-devvit
- redis
- typescript
- vite
- vitest
- websockets
Log in or sign up for Devpost to join the conversation.