npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

miaoda-game-grid-core

v0.2.0

Published

Engine-agnostic grid primitives: typed grid model, direction math, A* pathfinding. No rendering, no engine dependency. Backs match-3, tactics, tower-defense, roguelike and tank-battle style games.

Readme

miaoda-game-grid-core

Use this package when your game state is arranged on rectangular tiles: tactics, tower defense, roguelikes, match-3, Sokoban, minesweeper, tank games, or board-game movement. It provides a typed grid, direction helpers, pathfinding, movement ranges, distance fields, and area shapes without depending on a game engine.

Choose an engine adapter only for presentation and input:

  • miaoda-game-grid-cocos places Cocos nodes and converts taps to tiles.
  • miaoda-game-grid-phaser places Phaser Game Objects, converts pointers, and moves characters along paths.
  • miaoda-game-match3-core builds match-3 rules on top of this package.

Install

pnpm add miaoda-game-grid-core

Create and query a grid

import { Dir, Grid } from 'miaoda-game-grid-core';

// 0 = open, 1 = wall
const map = new Grid<number>({ width: 13, height: 13, empty: 0 });
map.set(6, 6, 1);

map.get(6, 6);              // 1
map.isEmpty(6, 6);          // false
map.neighbor(6, 6, Dir.Up); // { x: 6, y: 5 }
map.neighbors(0, 0);        // two in-bounds orthogonal neighbors

Grid<T> stores cells in row-major order. Coordinates use { x, y }, with (0, 0) at the top-left, X increasing right, and Y increasing down. Set wrap: true for a grid whose opposite edges connect. On a non-wrapping grid, out-of-bounds reads return empty and out-of-bounds writes are ignored.

Find a path

import { findPath } from 'miaoda-game-grid-core';

const path = findPath(
  map,
  { x: 0, y: 0 },
  { x: 12, y: 12 },
  {
    isBlocked: (x, y) => map.get(x, y) === 1,
    cost: (x, y) => terrainCost(x, y),
  },
);

if (path && path.length > 1) {
  moveUnitToward(path[1]);
}

findPath returns the complete path including the start and goal, or null when the goal cannot be reached. The start is treated as passable because the moving unit may already occupy it; the goal must be passable.

Movement is orthogonal by default. Set allowDiagonal: true to use eight directions. Diagonal paths prevent corner cutting by default; set noCornerCutting: false only when units are allowed to pass between touching obstacles. Every movement cost must be at least 1.

Show movement and attack ranges

import { filledRing, findArea } from 'miaoda-game-grid-core';

const reachable = findArea(map, { x: 3, y: 3 }, 4, {
  isBlocked: (x, y) => map.get(x, y) === 1,
  cost: (x, y) => terrainCost(x, y),
});

for (const { x, y, cost } of reachable) {
  showMoveHighlight(x, y, cost);
}

const blast = filledRing({ x: 10, y: 10 }, 2, {
  metric: 'manhattan',
  filter: (x, y) => map.contains(x, y),
});

findArea uses the same blocked, diagonal, and movement-cost model as pathfinding. Each returned tile includes its accumulated cost. Use ring for only the boundary at a radius and filledRing for every tile within it. The manhattan metric produces a diamond; chebyshev produces a square.

Route many units to shared goals

For tower-defense waves or crowds moving toward the same exits, build one distance field and reuse it instead of finding a separate path for every unit.

import { buildDistanceField } from 'miaoda-game-grid-core';

const field = buildDistanceField(
  map,
  [{ x: 31, y: 8 }, { x: 31, y: 9 }],
  { isBlocked: (x, y) => map.get(x, y) === 1 },
);

const nextTile = field.nextStep(enemy.tileX, enemy.tileY);
const remainingCost = field.distanceAt(enemy.tileX, enemy.tileY);

if (!field.canReach(spawn.x, spawn.y)) {
  rejectTowerPlacement();
}

Rebuild the field whenever obstacles, exits, or terrain costs change. pathFrom(start) returns a complete path when a renderer or movement controller needs one.

Public API guide

| API | Use it for | | --- | --- | | Grid<T> | Cell storage, bounds, wrapping, neighbors, swaps, fills, and iteration | | Dir, DIR_DELTA, direction sets | Shared four-way and eight-way direction conventions | | findPath | One shortest path to one goal | | findArea | All reachable tiles within a movement budget | | buildDistanceField | Many units routing toward one or more shared goals | | ring, filledRing | Attack areas, auras, spawn bands, and range previews | | MinHeap<T> | A reusable priority queue for custom searches or event queues |

The package owns grid data and math only. Your game remains responsible for turns, collision response, rendering, animation, and deciding when a route should be recalculated.