diff --git a/README.md b/README.md index a995805..372fcbf 100644 --- a/README.md +++ b/README.md @@ -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 ( -
-

VIMazing Maze

-
- {gameStatus === "game-won" &&

🎉 You escaped!

} -
+
+
+ +

Status: {gameStatus}

+
+ ); +} +``` + +### 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 ( +
+
+ + + {(gameStatus === 'started' || gameStatus === 'has-key') && ( +
+

Time: {(timeValue / 1000).toFixed(1)}s

+

Efficiency: {efficiency}%

+

Keystrokes: {keystrokes} / {optimalSteps}

+
+ )} + + {gameStatus === 'game-won' &&

🎉 Final Score: {finalScore}

} +
); } ``` > **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 | -| `` | 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 | +| `` | 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 +``` ---