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-match3-core

v0.3.0

Published

Engine-agnostic rectangular and hex match-3 logic with line matching, flood-fill groups and rectangular gravity. No rendering or engine dependency.

Downloads

729

Readme

miaoda-game-match3-core

Use this engine-independent package for match-3 and blast-puzzle board logic. It provides a symbol grid, line matching, connected groups, match-shape classification, and gravity/refill move data; your engine animates and renders the result.

Install

pnpm add miaoda-game-match3-core

Swap, match, and collapse

import { Board, Matcher, collapse, fillMatchFree } from 'miaoda-game-match3-core';

const board = new Board({ width: 8, height: 8 });
board.fill(() => Math.floor(Math.random() * 6));
const matcher = new Matcher(board);

board.swap({ x: 2, y: 3 }, { x: 3, y: 3 });
const matches = matcher.matchAll(3);
for (const match of matches) {
  for (const cell of match.tiles) board.set(cell.x, cell.y, null);
}
const moves = collapse(board, () => Math.floor(Math.random() * 6));

Symbol is number | string | null; null is an empty cell. collapse returns falls for existing tiles and spawns for new tiles. Animate clears first, then falls and spawns. SpawnMove.order is the refill order within its column.

For a fresh board with no starting runs, keep randomness in the host and let the core enforce the retry budget and matcher semantics:

const { attempts } = fillMatchFree(
  board,
  matcher,
  () => Math.floor(random() * 6),
  { pattern: 3, maxAttempts: 1000, requireLegalSwap: true },
);

fillMatchFree performs complete fills and throws explicitly when the attempt budget is exhausted. It does not seed or own the RNG, and it uses the supplied Matcher, including its directions, masks, and wildcard rules. requireLegalSwap is opt-in so existing seeded generation keeps the same RNG consumption and board distribution.

Match styles

  • matchAll(length) scans horizontal/vertical runs by default; directions: 8 enables diagonals.
  • matchAtDir and anyMatch handle directional or fast checks.
  • findLegalSwaps(pattern) returns adjacent swaps that create a match; hasLegalSwap(pattern) is the early-exit dead-board check. Both require a stable board and restore every tested swap before returning.
  • group(x, y) flood-fills an orthogonally connected same-symbol blob for blast games.
  • classifyMatches merges overlapping runs into line3, line4, line5, L, T, cross, or blob groups with a pivot for special items.
  • Match patterns can be a length or a specific symbol pattern such as [1, 2, 1].

The line-match direction setting does not change group: blast connectivity is always four-way.

Hex boards

Use HexMatcher with the explicit offset layout used to store the hex board:

import { Board, HexMatcher } from 'miaoda-game-match3-core';

const board = new Board({ width: 9, height: 9 });
const matcher = new HexMatcher(board, 'odd-r', -1); // -1 is an optional wildcard
const lines = matcher.matchAll(3); // E, NE, NW axes; opposite directions are not rescanned
const connected = matcher.group(4, 4); // six-neighbor flood fill

HexMatchResult.axis is 0 | 1 | 2 in the stable axial E/NE/NW order. All four common offset layouts are supported. Wrapped hex boards are rejected because rectangular x/y modulo wrapping does not define a correct toroidal hex map.

Do not pass hex results to classifyMatches: its L/T/cross vocabulary is intentionally rectangular. Hex gravity is also game-specific; provide ordered lanes and spawn boundaries in game rules rather than calling rectangular collapse.

Rendering and game rules

Read cells through board.get(x, y) and map each symbol to your tile asset. Board also exposes set, swap, inBounds, contains, fill, and grid dimensions. The core can enumerate legal swaps but does not choose one, score matches, create power-ups, play animations, or provide a random seed; inject an authoritative random callback into collapse and your game rules.

Public API

Board, Matcher, HexMatcher, fillMatchFree, collapse, classifyMatches, Symbol, MatchResult, LegalSwap, HexMatchResult, MatchGroup, CollapseResult, FallMove, and SpawnMove are exported. Compose with a Cocos, Phaser, or React adapter for input and presentation.