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

@sacckey/mahjong

v0.4.0

Published

Four-player riichi mahjong scoring and hand analysis for JavaScript and TypeScript, powered by MoonBit.

Readme

@sacckey/mahjong

MoonBitで実装した四人リーチ麻雀の点数計算・牌姿解析ライブラリを、JavaScript・TypeScriptから利用するためのnpmパッケージです。

インストール

npm install @sacckey/mahjong

使い方

import { API_VERSION, createCalculator } from "@sacckey/mahjong";

const tile = (kind, red = false) => ({ kind, red });
const calculator = await createCalculator();
const result = calculator.score({
  apiVersion: API_VERSION,
  hand: {
    concealedTiles: [
      "1m", "2m", "3m", "4m", "5m", "6m", "1p",
      "2p", "3p", "7s", "8s", "5p", "5p",
    ].map((kind) => tile(kind)),
    winningTile: tile("9s"),
    melds: [],
    doraIndicators: [],
    uraDoraIndicators: [],
  },
  context: {
    winMethod: "ron",
    seatWind: "south",
    roundWind: "east",
    riichi: "riichi",
    ippatsu: false,
    rinshan: false,
    chankan: false,
    haitei: false,
    houtei: false,
    tenhou: false,
    chiihou: false,
    honba: 0,
    riichiSticks: 0,
  },
  rules: "standard",
});

console.log(result.han, result.fu, result.payment);

createCalculator()はWasm-GCを優先し、利用できない環境ではJavaScript生成物へ自動的に切り替えます。使用中の実装はcalculator.backendで確認できます。

Cloudflare Workersなどのworkerd環境でJavaScript生成物を使用する場合は、createCalculator({ backend: "javascript" })を指定できます。この場合、WasmファイルのURL解決や取得は行いません。

シャンテン数・聴牌・待ち牌

concealedTilesには、現在の副露前の手牌をすべて渡します。点数計算の入力とは異なり、和了牌を分離しません。副露がなければmeldsは省略できます。

const input = {
  concealedTiles: [
    "1m", "2m", "3m", "4m", "5m", "6m", "1p",
    "2p", "3p", "7s", "8s", "5p", "5p",
  ].map((kind) => tile(kind)),
};

const shanten = calculator.calculateShanten(input);
const tenpai = calculator.isTenpai(input);
const waits = calculator.waitingTiles(input);

console.log(shanten.minimum); // 0
console.log(tenpai); // true
console.log(waits); // ["6s", "9s"]

waitingTiles()isTenpai()は構造上13枚の入力だけを受け付けます。役、フリテン、河や山に残る枚数は考慮しません。

同一牌を5枚以上使うゲーム

通常は同一牌種を4枚までに制限します。牌を追加できるゲームでは、第2引数にmaxCopiesPerKind: nullを渡すと、この物理枚数制限だけを解除できます。構造上の手牌枚数、和了形、役、符などのルールは変わりません。

const tileCopyRules = { maxCopiesPerKind: null };

const result = calculator.score(request, tileCopyRules);
const shanten = calculator.calculateShanten(input, tileCopyRules);
const tenpai = calculator.isTenpai(input, tileCopyRules);
const waits = calculator.waitingTiles(input, tileCopyRules);

たとえば同じ牌が5枚ある場合も、通常の面子分解によって雀頭2枚と刻子3枚として利用できます。正の整数を指定すれば、その枚数を上限にできます。立直後暗槓判定は通常の四人麻雀専用で、この設定の対象外です。

立直後の暗槓

立直時に固定された13枚と、その後にツモった4枚目の牌を渡します。すべての和了形で元の3枚が独立した暗刻になる場合だけtrueを返します。

const input = {
  concealedTiles: [
    "5m", "5m", "5m", "1p", "2p", "3p", "4p",
    "5p", "6p", "7s", "8s", "east", "east",
  ].map((kind) => tile(kind)),
};

const allowed = calculator.canClosedKanAfterRiichi(input, tile("5m"));
console.log(allowed); // true

エラー

score()は入力や和了判定のエラー時にMahjongErrorをthrowします。codeを画面表示用の文言に対応付けてください。

import { MahjongError } from "@sacckey/mahjong";

try {
  const result = calculator.score(request);
} catch (error) {
  if (error instanceof MahjongError) {
    console.error(error.code, error.message, error.details);
  }
}

throwせずに成功・失敗のエンベロープを受け取る場合はscoreResponse()またはanalysisResponse()、JSON文字列の低レベル境界を直接使う場合はscoreJson()またはanalysisJson()を使用します。

JSON API v1の詳細はGitHubの仕様を参照してください。

開発

リポジトリのルートでMoonBitとNode.jsを利用できるようにし、次を実行します。ビルド時にMoonBitのJavaScript・Wasm-GC生成物がdistへコピーされます。

cd packages/npm
npm test
npm pack --dry-run