@sacckey/mahjong
v0.4.0
Published
Four-player riichi mahjong scoring and hand analysis for JavaScript and TypeScript, powered by MoonBit.
Maintainers
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