I've been building this on evenings and weekends for several months. It's a browser-based dungeon crawler inspired by a classic 1980s handheld game.
The core loop is simple: navigate an 8x8 grid maze, find hidden treasure, return it to your starting chamber before the dragon catches you.
A few implementation notes that might be interesting:
Dragon AI: The dragon has three states — Asleep, Awake, Hunting. It moves diagonally (both axes via Math.sign each turn), which feels more threatening than cardinal-only movement. It only attacks when it occupies the same tile as the player, so the tension comes from watching it close distance.
vs CPU mode: The AI opponent uses BFS on a "known map" that it builds by discovering walls the same way the human player does — by attempting to move through them. It cycles through behavior phases: Explore (BFS toward unknown territory), SeekTreasure,
ReturnToBase, EvadeDragon. A 15% random move chance prevents perfect play.
State management: Built with Zustand. One gotcha I ran into: the game engine mutates state in-place, so setting Zustand state with the same object reference caused React to skip re-renders. Fixed by always spreading { ...state } on updates.
Maze generation uses a recursive backtracker algorithm seeded fresh each game.
GRRRillaDev•2h ago
The core loop is simple: navigate an 8x8 grid maze, find hidden treasure, return it to your starting chamber before the dragon catches you.
A few implementation notes that might be interesting:
Dragon AI: The dragon has three states — Asleep, Awake, Hunting. It moves diagonally (both axes via Math.sign each turn), which feels more threatening than cardinal-only movement. It only attacks when it occupies the same tile as the player, so the tension comes from watching it close distance.
vs CPU mode: The AI opponent uses BFS on a "known map" that it builds by discovering walls the same way the human player does — by attempting to move through them. It cycles through behavior phases: Explore (BFS toward unknown territory), SeekTreasure, ReturnToBase, EvadeDragon. A 15% random move chance prevents perfect play.
State management: Built with Zustand. One gotcha I ran into: the game engine mutates state in-place, so setting Zustand state with the same object reference caused React to skip re-renders. Fixed by always spreading { ...state } on updates.
Maze generation uses a recursive backtracker algorithm seeded fresh each game.
Stack: React 18, TypeScript, Vite, Tailwind, Framer Motion, Howler.js, Zustand.
The source is on GitHub: https://github.com/GRRRillaNinja/modern-dnd-labyrinth
Play it at https://delvedash.com — no account, no install.
Happy to go into more detail on any of the design or implementation decisions.