updated README
This commit is contained in:
@@ -9,12 +9,14 @@ Part of the [VIMazing](https://vimazing.com) project - [GitHub](https://github.c
|
||||
---
|
||||
|
||||
## Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Core Hooks](#core-hooks)
|
||||
- [Utilities](#utilities)
|
||||
- [API Overview](#api-overview)
|
||||
- [Unified Architecture](#unified-architecture)
|
||||
- [Example App](#example-app)
|
||||
- [Documentation](#documentation)
|
||||
- [Build & Release](#build--release)
|
||||
- [License](#license)
|
||||
- [Acknowledgements](#acknowledgements)
|
||||
@@ -22,11 +24,15 @@ Part of the [VIMazing](https://vimazing.com) project - [GitHub](https://github.c
|
||||
---
|
||||
|
||||
## Features
|
||||
- **Drop-in hooks** for maze generation, player control, scoring, and key logging.
|
||||
- **Typed API** with generated declaration files for editor IntelliSense.
|
||||
- **VIM-style navigation** (`h`, `j`, `k`, `l`, counts, `i` to start, etc.).
|
||||
- **Composable architecture** – bring your own rendering and platform-specific bindings.
|
||||
- **Tree-shakeable exports** to keep bundles lean.
|
||||
|
||||
- **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 `hjkl` motion 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
|
||||
|
||||
---
|
||||
|
||||
@@ -48,76 +54,199 @@ bun add @vimazing/vim-maze
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Minimal Setup
|
||||
|
||||
```tsx
|
||||
import { useEffect } from "react";
|
||||
import { useGame } from "@vimazing/vim-maze";
|
||||
import "@vimazing/vim-maze/game.css";
|
||||
|
||||
export function MazeGame() {
|
||||
const { containerRef, gameStatus, startGame } = useGame(20, 16);
|
||||
|
||||
useEffect(() => {
|
||||
startGame();
|
||||
}, [startGame]);
|
||||
function MazeGame() {
|
||||
const gameManager = useGame({ cols: 32, rows: 24 });
|
||||
const { containerRef, gameStatus, startGame } = gameManager;
|
||||
|
||||
return (
|
||||
<section className="mx-auto w-fit space-y-4">
|
||||
<h1 className="text-2xl font-bold">VIMazing Maze</h1>
|
||||
<div ref={containerRef} className="relative" />
|
||||
{gameStatus === "game-won" && <p>🎉 You escaped!</p>}
|
||||
</section>
|
||||
<div>
|
||||
<div ref={containerRef} />
|
||||
<button onClick={startGame}>Start Game</button>
|
||||
<p>Status: {gameStatus}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### With Scoring and Full Features
|
||||
|
||||
```tsx
|
||||
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:
|
||||
### Default Controls
|
||||
|
||||
| Key | Action |
|
||||
| ------------------- | --------------- |
|
||||
| `i` | Start game |
|
||||
| `h` / `j` / `k` / `l` | Move player |
|
||||
| `<count><motion>` | Move with count |
|
||||
| `q` | Quit game |
|
||||
| `Esc` | Cancel / pause |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
## Core Hooks
|
||||
## API Overview
|
||||
|
||||
| Hook | Description |
|
||||
| ---- | ----------- |
|
||||
| `useGame(cols, rows, platformHook?)` | One-stop hook that wires maze generation, rendering, player state, scoring, and keyboard bindings. Returns refs, status helpers, and managers you can compose with your UI. |
|
||||
| `useMaze()` | Generates maze paths, keeps references to the current maze instance, and exposes helpers to reset or rebuild. |
|
||||
| `useKeyBindings()` | Provides VIM-style key bindings, including repeat counts and movement helpers. |
|
||||
| `useScore()` | Tracks timers, optimal steps, efficiency, and final score calculation. |
|
||||
### Main Entry Point: `useGame()`
|
||||
|
||||
Each hook is exported individually, so you can cherry-pick only what you need:
|
||||
Initialize a complete game with a single call:
|
||||
|
||||
```tsx
|
||||
const gameManager = useGame(
|
||||
{ cols: 32, rows: 24 }, // GameOptions
|
||||
platformHook // Optional: function for platform integration
|
||||
);
|
||||
```
|
||||
|
||||
**Returns: `GameManager`**
|
||||
- `containerRef` – DOM ref for maze rendering
|
||||
- `gameStatus` – Current game state
|
||||
- `startGame()` / `togglePause()` / `quitGame()` – Game lifecycle
|
||||
- `cursor` – Player movement manager
|
||||
- `hero` – Player position and interactions
|
||||
- `scoreManager` – Scoring metrics
|
||||
- `renderer` – Hero rendering manager
|
||||
- `keyLog` – 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
|
||||
|
||||
```ts
|
||||
import { useScore } from "@vimazing/vim-maze/useScore";
|
||||
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:
|
||||
|
||||
```ts
|
||||
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";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utilities
|
||||
## Unified Architecture
|
||||
|
||||
Besides the hooks, the library exports:
|
||||
The library uses a **composable hook architecture** with modular organization:
|
||||
|
||||
- `MazeGenerator` – procedural maze generation class.
|
||||
- `MazeRenderer` – canvas renderer tailored for the generated mazes.
|
||||
- `types` – shared TypeScript types such as `GameStatus`, `MazeCell`, and `KeyLogEntry`.
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
```ts
|
||||
import { MazeGenerator, GameStatus } from "@vimazing/vim-maze";
|
||||
### Type Organization
|
||||
|
||||
- **`useBoard/types.ts`** – Coord, MazeCell, MazeData, BoardManager
|
||||
- **`useCursor/types.ts`** – CursorManager, HeroManager, CursorMode, VimMotionSystem
|
||||
- **`useGameStatus/types.ts`** – GameStatus, GameStatusManager
|
||||
- **`useScore/types.ts`** – ScoreManager, TimerManager
|
||||
- **`types.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:
|
||||
|
||||
```tsx
|
||||
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 demo application lives under `example/` and consumes the package directly.
|
||||
A complete demo application lives under `example/` showcasing the full API:
|
||||
|
||||
```bash
|
||||
cd example
|
||||
@@ -125,7 +254,22 @@ bun install
|
||||
bun run dev
|
||||
```
|
||||
|
||||
During local development the Vite config aliases `@vimazing/vim-maze` to the source folder so you can iterate without rebuilding. When publishing, run the build first (see below) so editors consume the generated declarations in `dist/`.
|
||||
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](./UNIFIED_API.md)**.
|
||||
|
||||
Key sections:
|
||||
- Architecture Overview & Core Composition
|
||||
- Complete Type System Reference
|
||||
- Detailed Hook Documentation
|
||||
- Implementation Patterns & Examples
|
||||
- Integration Guide for New Games
|
||||
|
||||
---
|
||||
|
||||
@@ -137,7 +281,14 @@ Build the distributable bundle and type declarations:
|
||||
bun run build
|
||||
```
|
||||
|
||||
This writes JavaScript, type definitions, and styles to `dist/`. The `prepublishOnly` hook reuses the same command to guarantee fresh artifacts before publishing.
|
||||
This writes JavaScript, type definitions, and styles to `dist/`. The `prepublishOnly` hook runs this automatically before publishing.
|
||||
|
||||
To publish a new version:
|
||||
|
||||
```bash
|
||||
npm version patch # or minor / major
|
||||
npm publish
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user