@vimazing/vim-maze
Lightweight, typed React hooks and utilities for building interactive maze games with VIM-inspired controls.
Part of the VIMazing project - GitHub.
Contents
- Features
- Installation
- Quick Start
- API Overview
- Unified Architecture
- Example App
- Documentation
- Build & Release
- License
- Acknowledgements
Features
- Unified API – Single entry point
useGame()for complete game setup with composition hooks for granular control - Fully Typed – Comprehensive TypeScript support with organized, module-specific type definitions
- VIM-style Navigation – Full
hjklmotion support with counts, anchors (^,$,g,G), and repeat (.) - Modular Hooks – Independent, reusable hooks:
useBoard,useCursor,useScore,useGameStatus - Scoring System – Built-in timer, efficiency tracking, keystroke logging, and optimal path calculation
- Platform Integration – Optional platform hook for analytics, custom bindings, and platform-specific logic
- Tree-shakeable Exports – Import only what you need to keep bundles lean
- CSS Included – Pre-styled maze rendering with optional coordinate display
Installation
Using npm:
npm install @vimazing/vim-maze
Or with bun:
bun add @vimazing/vim-maze
Quick Start
Minimal Setup
import { useGame } from "@vimazing/vim-maze";
import "@vimazing/vim-maze/game.css";
function MazeGame() {
const gameManager = useGame({ cols: 32, rows: 24 });
const { containerRef, gameStatus, startGame } = gameManager;
return (
<div>
<div ref={containerRef} />
<button onClick={startGame}>Start Game</button>
<p>Status: {gameStatus}</p>
</div>
);
}
With Scoring and Full Features
import { useGame } from "@vimazing/vim-maze";
import "@vimazing/vim-maze/game.css";
function MazeGame() {
const gameManager = useGame({ cols: 32, rows: 24 });
const { containerRef, gameStatus, startGame, scoreManager } = gameManager;
const { timeValue, efficiency, keystrokes, optimalSteps, finalScore } = scoreManager;
return (
<div>
<div ref={containerRef} />
<button onClick={startGame}>Start</button>
{(gameStatus === 'started' || gameStatus === 'has-key') && (
<div>
<p>Time: {(timeValue / 1000).toFixed(1)}s</p>
<p>Efficiency: {efficiency}%</p>
<p>Keystrokes: {keystrokes} / {optimalSteps}</p>
</div>
)}
{gameStatus === 'game-won' && <p>🎉 Final Score: {finalScore}</p>}
</div>
);
}
Note: You must manually import the CSS file. The package exports styles but does not auto-import them, giving you control over when and how styles are loaded.
Default Controls
| Key | Action |
|---|---|
Space |
Start game / Restart |
h / j / k / l |
Move left / down / up / right |
^ |
Move to row start |
$ |
Move to row end |
g |
Move to maze top |
G |
Move to maze bottom |
. |
Repeat last motion |
<count><motion> |
Move with count (e.g., 5j = down 5) |
q |
Quit game |
API Overview
Main Entry Point: useGame()
Initialize a complete game with a single call:
const gameManager = useGame(
{ cols: 32, rows: 24 }, // GameOptions
platformHook // Optional: function for platform integration
);
Returns: GameManager
containerRef– DOM ref for maze renderinggameStatus– Current game statestartGame()/togglePause()/quitGame()– Game lifecyclecursor– Player movement managerhero– Player position and interactionsscoreManager– Scoring metricsrenderer– Hero rendering managerkeyLog– Keystroke history
Core Hooks
| Hook | Purpose |
|---|---|
useBoard(cols, rows) |
Generates maze, manages board data, handles DOM rendering |
useCursor(board, gameStatus) |
Input handling, vim motion processing, movement commands |
useScore({ board, hero, gameStatusManager, keyManager }) |
Timer, efficiency, keystroke tracking, score calculation |
useGameStatus() |
Game state machine (waiting → started → has-key → game-won) |
Utility Functions & Classes
import { MazeGenerator, MazeRenderer } from "@vimazing/vim-maze";
// Procedural maze generation
const generator = new MazeGenerator(32, 24);
generator.placeKey();
const maze = generator.maze;
// DOM rendering for mazes
const renderer = new MazeRenderer(generator.getData(), true);
renderer.display(containerElement);
Types
All types are organized by module:
import type {
GameManager,
GameStatus,
GameOptions,
Coord,
CursorManager,
HeroManager,
ScoreManager,
BoardManager,
} from "@vimazing/vim-maze";
// Or import directly from module types:
import type { CursorManager } from "@vimazing/vim-maze/useCursor";
import type { BoardManager } from "@vimazing/vim-maze/useBoard";
Unified Architecture
The library uses a composable hook architecture with modular organization:
useGame()
├─ useBoard() → maze generation & rendering
├─ useCursor() → vim motions & input handling
│ ├─ useHero() → player position & interactions
│ └─ useVimMotions() → vim keybinding processor
├─ useGameStatus() → game lifecycle state
└─ useScore() → timing & scoring metrics
Type Organization
useBoard/types.ts– Coord, MazeCell, MazeData, BoardManageruseCursor/types.ts– CursorManager, HeroManager, CursorMode, VimMotionSystemuseGameStatus/types.ts– GameStatus, GameStatusManageruseScore/types.ts– ScoreManager, TimerManagertypes.ts– Re-exports all + useGame-specific types (GameOptions, GameManager, KeyLogEntry)
Each module's types.ts keeps types colocated with their logic, making the architecture easy to understand and extend.
Platform Integration
Integrate with your platform using an optional platform hook:
function platformHook(gameManager: GameManager) {
// Track analytics
console.log('Game initialized');
// Add custom key handlers
window.addEventListener('keydown', (e) => {
if (e.key === 'p') gameManager.togglePause();
});
// Monitor game state
const interval = setInterval(() => {
if (gameManager.gameStatus === 'game-won') {
console.log('Victory!', gameManager.scoreManager.finalScore);
clearInterval(interval);
}
}, 100);
}
const gameManager = useGame({ cols: 32, rows: 24 }, platformHook);
Example App
A complete demo application lives under example/ showcasing the full API:
cd example
bun install
bun run dev
Open http://localhost:5173 to play the game with scoring, efficiency tracking, and status display.
During local development, the Vite config aliases @vimazing/vim-maze to source files for hot reloading. When publishing, run the build first so TypeScript declarations are generated.
Documentation
For comprehensive documentation of all hooks, types, and patterns, see UNIFIED_API.md.
Key sections:
- Architecture Overview & Core Composition
- Complete Type System Reference
- Detailed Hook Documentation
- Implementation Patterns & Examples
- Integration Guide for New Games
Build & Release
Build the distributable bundle and type declarations:
bun run build
This writes JavaScript, type definitions, and styles to dist/. The prepublishOnly hook runs this automatically before publishing.
To publish a new version:
npm version patch # or minor / major
npm publish
License
MIT © André Padez
Acknowledgements
Inspired by The Art of Web – Random Maze Generator and extended for the VIMazing platform.
