headless-game-grid
v1.0.1
Published
Portable, framework-agnostic, ready-to-use headless 2D grid engine for turn-based games and puzzles.
Maintainers
Readme
Headless Game Grid
A lightweight, high-performance 2D grid engine for turn-based games, puzzle UIs, and board-style interactions. Specifically designed for TypeScript with a thin, optional React layer.
Key Features
- Portable State: Headless logic for grids, cells, players, and games.
- Transactional History: Built-in undo/redo with snapshot-based state management.
- Subgrid System: Efficiently manage rows, columns, diagonals, and custom overlapping regions.
- Validation Layer: Register pre-action and state-transition validators.
- Event System: Reactive subscriptions for both Grid and Game instances.
- Logic Utilities: A* and BFS pathfinding, bitmask encoding, and sequential navigation.
- React Components: High-performance
<Grid />with built-in drag-and-drop and selection.
Installation
npm install headless-game-gridCore Abstractions
1. Grid & Cells
The foundation of the engine. Grids manage a 2D matrix of typed cells.
import { AbxGrid, AbxCell } from 'headless-game-grid';
const grid = new AbxGrid({
width: 10,
height: 10,
cellFactory: (row, col) => new AbxCell({
coordinate: { row, col },
initialState: { terrain: 'grass', unit: null }
})
});
// History & State
grid.setCellState({ row: 0, col: 0 }, { terrain: 'water', unit: null });
grid.undo(); // Back to grass
grid.redo(); // Back to water2. Game Mechanics & Actions
Orchestrate players and turn-based logic through Actions.
import { AbxGame, AbxPlayer, AbxAction } from 'headless-game-grid';
const players = [
new AbxPlayer({ id: 'p1', name: 'Alice', symbol: 'W' }),
new AbxPlayer({ id: 'p2', name: 'Bob', symbol: 'B' })
];
const game = new AbxGame({
grid,
players,
initialState: 'setup'
});
// Defining an Action
const moveUnit = new AbxAction({
id: 'action-001',
name: 'MoveUnit',
player: players[0],
payload: { from: { row: 0, col: 0 }, to: { row: 0, col: 1 } },
execute: (game, payload) => {
// Logic goes here
return true;
}
});
game.makeAction(moveUnit);3. Validation Layer
Strictly control how the game state evolves.
// Action Validation
game.registerActionValidator('MoveUnit', (g, action) => {
if (g.state !== 'playing') return { isValid: false, message: 'Game not started' };
return { isValid: true };
});
// State Transition Validation
game.setStateValidator((from, to) => {
if (from === 'setup' && to === 'ended') return { isValid: false, message: 'Cannot skip playing' };
return { isValid: true };
});React Integration
1. The <Grid /> Component
A flexible, high-performance renderer for the grid.
import { Grid } from 'headless-game-grid';
function MyGame() {
const { grid } = useGrid(myGridInstance);
return (
<Grid
rows={grid.height}
cols={grid.width}
data={grid.cells.map(row => row.map(c => c.state))}
renderCell={({ value, isActive, isDragging }) => (
<div style={{ background: isDragging ? 'blue' : 'white' }}>
{value.terrain}
</div>
)}
draggable={true}
onCellDrop={(from, to) => handleMove(from, to)}
canDrop={(from, to) => isValidMove(from, to)}
/>
);
}2. Reactive Hooks
Keep your UI in sync with the headless engine.
import { useGrid, useGame } from 'headless-game-grid';
function GameDashboard({ gameInstance }) {
const { state, currentPlayer } = useGame(gameInstance);
const { history, canUndo, undo } = useGrid(gameInstance.grid);
return (
<div>
<p>Current Turn: {currentPlayer.name}</p>
<button onClick={undo} disabled={!canUndo}>Undo</button>
</div>
);
}Utilities
Pathfinding (A* & BFS)
Powerful pathfinding on your grid instance.
import { AbxPathfinder } from 'headless-game-grid';
const pathfinder = new AbxPathfinder(grid);
const path = pathfinder.findPath(
{ row: 0, col: 0 },
{ row: 5, col: 5 },
{
isWalkable: (cell) => cell.state.terrain !== 'mountain',
includeDiagonals: true
}
);Navigation & Neighbors
Simplified coordinate math.
// Get neighbors (orthogonal by default)
const neighbors = grid.getNeighbors({ row: 1, col: 1 }, { includeDiagonals: true });
// Sequential navigation
import { getNextCell } from 'headless-game-grid';
const next = getNextCell(path, current, 'forward');Persistence
Save and load your entire game state with full type safety via JSON.
// Save
const savedState = game.toJSON();
localStorage.setItem('my-game', JSON.stringify(savedState));
// Load
const loadedData = JSON.parse(localStorage.getItem('my-game'));
game.fromJSON(loadedData);License
MIT
