@pai-forge/riichi-mahjong 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -807,18 +807,17 @@ const checkIipeikou = (hand) => {
807
807
  if (shuntsuList.length < 2) {
808
808
  return false;
809
809
  }
810
- for (let i = 0; i < shuntsuList.length; i++) {
811
- for (let j = i + 1; j < shuntsuList.length; j++) {
812
- const shuntsuA = shuntsuList[i];
813
- const shuntsuB = shuntsuList[j];
814
- if (!shuntsuA || !shuntsuB) continue;
815
- const isSame = shuntsuA.hais[0] === shuntsuB.hais[0] && shuntsuA.hais[1] === shuntsuB.hais[1] && shuntsuA.hais[2] === shuntsuB.hais[2];
816
- if (isSame) {
817
- return true;
818
- }
819
- }
810
+ const shuntsuCounts = /* @__PURE__ */ new Map();
811
+ for (const shuntsu of shuntsuList) {
812
+ const key = shuntsu.hais[0];
813
+ const currentCount = shuntsuCounts.get(key) ?? 0;
814
+ shuntsuCounts.set(key, currentCount + 1);
820
815
  }
821
- return false;
816
+ let pairCount = 0;
817
+ for (const count of shuntsuCounts.values()) {
818
+ pairCount += Math.floor(count / 2);
819
+ }
820
+ return pairCount === 1;
822
821
  };
823
822
  const iipeikouDefinition = createYakuDefinition(
824
823
  IIPEIKO_YAKU,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/types.ts","../src/errors.ts","../src/utils/assertions.ts","../src/core/hai.ts","../src/core/tehai.ts","../src/core/mentsu.ts","../src/core/dora.ts","../src/core/machi.ts","../src/features/shanten/logic/chiitoitsu.ts","../src/features/shanten/logic/kokushi.ts","../src/features/shanten/logic/mentsu-te.ts","../src/features/shanten/index.ts","../src/features/machi/index.ts","../src/features/yaku/lib/structures/mentsu-te.ts","../src/features/yaku/lib/structures/chiitoitsu.ts","../src/features/yaku/lib/structures/kokushi.ts","../src/features/yaku/lib/structures/index.ts","../src/features/yaku/utils.ts","../src/features/yaku/factory.ts","../src/features/yaku/lib/definitions/tanyao.ts","../src/features/yaku/lib/definitions/pinfu.ts","../src/features/yaku/lib/definitions/iipeikou.ts","../src/features/yaku/lib/definitions/ryanpeikou.ts","../src/features/yaku/lib/definitions/sanankou.ts","../src/features/yaku/lib/definitions/suuankou.ts","../src/features/yaku/lib/definitions/sankantsu.ts","../src/features/yaku/lib/definitions/suukantsu.ts","../src/features/yaku/lib/definitions/toitoi.ts","../src/features/yaku/lib/definitions/chiitoitsu.ts","../src/features/yaku/lib/definitions/honchan.ts","../src/features/yaku/lib/definitions/junchan.ts","../src/features/yaku/lib/definitions/honroutou.ts","../src/features/yaku/lib/definitions/chinroutou.ts","../src/features/yaku/lib/definitions/shousangen.ts","../src/features/yaku/lib/definitions/daisangen.ts","../src/features/yaku/lib/definitions/tsuuiisou.ts","../src/features/yaku/lib/definitions/ryuuiisou.ts","../src/features/yaku/lib/definitions/shousuushii.ts","../src/features/yaku/lib/definitions/daisuushii.ts","../src/features/yaku/lib/definitions/chuuren-poutou.ts","../src/features/yaku/lib/definitions/kokushi.ts","../src/features/yaku/lib/definitions/sanshoku-doujun.ts","../src/features/yaku/lib/definitions/sanshoku-doukou.ts","../src/features/yaku/lib/definitions/ikkitsuukan.ts","../src/features/yaku/lib/definitions/honitsu.ts","../src/features/yaku/lib/definitions/chinitsu.ts","../src/features/yaku/lib/definitions/yakuhai.ts","../src/features/yaku/lib/definitions/menzen-tsumo.ts","../src/features/yaku/lib/definitions/index.ts","../src/features/yaku/index.ts","../src/features/parser/mspz.ts","../src/features/parser/index.ts","../src/features/score/lib/fu/constants.ts","../src/features/score/lib/fu/lib/chiitoitsu.ts","../src/features/score/lib/fu/lib/kokushi.ts","../src/features/score/lib/fu/lib/mentsu.ts","../src/features/score/lib/fu/index.ts","../src/features/score/constants.ts","../src/features/score/types.ts","../src/features/score/index.ts"],"sourcesContent":["/**\n * 牌種IDの定数セット\n *\n * - 0-8: 萬子 (ManZu)\n * - 9-17: 筒子 (PinZu)\n * - 18-26: 索子 (SouZu)\n * - 27-33: 字牌 (JiHai)\n */\nexport const HAI_KIND_IDS = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,\n] as const;\n\n// Utility for fixed-length tuple\ntype TupleOf<\n T,\n N extends number,\n R extends unknown[] = [],\n> = R[\"length\"] extends N ? R : TupleOf<T, N, [T, ...R]>;\n\n/**\n * 牌の種類の総数 (34)\n */\nexport type HaiKindCount = (typeof HAI_KIND_IDS)[\"length\"];\n\n/**\n * 特定の種類の牌の所持数 (0-4枚)\n * 各種類の牌は最大4枚まで存在します。\n */\nexport type HaiQuantity = 0 | 1 | 2 | 3 | 4;\n\n/**\n * 全34種類の牌の所持数分布配列。\n * インデックスは HaiKindId (0-33) に対応します。\n * 各要素はその種類の牌の所持数 (0-4) です。\n *\n * @example\n * // \"113m 1z\" (MPSZ形式) に対応\n * const dist: HaiKindDistribution = [\n * 2, 0, 1, 0, 0, 0, 0, 0, 0, // 萬子 (1m x2, 3m x1)\n * 0, 0, 0, 0, 0, 0, 0, 0, 0, // 筒子\n * 0, 0, 0, 0, 0, 0, 0, 0, 0, // 索子\n * 1, 0, 0, 0, 0, 0, 0 // 字牌 (1z x1)\n * ];\n */\nexport type HaiKindDistribution = Readonly<TupleOf<HaiQuantity, HaiKindCount>>;\n\n/**\n * 物理的な牌の一意な識別子 (HaiId)\n *\n * 麻雀セットに含まれる136枚の牌それぞれに一意のIDが割り当てられます (0-135)。\n *\n * - 0-35: 萬子\n * - 36-71: 筒子\n * - 72-107: 索子\n * - 108-135: 字牌\n *\n * Branded Type を使用して通常の number と区別します。\n */\nexport type HaiId = number & { readonly __brand: \"HaiId\" };\n\n/**\n * 牌種ID (HaiKindId)\n *\n * 麻雀の34種類の牌を一意に識別するID。\n */\nexport type HaiKindId = (typeof HAI_KIND_IDS)[number];\n\n/**\n * 牌種 (HaiKind)\n *\n * 牌種IDに対応する定数定義。\n */\nexport const HaiKind = {\n ManZu1: 0,\n ManZu2: 1,\n ManZu3: 2,\n ManZu4: 3,\n ManZu5: 4,\n ManZu6: 5,\n ManZu7: 6,\n ManZu8: 7,\n ManZu9: 8,\n PinZu1: 9,\n PinZu2: 10,\n PinZu3: 11,\n PinZu4: 12,\n PinZu5: 13,\n PinZu6: 14,\n PinZu7: 15,\n PinZu8: 16,\n PinZu9: 17,\n SouZu1: 18,\n SouZu2: 19,\n SouZu3: 20,\n SouZu4: 21,\n SouZu5: 22,\n SouZu6: 23,\n SouZu7: 24,\n SouZu8: 25,\n SouZu9: 26,\n Ton: 27,\n Nan: 28,\n Sha: 29,\n Pei: 30,\n Haku: 31,\n Hatsu: 32,\n Chun: 33,\n} as const;\n\nexport type HaiKind = (typeof HaiKind)[keyof typeof HaiKind];\n\n/**\n * 牌種タイプ (HaiType)\n */\nexport const HaiType = {\n Manzu: \"Manzu\",\n Pinzu: \"Pinzu\",\n Souzu: \"Souzu\",\n Jihai: \"Jihai\",\n} as const;\n\nexport type HaiType = (typeof HaiType)[keyof typeof HaiType];\n\nexport type Suupai =\n | typeof HaiType.Manzu\n | typeof HaiType.Pinzu\n | typeof HaiType.Souzu;\n\nexport type Jihai = typeof HaiType.Jihai;\n\n/**\n * シャンテン数 (ShantenNumber)\n *\n * 和了までの手数 - 1 を表す数値。\n *\n * - 0: テンパイ (Tenpai) - 次の1手で和了できる状態\n * - 1: 1シャンテン - テンパイまであと1手\n * - ...\n * - 13: 理論上の最大値 (例: 13面待ちでない国士無双の初期状態など)\n *\n * ※ 本ライブラリでは、ツモる前の13枚の手牌に対する計算を前提とするため、\n * 和了 (-1) はこの型には含めない。\n */\nexport type ShantenNumber =\n | 0\n | 1\n | 2\n | 3\n | 4\n | 5\n | 6\n | 7\n | 8\n | 9\n | 10\n | 11\n | 12\n | 13;\n\n/**\n * 符 (Fu)\n *\n * 和了時の手牌構成から算出される符の値。\n *\n * - 20: 副底(基本符)\n * - 25: 七対子専用\n * - 30〜110: 10符刻みの値\n */\nexport type Fu = 20 | 25 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | 110;\n\n/**\n * 他家 (Tacha)\n *\n * 自分から見た他家の位置関係(相対席)。\n * 副露(鳴き)の発生元などを表現するために使用する。\n *\n * - 1: 下家 (Shimocha) - 右側\n * - 2: 対面 (Toimen) - 正面\n * - 3: 上家 (Kamicha) - 左側\n */\nexport const Tacha = {\n Shimocha: 1,\n Toimen: 2,\n Kamicha: 3,\n} as const;\n\nexport type Tacha = (typeof Tacha)[keyof typeof Tacha];\n\n/**\n * 副露種別 (FuroType)\n */\nexport const FuroType = {\n Chi: \"Chi\",\n Pon: \"Pon\",\n Daiminkan: \"Daiminkan\",\n Kakan: \"Kakan\",\n} as const;\n\nexport type FuroType = (typeof FuroType)[keyof typeof FuroType];\n\n/**\n * 副露 (Furo)\n *\n * 面子に対する「鳴き」のメタ情報。\n * ここでは副露を「自分の手牌が不足している面子を、他家が捨てた牌を取って完成させる行為」と定義し、\n * 暗槓(自力で4枚揃える行為)はここには含めない。\n *\n * 構成する牌自体はここには含めず、この型を持つ親(Mentsuなど)が保持することを想定する。\n */\nexport type Furo =\n | { readonly type: typeof FuroType.Chi; readonly from: Tacha }\n | { readonly type: typeof FuroType.Pon; readonly from: Tacha }\n | { readonly type: typeof FuroType.Daiminkan; readonly from: Tacha }\n | { readonly type: typeof FuroType.Kakan; readonly from: Tacha };\n\n/**\n * 面子種別 (MentsuType)\n */\nexport const MentsuType = {\n Shuntsu: \"Shuntsu\", // 順子 (123)\n Koutsu: \"Koutsu\", // 刻子 (111)\n Kantsu: \"Kantsu\", // 槓子 (1111)\n Toitsu: \"Toitsu\", // 対子 (11)\n Tatsu: \"Tatsu\", // 塔子 (12, 13)\n} as const;\n\nexport type MentsuType = (typeof MentsuType)[keyof typeof MentsuType];\n\n/**\n * 基本的な面子構造 (ジェネリック)\n *\n * 牌の型をジェネリクス `T` で抽象化することで、以下の両方のユースケースに対応します:\n * 1. `HaiKindId`: MPSZ形式の手牌をもとにシャンテン計算を行うなど、牌の種類のみに関心がある場合(抽象的な計算)。\n * 2. `HaiId`: 実際のゲームの牌譜など、牌の物理的なIDを処理対象とする場合(具象的な計算)。\n */\ninterface BaseMentsu<T extends HaiKindId | HaiId> {\n readonly type: MentsuType;\n /**\n * 構成する牌のリスト。\n *\n * 各面子型(Shuntsu等)において固定長タプル(例: `[T, T, T]`)として再定義することで、\n * 面子の種類ごとの正しい牌枚数(順子なら3枚、槓子なら4枚など)を型レベルで強制します。\n */\n hais: readonly T[];\n}\n\n/**\n * 順子 (Shuntsu)\n */\nexport type Shuntsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Shuntsu;\n readonly hais: readonly [T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 刻子 (Koutsu)\n */\nexport type Koutsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Koutsu;\n readonly hais: readonly [T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 槓子 (Kantsu)\n */\nexport type Kantsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Kantsu;\n readonly hais: readonly [T, T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 対子 (Toitsu)\n */\nexport type Toitsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Toitsu;\n readonly hais: readonly [T, T];\n readonly furo?: never;\n};\n\n/**\n * 塔子 (Tatsu)\n */\nexport type Tatsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Tatsu;\n readonly hais: readonly [T, T];\n readonly furo?: never;\n};\n\n/**\n * 完成面子 (CompletedMentsu)\n * - 順子 (Shuntsu)\n * - 刻子 (Koutsu)\n * - 槓子 (Kantsu)\n */\nexport type CompletedMentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | Shuntsu<T>\n | Koutsu<T>\n | Kantsu<T>;\n\n/**\n * 未完成面子 (IncompletedMentsu)\n * - 対子 (Toitsu)\n * - 塔子 (Tatsu)\n */\nexport type IncompletedMentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | Toitsu<T>\n | Tatsu<T>;\n\n/**\n * 面子 (Mentsu)\n *\n * 広義の面子(ブロック)。指定がない場合は HaiKindId のリストを持つ。\n */\nexport type Mentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | CompletedMentsu<T>\n | IncompletedMentsu<T>;\n\n/** 自風・場風を表す牌 (東・南・西・北) */\nexport type Kazehai =\n | typeof HaiKind.Ton\n | typeof HaiKind.Nan\n | typeof HaiKind.Sha\n | typeof HaiKind.Pei;\n\nexport type HaiIsYaocyu<ID extends HaiKindId | HaiId> = ID extends 0 | 8 | 9\n ? true\n : false;\n\n/**\n * 手牌 (Tehai)\n *\n * 純手牌と副露を合わせたもの。\n * @template T 牌の型 (HaiKindId | HaiId)\n */\nexport interface Tehai<T extends HaiKindId | HaiId = HaiKindId> {\n readonly closed: readonly T[];\n readonly exposed: readonly CompletedMentsu<T>[];\n}\n\n/**\n * ツモる前の手牌 (13枚)\n */\nexport type Tehai13<T extends HaiKindId | HaiId = HaiKindId> = Tehai<T>;\n\n/**\n * ツモった後の手牌 (14枚)\n */\nexport type Tehai14<T extends HaiKindId | HaiId = HaiKindId> = Tehai<T>;\n\n// ... types existing ...\n","/**\n * riichi-mahjong ライブラリの基本エラークラス\n * 全てのカスタムエラーはこのクラスを継承します。\n */\nexport class MahjongError extends Error {\n /**\n *\n */\n constructor(message: string) {\n super(message);\n this.name = \"MahjongError\";\n\n // TypeScriptでカスタムエラーを正しく動作させるためのハック\n // TypeScriptでカスタムエラーを正しく動作させるためのハック\n Object.setPrototypeOf(this, MahjongError.prototype);\n }\n}\n\n/**\n * ツモれなかった場合のエラー (少牌)\n * 手牌が規定枚数(13枚)より少ない場合にスローされます。\n */\nexport class ShoushaiError extends MahjongError {\n /**\n *\n */\n constructor(message = \"手牌が規定枚数(13枚)より少ないです。\") {\n super(message);\n this.name = \"ShoushaiError\";\n\n Object.setPrototypeOf(this, ShoushaiError.prototype);\n }\n}\n\n/**\n * 切り忘れの場合のエラー (多牌)\n * 手牌が規定枚数(13枚)より多い場合にスローされます。\n */\nexport class TahaiError extends MahjongError {\n /**\n *\n */\n constructor(message = \"手牌が規定枚数(13枚)より多いです。\") {\n super(message);\n this.name = \"TahaiError\";\n\n Object.setPrototypeOf(this, TahaiError.prototype);\n }\n}\n\n/**\n * 引数が不正な場合のエラー\n * 必要なパラメータが不足している、または不正な値の場合にスローされます。\n */\nexport class MahjongArgumentError extends MahjongError {\n /**\n *\n */\n constructor(message: string) {\n super(message);\n this.name = \"MahjongArgumentError\";\n\n Object.setPrototypeOf(this, MahjongArgumentError.prototype);\n }\n}\n\n/**\n * 牌IDが重複している場合のエラー\n * (物理的な牌IDは一意である必要があります)\n */\nexport class DuplicatedHaiIdError extends MahjongError {\n /**\n *\n */\n constructor(message = \"牌IDが重複しています。\") {\n super(message);\n this.name = \"DuplicatedHaiIdError\";\n\n Object.setPrototypeOf(this, DuplicatedHaiIdError.prototype);\n }\n}\n\n/**\n * 牌の枚数が不正な場合のエラー\n * (同種の牌は最大4枚までです)\n */\nexport class InvalidHaiQuantityError extends MahjongError {\n /**\n *\n */\n constructor(message = \"同種の牌が5枚以上存在します。\") {\n super(message);\n this.name = \"InvalidHaiQuantityError\";\n\n Object.setPrototypeOf(this, InvalidHaiQuantityError.prototype);\n }\n}\n\n/**\n * チョンボ(錯和)の基底エラークラス\n *\n * 不正な和了宣言に関するエラーの基底クラス。\n * 具体的なチョンボ種別(役なし、フリテン等)はサブクラスで定義する。\n */\nexport class ChomboError extends MahjongError {\n /**\n *\n */\n constructor(message = \"不正な和了です。\") {\n super(message);\n this.name = \"ChomboError\";\n\n Object.setPrototypeOf(this, ChomboError.prototype);\n }\n}\n\n/**\n * 役なし和了のエラー\n *\n * 和了形は成立しているが、役が一つも成立していない場合にスローされます。\n */\nexport class NoYakuError extends ChomboError {\n /**\n *\n */\n constructor(message = \"役が成立していません。\") {\n super(message);\n this.name = \"NoYakuError\";\n\n Object.setPrototypeOf(this, NoYakuError.prototype);\n }\n}\n","import { type HaiId, type HaiKindId } from \"../types\";\n\n/**\n * Checks if the array has exactly 2 elements and narrows the type to a tuple.\n */\nexport function isTuple2<T>(arr: readonly T[]): arr is readonly [T, T] {\n return arr.length === 2;\n}\n\n/**\n * Checks if the array has exactly 3 elements and narrows the type to a tuple.\n */\nexport function isTuple3<T>(arr: readonly T[]): arr is readonly [T, T, T] {\n return arr.length === 3;\n}\n\n/**\n * Checks if the array has exactly 4 elements and narrows the type to a tuple.\n */\nexport function isTuple4<T>(arr: readonly T[]): arr is readonly [T, T, T, T] {\n return arr.length === 4;\n}\n\n/**\n * Casts a number to HaiKindId safely (conceptually).\n * Use this only when you are sure the number is a valid HaiKindId.\n */\nexport function asHaiKindId(id: number): HaiKindId {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return id as HaiKindId;\n}\n\n/**\n * Casts a number to HaiId safely (conceptually).\n */\nexport function asHaiId(id: number): HaiId {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return id as HaiId;\n}\n","import { type HaiId, HaiKind, type HaiKindId, HaiType } from \"../types\";\nimport { asHaiKindId } from \"../utils/assertions\";\n\n/**\n * 牌種IDから牌種タイプを取得する\n */\nexport function kindIdToHaiType(kind: HaiKindId): HaiType {\n if (kind >= HaiKind.ManZu1 && kind <= HaiKind.ManZu9) {\n return HaiType.Manzu;\n }\n if (kind >= HaiKind.PinZu1 && kind <= HaiKind.PinZu9) {\n return HaiType.Pinzu;\n }\n if (kind >= HaiKind.SouZu1 && kind <= HaiKind.SouZu9) {\n return HaiType.Souzu;\n }\n return HaiType.Jihai;\n}\n\n/**\n * 物理牌IDから牌種IDを取得する\n * 0-35: 萬子 (36枚 = 9種 * 4枚) -> 0-8\n * 36-71: 筒子 (36枚) -> 9-17\n * 72-107: 索子 (36枚) -> 18-26\n * 108-135: 字牌 (28枚 = 7種 * 4枚) -> 27-33\n */\nexport function haiIdToKindId(id: HaiId): HaiKindId {\n if (id < 36) return asHaiKindId(Math.floor(id / 4));\n if (id < 72) return asHaiKindId(Math.floor((id - 36) / 4) + 9);\n if (id < 108) return asHaiKindId(Math.floor((id - 72) / 4) + 18);\n return asHaiKindId(Math.floor((id - 108) / 4) + 27);\n}\n\n/**\n * 牌種IDから数値(1-9)を取得する\n * 字牌の場合は undefined を返す\n */\nexport function haiKindToNumber(kind: HaiKindId): number | undefined {\n const type = kindIdToHaiType(kind);\n if (type === HaiType.Jihai) return undefined;\n\n if (type === HaiType.Manzu) return kind - HaiKind.ManZu1 + 1;\n if (type === HaiType.Pinzu) return kind - HaiKind.PinZu1 + 1;\n // if (kindIdToHaiType(kind) === HaiType.Souzu)\n return kind - HaiKind.SouZu1 + 1;\n}\n\n/**\n * 数牌かどうかを判定する\n */\nexport function isSuupai(kind: HaiKindId): boolean {\n return kindIdToHaiType(kind) !== HaiType.Jihai;\n}\n\n/**\n * 么九牌(1,9,字牌)の牌種IDセット\n */\nexport const YAOCHU_KIND_IDS = [\n HaiKind.ManZu1,\n HaiKind.ManZu9,\n HaiKind.PinZu1,\n HaiKind.PinZu9,\n HaiKind.SouZu1,\n HaiKind.SouZu9,\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n HaiKind.Haku,\n HaiKind.Hatsu,\n HaiKind.Chun,\n] as const;\n\n/**\n * 么九牌(1,9,字牌)かどうかを判定する\n */\nexport function isYaochu(kind: HaiKindId): boolean {\n return YAOCHU_KIND_IDS.some((k) => k === kind);\n}\n","import {\n DuplicatedHaiIdError,\n InvalidHaiQuantityError,\n ShoushaiError,\n TahaiError,\n} from \"../errors\";\nimport type {\n HaiId,\n HaiKindDistribution,\n HaiKindId,\n Tehai,\n Tehai13,\n Tehai14,\n} from \"../types\";\n\n/**\n * 手牌の有効枚数を計算します。\n * 副露(槓子含む)は一律3枚として計算します。\n */\nexport function calculateTehaiCount<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): number {\n return tehai.closed.length + tehai.exposed.length * 3;\n}\n\n/**\n * 牌種ごとの枚数をカウントします。\n */\nexport function countHaiKind(hais: readonly HaiKindId[]): HaiKindDistribution {\n const counts = Array.from({ length: 34 }, () => 0);\n for (const hai of hais) {\n counts[hai] = (counts[hai] ?? 0) + 1;\n }\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return counts as unknown as HaiKindDistribution;\n}\n\n/**\n * 手牌がTehai13(有効枚数13枚)であるか検証します。\n * @throws {ShoushaiError} 枚数が不足している場合\n * @throws {TahaiError} 枚数が超過している場合\n */\nexport function validateTehai13<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 13) {\n throw new ShoushaiError();\n }\n if (count > 13) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌がTehai14(有効枚数14枚)であるか検証します。\n * @throws {ShoushaiError} 枚数が不足している場合\n * @throws {TahaiError} 枚数が超過している場合\n * @throws {InvalidHaiQuantityError} 同一種の牌が5枚以上ある場合\n * @throws {DuplicatedHaiIdError} 物理牌モードでIDが重複している場合\n */\nexport function validateTehai14<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 14) {\n throw new ShoushaiError();\n }\n if (count > 14) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌がTehai13またはTehai14(有効枚数が13または14枚)であるか検証します。\n * シャンテン計算や待ち判定など、13枚/14枚の区別なく手牌として扱いたい場合に使用します。\n *\n * @throws {ShoushaiError} 枚数が不足している場合 (< 13)\n * @throws {TahaiError} 枚数が超過している場合 (> 14)\n * @throws {InvalidHaiQuantityError} 同一種の牌が5枚以上ある場合\n * @throws {DuplicatedHaiIdError} 物理牌モードでIDが重複している場合\n */\nexport function validateTehai<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 13) {\n throw new ShoushaiError();\n }\n if (count > 14) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌の整合性を検証します。\n * - 物理的な牌ID (`HaiId`) が使われている場合、重複チェックを行います。\n * - 牌種ID (`HaiKindId`) の場合(または変換後)、同一牌種が5枚以上ないかチェックします。\n *\n * @throws {DuplicatedHaiIdError}\n * @throws {InvalidHaiQuantityError}\n */\nfunction validateHaiConsistency<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const allHais: number[] = [\n ...tehai.closed,\n ...tehai.exposed.flatMap((m) => m.hais),\n ];\n\n // 1. Check for physical HaiId usage (any id > 33)\n // HaiKindId is 0-33. Any value > 33 implies HaiId (0-135).\n // Note: Low HaiIds (0-33) are ambiguous, but if mix of high and low exists, it's HaiId.\n // If only low values exist, we can't strictly distinguish, but treating as KindId is safe\n // unless the user provided [0, 0] intending HaiId 0 and HaiId 0.\n // However, normally HaiKindId 0 is ManZu1.\n // Strategy: If MAX(id) > 33, treat as HaiId.\n const isHaiIdMode = allHais.some((h) => h > 33);\n\n if (isHaiIdMode) {\n // Check for duplicate HaiIds\n const uniqueIds = new Set(allHais);\n if (uniqueIds.size !== allHais.length) {\n throw new DuplicatedHaiIdError();\n }\n }\n\n // 2. Check for Kind quantity (max 4 per kind)\n const counts = new Map<number, number>();\n for (const hai of allHais) {\n // If HaiId mode, convert to KindId\n // If KindId mode, use as is\n // import { haiIdToKindId } from \"./hai\"; <--- Need to import or implement logic\n // Since we are in core/tehai, and core/hai depends on types.\n // Let's defer strict conversion.\n // For now, assume generic T validation behavior.\n // But we need `haiIdToKindId`.\n // Let's implement logic inline or use import.\n // Circular dependency risk? core/tehai -> core/hai.\n // core/hai imports types. core/tehai imports types. Should be fine.\n // But I need to import it at top of file.\n\n // Using inline logic to avoid circular deps if any (though likely safe)\n // 0-35 -> 0-8, etc.\n let kind: number = hai;\n if (hai > 33) {\n if (hai < 36) kind = Math.floor(hai / 4);\n else if (hai < 72) kind = Math.floor((hai - 36) / 4) + 9;\n else if (hai < 108) kind = Math.floor((hai - 72) / 4) + 18;\n else kind = Math.floor((hai - 108) / 4) + 27;\n }\n\n const current = counts.get(kind) ?? 0;\n if (current + 1 > 4) {\n throw new InvalidHaiQuantityError();\n }\n counts.set(kind, current + 1);\n }\n}\n\n/**\n * Type Guard for Tehai13\n */\nexport function isTehai13<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): tehai is Tehai13<T> {\n try {\n validateTehai13(tehai);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Type Guard for Tehai14\n */\nexport function isTehai14<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): tehai is Tehai14<T> {\n try {\n validateTehai14(tehai);\n return true;\n } catch {\n return false;\n }\n}\n","import type { HaiKindId } from \"../types\";\nimport { haiKindToNumber, isSuupai, kindIdToHaiType } from \"./hai\";\nimport { isTuple2, isTuple3, isTuple4 } from \"../utils/assertions\";\n\n// バリデーションロジックは「HaiKindId の配列」に対して行うものと定義する。\n// HaiId を持つ Mentsu を検証したい場合は、呼び出し側で KindId に変換してから渡す必要がある。\n// ただし、利便性のために `convertHaiIdToKindId` ヘルパーを export する。\n\n/**\n * 順子かどうかを検証する\n\n */\nexport function isValidShuntsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple3(kindIds)) return false;\n\n const [a, b, c] = kindIds;\n if (!isSuupai(a) || !isSuupai(b) || !isSuupai(c)) return false;\n\n const typeA = kindIdToHaiType(a);\n const typeB = kindIdToHaiType(b);\n const typeC = kindIdToHaiType(c);\n\n if (typeA !== typeB || typeA !== typeC) return false;\n\n const numA = haiKindToNumber(a);\n const numB = haiKindToNumber(b);\n const numC = haiKindToNumber(c);\n\n if (numA === undefined || numB === undefined || numC === undefined)\n return false;\n\n // ソートして連続性をチェック\n const sorted = [numA, numB, numC].sort((x, y) => x - y);\n\n // Safe to access since we just created it with 3 elements\n // But strict check might complain about index access on array\n if (!isTuple3(sorted)) return false; // Should be always true\n return sorted[0] + 1 === sorted[1] && sorted[1] + 1 === sorted[2];\n}\n\n/**\n * 刻子かどうかを検証する\n\n */\nexport function isValidKoutsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple3(kindIds)) return false;\n const [a, b, c] = kindIds;\n return a === b && b === c;\n}\n\n/**\n * 槓子かどうかを検証する\n\n */\nexport function isValidKantsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple4(kindIds)) return false;\n const [a, b, c, d] = kindIds;\n return a === b && b === c && c === d;\n}\n\n/**\n * 対子かどうかを検証する\n\n */\nexport function isValidToitsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple2(kindIds)) return false;\n const [a, b] = kindIds;\n return a === b;\n}\n\n/**\n * 塔子かどうかを検証する\n\n */\nexport function isValidTatsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple2(kindIds)) return false;\n const [a, b] = kindIds;\n\n // 数牌でなければならない\n if (!isSuupai(a) || !isSuupai(b)) return false;\n\n // 同じ種類でなければならない\n const typeA = kindIdToHaiType(a);\n const typeB = kindIdToHaiType(b);\n if (typeA !== typeB) return false;\n\n const numA = haiKindToNumber(a);\n const numB = haiKindToNumber(b);\n\n if (numA === undefined || numB === undefined) return false;\n\n const diff = Math.abs(numA - numB);\n // 差が1 (ペンチャン/リャンメン) または 2 (カンチャン) ならOK\n return diff === 1 || diff === 2;\n}\n","import { HaiKind, type HaiKindId, type Tehai } from \"../types\";\nimport { kindIdToHaiType } from \"./hai\";\nimport { HaiType } from \"../types\";\nimport { asHaiKindId } from \"../utils/assertions\";\n\n/**\n * ドラ表示牌から次の牌(ドラ)を求める\n * @param indicator ドラ表示牌のID (HaiKindId)\n * @returns ドラ牌のID (HaiKindId)\n */\nexport function getDoraNext(indicator: HaiKindId): HaiKindId {\n const type = kindIdToHaiType(indicator);\n\n if (type === HaiType.Manzu) {\n if (indicator === HaiKind.ManZu9) return HaiKind.ManZu1;\n return asHaiKindId(indicator + 1);\n }\n\n if (type === HaiType.Pinzu) {\n if (indicator === HaiKind.PinZu9) return HaiKind.PinZu1;\n return asHaiKindId(indicator + 1);\n }\n\n if (type === HaiType.Souzu) {\n if (indicator === HaiKind.SouZu9) return HaiKind.SouZu1;\n return asHaiKindId(indicator + 1);\n }\n\n // Jihai\n // Ton(27) -> Nan(28) -> Sha(29) -> Pei(30) -> Ton(27)\n if (indicator === HaiKind.Pei) return HaiKind.Ton;\n if (indicator >= HaiKind.Ton && indicator < HaiKind.Pei) {\n return asHaiKindId(indicator + 1);\n }\n\n // Haku(31) -> Hatsu(32) -> Chun(33) -> Haku(31)\n if (indicator === HaiKind.Chun) return HaiKind.Haku;\n if (indicator >= HaiKind.Haku && indicator < HaiKind.Chun) {\n return asHaiKindId(indicator + 1);\n }\n\n // Should not happen for valid HaiKindId\n return indicator;\n}\n\n/**\n * 手牌に含まれるドラの数を数える\n * @param tehai 手牌\n * @param indicators ドラ表示牌のリスト\n * @returns ドラの総数\n */\nexport function countDora(\n tehai: Tehai,\n indicators: readonly HaiKindId[],\n): number {\n let count = 0;\n // Calculate actual dora hinds\n const doraHais = indicators.map(getDoraNext);\n\n // Count in closed hand\n for (const hai of tehai.closed) {\n for (const dora of doraHais) {\n if (hai === dora) count++;\n }\n }\n\n // Count in exposed mentsu\n for (const mentsu of tehai.exposed) {\n for (const hai of mentsu.hais) {\n for (const dora of doraHais) {\n if (hai === dora) count++;\n }\n }\n }\n\n // TODO: Add Akadora counting logic here\n\n return count;\n}\n","import type { HaiKindId, Shuntsu } from \"../types\";\nimport type { HouraStructure } from \"../features/yaku/types\";\n\n/** 待ちの形 */\nexport type MachiType =\n | \"Tanki\" // 単騎待ち\n | \"Shanpon\" // 双碰待ち (シャボ)\n | \"Ryanmen\" // 両面待ち\n | \"Kanchan\" // 嵌張待ち\n | \"Penchan\"; // 辺張待ち\n\n/**\n * 手牌構造と和了牌から待ちの形を判定する\n * @param hand 分解された手牌構造\n * @param agariHai 和了牌\n * @returns 待ちの形(判定できない、または Shanpon などの場合は undefined)\n */\nexport function classifyMachi(\n hand: HouraStructure,\n agariHai: HaiKindId,\n): MachiType | undefined {\n if (hand.type !== \"Mentsu\") return undefined;\n\n // 1. 雀頭での和了(単騎待ち)\n if (hand.jantou.hais.includes(agariHai)) {\n return \"Tanki\";\n }\n\n // 2. 順子・刻子・槓子での和了\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.type === \"Shuntsu\") {\n const machi = classifyShuntsuWait(mentsu, agariHai);\n if (machi) return machi;\n } else {\n // 3. 刻子・槓子での和了(双碰待ち)\n // 刻子の一部が和了牌=シャボ待ちで和了\n if (mentsu.hais.includes(agariHai)) {\n return \"Shanpon\";\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * 順子における待ちの形を判定する(内部ヘルパー)\n */\nfunction classifyShuntsuWait(\n shuntsu: Shuntsu,\n agariHai: HaiKindId,\n): MachiType | undefined {\n const { hais } = shuntsu;\n if (!hais.includes(agariHai)) return undefined;\n\n const [a, b, c] = hais; // 順子はソートされている前提\n\n if (agariHai === a) {\n // [Agari, b, c]\n const valC = c % 9;\n if (valC === 8) return \"Penchan\";\n return \"Ryanmen\";\n }\n\n if (agariHai === c) {\n // [a, b, Agari]\n const valA = a % 9;\n if (valA === 0) return \"Penchan\";\n return \"Ryanmen\";\n }\n\n if (agariHai === b) {\n // [a, Agari, c]\n return \"Kanchan\";\n }\n\n return undefined;\n}\n","import { countHaiKind, validateTehai13 } from \"../../../core/tehai\";\nimport type { Tehai13 } from \"../../../types\";\n\n/**\n * 七対子のシャンテン数を計算する\n *\n * @param tehai 手牌 (13枚)\n * @returns シャンテン数 (0: 聴牌, -1: 和了 - 理論上)\n */\nexport function calculateChiitoitsuShanten(tehai: Tehai13): number {\n // 防御的プログラミング (Defensive Programming):\n // 公開API(calculateShanten)側でもバリデーションが行われる想定だが(Facadeパターン)、\n // 内部整合性を保つため、ここでも独立してバリデーションを実施する。\n validateTehai13(tehai);\n\n // シャンテン数を計算する前にバリデーションを実行する\n // 七対子は門前のみ\n\n if (tehai.exposed.length > 0) {\n return Infinity;\n }\n\n // HaiId/HaiKindId の正規化は廃止。呼び出し元で HaiKindId を保証する。\n const haiCounts = countHaiKind(tehai.closed);\n\n let pairs = 0;\n let kinds = 0;\n\n for (const count of haiCounts) {\n if (count > 0) {\n kinds++;\n }\n if (count >= 2) {\n pairs++;\n }\n }\n\n let shanten = 6 - pairs;\n\n // 種類不足ペナルティ\n if (kinds < 7) {\n shanten += 7 - kinds;\n }\n\n return shanten;\n}\n","import type { Tehai13 } from \"../../../types\";\nimport { isYaochu } from \"../../../core/hai\";\nimport { countHaiKind, validateTehai13 } from \"../../../core/tehai\";\nimport { asHaiKindId } from \"../../../utils/assertions\";\n\n/**\n * 国士無双のシャンテン数を計算します。\n *\n * ルール:\n * - 13種類の么九牌(1,9,字牌)を各1枚ずつ揃える。\n * - そのうちのどれか1種類が対子(2枚)になっている必要がある。\n * - 門前限定。\n *\n * 計算式:\n * シャンテン数 = 13 - (么九牌の種類数) - (么九牌の対子があるか ? 1 : 0)\n *\n * @param tehai 手牌\n * @returns シャンテン数 (0: 聴牌, -1: 和了(理論上))。副露している場合は Infinity。\n */\nexport function calculateKokushiShanten(tehai: Tehai13): number {\n // 防御的プログラミング (Defensive Programming):\n // 公開API(calculateShanten)側でもバリデーションが行われる想定だが(Facadeパターン)、\n // 内部整合性を保つため、ここでも独立してバリデーションを実施する。\n validateTehai13(tehai);\n\n // 国士無双は門前のみ\n if (tehai.exposed.length > 0) {\n return Infinity;\n }\n\n const dist = countHaiKind(tehai.closed);\n\n // 么九牌の種類数をカウント\n // 同時に、么九牌の対子が存在するかもチェック\n let uniqueYaochuCount = 0;\n let hasYaochuPair = false;\n\n for (let i = 0; i < dist.length; i++) {\n const kind = asHaiKindId(i);\n if (!isYaochu(kind)) {\n continue;\n }\n\n const count = dist[i];\n if (count !== undefined && count > 0) {\n uniqueYaochuCount++;\n if (count >= 2) {\n hasYaochuPair = true;\n }\n }\n }\n\n const pairBonus = hasYaochuPair ? 1 : 0;\n\n // シャンテン数 = 13 - (種類の数) - (対子ボーナス)\n return 13 - uniqueYaochuCount - pairBonus;\n}\n","import type { Tehai } from \"../../../types\";\n\nimport { countHaiKind, validateTehai } from \"../../../core/tehai\";\n\n/**\n * 面子手(4面子1雀頭)のシャンテン数を計算する\n *\n * @param tehai 手牌 (13枚 or 14枚)\n * @returns シャンテン数\n */\nexport function calculateMentsuTeShanten(tehai: Tehai): number {\n // 防御的プログラミング\n validateTehai(tehai);\n\n const counts = countHaiKind(tehai.closed);\n // Mutation is required for the algorithm, so we convert to a mutable number array\n const mutableCounts: number[] = Array.from(counts);\n const exposedCount = tehai.exposed.length;\n\n // 基本シャンテン数 (8 - 2 * 面子数)\n let minShanten = 8 - 2 * exposedCount;\n\n // 1. 雀頭がある場合\n for (let i = 0; i < 34; i++) {\n if ((mutableCounts[i] ?? 0) >= 2) {\n mutableCounts[i] = (mutableCounts[i] ?? 0) - 2;\n const { m, t } = searchMentsu(mutableCounts);\n const currentMentsu = exposedCount + m;\n const effectiveTaatsu = Math.min(4 - currentMentsu, t);\n const shanten = 8 - 2 * currentMentsu - effectiveTaatsu - 1;\n minShanten = Math.min(minShanten, shanten);\n mutableCounts[i] = (mutableCounts[i] ?? 0) + 2;\n }\n }\n\n // 2. 雀頭がない場合\n {\n const { m, t } = searchMentsu(mutableCounts);\n const currentMentsu = exposedCount + m;\n const effectiveTaatsu = Math.min(4 - currentMentsu, t);\n const shanten = 8 - 2 * currentMentsu - effectiveTaatsu;\n minShanten = Math.min(minShanten, shanten);\n }\n\n return minShanten;\n}\n\n/**\n * 探索結果の型\n */\ninterface SearchResult {\n m: number;\n t: number;\n}\n\n/**\n * 面子と塔子の最大数を探索する\n */\nfunction searchMentsu(counts: readonly number[]): SearchResult {\n let maxScore = -1;\n let bestResult: SearchResult = { m: 0, t: 0 };\n // copies needed because we mutate counts\n const w = [...counts];\n\n const search = (index: number, m: number) => {\n // 34種類すべて見終わったら塔子を数える\n if (index >= 34) {\n const t = countTaatsu(w);\n const score = 2 * m + t;\n if (score > maxScore) {\n maxScore = score;\n bestResult = { m, t };\n }\n return;\n }\n\n // 牌がない場合は次に進む\n if ((w[index] ?? 0) === 0) {\n search(index + 1, m);\n return;\n }\n\n // A. 刻子 (3枚) の場合\n if ((w[index] ?? 0) >= 3) {\n w[index] = (w[index] ?? 0) - 3;\n search(index, m + 1);\n w[index] = (w[index] ?? 0) + 3;\n }\n\n // B. 順子 (3枚) の場合\n if (index < 27) {\n const mod = index % 9;\n if (mod < 7) {\n if (\n (w[index] ?? 0) > 0 &&\n (w[index + 1] ?? 0) > 0 &&\n (w[index + 2] ?? 0) > 0\n ) {\n w[index] = (w[index] ?? 0) - 1;\n w[index + 1] = (w[index + 1] ?? 0) - 1;\n w[index + 2] = (w[index + 2] ?? 0) - 1;\n search(index, m + 1);\n w[index] = (w[index] ?? 0) + 1;\n w[index + 1] = (w[index + 1] ?? 0) + 1;\n w[index + 2] = (w[index + 2] ?? 0) + 1;\n }\n }\n }\n\n // C. 面子として使わない場合\n search(index + 1, m);\n };\n\n search(0, 0);\n return bestResult;\n}\n\n/**\n * 残った牌から塔子(対子、両面、嵌張、辺張)の数を数える\n */\nfunction countTaatsu(counts: readonly number[]): number {\n let taatsu = 0;\n // countsのコピーを作成\n const w = [...counts];\n\n for (let i = 0; i < 34; i++) {\n if ((w[i] ?? 0) === 0) continue;\n\n // 順子系の塔子 (1枚 + 1枚)\n if (i < 27) {\n const mod = i % 9;\n // 辺張・両面 (i, i+1)\n if (mod < 8) {\n if ((w[i] ?? 0) > 0 && (w[i + 1] ?? 0) > 0) {\n w[i] = (w[i] ?? 0) - 1;\n w[i + 1] = (w[i + 1] ?? 0) - 1;\n taatsu++;\n }\n }\n\n // 嵌張 (i, i+2)\n if (mod < 7) {\n if ((w[i] ?? 0) > 0 && (w[i + 2] ?? 0) > 0) {\n w[i] = (w[i] ?? 0) - 1;\n w[i + 2] = (w[i + 2] ?? 0) - 1;\n taatsu++;\n }\n }\n }\n\n // 対子 (2枚)\n if ((w[i] ?? 0) >= 2) {\n w[i] = (w[i] ?? 0) - 2;\n taatsu++;\n }\n }\n return taatsu;\n}\n","import type { Tehai13 } from \"../../types\";\nimport { validateTehai13 } from \"../../core/tehai\";\nimport { calculateChiitoitsuShanten } from \"./logic/chiitoitsu\";\nimport { calculateKokushiShanten } from \"./logic/kokushi\";\nimport { calculateMentsuTeShanten } from \"./logic/mentsu-te\";\n\nexport {\n calculateChiitoitsuShanten,\n calculateKokushiShanten,\n calculateMentsuTeShanten,\n};\n\n/**\n * シャンテン数を計算します。\n * 面子手、七対子、国士無双のシャンテン数のうち最小値を返します。\n *\n * NOTE: 入力は必ず牌種ID (`HaiKindId`) である必要があります。\n * 物理牌ID (`HaiId`) を持っている場合は、事前に `haiIdToKindId` で変換してください。\n *\n * @param tehai 手牌\n * @returns シャンテン数\n */\nexport function calculateShanten(\n tehai: Tehai13,\n useChiitoitsu = true,\n useKokushi = true,\n): number {\n // Facadeパターン: 公開APIのエントリーポイントで入力を保証する\n validateTehai13(tehai);\n\n const chiitoitsuShanten = useChiitoitsu\n ? calculateChiitoitsuShanten(tehai)\n : Infinity;\n const kokushiShanten = useKokushi ? calculateKokushiShanten(tehai) : Infinity;\n const mentsuShanten = calculateMentsuTeShanten(tehai);\n\n return Math.min(chiitoitsuShanten, kokushiShanten, mentsuShanten);\n}\n","import type { HaiKindId, Tehai13 } from \"../../types\";\nimport { countHaiKind } from \"../../core/tehai\";\nimport { calculateMentsuTeShanten } from \"../shanten\";\n\n/**\n * 手牌の受け入れ(有効牌)を計算する。\n * 今回は面子手のみを対象とし、七対子や国士無双は考慮しない。\n *\n * @param tehai 手牌 (13枚)\n * @returns シャンテン数を進める牌のリスト\n */\nexport function getUkeire(tehai: Tehai13): HaiKindId[] {\n const currentShanten = calculateMentsuTeShanten(tehai);\n const ukeireList: HaiKindId[] = [];\n\n // 手牌の全ての牌(純手牌 + 副露)をカウント\n const allHais: HaiKindId[] = [\n ...tehai.closed,\n ...tehai.exposed.flatMap((m) => m.hais),\n ];\n const haiCounts = countHaiKind(allHais);\n\n // 全34種の牌について、1枚加えてシャンテン数が下がるか試す\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const tile = i as HaiKindId;\n\n // 4枚使い切っている牌種はスキップ(山に残っていない)\n if (haiCounts[tile] >= 4) {\n continue;\n }\n\n // 手牌のコピーを作成(副作用を防ぐため、常に新しいオブジェクトで試行)\n // Tehai13に1枚足すので、厳密にはTehai14として扱う必要があるが、\n // calculateMentsuTeShanten は Tehai<HaiKindId> を受け付けるので、\n // 構造的に { closed, exposed } が適合していればOK。\n // ただし、Tehai13型に準拠したオブジェクトに1枚足すと枚数オーバーになるため、\n // バリデーションを通過させるために Tehai14 として構築するか、\n // calculateMentsuTeShanten 側が Generics で受け入れる点を利用する。\n\n // 配列のコピーを作成\n const newClosed = [...tehai.closed, tile];\n const newTehai = {\n closed: newClosed,\n exposed: tehai.exposed,\n };\n\n // シャンテン数を計算\n // ここでバリデーションエラーが出ないように、calculateMentsuTeShanten 側は validateTehai を使用している。\n const newShanten = calculateMentsuTeShanten(newTehai);\n\n if (newShanten < currentShanten) {\n ukeireList.push(tile);\n }\n }\n\n return ukeireList;\n}\n","import type { CompletedMentsu } from \"../../../../types\";\nimport type { MentsuHouraStructure } from \"../../types\";\nimport { validateTehai14, countHaiKind } from \"../../../../core/tehai\";\nimport { isTuple4 } from \"../../../../utils/assertions\";\nimport type { Tehai14, HaiKindId, Shuntsu, Koutsu } from \"../../../../types\";\n\n/**\n * 手牌を標準形(4面子1雀頭)に構造化する。\n * 七対子や国士無双は対象外。\n *\n * 【役判定について】\n * この関数は純粋に「4面子1雀頭」の形になっているかのみを検証します。\n * 役が成立しているかどうか(和了できるかどうか)は判定しません。\n * そのため、役なし(Yakunashi)の手牌であっても構造的に整合していれば結果を返します。\n *\n * 【戻り値が配列である理由について】\n * 麻雀の手牌は、同じ牌構成であっても複数の解釈(多義性)が成立する場合があります。\n * 例: `111222333m`\n * - 三暗刻 (111 + 222 + 333)\n * - 三連刻/一盃口 (123 + 123 + 123)\n *\n * このように成立する役が変わる可能性があるため、可能な全ての構造化パターンをリストとして返します。\n * 利用側は、これらのパターンのうち最も高得点となるものを選択する必要があります。\n *\n * @param tehai 和了形の手牌\n * @returns 可能な構造化パターンのリスト。構造化できない場合は空配列。\n */\nexport function getHouraStructuresForMentsuTe(\n tehai: Tehai14,\n): MentsuHouraStructure[] {\n validateTehai14(tehai);\n\n // HaiKindDistributionはreadonlyなので、可変配列に複製する\n const counts = [...countHaiKind(tehai.closed)];\n const results: MentsuHouraStructure[] = [];\n\n // 1. 雀頭候補を探す\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n if ((counts[kind] ?? 0) >= 2) {\n // 雀頭抜き出し\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! -= 2;\n\n // 残りの牌で面子分解\n const requiredMentsuCount = 4 - tehai.exposed.length;\n const subResults = decomposeClosedMentsu(counts, requiredMentsuCount);\n\n // subResultsには閉じた部分で見つかった面子のリストが含まれる\n for (const closedMentsu of subResults) {\n // 副露面子と結合して完全な構成を作成する\n const fullMentsuList = [...closedMentsu, ...tehai.exposed];\n\n // 4面子であることを確認(ロジック上は保証されているはずだが、念のため)\n if (isTuple4(fullMentsuList)) {\n results.push({\n type: \"Mentsu\",\n fourMentsu: fullMentsuList,\n jantou: { type: \"Toitsu\", hais: [kind, kind] },\n });\n }\n }\n\n // バックトラック\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! += 2;\n }\n }\n\n return results;\n}\n\n/**\n * 閉じた手牌の残りを面子に分解する再帰関数\n */\nfunction decomposeClosedMentsu(\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n counts: number[],\n requiredCount: number,\n): CompletedMentsu[][] {\n if (requiredCount === 0) {\n // 全ての牌が使用されたか確認\n const remaining = counts.reduce((acc, c) => acc + c, 0);\n return remaining === 0 ? [[]] : [];\n }\n\n // 面子の重複順列を防ぎ決定論的な順序を強制するため、カウントが0より大きい最初の牌を見つける\n let firstIndex = -1;\n for (let i = 0; i < 34; i++) {\n if ((counts[i] ?? 0) > 0) {\n firstIndex = i;\n break;\n }\n }\n\n if (firstIndex === -1) {\n // Should not happen if requiredCount > 0, unless invalid hand\n return [];\n }\n\n const results: CompletedMentsu[][] = [];\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = firstIndex as HaiKindId;\n\n // 刻子を試す\n if ((counts[kind] ?? 0) >= 3) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! -= 3;\n const tails = decomposeClosedMentsu(counts, requiredCount - 1);\n const koutsu: Koutsu = { type: \"Koutsu\", hais: [kind, kind, kind] };\n for (const tail of tails) {\n results.push([koutsu, ...tail]);\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! += 3; // バックトラック\n }\n\n // 順子を試す\n // 数牌(0-26)かつ7を超えない(n, n+1, n+2を作れる)場合のみ有効\n if (kind < 27 && kind % 9 <= 6) {\n const k1 = kind;\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const k2 = (kind + 1) as HaiKindId;\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const k3 = (kind + 2) as HaiKindId;\n\n if ((counts[k2] ?? 0) > 0 && (counts[k3] ?? 0) > 0) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k1]! -= 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k2]! -= 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k3]! -= 1;\n\n const tails = decomposeClosedMentsu(counts, requiredCount - 1);\n const shuntsu: Shuntsu = { type: \"Shuntsu\", hais: [k1, k2, k3] };\n for (const tail of tails) {\n results.push([shuntsu, ...tail]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k1]! += 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k2]! += 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k3]! += 1; // バックトラック\n }\n }\n\n return results;\n}\n","import type { Tehai14, HaiKindId, Toitsu } from \"../../../../types\";\nimport type { ChiitoitsuHouraStructure } from \"../../types\";\nimport { countHaiKind } from \"../../../../core/tehai\";\n\n/**\n * 手牌を七対子(7つの対子)として構造化する。\n */\nexport function getHouraStructuresForChiitoitsu(\n tehai: Tehai14,\n): ChiitoitsuHouraStructure[] {\n // 七対子は門前のみ(定義によっては鳴きも許容する場合があるが、一般的には門前)\n if (tehai.exposed.length > 0) return [];\n\n const counts = countHaiKind(tehai.closed);\n const pairs: Toitsu[] = [];\n\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n const count = counts[kind];\n\n if (count === 2) {\n pairs.push({ type: \"Toitsu\", hais: [kind, kind] });\n } else if (count === 4) {\n // 4枚使いの七対子を認めるか(ローカルルール次第だが、通常は認めない)\n // ここでは標準的なルールに従い、4枚あっても2対子とはみなさない実装とする\n // ※4枚使い七対子を実装する場合は pairs.push(...) を2回行う\n return [];\n } else if (count > 0) {\n // 2枚でない牌がある場合は七対子不成立\n return [];\n }\n }\n\n if (pairs.length !== 7) return [];\n\n return [\n {\n type: \"Chiitoitsu\",\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n pairs: pairs as unknown as [\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n ],\n },\n ];\n}\n","import type { Tehai14, HaiKindId } from \"../../../../types\";\nimport type { KokushiHouraStructure } from \"../../types\";\nimport { countHaiKind } from \"../../../../core/tehai\";\nimport { isYaochu } from \"../../../../core/hai\";\n\n/**\n * 手牌を国士無双(13種の么九牌+雀頭)として構造化する。\n */\nexport function getHouraStructuresForKokushi(\n tehai: Tehai14,\n): KokushiHouraStructure[] {\n // 国士無双は門前のみ\n if (tehai.exposed.length > 0) return [];\n\n const counts = countHaiKind(tehai.closed);\n const yaochuList: HaiKindId[] = [];\n let jantou: HaiKindId | undefined;\n\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n\n const count = counts[kind];\n\n if (count > 0) {\n if (!isYaochu(kind)) return []; // 么九牌以外が含まれていれば不成立\n\n if (count === 1) {\n yaochuList.push(kind);\n } else if (count === 2) {\n if (jantou !== undefined) return []; // 雀頭が既に存在すれば不成立(複数棋の雀頭候補)\n jantou = kind;\n yaochuList.push(kind);\n } else {\n return []; // 3枚以上あれば不成立\n }\n }\n }\n\n if (yaochuList.length !== 13 || jantou === undefined) return [];\n\n return [\n {\n type: \"Kokushi\",\n yaochu: yaochuList,\n jantou,\n },\n ];\n}\n","import type { Tehai14 } from \"../../../../types\";\nimport type { HouraStructure } from \"../../types\";\nimport { getHouraStructuresForMentsuTe } from \"./mentsu-te\";\nimport { getHouraStructuresForChiitoitsu } from \"./chiitoitsu\";\nimport { getHouraStructuresForKokushi } from \"./kokushi\";\n\nexport * from \"./mentsu-te\";\nexport * from \"./chiitoitsu\";\nexport * from \"./kokushi\";\n\n/**\n * 手牌をすべての可能な和了形に構造化する。\n * 面子手、七対子、国士無双の全ての可能性を探索する。\n */\nexport function getHouraStructures(tehai: Tehai14): HouraStructure[] {\n return [\n ...getHouraStructuresForMentsuTe(tehai),\n ...getHouraStructuresForChiitoitsu(tehai),\n ...getHouraStructuresForKokushi(tehai),\n ];\n}\n","import {\n HaiKind,\n type HaiKindId,\n type Kazehai,\n type Tehai14,\n} from \"../../types\";\n\n/**\n * 手牌が門前(メンゼン)かどうかを判定する。\n *\n * 門前の定義:\n * - 明刻、明順、明槓などの「晒し」が含まれていないこと。\n * - 暗槓は門前として扱う。\n *\n * @param tehai 判定対象の手牌\n * @returns 門前であれば true、そうでなければ false\n */\nexport function isMenzen(tehai: Tehai14): boolean {\n // exposed(副露ブロック)が空なら門前\n if (tehai.exposed.length === 0) {\n return true;\n }\n\n // 副露ブロックがある場合、全てが「暗槓」であれば門前とみなす\n // 暗槓の定義: typeが\"Kantsu\"かつfuro情報を持たない(現状のデータ構造における定義)\n return tehai.exposed.every((m) => {\n return m.type === \"Kantsu\" && !m.furo;\n });\n}\n/**\n * 指定された牌が風牌かどうかを判定する。\n *\n * @param id 判定対象の牌種ID\n * @returns 風牌(東・南・西・北)であれば true\n */\nexport function isKazehai(id: HaiKindId): id is Kazehai {\n return (\n id === HaiKind.Ton ||\n id === HaiKind.Nan ||\n id === HaiKind.Sha ||\n id === HaiKind.Pei\n );\n}\n","import type { YakuHanConfig, YakuName, HouraStructure } from \"./types\";\nimport { YakuDefinition, HouraContext } from \"./types\";\n\n/**\n * 役定義を作成するファクトリ関数\n *\n * @param yaku 役の設定情報(名前、翻数など)\n * @param check 役の成立条件を判定する関数 (真偽値を返す)\n * @returns YakuDefinition (isSatisfied, getHansu を持つ)\n */\nexport function createYakuDefinition(\n yaku: Readonly<{ name: YakuName; han: YakuHanConfig }>,\n check: (hand: HouraStructure, context: HouraContext) => boolean,\n): YakuDefinition {\n return {\n yaku,\n isSatisfied: check,\n getHansu: (hand, context) => {\n if (!check(hand, context)) {\n return 0;\n }\n return context.isMenzen ? yaku.han.closed : yaku.han.open;\n },\n };\n}\n","import { isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst TANYAO_YAKU: Yaku = {\n name: \"Tanyao\",\n han: {\n open: 1,\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkTanyao = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n // 雀頭のチェック\n if (isYaochu(hand.jantou.hais[0])) return false;\n\n // 面子のチェック\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.hais.some(isYaochu)) return false;\n }\n\n return true;\n};\n\nexport const tanyaoDefinition: YakuDefinition = createYakuDefinition(\n TANYAO_YAKU,\n checkTanyao,\n);\n","import { HaiKind, type HaiKindId } from \"../../../../types\";\n\nimport type {\n HouraStructure,\n Yaku,\n HouraContext,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport type { Shuntsu } from \"../../../../types\";\nimport { classifyMachi } from \"../../../../core/machi\";\nimport { MahjongArgumentError } from \"../../../../errors\";\nimport { createYakuDefinition } from \"../../factory\";\n\nconst PINFU_YAKU: Yaku = {\n name: \"Pinfu\",\n han: {\n open: 0,\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkPinfu: (hand: HouraStructure, context: HouraContext) => boolean = (\n hand,\n context,\n) => {\n // 1. 門前であること\n if (!context.isMenzen) return false;\n if (hand.type !== \"Mentsu\") return false;\n\n // 2. 雀頭が役牌でないこと\n // 三元牌、場風、自風が含まれていないことを確認\n const jantouKind = hand.jantou.hais[0];\n\n if (context.bakaze === undefined || context.jikaze === undefined) {\n throw new MahjongArgumentError(\n \"Pinfu check requires bakaze and jikaze in context\",\n );\n }\n\n const yakuhaiList: HaiKindId[] = [\n HaiKind.Haku,\n HaiKind.Hatsu,\n HaiKind.Chun,\n context.bakaze,\n context.jikaze,\n ];\n\n if (yakuhaiList.includes(jantouKind)) return false;\n\n // 3. 全て順子であること\n if (!hand.fourMentsu.every((m): m is Shuntsu => m.type === \"Shuntsu\"))\n return false;\n\n // 4. 両面待ちであること\n const waitType = classifyMachi(hand, context.agariHai);\n\n return waitType === \"Ryanmen\";\n};\n\nexport const pinfuDefinition: YakuDefinition = createYakuDefinition(\n PINFU_YAKU,\n checkPinfu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst IIPEIKO_YAKU: Yaku = {\n name: \"Iipeikou\",\n han: {\n open: 0, // 門前限定\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkIipeikou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n // 順子が2つ未満なら一盃口はあり得ない\n if (shuntsuList.length < 2) {\n return false;\n }\n\n // 同じ順子が2つあるか探す\n // haisの内容比較が必要。Shuntsu.haisはソート済みであることを前提とするか、\n // ここで比較用キーを作って判定する。\n // ライブラリの仕様としてShuntsuのhaisは [T, T, T] だが順序保証は型定義上は明示されていないものの、\n // 一般的な実装として昇順になっているはず。\n // 安全のため、各順子の牌をソートした文字列などをキーにして比較する。\n // ただし、Shuntsu定義上 [T, T, T] で、順子である以上連続しているため、\n // 先頭の牌(最小の牌)が同じで、種類(萬子/筒子/索子)が同じなら同一順子とみなせる。\n // しかし HaiKindId の単純な数値比較で十分。\n // 例えば 1m, 2m, 3m の順子は [0, 1, 2]。\n // 順子の構成牌IDが完全一致するかどうかを見れば良い。\n\n for (let i = 0; i < shuntsuList.length; i++) {\n for (let j = i + 1; j < shuntsuList.length; j++) {\n const shuntsuA = shuntsuList[i];\n const shuntsuB = shuntsuList[j];\n\n if (!shuntsuA || !shuntsuB) continue;\n\n // 牌のID列が完全に一致するか\n const isSame =\n shuntsuA.hais[0] === shuntsuB.hais[0] &&\n shuntsuA.hais[1] === shuntsuB.hais[1] &&\n shuntsuA.hais[2] === shuntsuB.hais[2];\n\n if (isSame) {\n return true;\n }\n }\n }\n\n return false;\n};\n\nexport const iipeikouDefinition: YakuDefinition = createYakuDefinition(\n IIPEIKO_YAKU,\n checkIipeikou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst RYANPEIKOU_YAKU: Yaku = {\n name: \"Ryanpeikou\",\n han: {\n open: 0, // 門前限定\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkRyanpeikou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n // 順子が4つなければ二盃口はあり得ない\n if (shuntsuList.length < 4) {\n return false;\n }\n\n // 各順子の出現数をカウントする\n // Shuntsuは [T, T, T] で、先頭の牌IDが同じなら同じ順子とみなす\n const shuntsuCounts = new Map<number, number>();\n\n for (const shuntsu of shuntsuList) {\n const key = shuntsu.hais[0];\n const currentCount = shuntsuCounts.get(key) ?? 0;\n shuntsuCounts.set(key, currentCount + 1);\n }\n\n let pairCount = 0;\n for (const count of shuntsuCounts.values()) {\n // 同じ順子が2つで1ペア。4つなら2ペア。\n pairCount += Math.floor(count / 2);\n }\n\n return pairCount >= 2;\n};\n\nexport const ryanpeikouDefinition: YakuDefinition = createYakuDefinition(\n RYANPEIKOU_YAKU,\n checkRyanpeikou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst SANANKOU_YAKU: Yaku = {\n name: \"Sanankou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanankou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n let ankouCount = 0;\n\n for (const triplet of triplets) {\n // 副露している刻子は暗刻ではない\n if (triplet.furo) continue;\n\n const isAgariHaiInTriplet = triplet.hais.includes(context.agariHai);\n\n // ロン和了の場合、和了牌を含む刻子は明刻扱いとなる(シャボ待ちの場合)。\n // ただし、単騎待ちの場合は暗刻扱いとなる。\n // 単騎待ちかどうかの判定: 雀頭の牌が和了牌と同じかどうか\n const isTanki = hand.jantou.hais[0] === context.agariHai;\n\n if (context.isTsumo) {\n // ツモなら、副露していなければ全て暗刻\n ankouCount++;\n } else {\n // ロン和了の場合\n if (isAgariHaiInTriplet) {\n // 和了牌を含む刻子の場合\n if (isTanki) {\n // 単騎待ちなら暗刻\n ankouCount++;\n } else {\n // シャボ(等の)待ちでロンした場合は明刻扱いなのでカウントしない\n }\n } else {\n // 和了牌を含まない刻子は暗刻\n ankouCount++;\n }\n }\n }\n\n return ankouCount >= 3;\n};\n\nexport const sanankouDefinition: YakuDefinition = createYakuDefinition(\n SANANKOU_YAKU,\n checkSanankou,\n);\n","import type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst SUUANKOU_YAKU: Yaku = {\n name: \"Suuankou\",\n han: {\n open: 0, // 門前限定(構造上必然的にそうなるが、定義としても門前)\n closed: 13, // 通常役満(13) または ダブル役満(26)\n } satisfies YakuHanConfig,\n};\n\nconst checkSuuankou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n let ankouCount = 0;\n\n for (const triplet of triplets) {\n // 副露している刻子は暗刻ではない\n if (triplet.furo) continue;\n\n const isAgariHaiInTriplet = triplet.hais.includes(context.agariHai);\n\n const isTanki = hand.jantou.hais[0] === context.agariHai;\n\n if (context.isTsumo) {\n // ツモなら、副露していなければ全て暗刻\n ankouCount++;\n } else {\n // ロン和了の場合\n if (isAgariHaiInTriplet) {\n // 和了牌を含む刻子(シャボ待ちロン)は明刻\n // 単騎待ちロンなら暗刻\n if (isTanki) {\n ankouCount++;\n }\n } else {\n // 和了牌を含まない刻子は暗刻\n ankouCount++;\n }\n }\n }\n\n return ankouCount === 4;\n};\n\n// 四暗刻はダブル役満(単騎待ち)判定が必要なため、\n// createYakuDefinitionのデフォルトのgetHansuロジックをオーバーライドするか、\n// このcheck関数内で判定してフラグを渡すなどの工夫が必要。\n// しかし createYakuDefinition は boolean を返す check 関数しか受け取らない。\n// ここでは factory 側を拡張するのではなく、この定義でカスタム実装を行うか、\n// あるいは factory を通さずに直接 YakuDefinition を作る。\n// factoryの改修はスコープ外のため、ここではオブジェクトリテラルで定義を作成する。\n\nexport const suuankouDefinition: YakuDefinition = {\n yaku: SUUANKOU_YAKU,\n isSatisfied: (hand, context) => checkSuuankou(hand, context),\n getHansu: (hand, context) => {\n if (!checkSuuankou(hand, context)) return 0;\n\n // 四暗刻単騎の判定\n const isTanki =\n hand.type === \"Mentsu\" && hand.jantou.hais[0] === context.agariHai;\n\n // 単騎待ちならダブル役満(26)\n if (isTanki) {\n return 26;\n }\n\n return 13;\n },\n};\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANKANTSU_YAKU: Yaku = {\n name: \"Sankantsu\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSankantsu = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 槓子を抽出\n const kantsuList = hand.fourMentsu.filter((m) => m.type === \"Kantsu\");\n\n // 2. 槓子が3つ以上あれば成立\n return kantsuList.length >= 3;\n};\n\nexport const sankantsuDefinition: YakuDefinition = createYakuDefinition(\n SANKANTSU_YAKU,\n checkSankantsu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SUUKANTSU_YAKU: Yaku = {\n name: \"Suukantsu\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkSuukantsu = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 槓子を抽出\n const kantsuList = hand.fourMentsu.filter((m) => m.type === \"Kantsu\");\n\n // 2. 槓子が4つあれば成立\n return kantsuList.length === 4;\n};\n\nexport const suukantsuDefinition: YakuDefinition = createYakuDefinition(\n SUUKANTSU_YAKU,\n checkSuukantsu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst TOITOI_YAKU: Yaku = {\n name: \"Toitoi\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkToitoi = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 全ての面子が刻子(Koutsu)または槓子(Kantsu)であることを確認\n return hand.fourMentsu.every(\n (mentsu) => mentsu.type === \"Koutsu\" || mentsu.type === \"Kantsu\",\n );\n};\n\nexport const toitoiDefinition: YakuDefinition = createYakuDefinition(\n TOITOI_YAKU,\n checkToitoi,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHIITOITSU_YAKU: Yaku = {\n name: \"Chiitoitsu\",\n han: {\n open: 0, // 門前限定\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkChiitoitsu = (hand: HouraStructure): boolean => {\n return hand.type === \"Chiitoitsu\";\n};\n\nexport const chiitoitsuDefinition: YakuDefinition = createYakuDefinition(\n CHIITOITSU_YAKU,\n checkChiitoitsu,\n);\n","import { isYaochu, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONCHAN_YAKU: Yaku = {\n name: \"Honchan\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonchan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 1. 全ての面子・雀頭に么九牌(1・9・字牌)が含まれること\n const allHasYaochu = allBlocks.every((block) =>\n block.hais.some((k) => isYaochu(k)),\n );\n if (!allHasYaochu) return false;\n\n // 2. 少なくとも1つの順子が含まれること(混老頭の除外)\n const hasShuntsu = hand.fourMentsu.some((m) => m.type === \"Shuntsu\");\n if (!hasShuntsu) return false;\n\n // 3. 少なくとも1つの字牌が含まれること(純全帯幺九の除外)\n const hasJihai = allBlocks.some((block) =>\n block.hais.some((k) => kindIdToHaiType(k) === HaiType.Jihai),\n );\n if (!hasJihai) return false;\n\n return true;\n};\n\nexport const honchanDefinition: YakuDefinition = createYakuDefinition(\n HONCHAN_YAKU,\n checkHonchan,\n);\n","import { isSuupai, isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst JUNCHAN_YAKU: Yaku = {\n name: \"Junchan\",\n han: {\n open: 2,\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkJunchan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 1. 全ての面子・雀頭に老頭牌(1・9)が含まれること\n // isYaochu(k) && isSuupai(k) で「字牌を除く么九牌」=「老頭牌」となる\n const allHasRoutou = allBlocks.every((block) =>\n block.hais.some((k) => isYaochu(k) && isSuupai(k)),\n );\n if (!allHasRoutou) return false;\n\n // 2. 少なくとも1つの順子が含まれること(清老頭の除外)\n const hasShuntsu = hand.fourMentsu.some((m) => m.type === \"Shuntsu\");\n if (!hasShuntsu) return false;\n\n return true;\n};\n\nexport const junchanDefinition: YakuDefinition = createYakuDefinition(\n JUNCHAN_YAKU,\n checkJunchan,\n);\n","import { isYaochu, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONROUTOU_YAKU: Yaku = {\n name: \"Honroutou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonroutou = (hand: HouraStructure): boolean => {\n let blocks;\n\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // 1. 全ての牌が么九牌(1・9・字牌)であること\n // これにより順子(123など)が含まれる可能性も排除される(2,3は么九牌ではないため)\n const allYaochu = blocks.every((block) =>\n block.hais.every((k) => isYaochu(k)),\n );\n if (!allYaochu) return false;\n\n // 2. 少なくとも1つの字牌が含まれること(清老頭の除外)\n const hasJihai = blocks.some((block) =>\n block.hais.some((k) => kindIdToHaiType(k) === HaiType.Jihai),\n );\n if (!hasJihai) return false;\n\n return true;\n};\n\nexport const honroutouDefinition: YakuDefinition = createYakuDefinition(\n HONROUTOU_YAKU,\n checkHonroutou,\n);\n","import { isSuupai, isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHINROUTOU_YAKU: Yaku = {\n name: \"Chinroutou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkChinroutou = (hand: HouraStructure): boolean => {\n // 老頭牌は6種類(1m,9m,1p,9p,1s,9s)しかないため、七対子(7種)は成立しない\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 全てが老頭牌(字牌以外の么九牌)で構成されていること\n const allRoutou = allBlocks.every((block) =>\n block.hais.every((k) => isYaochu(k) && isSuupai(k)),\n );\n if (!allRoutou) return false;\n\n return true;\n};\n\nexport const chinroutouDefinition: YakuDefinition = createYakuDefinition(\n CHINROUTOU_YAKU,\n checkChinroutou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst SHOUSANGEN_YAKU: Yaku = {\n name: \"Shousangen\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkShousangen = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const sangenpai: HaiKindId[] = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];\n\n // 1. 三元牌の刻子・槓子をカウント\n let sangenKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (sangenpai.includes(triplet.hais[0])) {\n sangenKoutsuCount++;\n }\n }\n\n // 2. 三元牌の雀頭があるかチェック\n const isSangenJantou = sangenpai.includes(hand.jantou.hais[0]);\n\n // 小三元の条件: 三元牌の刻子が2つ かつ 三元牌の雀頭が1つ\n return sangenKoutsuCount === 2 && isSangenJantou;\n};\n\nexport const shousangenDefinition: YakuDefinition = createYakuDefinition(\n SHOUSANGEN_YAKU,\n checkShousangen,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst DAISANGEN_YAKU: Yaku = {\n name: \"Daisangen\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkDaisangen = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const sangenpai: HaiKindId[] = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];\n\n // 1. 三元牌の刻子・槓子をカウント\n let sangenKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (sangenpai.includes(triplet.hais[0])) {\n sangenKoutsuCount++;\n }\n }\n\n // 大三元の条件: 三元牌の刻子が3つ全てあること\n return sangenKoutsuCount === 3;\n};\n\nexport const daisangenDefinition: YakuDefinition = createYakuDefinition(\n DAISANGEN_YAKU,\n checkDaisangen,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind } from \"../../../../types\";\n\nconst TSUUIISOU_YAKU: Yaku = {\n name: \"Tsuuiisou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst isJihai = (id: number): boolean => {\n return id >= HaiKind.Ton && id <= HaiKind.Chun;\n};\n\nconst checkTsuuiisou = (hand: HouraStructure): boolean => {\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else if (hand.type === \"Chiitoitsu\") {\n // 七対子の場合\n for (const pair of hand.pairs) {\n allHais.push(...pair.hais);\n }\n } else {\n // 国士無双など(国士は字一色にはなり得ないが、構造上は考慮)\n return false;\n }\n\n // 全ての牌が字牌であれば成立\n return allHais.every(isJihai);\n};\n\nexport const tsuuiisouDefinition: YakuDefinition = createYakuDefinition(\n TSUUIISOU_YAKU,\n checkTsuuiisou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind } from \"../../../../types\";\n\nconst RYUUIISOU_YAKU: Yaku = {\n name: \"Ryuuiisou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst GREEN_TILES = new Set<number>([\n HaiKind.SouZu2,\n HaiKind.SouZu3,\n HaiKind.SouZu4,\n HaiKind.SouZu6,\n HaiKind.SouZu8,\n HaiKind.Hatsu,\n]);\n\nconst isGreen = (id: number): boolean => {\n return GREEN_TILES.has(id);\n};\n\nconst checkRyuuiisou = (hand: HouraStructure): boolean => {\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else if (hand.type === \"Chiitoitsu\") {\n // 七対子の場合\n for (const pair of hand.pairs) {\n allHais.push(...pair.hais);\n }\n } else {\n // 国士無双など\n return false;\n }\n\n // 全ての牌が緑色牌であれば成立\n return allHais.every(isGreen);\n};\n\nexport const ryuuiisouDefinition: YakuDefinition = createYakuDefinition(\n RYUUIISOU_YAKU,\n checkRyuuiisou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst SHOUSUUSHII_YAKU: Yaku = {\n name: \"Shousuushii\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkShousuushii = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const windTiles: HaiKindId[] = [\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n ];\n\n // 1. 風牌の刻子・槓子をカウント\n let windKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (windTiles.includes(triplet.hais[0])) {\n windKoutsuCount++;\n }\n }\n\n // 2. 風牌の雀頭があるかチェック\n const isWindJantou = windTiles.includes(hand.jantou.hais[0]);\n\n // 小四喜の条件: 風牌の刻子が3つ かつ 風牌の雀頭が1つ\n // (合計で4種類の風牌が揃うことになる。例: 東東東 南南南 西西西 北北)\n return windKoutsuCount === 3 && isWindJantou;\n};\n\nexport const shousuushiiDefinition: YakuDefinition = createYakuDefinition(\n SHOUSUUSHII_YAKU,\n checkShousuushii,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst DAISUUSHII_YAKU: Yaku = {\n name: \"Daisuushii\",\n han: {\n // TODO: ダブル役満(26翻)とするかはルールによるため、一旦通常の役満として実装\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkDaisuushii = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const windTiles: HaiKindId[] = [\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n ];\n\n // 1. 風牌の刻子・槓子をカウント\n let windKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (windTiles.includes(triplet.hais[0])) {\n windKoutsuCount++;\n }\n }\n\n // 大四喜の条件: 風牌の刻子が4つ全てあること\n return windKoutsuCount === 4;\n};\n\nexport const daisuushiiDefinition: YakuDefinition = createYakuDefinition(\n DAISUUSHII_YAKU,\n checkDaisuushii,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst CHUUREN_POUTOU_YAKU: Yaku = {\n name: \"ChuurenPoutou\",\n han: {\n open: 0, // 門前限定\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkChuurenPoutou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n // 1. 門前でなければならない\n if (!context.isMenzen) {\n return false;\n }\n\n // 構造は Mentsu 手のみ(基本的には)\n // 構造解析結果がどうあれ、元の手牌構成が九蓮宝燈の形かどうかを確認する\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else {\n // 九蓮宝燈は通常、面子手の特殊形として扱われることが多いが、\n // 構造解析器が Mentsu として解釈できない場合も考慮すべきか?\n // 一旦 Mentsu 型として解釈されていることを前提とする\n // (九蓮宝燈は 111+234+567+8999+α のように分解可能なので Mentsu になるはず)\n return false;\n }\n\n // 2. 混一色チェック(全て同じ色、字牌なし)\n if (allHais.length === 0) return false;\n const firstHai = allHais[0];\n if (firstHai === undefined) return false;\n\n // 字牌が含まれていたらNG\n if (firstHai >= 27) return false;\n\n const suit = Math.floor(firstHai / 9); // 0, 1, 2\n\n for (const hai of allHais) {\n if (hai >= 27) return false; // 字牌混入\n if (Math.floor(hai / 9) !== suit) return false; // 色混在\n }\n\n // 3. 数牌のカウントチェック\n // 1が3枚以上, 9が3枚以上, 2-8が1枚以上\n const counts = Array(9).fill(0);\n for (const hai of allHais) {\n const num = hai % 9; // 0-8\n counts[num]++;\n }\n\n // 1 (index 0) >= 3\n if (counts[0] < 3) return false;\n // 9 (index 8) >= 3\n if (counts[8] < 3) return false;\n // 2-8 (index 1-7) >= 1\n for (let i = 1; i <= 7; i++) {\n if (counts[i] < 1) return false;\n }\n\n // 合計14枚で上記を満たしていれば、必ず九蓮宝燈の形になる\n // (3+3+7 = 13枚が必須パーツで、残り1枚は何でもよいため)\n\n return true;\n};\n\nexport const chuurenPoutouDefinition: YakuDefinition = createYakuDefinition(\n CHUUREN_POUTOU_YAKU,\n checkChuurenPoutou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst checkKokushi = (hand: HouraStructure): boolean => {\n return hand.type === \"Kokushi\";\n};\n\nconst KOKUSHI_HAN: YakuHanConfig = {\n closed: 13,\n open: 0,\n};\n\nexport const kokushiDefinition: YakuDefinition = createYakuDefinition(\n {\n name: \"KokushiMusou\",\n han: KOKUSHI_HAN,\n },\n checkKokushi,\n);\n","import { createYakuDefinition } from \"../../factory\";\n\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANSHOKU_DOUJUN_YAKU: Yaku = {\n name: \"SanshokuDoujun\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanshokuDoujun = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n if (shuntsuList.length < 3) {\n return false;\n }\n\n // 順子リストから3つの組み合わせを全てチェックし、三色同順の条件を満たすものを探す\n // 条件:\n // 1. 3つの順子がそれぞれ異なる色(萬子、筒子、索子)であること\n // 2. 3つの順子の構成数字が同じであること(例: 123m, 123p, 123s)\n\n // ヘルパーロジック:\n // HaiKindId の範囲: 0-8 (ManZu), 9-17 (PinZu), 18-26 (SouZu)\n // 9で割った商が色(0, 1, 2)、余りが数値(0-8)を表す\n\n for (let i = 0; i < shuntsuList.length; i++) {\n for (let j = i + 1; j < shuntsuList.length; j++) {\n for (let k = j + 1; k < shuntsuList.length; k++) {\n const s1 = shuntsuList[i];\n const s2 = shuntsuList[j];\n const s3 = shuntsuList[k];\n\n if (!s1 || !s2 || !s3) continue;\n\n const firstHai1 = s1.hais[0];\n const firstHai2 = s2.hais[0];\n const firstHai3 = s3.hais[0];\n\n const suit1 = Math.floor(firstHai1 / 9);\n const suit2 = Math.floor(firstHai2 / 9);\n const suit3 = Math.floor(firstHai3 / 9);\n\n // 異なる色(0, 1, 2)でなければならない\n const suits = new Set([suit1, suit2, suit3]);\n if (suits.size !== 3) continue;\n\n // 全て数牌(0, 1, 2)でなければならない\n // ※通常、Shuntsuに字牌は含まれないが、念のためチェック\n if (suit1 > 2 || suit2 > 2 || suit3 > 2) continue;\n\n // 数値(インデックス)が一致するかチェック\n const num1 = firstHai1 % 9;\n const num2 = firstHai2 % 9;\n const num3 = firstHai3 % 9;\n\n if (num1 === num2 && num2 === num3) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const sanshokuDoujunDefinition: YakuDefinition = createYakuDefinition(\n SANSHOKU_DOUJUN_YAKU,\n checkSanshokuDoujun,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANSHOKU_DOUKOU_YAKU: Yaku = {\n name: \"SanshokuDoukou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanshokuDoukou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n if (triplets.length < 3) {\n return false;\n }\n\n // 2. 刻子の組み合わせ(3つ)をチェック\n for (let i = 0; i < triplets.length; i++) {\n for (let j = i + 1; j < triplets.length; j++) {\n for (let k = j + 1; k < triplets.length; k++) {\n const t1 = triplets[i];\n const t2 = triplets[j];\n const t3 = triplets[k];\n\n if (!t1 || !t2 || !t3) continue;\n\n const id1 = t1.hais[0];\n const id2 = t2.hais[0];\n const id3 = t3.hais[0];\n\n // 字牌が含まれていたら対象外 (字牌ID >= 27)\n if (id1 >= 27 || id2 >= 27 || id3 >= 27) continue;\n\n const suit1 = Math.floor(id1 / 9);\n const suit2 = Math.floor(id2 / 9);\n const suit3 = Math.floor(id3 / 9);\n\n // ※ 0:萬子, 1:筒子, 2:索子\n const suits = new Set([suit1, suit2, suit3]);\n // 異なる3色でなければならない\n if (suits.size !== 3) continue;\n\n const num1 = id1 % 9;\n const num2 = id2 % 9;\n const num3 = id3 % 9;\n\n // 同じ数字でなければならない\n if (num1 === num2 && num2 === num3) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const sanshokuDoukouDefinition: YakuDefinition = createYakuDefinition(\n SANSHOKU_DOUKOU_YAKU,\n checkSanshokuDoukou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst IKKITSUUKAN_YAKU: Yaku = {\n name: \"Ikkitsuukan\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkIkkitsuukan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n if (shuntsuList.length < 3) {\n return false;\n }\n\n // 順子リストから3つの組み合わせを全てチェックし、一気通貫の条件を満たすものを探す\n // 条件:\n // 1. 3つの順子が全て同じ色(萬子、筒子、索子のいずれか)であること\n // 2. 3つの順子がそれぞれ 1-2-3, 4-5-6, 7-8-9 であること\n // (インデックスの余りが 0, 3, 6 となること)\n\n for (let i = 0; i < shuntsuList.length; i++) {\n for (let j = i + 1; j < shuntsuList.length; j++) {\n for (let k = j + 1; k < shuntsuList.length; k++) {\n const s1 = shuntsuList[i];\n const s2 = shuntsuList[j];\n const s3 = shuntsuList[k];\n\n if (!s1 || !s2 || !s3) continue;\n\n const firstHai1 = s1.hais[0];\n const firstHai2 = s2.hais[0];\n const firstHai3 = s3.hais[0];\n\n const suit1 = Math.floor(firstHai1 / 9);\n const suit2 = Math.floor(firstHai2 / 9);\n const suit3 = Math.floor(firstHai3 / 9);\n\n // 全て同じ色でなければならない\n if (suit1 !== suit2 || suit2 !== suit3) continue;\n\n // 字牌が含まれていないかチェック(念のため)\n if (suit1 > 2) continue;\n\n // 数値(インデックス)を取得\n const num1 = firstHai1 % 9;\n const num2 = firstHai2 % 9;\n const num3 = firstHai3 % 9;\n\n // 0 (1-2-3), 3 (4-5-6), 6 (7-8-9) が揃っていれば成立\n const nums = new Set([num1, num2, num3]);\n if (nums.has(0) && nums.has(3) && nums.has(6)) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const ikkitsuukanDefinition: YakuDefinition = createYakuDefinition(\n IKKITSUUKAN_YAKU,\n checkIkkitsuukan,\n);\n","import { isSuupai, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONITSU_YAKU: Yaku = {\n name: \"Honitsu\",\n han: {\n open: 2,\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonitsu = (hand: HouraStructure): boolean => {\n let blocks;\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // ブロック内の全ての牌をフラットな配列にする\n const allHais = blocks.flatMap((b) => b.hais);\n\n // 1. 字牌が少なくとも1つ含まれること(清一色の除外)\n const hasJihai = allHais.some((k) => kindIdToHaiType(k) === HaiType.Jihai);\n if (!hasJihai) return false;\n\n // 2. 数牌が全て同じ種類であること\n const suupais = allHais.filter((k) => isSuupai(k));\n\n // 数牌が含まれていない場合は字一色(または不成立)なので、ホンイツではない\n if (suupais.length === 0) return false;\n\n const firstSuupai = suupais[0];\n if (firstSuupai === undefined) return false;\n\n const firstSuupaiType = kindIdToHaiType(firstSuupai);\n const isAllSameType = suupais.every(\n (k) => kindIdToHaiType(k) === firstSuupaiType,\n );\n\n return isAllSameType;\n};\n\nexport const honitsuDefinition: YakuDefinition = createYakuDefinition(\n HONITSU_YAKU,\n checkHonitsu,\n);\n","import { isSuupai, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHINITSU_YAKU: Yaku = {\n name: \"Chinitsu\",\n han: {\n open: 5,\n closed: 6,\n } satisfies YakuHanConfig,\n};\n\nconst checkChinitsu = (hand: HouraStructure): boolean => {\n let blocks;\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // ブロック内の全ての牌をフラットな配列にする\n const allHais = blocks.flatMap((b) => b.hais);\n\n // 1. 字牌が含まれていないこと\n const hasJihai = allHais.some((k) => kindIdToHaiType(k) === HaiType.Jihai);\n if (hasJihai) return false;\n\n // 2. 数牌が全て同じ種類であること\n const suupais = allHais.filter((k) => isSuupai(k));\n\n // 数牌が含まれていない(字一色想定だが上記でJihaiチェック済みなので事実上ありえない)場合は不成立\n if (suupais.length === 0) return false;\n\n const firstSuupai = suupais[0];\n if (firstSuupai === undefined) return false;\n\n const firstSuupaiType = kindIdToHaiType(firstSuupai);\n const isAllSameType = suupais.every(\n (k) => kindIdToHaiType(k) === firstSuupaiType,\n );\n\n return isAllSameType;\n};\n\nexport const chinitsuDefinition: YakuDefinition = createYakuDefinition(\n CHINITSU_YAKU,\n checkChinitsu,\n);\n","import { HaiKind, HaiKindId } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nfunction createYakuhaiDefinition(\n name: \"Haku\" | \"Hatsu\" | \"Chun\",\n tile: HaiKindId,\n): YakuDefinition {\n const HAN_CONFIG: YakuHanConfig = { closed: 1, open: 1 };\n\n const check = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.type === \"Koutsu\" || mentsu.type === \"Kantsu\") {\n if (mentsu.hais[0] === tile) {\n return true;\n }\n }\n }\n return false;\n };\n\n return createYakuDefinition({ name, han: HAN_CONFIG }, check);\n}\n\nexport const hakuDefinition = createYakuhaiDefinition(\"Haku\", HaiKind.Haku);\nexport const hatsuDefinition = createYakuhaiDefinition(\"Hatsu\", HaiKind.Hatsu);\nexport const chunDefinition = createYakuhaiDefinition(\"Chun\", HaiKind.Chun);\n","import type { YakuDefinition, HouraStructure, HouraContext } from \"../../types\";\nimport { createYakuDefinition } from \"../../factory\";\n\nconst definition = {\n name: \"MenzenTsumo\",\n han: { open: 0, closed: 1 },\n} as const;\n\nexport const menzenTsumoDefinition: YakuDefinition = createYakuDefinition(\n definition,\n (hand: HouraStructure, context: HouraContext): boolean => {\n return context.isMenzen && !!context.isTsumo;\n },\n);\n","import { tanyaoDefinition } from \"./tanyao\";\nimport { pinfuDefinition } from \"./pinfu\";\nimport { iipeikouDefinition } from \"./iipeikou\";\nimport { ryanpeikouDefinition } from \"./ryanpeikou\";\nimport { sanankouDefinition } from \"./sanankou\";\nimport { suuankouDefinition } from \"./suuankou\";\nimport { sankantsuDefinition } from \"./sankantsu\";\nimport { suukantsuDefinition } from \"./suukantsu\";\nimport { toitoiDefinition } from \"./toitoi\";\nimport { chiitoitsuDefinition } from \"./chiitoitsu\";\nimport { honchanDefinition } from \"./honchan\";\nimport { junchanDefinition } from \"./junchan\";\nimport { honroutouDefinition } from \"./honroutou\";\nimport { chinroutouDefinition } from \"./chinroutou\";\nimport { shousangenDefinition } from \"./shousangen\";\nimport { daisangenDefinition } from \"./daisangen\";\nimport { tsuuiisouDefinition } from \"./tsuuiisou\";\nimport { ryuuiisouDefinition } from \"./ryuuiisou\";\nimport { shousuushiiDefinition } from \"./shousuushii\";\nimport { daisuushiiDefinition } from \"./daisuushii\";\nimport { chuurenPoutouDefinition } from \"./chuuren-poutou\";\nimport { kokushiDefinition } from \"./kokushi\";\nimport { sanshokuDoujunDefinition } from \"./sanshoku-doujun\";\nimport { sanshokuDoukouDefinition } from \"./sanshoku-doukou\";\nimport { ikkitsuukanDefinition } from \"./ikkitsuukan\";\nimport { honitsuDefinition } from \"./honitsu\";\nimport { chinitsuDefinition } from \"./chinitsu\";\nimport { hakuDefinition, hatsuDefinition, chunDefinition } from \"./yakuhai\";\nimport { menzenTsumoDefinition } from \"./menzen-tsumo\";\nimport type { YakuDefinition } from \"../../types\";\n\nexport * from \"./tanyao\";\nexport * from \"./pinfu\";\nexport * from \"./iipeikou\";\nexport * from \"./ryanpeikou\";\nexport * from \"./sanankou\";\nexport * from \"./suuankou\";\nexport * from \"./sankantsu\";\nexport * from \"./suukantsu\";\nexport * from \"./toitoi\";\nexport * from \"./chiitoitsu\";\nexport * from \"./honchan\";\nexport * from \"./junchan\";\nexport * from \"./honroutou\";\nexport * from \"./chinroutou\";\nexport * from \"./shousangen\";\nexport * from \"./daisangen\";\nexport * from \"./tsuuiisou\";\nexport * from \"./ryuuiisou\";\nexport * from \"./shousuushii\";\nexport * from \"./daisuushii\";\nexport * from \"./chuuren-poutou\";\nexport * from \"./kokushi\";\nexport * from \"./sanshoku-doujun\";\nexport * from \"./sanshoku-doukou\";\nexport * from \"./ikkitsuukan\";\nexport * from \"./honitsu\";\nexport * from \"./chinitsu\";\nexport * from \"./yakuhai\";\n\nexport const ALL_YAKU_DEFINITIONS: YakuDefinition[] = [\n tanyaoDefinition,\n pinfuDefinition,\n iipeikouDefinition,\n ryanpeikouDefinition,\n sanankouDefinition,\n suuankouDefinition,\n sankantsuDefinition,\n suukantsuDefinition,\n toitoiDefinition,\n chiitoitsuDefinition,\n honchanDefinition,\n junchanDefinition,\n honroutouDefinition,\n chinroutouDefinition,\n shousangenDefinition,\n daisangenDefinition,\n tsuuiisouDefinition,\n ryuuiisouDefinition,\n shousuushiiDefinition,\n daisuushiiDefinition,\n chuurenPoutouDefinition,\n kokushiDefinition,\n sanshokuDoujunDefinition,\n sanshokuDoukouDefinition,\n ikkitsuukanDefinition,\n honitsuDefinition,\n chinitsuDefinition,\n hakuDefinition,\n hatsuDefinition,\n chunDefinition,\n menzenTsumoDefinition,\n];\n","import type { Tehai14, HaiKindId } from \"../../types\";\nimport type { YakuResult, YakuName, Hansu, HouraStructure } from \"./types\";\n\nimport { getHouraStructures } from \"./lib/structures\";\nimport { isMenzen, isKazehai } from \"./utils\";\nimport { ALL_YAKU_DEFINITIONS } from \"./lib/definitions\";\nimport type { HouraContext } from \"./types\";\n\nexport type {\n HouraStructure,\n YakuResult,\n YakuName,\n Hansu,\n TehaiYaku,\n YakuHanConfig,\n Yakuhai,\n} from \"./types\";\nexport * from \"./lib\";\n\n/**\n * 役満の翻数閾値\n *\n * 麻雀において役満は13翻以上と定義される。\n * 数え役満(通常役の合計が13翻以上)とは異なり、\n * ここでは個々の役が単体で持つ翻数が13以上であるかを判定する基準として使用する。\n */\nconst YAKUMAN_HAN_THRESHOLD = 13;\n\n/**\n * 単一の和了構造に対して役を判定する\n *\n * 役満が成立している場合、通常役は除外して役満のみを返す。\n *\n * @param hand 和了構造(面子手、七対子、国士無双のいずれか)\n * @param context 和了コンテキスト\n * @returns 成立した役と翻数のリスト\n */\nexport function detectYakuForStructure(\n hand: HouraStructure,\n context: HouraContext,\n): YakuResult {\n const result: [YakuName, Hansu][] = [];\n\n for (const definition of ALL_YAKU_DEFINITIONS) {\n if (definition.isSatisfied(hand, context)) {\n const hansu = definition.getHansu(hand, context);\n if (hansu === 0) continue;\n result.push([definition.yaku.name, hansu]);\n }\n }\n\n // 一般ルール: 役満(13翻以上)が成立した場合、役満未満の通常役は複合しない。\n // 複数の役満が同時に成立した場合(例: 字一色 + 大四喜)、全ての役満を返す。\n //\n // NOTE: 複数役満の同時成立をダブル役満・トリプル役満として合算する特殊ルールには\n // 現在未対応。現在の実装では個々の役満をそのまま返しており、合算してダブル役満等\n // として扱う処理は行っていない。\n const hasYakuman = result.some(([, han]) => han >= YAKUMAN_HAN_THRESHOLD);\n if (hasYakuman) {\n return result.filter(([, han]) => han >= YAKUMAN_HAN_THRESHOLD);\n }\n\n return result;\n}\n\n/**\n * YakuResult から総翻数を計算する\n */\nfunction getTotalHan(\n yakuResult: readonly (readonly [YakuName, Hansu])[],\n): number {\n return yakuResult.reduce((sum, [, han]) => sum + han, 0);\n}\n\n/**\n * 手牌の構造役を検出する\n *\n * @param tehai 判定対象の手牌\n * @param agariHai 和了牌\n * @returns 成立した役と翻数のリスト(最も高得点となる解釈の結果)\n */\nexport function detectYaku(\n tehai: Tehai14,\n agariHai: HaiKindId,\n bakaze?: HaiKindId,\n jikaze?: HaiKindId,\n doraMarkers?: readonly HaiKindId[],\n uraDoraMarkers?: readonly HaiKindId[],\n isTsumo?: boolean,\n): YakuResult {\n const context: HouraContext = {\n isMenzen: isMenzen(tehai),\n agariHai,\n bakaze: bakaze !== undefined && isKazehai(bakaze) ? bakaze : undefined,\n jikaze: jikaze !== undefined && isKazehai(jikaze) ? jikaze : undefined,\n doraMarkers: doraMarkers ?? [],\n uraDoraMarkers: uraDoraMarkers ?? [],\n isTsumo,\n };\n\n const structuralInterpretations = getHouraStructures(tehai);\n\n let bestResult: YakuResult = [];\n let maxHan = -1;\n\n for (const hand of structuralInterpretations) {\n const currentResult = detectYakuForStructure(hand, context);\n const currentHan = getTotalHan(currentResult);\n\n if (currentHan > maxHan) {\n maxHan = currentHan;\n bestResult = currentResult;\n }\n }\n\n return bestResult;\n}\n","import { asHaiKindId, isTuple3, isTuple4 } from \"../../utils/assertions\";\nimport { ShoushaiError, TahaiError } from \"../../errors\";\nimport {\n CompletedMentsu,\n FuroType,\n HaiId,\n HaiKind,\n HaiKindDistribution,\n HaiKindId,\n Kantsu,\n Koutsu,\n MentsuType,\n Shuntsu,\n Tacha,\n} from \"../../types\";\nimport { haiIdToKindId, haiKindToNumber } from \"../../core/hai\";\n\n// 1つ以上の数字 + 1つのサフィックス (m, p, s, z)\nconst BLOCK_PATTERN = \"\\\\d+[mpsz]\";\n// 標準的なMSPZ: ブロックの繰り返し (空文字列も許容)\nconst STANDARD_MSPZ_REGEX = new RegExp(`^(${BLOCK_PATTERN})*$`);\n\n// 拡張パート: [...] または (...) で囲まれたブロック (囲みの中も同様のブロック構造)\n// 注: 現在のパーサー実装では、[] の中は単純な BLOCK_PATTERN の連続を許容しているか?\n// parseMentsuString: mspzStringToHaiKindIds を呼んでいる。\n// mspzStringToHaiKindIds: \\d+[mpsz] をパースする。\n// したがって、[] の中身も BLOCK_PATTERN の繰り返しであるべき。\nconst EXTENDED_BLOCK_PATTERN = `(${BLOCK_PATTERN}|\\\\[(${BLOCK_PATTERN})+\\\\]|\\\\((${BLOCK_PATTERN})+\\\\))`;\nconst EXTENDED_MSPZ_REGEX = new RegExp(`^${EXTENDED_BLOCK_PATTERN}*$`);\n\n/**\n * 標準的なMSPZ形式の文字列(拡張記法を含まない)\n */\nexport type MspzString = string & { readonly __brand: \"MspzString\" };\n\n/**\n * 拡張MSPZ形式の文字列\n * 通常のMSPZに加え、`[...]` (副露) や `(...)` (暗槓) を含むことができます。\n */\nexport type ExtendedMspzString = string & {\n readonly __brand: \"ExtendedMspzString\";\n};\n\n/**\n * 文字列が拡張MSPZ形式(`[` または `(` を含み、かつ正しい書式)かどうかを判定します。\n * @param input 判定対象の文字列\n * @returns 拡張MSPZ形式であれば true\n */\nexport function isExtendedMspz(input: string): input is ExtendedMspzString {\n // ブラケットを含む、かつ拡張書式にマッチする場合のみ true\n // (ブラケットを含まない適正なMSPZは、ここでの定義上 ExtendedMspzString とはみなさない = MspzString と区別する)\n return (\n (input.includes(\"[\") || input.includes(\"(\")) &&\n EXTENDED_MSPZ_REGEX.test(input)\n );\n}\n\n/**\n * 文字列を ExtendedMspzString 型として扱います。\n * 拡張MSPZ形式であることを検証します。\n *\n * @param input 変換対象の文字列\n * @throws {Error} 拡張MSPZ形式でない場合\n * @returns ExtendedMspzString\n */\nexport function asExtendedMspz(input: string): ExtendedMspzString {\n if (!isExtendedMspz(input)) {\n throw new Error(`Invalid Extended MSPZ string: ${input}`);\n }\n return input;\n}\n\n/**\n * 文字列が標準的なMSPZ形式(拡張記法を含まず、かつ正しい書式)かどうかを判定します。\n * @param input 判定対象の文字列\n * @returns 標準MSPZ形式であれば true\n */\nexport function isMspz(input: string): input is MspzString {\n return STANDARD_MSPZ_REGEX.test(input);\n}\n\n/**\n * 拡張MSPZ解析結果\n */\nexport interface ExtendedMspzParseResult {\n readonly closed: readonly HaiKindId[];\n readonly exposed: readonly CompletedMentsu[];\n}\n\n/**\n * 拡張MSPZ形式の文字列を解析して、純手牌と副露のリストに変換します。\n *\n * @param input 拡張MSPZ形式の文字列\n * @returns 解析結果オブジェクト\n */\nexport function parseExtendedMspz(input: string): ExtendedMspzParseResult {\n const closedParts: string[] = [];\n const exposed: CompletedMentsu[] = [];\n\n let current = \"\";\n let mode: \"closed\" | \"open\" | \"ankan\" = \"closed\";\n\n // 文字単位でパース\n for (const char of input) {\n if (char === \"[\") {\n if (mode !== \"closed\")\n throw new Error(\"Nested brackets are not supported\");\n if (current.length > 0) closedParts.push(current);\n // current = \"\"; // OLD\n current = \"[\"; // NEW: Start capturing with bracket\n mode = \"open\";\n } else if (char === \"]\") {\n if (mode !== \"open\") throw new Error(\"Unexpected closing bracket ']'\");\n current += \"]\"; // NEW: End capturing with bracket\n // exposed.push(parseMentsuString(current, \"open\")); // OLD\n exposed.push(parseMentsuFromExtendedMspz(asExtendedMspz(current))); // NEW\n current = \"\";\n mode = \"closed\";\n } else if (char === \"(\") {\n if (mode !== \"closed\")\n throw new Error(\"Nested parentheses are not supported\");\n if (current.length > 0) closedParts.push(current);\n // current = \"\"; // OLD\n current = \"(\"; // NEW\n mode = \"ankan\";\n } else if (char === \")\") {\n if (mode !== \"ankan\")\n throw new Error(\"Unexpected closing parenthesis ')'\");\n current += \")\"; // NEW\n // exposed.push(parseMentsuString(current, \"ankan\")); // OLD\n exposed.push(parseMentsuFromExtendedMspz(asExtendedMspz(current))); // NEW\n current = \"\";\n mode = \"closed\";\n } else {\n current += char;\n }\n }\n\n // 残りのclosed部分\n if (current.length > 0) {\n if (mode !== \"closed\") throw new Error(\"Unclosed bracket or parenthesis\");\n closedParts.push(current);\n }\n\n // closed部分を結合してパース\n const fullClosedMspz = closedParts.join(\"\");\n const closedIds = parseMspzToHaiKindIds(asMspz(fullClosedMspz));\n\n return {\n closed: closedIds,\n exposed: exposed,\n };\n}\n\n/**\n * 副露・暗槓のブロック文字列(例: \"[123m]\", \"(11z)\")を解析してCompletedMentsuを生成する内部関数\n * 括弧の種類から副露か暗槓かを自動判定します。\n */\nfunction parseMentsuFromExtendedMspz(\n block: ExtendedMspzString,\n): CompletedMentsu {\n let mode: \"open\" | \"ankan\";\n let content: string;\n\n if (block.startsWith(\"[\") && block.endsWith(\"]\")) {\n mode = \"open\";\n content = block.slice(1, -1);\n } else if (block.startsWith(\"(\") && block.endsWith(\")\")) {\n mode = \"ankan\";\n content = block.slice(1, -1);\n } else {\n throw new Error(\n `Invalid Extended MSPZ block: ${block} (must be [...] or (...))`,\n );\n }\n\n // 中身は標準MSPZ形式である必要がある\n const ids = parseMspzToHaiKindIds(asMspz(content));\n if (ids.length === 0) {\n throw new Error(\"Empty mentsu specification\");\n }\n\n // 枚数チェック & 種類判定\n const count = ids.length;\n const isAllSame = ids.every((id) => id === ids[0]);\n\n // 暗槓 (Ankan)\n if (mode === \"ankan\") {\n if (count !== 4 || !isAllSame) {\n throw new Error(`Invalid Ankan: ${block} (must be 4 identical tiles)`);\n }\n if (!isTuple4(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const kantsu: Kantsu = {\n type: MentsuType.Kantsu,\n hais: ids,\n // Ankan has no furo info (or minimal)\n };\n return kantsu;\n }\n\n // 副露 (Open)\n if (count === 4 && isAllSame) {\n // Daiminkan\n if (!isTuple4(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const kantsu: Kantsu = {\n type: MentsuType.Kantsu,\n hais: ids,\n furo: { type: FuroType.Daiminkan, from: Tacha.Toimen }, // Default\n };\n return kantsu;\n } else if (count === 3 && isAllSame) {\n // Pon\n if (!isTuple3(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const koutsu: Koutsu = {\n type: MentsuType.Koutsu,\n hais: ids,\n furo: { type: FuroType.Pon, from: Tacha.Toimen }, // Default\n };\n return koutsu;\n } else if (count === 3) {\n // Chi (Should check continuity, strictly speaking but relying on user for now or implicit check)\n // Minimal check: sorted? mspzStringToHaiKindIds sorts by default?\n // mspzStringToHaiKindIds does NOT sort across different suits, but \"123m\" results in sorted array.\n // Let's assume valid sequence for now.\n if (!isTuple3(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const shuntsu: Shuntsu = {\n type: MentsuType.Shuntsu,\n hais: ids,\n furo: { type: FuroType.Chi, from: Tacha.Kamicha }, // Default\n };\n return shuntsu;\n }\n\n throw new Error(\n `Invalid Mentsu specification: ${block} (must be 3 or 4 tiles)`,\n );\n}\n\n/**\n * 13枚の牌種ID配列を 34種の牌種分布(所持数分布)に変換します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function createDistribution(\n hais: readonly HaiKindId[],\n): HaiKindDistribution {\n if (hais.length < 13) {\n throw new ShoushaiError(\n `Invalid number of tiles: expected 13, got ${hais.length}`,\n );\n }\n if (hais.length > 13) {\n throw new TahaiError(\n `Invalid number of tiles: expected 13, got ${hais.length}`,\n );\n }\n\n const counts = Array.from({ length: 34 }, () => 0);\n\n for (const kind of hais) {\n counts[kind] = (counts[kind] ?? 0) + 1;\n }\n\n // Tupleへの変換はアサーションが必要だが、生成ロジックが保証しているため安全\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return counts as unknown as HaiKindDistribution;\n}\n\n/**\n * 13枚の牌ID配列を 34種の牌種分布(所持数分布)に変換します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function haiIdsToDistribution(\n // Branded type makes linter think it's mutable object, but it's primitive number.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n hais: readonly HaiId[],\n): HaiKindDistribution {\n const kinds = hais.map(haiIdToKindId);\n return createDistribution(kinds);\n}\n\n/**\n * 13枚の牌種ID配列を MSPZ形式の文字列(例: \"123m456p...\")に変換します。\n * すべての牌をソートして表記します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function haiKindIdsToMspzString(hais: readonly HaiKindId[]): string {\n const counts = createDistribution(hais);\n let result = \"\";\n\n // 萬子\n const manzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.ManZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) manzu.push(num);\n }\n }\n if (manzu.length > 0) {\n result += manzu.join(\"\") + \"m\";\n }\n\n // 筒子\n const pinzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.PinZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) pinzu.push(num);\n }\n }\n if (pinzu.length > 0) {\n result += pinzu.join(\"\") + \"p\";\n }\n\n // 索子\n const souzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.SouZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) souzu.push(num);\n }\n }\n if (souzu.length > 0) {\n result += souzu.join(\"\") + \"s\";\n }\n\n // 字牌\n const jihai: number[] = [];\n for (let i = 0; i < 7; i++) {\n const kind = asHaiKindId(HaiKind.Ton + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n // 字牌は 1-7 で表すことが多い\n const num = i + 1;\n jihai.push(num);\n }\n }\n if (jihai.length > 0) {\n result += jihai.join(\"\") + \"z\";\n }\n\n return result;\n}\n\n/**\n * 文字列を MspzString 型として扱います。\n * 標準的なMSPZ形式(拡張記法を含まない)であることを検証します。\n *\n * @param input 変換対象の文字列\n * @throws {Error} 拡張記法が含まれている場合\n * @returns MspzString\n */\nexport function asMspz(input: string): MspzString {\n if (!isMspz(input)) {\n throw new Error(`Invalid MSPZ string: ${input}`);\n }\n return input;\n}\n\n/**\n * MSPZ形式の文字列(例: \"123m456p\")を解析して HaiKindId の配列に変換します。\n * 主にテストデータの作成用途で使用します。\n *\n * @param mspz MSPZ形式の文字列\n * @returns HaiKindId の配列\n */\nexport function parseMspzToHaiKindIds(mspz: MspzString): HaiKindId[] {\n const result: HaiKindId[] = [];\n let currentNumbers: number[] = [];\n\n for (const char of mspz) {\n if (char >= \"0\" && char <= \"9\") {\n currentNumbers.push(parseInt(char, 10));\n } else {\n // Suffix handling\n let base: HaiKindId | undefined;\n\n switch (char) {\n case \"m\":\n base = HaiKind.ManZu1;\n break;\n case \"p\":\n base = HaiKind.PinZu1;\n break;\n case \"s\":\n base = HaiKind.SouZu1;\n break;\n case \"z\":\n base = HaiKind.Ton;\n break;\n default:\n // 無視する\n currentNumbers = [];\n continue;\n }\n\n for (const num of currentNumbers) {\n if (char === \"z\") {\n // 字牌: 1=東(27), ... 7=中(33)\n if (num >= 1 && num <= 7) {\n result.push(asHaiKindId(base + num - 1));\n }\n } else {\n // 数牌: 1-9\n if (num >= 1) {\n result.push(asHaiKindId(base + num - 1));\n }\n }\n }\n currentNumbers = [];\n }\n }\n\n return result;\n}\n","import {\n parseExtendedMspz as internalParseExtendedMspz,\n parseMspzToHaiKindIds,\n asMspz,\n asExtendedMspz,\n} from \"./mspz\";\nimport type { Tehai } from \"../../types\";\n\nexport type { MspzString, ExtendedMspzString } from \"./mspz\";\nexport { isExtendedMspz } from \"./mspz\";\n\n/**\n * 標準的なMSPZ文字列(例: \"123m456p...\")を解析して手牌オブジェクトを生成します。\n * 副露牌は含まれません。\n *\n * @param input MSPZ形式の文字列\n * @returns 手牌オブジェクト\n */\nexport function parseMspz(input: string): Tehai {\n const ids = parseMspzToHaiKindIds(asMspz(input));\n return {\n closed: ids,\n exposed: [],\n };\n}\n\n/**\n * 拡張MSPZ文字列(例: \"123m[123p]...\")を解析して手牌オブジェクトを生成します。\n * 副露牌(`[...]`)や暗槓(`(...)`)を含めることができます。\n *\n * @param input 拡張MSPZ形式の文字列\n * @returns 手牌オブジェクト\n */\nexport function parseExtendedMspz(input: string): Tehai {\n // parseExtendedMspz returns { closed: HaiKindId[], exposed: CompletedMentsu[] }\n // which is compatible with Tehai interface.\n return internalParseExtendedMspz(asExtendedMspz(input));\n}\n","/**\n * 符計算に関する定数定義\n */\n\n// 符底 (FuTei)\nexport const FU_BASE = {\n NORMAL: 20,\n CHIITOITSU: 25,\n KOKUSHI: 20, // 便宜上\n} as const;\n\n// 和了符 (AgariFu)\nexport const FU_AGARI = {\n TSUMO: 2,\n MENZEN_RON: 10,\n} as const;\n\n// 雀頭符 (JantouFu)\nexport const FU_JANTOU = {\n YAKUHAI: 2,\n // 連風牌の扱い(設定により4符または2符)\n DOUBLE_WIND_CAP: 2,\n} as const;\n\n// 待ち符 (MachiFu)\nexport const FU_MACHI = {\n KANCHAN: 2,\n PENCHAN: 2,\n TANKI: 2,\n RYANMEN: 0,\n SHANPON: 0,\n} as const;\n\n// 面子符 (MentsuFu)\n// [Yaochu/Suupai]_[Open/Closed]\nexport const FU_KOUTSU = {\n SUUPAI_OPEN: 2,\n SUUPAI_CLOSED: 4,\n YAOCHU_OPEN: 4,\n YAOCHU_CLOSED: 8,\n} as const;\n\nexport const FU_KANTSU = {\n SUUPAI_OPEN: 8,\n SUUPAI_CLOSED: 16,\n YAOCHU_OPEN: 16,\n YAOCHU_CLOSED: 32,\n} as const;\n\n// 例外処理用定数\nexport const FU_PINFU_TSUMO = 20;\nexport const FU_OPEN_PINFU_GLAZE = 30; // 喰いタン平和形などの20符切り上げ\n","import type { FuResult, FuDetails } from \"../types\";\nimport { FU_BASE } from \"../constants\";\n\n/**\n * 七対子の符を計算する\n */\nexport function calculateChiitoitsuFu(): FuResult {\n const details: FuDetails = {\n base: FU_BASE.CHIITOITSU,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n return {\n total: FU_BASE.CHIITOITSU,\n details,\n };\n}\n","import type { FuResult, FuDetails } from \"../types\";\nimport { FU_BASE } from \"../constants\";\n\n/**\n * 国士無双の符を計算する\n * (点数計算上は役満固定だが、便宜上符底のみを返す)\n */\nexport function calculateKokushiFu(): FuResult {\n const details: FuDetails = {\n base: FU_BASE.KOKUSHI,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n return {\n total: FU_BASE.KOKUSHI,\n details,\n };\n}\n","import type { FuResult } from \"../types\";\nimport type { HouraContext } from \"../../../../yaku/types\";\nimport type { MentsuHouraStructure } from \"../../../../yaku/types\";\nimport { isYaochu } from \"../../../../../core/hai\";\nimport { HaiKind, type Fu } from \"../../../../../types\";\nimport { classifyMachi } from \"../../../../../core/machi\";\nimport {\n FU_BASE,\n FU_KOUTSU,\n FU_KANTSU,\n FU_JANTOU,\n FU_MACHI,\n FU_AGARI,\n FU_PINFU_TSUMO,\n FU_OPEN_PINFU_GLAZE,\n} from \"../constants\";\n\n/** 有効な符の値 */\nconst VALID_FU_VALUES: readonly Fu[] = [\n 20, 25, 30, 40, 50, 60, 70, 80, 90, 100, 110,\n];\n\n/**\n * 数値を Fu 型に変換する(無効な値の場合はエラー)\n */\nfunction toFu(value: number): Fu {\n const fu = VALID_FU_VALUES.find((f) => f === value);\n if (fu === undefined) {\n throw new Error(`Invalid fu value: ${value}`);\n }\n return fu;\n}\n\n/**\n * 面子手の符を計算する\n *\n * @param hand 面子手の構造\n * @param context 和了コンテキスト\n * @param isPinfu 平和成立フラグ\n */\nexport function calculateMentsuFu(\n hand: MentsuHouraStructure,\n context: HouraContext,\n isPinfu: boolean,\n): FuResult {\n const details = {\n base: FU_BASE.NORMAL,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n\n // 1. 面子符 (MentsuFu)\n for (const mentsu of hand.fourMentsu) {\n let fu = 0;\n const isYaochuMentsu = isYaochu(mentsu.hais[0]);\n\n if (mentsu.type === \"Koutsu\") {\n // 刻子\n const openFu = isYaochuMentsu\n ? FU_KOUTSU.YAOCHU_OPEN\n : FU_KOUTSU.SUUPAI_OPEN;\n const closedFu = isYaochuMentsu\n ? FU_KOUTSU.YAOCHU_CLOSED\n : FU_KOUTSU.SUUPAI_CLOSED;\n\n fu = closedFu;\n\n // 明刻判定\n let isOpen = !!mentsu.furo;\n if (!isOpen && !context.isTsumo) {\n // ロン和了で、かつその牌を含む刻子であれば明刻扱い\n if (mentsu.hais.includes(context.agariHai)) {\n isOpen = true;\n }\n }\n\n if (isOpen) {\n fu = openFu;\n }\n details.mentsu += fu;\n } else if (mentsu.type === \"Kantsu\") {\n // 槓子\n const openFu = isYaochuMentsu\n ? FU_KANTSU.YAOCHU_OPEN\n : FU_KANTSU.SUUPAI_OPEN;\n const closedFu = isYaochuMentsu\n ? FU_KANTSU.YAOCHU_CLOSED\n : FU_KANTSU.SUUPAI_CLOSED;\n\n fu = closedFu;\n if (mentsu.furo) {\n fu = openFu;\n }\n details.mentsu += fu;\n }\n }\n\n // 2. 雀頭符 (JantouFu)\n const headHai = hand.jantou.hais[0];\n let jantouFu = 0;\n\n if (\n headHai === HaiKind.Haku ||\n headHai === HaiKind.Hatsu ||\n headHai === HaiKind.Chun\n ) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n if (headHai === context.bakaze) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n if (headHai === context.jikaze) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n\n // 連風牌の加算上限\n if (jantouFu > FU_JANTOU.DOUBLE_WIND_CAP) {\n jantouFu = FU_JANTOU.DOUBLE_WIND_CAP;\n }\n details.jantou = jantouFu;\n\n // 3. 待ち符 (MachiFu)\n const machiType = classifyMachi(hand, context.agariHai);\n if (machiType === \"Kanchan\") details.machi = FU_MACHI.KANCHAN;\n else if (machiType === \"Penchan\") details.machi = FU_MACHI.PENCHAN;\n else if (machiType === \"Tanki\") details.machi = FU_MACHI.TANKI;\n else details.machi = 0; // Ryanmen, Shanpon\n\n // 4. 和了符 (AgariFu)\n if (context.isTsumo) {\n if (!isPinfu) {\n details.agari = FU_AGARI.TSUMO;\n }\n } else {\n if (context.isMenzen) {\n details.agari = FU_AGARI.MENZEN_RON;\n }\n }\n\n // 合計\n let sum =\n details.base +\n details.mentsu +\n details.jantou +\n details.machi +\n details.agari;\n\n // 平和ツモ例外\n if (isPinfu && context.isTsumo) {\n return { total: FU_PINFU_TSUMO, details };\n }\n\n // 切り上げ (喰いタン平和形など)\n if (sum === 20 && !context.isTsumo && !context.isMenzen) {\n sum = FU_OPEN_PINFU_GLAZE;\n } else {\n sum = Math.ceil(sum / 10) * 10;\n }\n\n return {\n total: toFu(sum),\n details,\n };\n}\n","import type { HouraStructure, HouraContext } from \"../../../yaku/types\";\nimport type { FuResult } from \"./types\";\nimport { calculateChiitoitsuFu } from \"./lib/chiitoitsu\";\nimport { calculateKokushiFu } from \"./lib/kokushi\";\nimport { calculateMentsuFu } from \"./lib/mentsu\";\n\n/**\n * 手牌の符を計算する (calculateFu)\n *\n * @param hand 和了形の手牌構造\n * @param context 和了コンテキスト (場風、自風、ツモ/ロン等)\n * @param isPinfu 平和が成立しているかどうか (平和ツモ20符例外の適用に必要)\n * @returns 符計算結果\n */\nexport function calculateFu(\n hand: HouraStructure,\n context: HouraContext,\n isPinfu = false,\n): FuResult {\n // 1. 七対子 (ChiiToitsu)\n if (hand.type === \"Chiitoitsu\") {\n return calculateChiitoitsuFu();\n }\n\n // 2. 国士無双 (Kokushi)\n if (hand.type === \"Kokushi\") {\n return calculateKokushiFu();\n }\n\n // 3. 面子手 (Mentsu)\n return calculateMentsuFu(hand, context, isPinfu);\n}\n","export const SCORE_OYA_MULTIPLIER = 1.5; // 親は1.5倍\n\nexport const SCORE_BASE_MANGAN = 2000;\nexport const SCORE_BASE_HANEMAN = 3000;\nexport const SCORE_BASE_BAIMAN = 4000;\nexport const SCORE_BASE_SANBAIMAN = 6000;\nexport const SCORE_BASE_YAKUMAN = 8000;\n\n/**\n * 満貫以上の判定基準となる翻数\n *\n * \"5翻\" であれば符数に関わらず満貫以上が確定するため 5 を設定しています。\n *\n * Q: 4翻は満貫ではないのか?\n * A: 4翻でも符数が高ければ(40符以上など)満貫になりますが、以下のようなケースでは満貫(8000点)に届きません。\n * - 七対子 (25符): 6400点\n * - 門前ツモ・愚形など (30符): 7900点 (※切り上げ満貫なしの場合)\n * - 鳴き手・ロン (30符): 7700点 (※切り上げ満貫なしの場合)\n *\n * そのため、4翻以下の場合は計算による基本点が基準値(2000)を超えたかどうかで判定します。\n */\nexport const HAN_MANGAN = 5;\nexport const HAN_HANEMAN = 6;\nexport const HAN_BAIMAN = 8;\nexport const HAN_SANBAIMAN = 11;\nexport const HAN_YAKUMAN = 13;\n\nexport const HAS_YAKUMAN = 13; // 便宜上の翻数\nexport const HAS_DOUBLE_YAKUMAN = 26;\n\n// 切り上げ満貫の閾値 (30符4翻 = 1920 -> 2000? 60符3翻=1920)\n// 一般的には 2000点(子) / 3000点(親) が満貫の最低点(ベース)\n// 符計算による基本点が 2000 を超えたら満貫\nexport const BASE_SCORE_LIMIT = 2000;\n","import type { Fu, HaiKindId, Kazehai } from \"../../types\";\nimport type { HouraContext } from \"../yaku/types\";\n\n/**\n * 点数計算用コンテキスト (ScoreContext)\n *\n * HouraContext を拡張し、点数計算に必要な追加情報を持つ。\n *\n * HouraContext は役判定に特化しており、isOya は役の成立・翻数に影響しない。\n * isOya は支払い計算(親ロン/子ロン、親ツモ/子ツモの倍率差)にのみ必要なため、\n * 点数計算用のコンテキストとして分離している。\n */\nexport interface ScoreContext extends HouraContext {\n /** 和了者が親かどうか */\n readonly isOya: boolean;\n}\n\nexport interface ScoreCalculationConfig {\n /** 和了牌 */\n agariHai: HaiKindId;\n /** ツモ和了かどうか (必須) */\n isTsumo: boolean;\n /** 自風 (必須) */\n jikaze: Kazehai;\n /** 場風 (必須) */\n bakaze: Kazehai;\n /** ドラ表示牌 (必須、なければ空配列) */\n doraMarkers: readonly HaiKindId[];\n /** 裏ドラ表示牌 (任意) */\n uraDoraMarkers?: readonly HaiKindId[];\n}\n\n/** ロン和了時の支払い */\nexport interface Ron {\n type: \"ron\";\n /** 振り込んだプレイヤーが支払う点数 */\n amount: number;\n}\n\n/** 子のツモ和了時の支払い */\nexport interface KoTsumo {\n type: \"koTsumo\";\n /** [子の支払い, 親の支払い] */\n readonly amount: readonly [number, number];\n}\n\n/** 親のツモ和了時の支払い */\nexport interface OyaTsumo {\n type: \"oyaTsumo\";\n /** 子全員が支払う点数(オール) */\n amount: number;\n}\n\n/** 支払い情報 */\nexport type Payment = Ron | KoTsumo | OyaTsumo;\n\n/**\n * 点数レベル (ScoreLevel)\n *\n * 翻数と基本点から決まる点数の区分。\n * 満貫以上では符に関係なく固定の基本点が適用される。\n */\nexport const ScoreLevel = {\n /** 満貫未満(通常計算) */\n Normal: \"Normal\",\n /** 満貫(5翻、または基本点2000以上) */\n Mangan: \"Mangan\",\n /** 跳満(6-7翻) */\n Haneman: \"Haneman\",\n /** 倍満(8-10翻) */\n Baiman: \"Baiman\",\n /** 三倍満(11-12翻) */\n Sanbaiman: \"Sanbaiman\",\n /** 役満(13翻以上) */\n Yakuman: \"Yakuman\",\n /** ダブル役満(26翻以上、または役満複合) */\n DoubleYakuman: \"DoubleYakuman\",\n} as const;\n\nexport type ScoreLevel = (typeof ScoreLevel)[keyof typeof ScoreLevel];\n\nexport interface ScoreResult {\n han: number;\n fu: Fu;\n scoreLevel: ScoreLevel;\n payment: Payment;\n}\n","import { type Tehai14, type Fu, HaiKind } from \"../../types\";\nimport { NoYakuError } from \"../../errors\";\nimport { countDora } from \"../../core/dora\";\nimport { getHouraStructures } from \"../yaku/lib/structures\";\nimport { detectYakuForStructure } from \"../yaku\";\nimport { calculateFu } from \"./lib/fu\";\nimport type { FuResult } from \"./lib/fu/types\";\nimport { isMenzen } from \"../yaku/utils\";\nimport {\n BASE_SCORE_LIMIT,\n HAN_BAIMAN,\n HAN_HANEMAN,\n HAN_MANGAN,\n HAN_SANBAIMAN,\n HAN_YAKUMAN,\n SCORE_BASE_BAIMAN,\n SCORE_BASE_HANEMAN,\n SCORE_BASE_MANGAN,\n SCORE_BASE_SANBAIMAN,\n SCORE_BASE_YAKUMAN,\n} from \"./constants\";\nimport {\n ScoreLevel,\n type ScoreCalculationConfig,\n type ScoreContext,\n type ScoreResult,\n type Payment,\n type Ron,\n type KoTsumo,\n type OyaTsumo,\n} from \"./types\";\nexport type {\n ScoreCalculationConfig,\n ScoreResult,\n Payment,\n Ron,\n KoTsumo,\n OyaTsumo,\n};\nexport { ScoreLevel };\n\n/**\n * 100点単位で切り上げる\n */\nfunction ceil100(points: number): number {\n return Math.ceil(points / 100) * 100;\n}\n\n/**\n * 基本点を計算する\n * 基本点 = 符 × 2^(2+翻)\n *\n * 基本点は子がツモ和了したときの他の子1人あたりの支払い点数に相当する。\n * - 子ツモ: 子の支払い = 基本点, 親の支払い = 基本点 × 2\n * - 子ロン: 支払い = 基本点 × 4\n * - 親ツモ: 各子の支払い = 基本点 × 2\n * - 親ロン: 支払い = 基本点 × 6\n */\nexport function calculateBasePoints(fu: Fu, han: number): number {\n return fu * Math.pow(2, 2 + han);\n}\n\n/**\n * 支払い情報から和了者が受け取る総点数を計算する\n */\nexport function getPaymentTotal(payment: Readonly<Payment>): number {\n switch (payment.type) {\n case \"ron\":\n return payment.amount;\n case \"koTsumo\":\n return payment.amount[0] * 2 + payment.amount[1];\n case \"oyaTsumo\":\n return payment.amount * 3;\n }\n}\n\n/**\n * 翻数と基本点から点数レベルを判定する\n *\n * @param han 翻数\n * @param basePoints 基本点(符 × 2^(2+翻))\n * @returns 点数レベル\n */\nexport function getScoreLevel(han: number, basePoints: number): ScoreLevel {\n if (han >= 26) {\n return ScoreLevel.DoubleYakuman;\n }\n if (han >= HAN_YAKUMAN) {\n return ScoreLevel.Yakuman;\n }\n if (han >= HAN_SANBAIMAN) {\n return ScoreLevel.Sanbaiman;\n }\n if (han >= HAN_BAIMAN) {\n return ScoreLevel.Baiman;\n }\n if (han >= HAN_HANEMAN) {\n return ScoreLevel.Haneman;\n }\n if (han >= HAN_MANGAN || basePoints >= BASE_SCORE_LIMIT) {\n return ScoreLevel.Mangan;\n }\n return ScoreLevel.Normal;\n}\n\n/**\n * 点数レベルに対応する基本点を取得する\n *\n * @param level 点数レベル\n * @returns 基本点(Normal の場合は null)\n */\nfunction getLimitBasePoints(level: ScoreLevel): number | null {\n switch (level) {\n case ScoreLevel.DoubleYakuman:\n return SCORE_BASE_YAKUMAN * 2;\n case ScoreLevel.Yakuman:\n return SCORE_BASE_YAKUMAN;\n case ScoreLevel.Sanbaiman:\n return SCORE_BASE_SANBAIMAN;\n case ScoreLevel.Baiman:\n return SCORE_BASE_BAIMAN;\n case ScoreLevel.Haneman:\n return SCORE_BASE_HANEMAN;\n case ScoreLevel.Mangan:\n return SCORE_BASE_MANGAN;\n case ScoreLevel.Normal:\n return null;\n }\n}\n\n/**\n * 点数計算用コンテキストを作成する\n *\n * @param tehai 手牌 (14枚)\n * @param config 点数計算の設定\n * @returns 点数計算用コンテキスト\n */\nfunction createScoreContext(\n tehai: Tehai14,\n config: Readonly<ScoreCalculationConfig>,\n): ScoreContext {\n return {\n isMenzen: isMenzen(tehai),\n agariHai: config.agariHai,\n bakaze: config.bakaze,\n jikaze: config.jikaze,\n isTsumo: config.isTsumo,\n isOya: config.jikaze === HaiKind.Ton,\n doraMarkers: config.doraMarkers,\n ...(config.uraDoraMarkers ? { uraDoraMarkers: config.uraDoraMarkers } : {}),\n };\n}\n\n/**\n * 手牌とコンテキストから点数を計算する(公開API)\n *\n * 手牌の構造解析を行い、最も高点となる解釈を採用して点数を返します。\n *\n * 注: 同一手牌で「翻数が高いが符が低い解釈」と「翻数が低いが符が高い解釈」が\n * 両立するケースは実質的に存在しないため、翻数最大の解釈を採用しています。\n *\n * @param tehai 手牌 (14枚)\n * @param config 点数計算の設定 (場風、自風、ドラなど)\n * @returns 点数計算結果\n */\nexport function calculateScoreForTehai(\n tehai: Tehai14,\n config: Readonly<ScoreCalculationConfig>,\n): ScoreResult {\n const context = createScoreContext(tehai, config);\n const structuralInterpretations = getHouraStructures(tehai);\n let bestResult: ScoreResult | null = null;\n let maxTotalPoints = -1;\n\n for (const hand of structuralInterpretations) {\n // 1. 役の判定\n const yakuResult = detectYakuForStructure(hand, context);\n const yakuHansu = yakuResult.reduce((sum, [, han]) => sum + han, 0);\n\n // 役がない場合はこの構造は不成立\n if (yakuHansu === 0) continue;\n\n // 2. 符の計算\n const isPinfu = yakuResult.some(([name]) => name === \"Pinfu\");\n const fuResult = calculateFu(hand, context, isPinfu);\n\n // 3. ドラの計算\n const dora = countDora(tehai, context.doraMarkers);\n\n // 4. 点数計算\n const result = calculateScoreFromHanAndFu(\n yakuHansu,\n fuResult,\n dora,\n context,\n );\n const total = getPaymentTotal(result.payment);\n\n if (total > maxTotalPoints) {\n maxTotalPoints = total;\n bestResult = result;\n }\n }\n\n if (!bestResult) {\n throw new NoYakuError();\n }\n\n return bestResult;\n}\n\n/**\n * 基本的な点数計算ロジック (内部用・テスト用)\n */\nexport function calculateScoreFromHanAndFu(\n yakuHansu: number,\n fuResult: Readonly<FuResult>,\n dora: number,\n context: Readonly<ScoreContext>,\n): ScoreResult {\n const totalHan = yakuHansu + dora;\n const fu = fuResult.total;\n\n // 基本点の計算\n const rawBasePoints = calculateBasePoints(fu, totalHan);\n\n // 点数レベルの判定\n const scoreLevel = getScoreLevel(totalHan, rawBasePoints);\n\n // 満貫以上なら固定の基本点、それ以外は計算値を使用\n const basePoints = getLimitBasePoints(scoreLevel) ?? rawBasePoints;\n\n // 支払い計算\n const payment = calculatePayment(basePoints, context);\n\n return {\n han: totalHan,\n fu: fu,\n scoreLevel,\n payment,\n };\n}\n\n/**\n * 基本点から支払い情報を計算する\n */\nfunction calculatePayment(\n basePoints: number,\n context: Readonly<ScoreContext>,\n): Payment {\n if (context.isTsumo) {\n if (context.isOya) {\n // 親ツモ: オール (基本点 * 2)\n const allPay = ceil100(basePoints * 2);\n return { type: \"oyaTsumo\", amount: allPay };\n } else {\n // 子ツモ: 親の支払い = 基本点 * 2, 子の支払い = 基本点 * 1\n const parentPay = ceil100(basePoints * 2);\n const childPay = ceil100(basePoints * 1);\n return { type: \"koTsumo\", amount: [childPay, parentPay] };\n }\n } else {\n // ロン和了\n if (context.isOya) {\n // 親ロン: 基本点 * 6\n const pay = ceil100(basePoints * 6);\n return { type: \"ron\", amount: pay };\n } else {\n // 子ロン: 基本点 * 4\n const pay = ceil100(basePoints * 4);\n return { type: \"ron\", amount: pay };\n }\n }\n}\n"],"names":["definition","parseExtendedMspz","internalParseExtendedMspz"],"mappings":"AAyEO,MAAM,UAAU;AAAA,EACrB,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AACR;AAOO,MAAM,UAAU;AAAA,EACrB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AA6DO,MAAM,QAAQ;AAAA,EACnB,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AACX;AAOO,MAAM,WAAW;AAAA,EACtB,KAAK;AAAA,EACL,KAAK;AAAA,EACL,WAAW;AAAA,EACX,OAAO;AACT;AAsBO,MAAM,aAAa;AAAA,EACxB,SAAS;AAAA;AAAA,EACT,QAAQ;AAAA;AAAA,EACR,QAAQ;AAAA;AAAA,EACR,QAAQ;AAAA;AAAA,EACR,OAAO;AAAA;AACT;AC7NO,MAAM,qBAAqB,MAAM;AAAA;AAAA;AAAA;AAAA,EAItC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAIZ,WAAO,eAAe,MAAM,aAAa,SAAS;AAAA,EACpD;AACF;AAMO,MAAM,sBAAsB,aAAa;AAAA;AAAA;AAAA;AAAA,EAI9C,YAAY,UAAU,wBAAwB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,cAAc,SAAS;AAAA,EACrD;AACF;AAMO,MAAM,mBAAmB,aAAa;AAAA;AAAA;AAAA;AAAA,EAI3C,YAAY,UAAU,uBAAuB;AAC3C,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAMO,MAAM,6BAA6B,aAAa;AAAA;AAAA;AAAA;AAAA,EAIrD,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,qBAAqB,SAAS;AAAA,EAC5D;AACF;AAMO,MAAM,6BAA6B,aAAa;AAAA;AAAA;AAAA;AAAA,EAIrD,YAAY,UAAU,gBAAgB;AACpC,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,qBAAqB,SAAS;AAAA,EAC5D;AACF;AAMO,MAAM,gCAAgC,aAAa;AAAA;AAAA;AAAA;AAAA,EAIxD,YAAY,UAAU,mBAAmB;AACvC,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,wBAAwB,SAAS;AAAA,EAC/D;AACF;AAQO,MAAM,oBAAoB,aAAa;AAAA;AAAA;AAAA;AAAA,EAI5C,YAAY,UAAU,YAAY;AAChC,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,YAAY,SAAS;AAAA,EACnD;AACF;AAOO,MAAM,oBAAoB,YAAY;AAAA;AAAA;AAAA;AAAA,EAI3C,YAAY,UAAU,eAAe;AACnC,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,WAAO,eAAe,MAAM,YAAY,SAAS;AAAA,EACnD;AACF;AC9HO,SAAS,SAAY,KAA2C;AACrE,SAAO,IAAI,WAAW;AACxB;AAKO,SAAS,SAAY,KAA8C;AACxE,SAAO,IAAI,WAAW;AACxB;AAKO,SAAS,SAAY,KAAiD;AAC3E,SAAO,IAAI,WAAW;AACxB;AAMO,SAAS,YAAY,IAAuB;AAEjD,SAAO;AACT;ACxBO,SAAS,gBAAgB,MAA0B;AACxD,MAAI,QAAQ,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AACpD,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,QAAQ,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AACpD,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,QAAQ,QAAQ,UAAU,QAAQ,QAAQ,QAAQ;AACpD,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO,QAAQ;AACjB;AASO,SAAS,cAAc,IAAsB;AAClD,MAAI,KAAK,GAAI,QAAO,YAAY,KAAK,MAAM,KAAK,CAAC,CAAC;AAClD,MAAI,KAAK,GAAI,QAAO,YAAY,KAAK,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC;AAC7D,MAAI,KAAK,IAAK,QAAO,YAAY,KAAK,OAAO,KAAK,MAAM,CAAC,IAAI,EAAE;AAC/D,SAAO,YAAY,KAAK,OAAO,KAAK,OAAO,CAAC,IAAI,EAAE;AACpD;AAMO,SAAS,gBAAgB,MAAqC;AACnE,QAAM,OAAO,gBAAgB,IAAI;AACjC,MAAI,SAAS,QAAQ,MAAO,QAAO;AAEnC,MAAI,SAAS,QAAQ,MAAO,QAAO,OAAO,QAAQ,SAAS;AAC3D,MAAI,SAAS,QAAQ,MAAO,QAAO,OAAO,QAAQ,SAAS;AAE3D,SAAO,OAAO,QAAQ,SAAS;AACjC;AAKO,SAAS,SAAS,MAA0B;AACjD,SAAO,gBAAgB,IAAI,MAAM,QAAQ;AAC3C;AAKO,MAAM,kBAAkB;AAAA,EAC7B,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACV;AAKO,SAAS,SAAS,MAA0B;AACjD,SAAO,gBAAgB,KAAK,CAAC,MAAM,MAAM,IAAI;AAC/C;AC3DO,SAAS,oBACd,OACQ;AACR,SAAO,MAAM,OAAO,SAAS,MAAM,QAAQ,SAAS;AACtD;AAKO,SAAS,aAAa,MAAiD;AAC5E,QAAM,SAAS,MAAM,KAAK,EAAE,QAAQ,GAAA,GAAM,MAAM,CAAC;AACjD,aAAW,OAAO,MAAM;AACtB,WAAO,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;AAAA,EACrC;AAEA,SAAO;AACT;AAOO,SAAS,gBACd,OACM;AACN,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,cAAA;AAAA,EACZ;AACA,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,WAAA;AAAA,EACZ;AACA,yBAAuB,KAAK;AAC9B;AASO,SAAS,gBACd,OACM;AACN,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,cAAA;AAAA,EACZ;AACA,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,WAAA;AAAA,EACZ;AACA,yBAAuB,KAAK;AAC9B;AAWO,SAAS,cACd,OACM;AACN,QAAM,QAAQ,oBAAoB,KAAK;AACvC,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,cAAA;AAAA,EACZ;AACA,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,WAAA;AAAA,EACZ;AACA,yBAAuB,KAAK;AAC9B;AAUA,SAAS,uBACP,OACM;AACN,QAAM,UAAoB;AAAA,IACxB,GAAG,MAAM;AAAA,IACT,GAAG,MAAM,QAAQ,QAAQ,CAAC,MAAM,EAAE,IAAI;AAAA,EAAA;AAUxC,QAAM,cAAc,QAAQ,KAAK,CAAC,MAAM,IAAI,EAAE;AAE9C,MAAI,aAAa;AAEf,UAAM,YAAY,IAAI,IAAI,OAAO;AACjC,QAAI,UAAU,SAAS,QAAQ,QAAQ;AACrC,YAAM,IAAI,qBAAA;AAAA,IACZ;AAAA,EACF;AAGA,QAAM,6BAAa,IAAA;AACnB,aAAW,OAAO,SAAS;AAezB,QAAI,OAAe;AACnB,QAAI,MAAM,IAAI;AACZ,UAAI,MAAM,GAAI,QAAO,KAAK,MAAM,MAAM,CAAC;AAAA,eAC9B,MAAM,GAAI,QAAO,KAAK,OAAO,MAAM,MAAM,CAAC,IAAI;AAAA,eAC9C,MAAM,IAAK,QAAO,KAAK,OAAO,MAAM,MAAM,CAAC,IAAI;AAAA,kBAC5C,KAAK,OAAO,MAAM,OAAO,CAAC,IAAI;AAAA,IAC5C;AAEA,UAAM,UAAU,OAAO,IAAI,IAAI,KAAK;AACpC,QAAI,UAAU,IAAI,GAAG;AACnB,YAAM,IAAI,wBAAA;AAAA,IACZ;AACA,WAAO,IAAI,MAAM,UAAU,CAAC;AAAA,EAC9B;AACF;AAKO,SAAS,UACd,OACqB;AACrB,MAAI;AACF,oBAAgB,KAAK;AACrB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,UACd,OACqB;AACrB,MAAI;AACF,oBAAgB,KAAK;AACrB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;ACjLO,SAAS,eAAe,SAAwC;AACrE,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAE/B,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAClB,MAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,EAAG,QAAO;AAEzD,QAAM,QAAQ,gBAAgB,CAAC;AAC/B,QAAM,QAAQ,gBAAgB,CAAC;AAC/B,QAAM,QAAQ,gBAAgB,CAAC;AAE/B,MAAI,UAAU,SAAS,UAAU,MAAO,QAAO;AAE/C,QAAM,OAAO,gBAAgB,CAAC;AAC9B,QAAM,OAAO,gBAAgB,CAAC;AAC9B,QAAM,OAAO,gBAAgB,CAAC;AAE9B,MAAI,SAAS,UAAa,SAAS,UAAa,SAAS;AACvD,WAAO;AAGT,QAAM,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAItD,MAAI,CAAC,SAAS,MAAM,EAAG,QAAO;AAC9B,SAAO,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC;AAClE;AAMO,SAAS,cAAc,SAAwC;AACpE,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAClB,SAAO,MAAM,KAAK,MAAM;AAC1B;AAMO,SAAS,cAAc,SAAwC;AACpE,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI;AACrB,SAAO,MAAM,KAAK,MAAM,KAAK,MAAM;AACrC;AAMO,SAAS,cAAc,SAAwC;AACpE,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,CAAC,IAAI;AACf,SAAO,MAAM;AACf;AAMO,SAAS,aAAa,SAAwC;AACnE,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAC/B,QAAM,CAAC,GAAG,CAAC,IAAI;AAGf,MAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,EAAG,QAAO;AAGzC,QAAM,QAAQ,gBAAgB,CAAC;AAC/B,QAAM,QAAQ,gBAAgB,CAAC;AAC/B,MAAI,UAAU,MAAO,QAAO;AAE5B,QAAM,OAAO,gBAAgB,CAAC;AAC9B,QAAM,OAAO,gBAAgB,CAAC;AAE9B,MAAI,SAAS,UAAa,SAAS,OAAW,QAAO;AAErD,QAAM,OAAO,KAAK,IAAI,OAAO,IAAI;AAEjC,SAAO,SAAS,KAAK,SAAS;AAChC;ACpFO,SAAS,YAAY,WAAiC;AAC3D,QAAM,OAAO,gBAAgB,SAAS;AAEtC,MAAI,SAAS,QAAQ,OAAO;AAC1B,QAAI,cAAc,QAAQ,OAAQ,QAAO,QAAQ;AACjD,WAAO,YAAY,YAAY,CAAC;AAAA,EAClC;AAEA,MAAI,SAAS,QAAQ,OAAO;AAC1B,QAAI,cAAc,QAAQ,OAAQ,QAAO,QAAQ;AACjD,WAAO,YAAY,YAAY,CAAC;AAAA,EAClC;AAEA,MAAI,SAAS,QAAQ,OAAO;AAC1B,QAAI,cAAc,QAAQ,OAAQ,QAAO,QAAQ;AACjD,WAAO,YAAY,YAAY,CAAC;AAAA,EAClC;AAIA,MAAI,cAAc,QAAQ,IAAK,QAAO,QAAQ;AAC9C,MAAI,aAAa,QAAQ,OAAO,YAAY,QAAQ,KAAK;AACvD,WAAO,YAAY,YAAY,CAAC;AAAA,EAClC;AAGA,MAAI,cAAc,QAAQ,KAAM,QAAO,QAAQ;AAC/C,MAAI,aAAa,QAAQ,QAAQ,YAAY,QAAQ,MAAM;AACzD,WAAO,YAAY,YAAY,CAAC;AAAA,EAClC;AAGA,SAAO;AACT;AAQO,SAAS,UACd,OACA,YACQ;AACR,MAAI,QAAQ;AAEZ,QAAM,WAAW,WAAW,IAAI,WAAW;AAG3C,aAAW,OAAO,MAAM,QAAQ;AAC9B,eAAW,QAAQ,UAAU;AAC3B,UAAI,QAAQ,KAAM;AAAA,IACpB;AAAA,EACF;AAGA,aAAW,UAAU,MAAM,SAAS;AAClC,eAAW,OAAO,OAAO,MAAM;AAC7B,iBAAW,QAAQ,UAAU;AAC3B,YAAI,QAAQ,KAAM;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAIA,SAAO;AACT;AC7DO,SAAS,cACd,MACA,UACuB;AACvB,MAAI,KAAK,SAAS,SAAU,QAAO;AAGnC,MAAI,KAAK,OAAO,KAAK,SAAS,QAAQ,GAAG;AACvC,WAAO;AAAA,EACT;AAGA,aAAW,UAAU,KAAK,YAAY;AACpC,QAAI,OAAO,SAAS,WAAW;AAC7B,YAAM,QAAQ,oBAAoB,QAAQ,QAAQ;AAClD,UAAI,MAAO,QAAO;AAAA,IACpB,OAAO;AAGL,UAAI,OAAO,KAAK,SAAS,QAAQ,GAAG;AAClC,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBACP,SACA,UACuB;AACvB,QAAM,EAAE,SAAS;AACjB,MAAI,CAAC,KAAK,SAAS,QAAQ,EAAG,QAAO;AAErC,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI;AAElB,MAAI,aAAa,GAAG;AAElB,UAAM,OAAO,IAAI;AACjB,QAAI,SAAS,EAAG,QAAO;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,GAAG;AAElB,UAAM,OAAO,IAAI;AACjB,QAAI,SAAS,EAAG,QAAO;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,GAAG;AAElB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;ACpEO,SAAS,2BAA2B,OAAwB;AAIjE,kBAAgB,KAAK;AAKrB,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,aAAa,MAAM,MAAM;AAE3C,MAAI,QAAQ;AACZ,MAAI,QAAQ;AAEZ,aAAW,SAAS,WAAW;AAC7B,QAAI,QAAQ,GAAG;AACb;AAAA,IACF;AACA,QAAI,SAAS,GAAG;AACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,IAAI;AAGlB,MAAI,QAAQ,GAAG;AACb,eAAW,IAAI;AAAA,EACjB;AAEA,SAAO;AACT;AC1BO,SAAS,wBAAwB,OAAwB;AAI9D,kBAAgB,KAAK;AAGrB,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,aAAa,MAAM,MAAM;AAItC,MAAI,oBAAoB;AACxB,MAAI,gBAAgB;AAEpB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,OAAO,YAAY,CAAC;AAC1B,QAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,CAAC;AACpB,QAAI,UAAU,UAAa,QAAQ,GAAG;AACpC;AACA,UAAI,SAAS,GAAG;AACd,wBAAgB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,gBAAgB,IAAI;AAGtC,SAAO,KAAK,oBAAoB;AAClC;AC9CO,SAAS,yBAAyB,OAAsB;AAE7D,gBAAc,KAAK;AAEnB,QAAM,SAAS,aAAa,MAAM,MAAM;AAExC,QAAM,gBAA0B,MAAM,KAAK,MAAM;AACjD,QAAM,eAAe,MAAM,QAAQ;AAGnC,MAAI,aAAa,IAAI,IAAI;AAGzB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,SAAK,cAAc,CAAC,KAAK,MAAM,GAAG;AAChC,oBAAc,CAAC,KAAK,cAAc,CAAC,KAAK,KAAK;AAC7C,YAAM,EAAE,GAAG,MAAM,aAAa,aAAa;AAC3C,YAAM,gBAAgB,eAAe;AACrC,YAAM,kBAAkB,KAAK,IAAI,IAAI,eAAe,CAAC;AACrD,YAAM,UAAU,IAAI,IAAI,gBAAgB,kBAAkB;AAC1D,mBAAa,KAAK,IAAI,YAAY,OAAO;AACzC,oBAAc,CAAC,KAAK,cAAc,CAAC,KAAK,KAAK;AAAA,IAC/C;AAAA,EACF;AAGA;AACE,UAAM,EAAE,GAAG,MAAM,aAAa,aAAa;AAC3C,UAAM,gBAAgB,eAAe;AACrC,UAAM,kBAAkB,KAAK,IAAI,IAAI,eAAe,CAAC;AACrD,UAAM,UAAU,IAAI,IAAI,gBAAgB;AACxC,iBAAa,KAAK,IAAI,YAAY,OAAO;AAAA,EAC3C;AAEA,SAAO;AACT;AAaA,SAAS,aAAa,QAAyC;AAC7D,MAAI,WAAW;AACf,MAAI,aAA2B,EAAE,GAAG,GAAG,GAAG,EAAA;AAE1C,QAAM,IAAI,CAAC,GAAG,MAAM;AAEpB,QAAM,SAAS,CAAC,OAAe,MAAc;AAE3C,QAAI,SAAS,IAAI;AACf,YAAM,IAAI,YAAY,CAAC;AACvB,YAAM,QAAQ,IAAI,IAAI;AACtB,UAAI,QAAQ,UAAU;AACpB,mBAAW;AACX,qBAAa,EAAE,GAAG,EAAA;AAAA,MACpB;AACA;AAAA,IACF;AAGA,SAAK,EAAE,KAAK,KAAK,OAAO,GAAG;AACzB,aAAO,QAAQ,GAAG,CAAC;AACnB;AAAA,IACF;AAGA,SAAK,EAAE,KAAK,KAAK,MAAM,GAAG;AACxB,QAAE,KAAK,KAAK,EAAE,KAAK,KAAK,KAAK;AAC7B,aAAO,OAAO,IAAI,CAAC;AACnB,QAAE,KAAK,KAAK,EAAE,KAAK,KAAK,KAAK;AAAA,IAC/B;AAGA,QAAI,QAAQ,IAAI;AACd,YAAM,MAAM,QAAQ;AACpB,UAAI,MAAM,GAAG;AACX,aACG,EAAE,KAAK,KAAK,KAAK,MACjB,EAAE,QAAQ,CAAC,KAAK,KAAK,MACrB,EAAE,QAAQ,CAAC,KAAK,KAAK,GACtB;AACA,YAAE,KAAK,KAAK,EAAE,KAAK,KAAK,KAAK;AAC7B,YAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK;AACrC,YAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK;AACrC,iBAAO,OAAO,IAAI,CAAC;AACnB,YAAE,KAAK,KAAK,EAAE,KAAK,KAAK,KAAK;AAC7B,YAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK;AACrC,YAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,KAAK;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAGA,WAAO,QAAQ,GAAG,CAAC;AAAA,EACrB;AAEA,SAAO,GAAG,CAAC;AACX,SAAO;AACT;AAKA,SAAS,YAAY,QAAmC;AACtD,MAAI,SAAS;AAEb,QAAM,IAAI,CAAC,GAAG,MAAM;AAEpB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,SAAK,EAAE,CAAC,KAAK,OAAO,EAAG;AAGvB,QAAI,IAAI,IAAI;AACV,YAAM,MAAM,IAAI;AAEhB,UAAI,MAAM,GAAG;AACX,aAAK,EAAE,CAAC,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,KAAK,KAAK,GAAG;AAC1C,YAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK;AACrB,YAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK;AAC7B;AAAA,QACF;AAAA,MACF;AAGA,UAAI,MAAM,GAAG;AACX,aAAK,EAAE,CAAC,KAAK,KAAK,MAAM,EAAE,IAAI,CAAC,KAAK,KAAK,GAAG;AAC1C,YAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK;AACrB,YAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK;AAC7B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,EAAE,CAAC,KAAK,MAAM,GAAG;AACpB,QAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK;AACrB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;ACvIO,SAAS,iBACd,OACA,gBAAgB,MAChB,aAAa,MACL;AAER,kBAAgB,KAAK;AAErB,QAAM,oBAAoB,gBACtB,2BAA2B,KAAK,IAChC;AACJ,QAAM,iBAAiB,aAAa,wBAAwB,KAAK,IAAI;AACrE,QAAM,gBAAgB,yBAAyB,KAAK;AAEpD,SAAO,KAAK,IAAI,mBAAmB,gBAAgB,aAAa;AAClE;AC1BO,SAAS,UAAU,OAA6B;AACrD,QAAM,iBAAiB,yBAAyB,KAAK;AACrD,QAAM,aAA0B,CAAA;AAGhC,QAAM,UAAuB;AAAA,IAC3B,GAAG,MAAM;AAAA,IACT,GAAG,MAAM,QAAQ,QAAQ,CAAC,MAAM,EAAE,IAAI;AAAA,EAAA;AAExC,QAAM,YAAY,aAAa,OAAO;AAGtC,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAE3B,UAAM,OAAO;AAGb,QAAI,UAAU,IAAI,KAAK,GAAG;AACxB;AAAA,IACF;AAWA,UAAM,YAAY,CAAC,GAAG,MAAM,QAAQ,IAAI;AACxC,UAAM,WAAW;AAAA,MACf,QAAQ;AAAA,MACR,SAAS,MAAM;AAAA,IAAA;AAKjB,UAAM,aAAa,yBAAyB,QAAQ;AAEpD,QAAI,aAAa,gBAAgB;AAC/B,iBAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;AC9BO,SAAS,8BACd,OACwB;AACxB,kBAAgB,KAAK;AAGrB,QAAM,SAAS,CAAC,GAAG,aAAa,MAAM,MAAM,CAAC;AAC7C,QAAM,UAAkC,CAAA;AAGxC,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAE3B,UAAM,OAAO;AACb,SAAK,OAAO,IAAI,KAAK,MAAM,GAAG;AAG5B,aAAO,IAAI,KAAM;AAGjB,YAAM,sBAAsB,IAAI,MAAM,QAAQ;AAC9C,YAAM,aAAa,sBAAsB,QAAQ,mBAAmB;AAGpE,iBAAW,gBAAgB,YAAY;AAErC,cAAM,iBAAiB,CAAC,GAAG,cAAc,GAAG,MAAM,OAAO;AAGzD,YAAI,SAAS,cAAc,GAAG;AAC5B,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,YAAY;AAAA,YACZ,QAAQ,EAAE,MAAM,UAAU,MAAM,CAAC,MAAM,IAAI,EAAA;AAAA,UAAE,CAC9C;AAAA,QACH;AAAA,MACF;AAIA,aAAO,IAAI,KAAM;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,sBAEP,QACA,eACqB;AACrB,MAAI,kBAAkB,GAAG;AAEvB,UAAM,YAAY,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC;AACtD,WAAO,cAAc,IAAI,CAAC,CAAA,CAAE,IAAI,CAAA;AAAA,EAClC;AAGA,MAAI,aAAa;AACjB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,SAAK,OAAO,CAAC,KAAK,KAAK,GAAG;AACxB,mBAAa;AACb;AAAA,IACF;AAAA,EACF;AAEA,MAAI,eAAe,IAAI;AAErB,WAAO,CAAA;AAAA,EACT;AAEA,QAAM,UAA+B,CAAA;AAErC,QAAM,OAAO;AAGb,OAAK,OAAO,IAAI,KAAK,MAAM,GAAG;AAE5B,WAAO,IAAI,KAAM;AACjB,UAAM,QAAQ,sBAAsB,QAAQ,gBAAgB,CAAC;AAC7D,UAAM,SAAiB,EAAE,MAAM,UAAU,MAAM,CAAC,MAAM,MAAM,IAAI,EAAA;AAChE,eAAW,QAAQ,OAAO;AACxB,cAAQ,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;AAAA,IAChC;AAEA,WAAO,IAAI,KAAM;AAAA,EACnB;AAIA,MAAI,OAAO,MAAM,OAAO,KAAK,GAAG;AAC9B,UAAM,KAAK;AAEX,UAAM,KAAM,OAAO;AAEnB,UAAM,KAAM,OAAO;AAEnB,SAAK,OAAO,EAAE,KAAK,KAAK,MAAM,OAAO,EAAE,KAAK,KAAK,GAAG;AAElD,aAAO,EAAE,KAAM;AAEf,aAAO,EAAE,KAAM;AAEf,aAAO,EAAE,KAAM;AAEf,YAAM,QAAQ,sBAAsB,QAAQ,gBAAgB,CAAC;AAC7D,YAAM,UAAmB,EAAE,MAAM,WAAW,MAAM,CAAC,IAAI,IAAI,EAAE,EAAA;AAC7D,iBAAW,QAAQ,OAAO;AACxB,gBAAQ,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;AAAA,MACjC;AAGA,aAAO,EAAE,KAAM;AAEf,aAAO,EAAE,KAAM;AAEf,aAAO,EAAE,KAAM;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;AChJO,SAAS,gCACd,OAC4B;AAE5B,MAAI,MAAM,QAAQ,SAAS,UAAU,CAAA;AAErC,QAAM,SAAS,aAAa,MAAM,MAAM;AACxC,QAAM,QAAkB,CAAA;AAExB,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAE3B,UAAM,OAAO;AACb,UAAM,QAAQ,OAAO,IAAI;AAEzB,QAAI,UAAU,GAAG;AACf,YAAM,KAAK,EAAE,MAAM,UAAU,MAAM,CAAC,MAAM,IAAI,GAAG;AAAA,IACnD,WAAW,UAAU,GAAG;AAItB,aAAO,CAAA;AAAA,IACT,WAAW,QAAQ,GAAG;AAEpB,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,EAAG,QAAO,CAAA;AAE/B,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA;AAAA,MAEN;AAAA,IAAA;AAAA,EASF;AAEJ;AC3CO,SAAS,6BACd,OACyB;AAEzB,MAAI,MAAM,QAAQ,SAAS,UAAU,CAAA;AAErC,QAAM,SAAS,aAAa,MAAM,MAAM;AACxC,QAAM,aAA0B,CAAA;AAChC,MAAI;AAEJ,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAE3B,UAAM,OAAO;AAEb,UAAM,QAAQ,OAAO,IAAI;AAEzB,QAAI,QAAQ,GAAG;AACb,UAAI,CAAC,SAAS,IAAI,UAAU,CAAA;AAE5B,UAAI,UAAU,GAAG;AACf,mBAAW,KAAK,IAAI;AAAA,MACtB,WAAW,UAAU,GAAG;AACtB,YAAI,WAAW,OAAW,QAAO,CAAA;AACjC,iBAAS;AACT,mBAAW,KAAK,IAAI;AAAA,MACtB,OAAO;AACL,eAAO,CAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,MAAM,WAAW,eAAkB,CAAA;AAE7D,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,MACR;AAAA,IAAA;AAAA,EACF;AAEJ;AClCO,SAAS,mBAAmB,OAAkC;AACnE,SAAO;AAAA,IACL,GAAG,8BAA8B,KAAK;AAAA,IACtC,GAAG,gCAAgC,KAAK;AAAA,IACxC,GAAG,6BAA6B,KAAK;AAAA,EAAA;AAEzC;ACHO,SAAS,SAAS,OAAyB;AAEhD,MAAI,MAAM,QAAQ,WAAW,GAAG;AAC9B,WAAO;AAAA,EACT;AAIA,SAAO,MAAM,QAAQ,MAAM,CAAC,MAAM;AAChC,WAAO,EAAE,SAAS,YAAY,CAAC,EAAE;AAAA,EACnC,CAAC;AACH;AAOO,SAAS,UAAU,IAA8B;AACtD,SACE,OAAO,QAAQ,OACf,OAAO,QAAQ,OACf,OAAO,QAAQ,OACf,OAAO,QAAQ;AAEnB;AChCO,SAAS,qBACd,MACA,OACgB;AAChB,SAAO;AAAA,IACL;AAAA,IACA,aAAa;AAAA,IACb,UAAU,CAAC,MAAM,YAAY;AAC3B,UAAI,CAAC,MAAM,MAAM,OAAO,GAAG;AACzB,eAAO;AAAA,MACT;AACA,aAAO,QAAQ,WAAW,KAAK,IAAI,SAAS,KAAK,IAAI;AAAA,IACvD;AAAA,EAAA;AAEJ;ACfA,MAAM,cAAoB;AAAA,EACxB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,cAAc,CAAC,SAAkC;AACrD,MAAI,KAAK,SAAS,SAAU,QAAO;AAGnC,MAAI,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC,EAAG,QAAO;AAG1C,aAAW,UAAU,KAAK,YAAY;AACpC,QAAI,OAAO,KAAK,KAAK,QAAQ,EAAG,QAAO;AAAA,EACzC;AAEA,SAAO;AACT;AAEO,MAAM,mBAAmC;AAAA,EAC9C;AAAA,EACA;AACF;ACpBA,MAAM,aAAmB;AAAA,EACvB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,aAAuE,CAC3E,MACA,YACG;AAEH,MAAI,CAAC,QAAQ,SAAU,QAAO;AAC9B,MAAI,KAAK,SAAS,SAAU,QAAO;AAInC,QAAM,aAAa,KAAK,OAAO,KAAK,CAAC;AAErC,MAAI,QAAQ,WAAW,UAAa,QAAQ,WAAW,QAAW;AAChE,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,cAA2B;AAAA,IAC/B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EAAA;AAGV,MAAI,YAAY,SAAS,UAAU,EAAG,QAAO;AAG7C,MAAI,CAAC,KAAK,WAAW,MAAM,CAAC,MAAoB,EAAE,SAAS,SAAS;AAClE,WAAO;AAGT,QAAM,WAAW,cAAc,MAAM,QAAQ,QAAQ;AAErD,SAAO,aAAa;AACtB;AAEO,MAAM,kBAAkC;AAAA,EAC7C;AAAA,EACA;AACF;ACtDA,MAAM,eAAqB;AAAA,EACzB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,gBAAgB,CAAC,SAAkC;AACvD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,WAAW;AAAA,IAClC,CAAC,WAA8B,OAAO,SAAS;AAAA,EAAA;AAIjD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,EACT;AAcA,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,aAAS,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC/C,YAAM,WAAW,YAAY,CAAC;AAC9B,YAAM,WAAW,YAAY,CAAC;AAE9B,UAAI,CAAC,YAAY,CAAC,SAAU;AAG5B,YAAM,SACJ,SAAS,KAAK,CAAC,MAAM,SAAS,KAAK,CAAC,KACpC,SAAS,KAAK,CAAC,MAAM,SAAS,KAAK,CAAC,KACpC,SAAS,KAAK,CAAC,MAAM,SAAS,KAAK,CAAC;AAEtC,UAAI,QAAQ;AACV,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,qBAAqC;AAAA,EAChD;AAAA,EACA;AACF;AC3DA,MAAM,kBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kBAAkB,CAAC,SAAkC;AACzD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,WAAW;AAAA,IAClC,CAAC,WAA8B,OAAO,SAAS;AAAA,EAAA;AAIjD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,EACT;AAIA,QAAM,oCAAoB,IAAA;AAE1B,aAAW,WAAW,aAAa;AACjC,UAAM,MAAM,QAAQ,KAAK,CAAC;AAC1B,UAAM,eAAe,cAAc,IAAI,GAAG,KAAK;AAC/C,kBAAc,IAAI,KAAK,eAAe,CAAC;AAAA,EACzC;AAEA,MAAI,YAAY;AAChB,aAAW,SAAS,cAAc,UAAU;AAE1C,iBAAa,KAAK,MAAM,QAAQ,CAAC;AAAA,EACnC;AAEA,SAAO,aAAa;AACtB;AAEO,MAAM,uBAAuC;AAAA,EAClD;AAAA,EACA;AACF;AC1CA,MAAM,gBAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,gBAAgB,CACpB,MACA,YACY;AACZ,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,MAAI,aAAa;AAEjB,aAAW,WAAW,UAAU;AAE9B,QAAI,QAAQ,KAAM;AAElB,UAAM,sBAAsB,QAAQ,KAAK,SAAS,QAAQ,QAAQ;AAKlE,UAAM,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,QAAQ;AAEhD,QAAI,QAAQ,SAAS;AAEnB;AAAA,IACF,OAAO;AAEL,UAAI,qBAAqB;AAEvB,YAAI,SAAS;AAEX;AAAA,QACF;AAAA,MAGF,OAAO;AAEL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,cAAc;AACvB;AAEO,MAAM,qBAAqC;AAAA,EAChD;AAAA,EACA;AACF;AC7DA,MAAM,gBAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA;AAAA,EAAA;AAEZ;AAEA,MAAM,gBAAgB,CACpB,MACA,YACY;AACZ,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,MAAI,aAAa;AAEjB,aAAW,WAAW,UAAU;AAE9B,QAAI,QAAQ,KAAM;AAElB,UAAM,sBAAsB,QAAQ,KAAK,SAAS,QAAQ,QAAQ;AAElE,UAAM,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,QAAQ;AAEhD,QAAI,QAAQ,SAAS;AAEnB;AAAA,IACF,OAAO;AAEL,UAAI,qBAAqB;AAGvB,YAAI,SAAS;AACX;AAAA,QACF;AAAA,MACF,OAAO;AAEL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,eAAe;AACxB;AAUO,MAAM,qBAAqC;AAAA,EAChD,MAAM;AAAA,EACN,aAAa,CAAC,MAAM,YAAY,cAAc,MAAM,OAAO;AAAA,EAC3D,UAAU,CAAC,MAAM,YAAY;AAC3B,QAAI,CAAC,cAAc,MAAM,OAAO,EAAG,QAAO;AAG1C,UAAM,UACJ,KAAK,SAAS,YAAY,KAAK,OAAO,KAAK,CAAC,MAAM,QAAQ;AAG5D,QAAI,SAAS;AACX,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;AC/EA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,KAAK,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ;AAGpE,SAAO,WAAW,UAAU;AAC9B;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;ACvBA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,aAAa,KAAK,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ;AAGpE,SAAO,WAAW,WAAW;AAC/B;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;ACvBA,MAAM,cAAoB;AAAA,EACxB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,cAAc,CAAC,SAAkC;AACrD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,SAAO,KAAK,WAAW;AAAA,IACrB,CAAC,WAAW,OAAO,SAAS,YAAY,OAAO,SAAS;AAAA,EAAA;AAE5D;AAEO,MAAM,mBAAmC;AAAA,EAC9C;AAAA,EACA;AACF;ACtBA,MAAM,kBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kBAAkB,CAAC,SAAkC;AACzD,SAAO,KAAK,SAAS;AACvB;AAEO,MAAM,uBAAuC;AAAA,EAClD;AAAA,EACA;AACF;ACbA,MAAM,eAAqB;AAAA,EACzB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,eAAe,CAAC,SAAkC;AACtD,MAAI,KAAK,SAAS,SAAU,QAAO;AAEnC,QAAM,YAAY,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAGlD,QAAM,eAAe,UAAU;AAAA,IAAM,CAAC,UACpC,MAAM,KAAK,KAAK,CAAC,MAAM,SAAS,CAAC,CAAC;AAAA,EAAA;AAEpC,MAAI,CAAC,aAAc,QAAO;AAG1B,QAAM,aAAa,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AACnE,MAAI,CAAC,WAAY,QAAO;AAGxB,QAAM,WAAW,UAAU;AAAA,IAAK,CAAC,UAC/B,MAAM,KAAK,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,QAAQ,KAAK;AAAA,EAAA;AAE7D,MAAI,CAAC,SAAU,QAAO;AAEtB,SAAO;AACT;AAEO,MAAM,oBAAoC;AAAA,EAC/C;AAAA,EACA;AACF;ACpCA,MAAM,eAAqB;AAAA,EACzB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,eAAe,CAAC,SAAkC;AACtD,MAAI,KAAK,SAAS,SAAU,QAAO;AAEnC,QAAM,YAAY,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAIlD,QAAM,eAAe,UAAU;AAAA,IAAM,CAAC,UACpC,MAAM,KAAK,KAAK,CAAC,MAAM,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC;AAAA,EAAA;AAEnD,MAAI,CAAC,aAAc,QAAO;AAG1B,QAAM,aAAa,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AACnE,MAAI,CAAC,WAAY,QAAO;AAExB,SAAO;AACT;AAEO,MAAM,oBAAoC;AAAA,EAC/C;AAAA,EACA;AACF;AC7BA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,MAAI;AAEJ,MAAI,KAAK,SAAS,UAAU;AAC1B,aAAS,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAAA,EAC3C,WAAW,KAAK,SAAS,cAAc;AACrC,aAAS,KAAK;AAAA,EAChB,OAAO;AACL,WAAO;AAAA,EACT;AAIA,QAAM,YAAY,OAAO;AAAA,IAAM,CAAC,UAC9B,MAAM,KAAK,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;AAAA,EAAA;AAErC,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,WAAW,OAAO;AAAA,IAAK,CAAC,UAC5B,MAAM,KAAK,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,QAAQ,KAAK;AAAA,EAAA;AAE7D,MAAI,CAAC,SAAU,QAAO;AAEtB,SAAO;AACT;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;ACvCA,MAAM,kBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kBAAkB,CAAC,SAAkC;AAEzD,MAAI,KAAK,SAAS,SAAU,QAAO;AAEnC,QAAM,YAAY,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAGlD,QAAM,YAAY,UAAU;AAAA,IAAM,CAAC,UACjC,MAAM,KAAK,MAAM,CAAC,MAAM,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC;AAAA,EAAA;AAEpD,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO;AACT;AAEO,MAAM,uBAAuC;AAAA,EAClD;AAAA,EACA;AACF;ACxBA,MAAM,kBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kBAAkB,CAAC,SAAkC;AACzD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,YAAyB,CAAC,QAAQ,MAAM,QAAQ,OAAO,QAAQ,IAAI;AAGzE,MAAI,oBAAoB;AACxB,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,aAAW,WAAW,UAAU;AAC9B,QAAI,UAAU,SAAS,QAAQ,KAAK,CAAC,CAAC,GAAG;AACvC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,UAAU,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC;AAG7D,SAAO,sBAAsB,KAAK;AACpC;AAEO,MAAM,uBAAuC;AAAA,EAClD;AAAA,EACA;AACF;ACrCA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,YAAyB,CAAC,QAAQ,MAAM,QAAQ,OAAO,QAAQ,IAAI;AAGzE,MAAI,oBAAoB;AACxB,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,aAAW,WAAW,UAAU;AAC9B,QAAI,UAAU,SAAS,QAAQ,KAAK,CAAC,CAAC,GAAG;AACvC;AAAA,IACF;AAAA,EACF;AAGA,SAAO,sBAAsB;AAC/B;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;ACpCA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,UAAU,CAAC,OAAwB;AACvC,SAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ;AAC5C;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,QAAM,UAAoB,CAAA;AAE1B,MAAI,KAAK,SAAS,UAAU;AAE1B,eAAW,UAAU,KAAK,YAAY;AACpC,cAAQ,KAAK,GAAG,OAAO,IAAI;AAAA,IAC7B;AACA,YAAQ,KAAK,GAAG,KAAK,OAAO,IAAI;AAAA,EAClC,WAAW,KAAK,SAAS,cAAc;AAErC,eAAW,QAAQ,KAAK,OAAO;AAC7B,cAAQ,KAAK,GAAG,KAAK,IAAI;AAAA,IAC3B;AAAA,EACF,OAAO;AAEL,WAAO;AAAA,EACT;AAGA,SAAO,QAAQ,MAAM,OAAO;AAC9B;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;ACtCA,MAAM,iBAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kCAAkB,IAAY;AAAA,EAClC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACV,CAAC;AAED,MAAM,UAAU,CAAC,OAAwB;AACvC,SAAO,YAAY,IAAI,EAAE;AAC3B;AAEA,MAAM,iBAAiB,CAAC,SAAkC;AACxD,QAAM,UAAoB,CAAA;AAE1B,MAAI,KAAK,SAAS,UAAU;AAE1B,eAAW,UAAU,KAAK,YAAY;AACpC,cAAQ,KAAK,GAAG,OAAO,IAAI;AAAA,IAC7B;AACA,YAAQ,KAAK,GAAG,KAAK,OAAO,IAAI;AAAA,EAClC,WAAW,KAAK,SAAS,cAAc;AAErC,eAAW,QAAQ,KAAK,OAAO;AAC7B,cAAQ,KAAK,GAAG,KAAK,IAAI;AAAA,IAC3B;AAAA,EACF,OAAO;AAEL,WAAO;AAAA,EACT;AAGA,SAAO,QAAQ,MAAM,OAAO;AAC9B;AAEO,MAAM,sBAAsC;AAAA,EACjD;AAAA,EACA;AACF;AC7CA,MAAM,mBAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,mBAAmB,CAAC,SAAkC;AAC1D,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,YAAyB;AAAA,IAC7B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EAAA;AAIV,MAAI,kBAAkB;AACtB,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,aAAW,WAAW,UAAU;AAC9B,QAAI,UAAU,SAAS,QAAQ,KAAK,CAAC,CAAC,GAAG;AACvC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,eAAe,UAAU,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC;AAI3D,SAAO,oBAAoB,KAAK;AAClC;AAEO,MAAM,wBAAwC;AAAA,EACnD;AAAA,EACA;AACF;AC3CA,MAAM,kBAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,KAAK;AAAA;AAAA,IAEH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,kBAAkB,CAAC,SAAkC;AACzD,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,YAAyB;AAAA,IAC7B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EAAA;AAIV,MAAI,kBAAkB;AACtB,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,aAAW,WAAW,UAAU;AAC9B,QAAI,UAAU,SAAS,QAAQ,KAAK,CAAC,CAAC,GAAG;AACvC;AAAA,IACF;AAAA,EACF;AAGA,SAAO,oBAAoB;AAC7B;AAEO,MAAM,uBAAuC;AAAA,EAClD;AAAA,EACA;AACF;AC1CA,MAAM,sBAA4B;AAAA,EAChC,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,qBAAqB,CACzB,MACA,YACY;AAEZ,MAAI,CAAC,QAAQ,UAAU;AACrB,WAAO;AAAA,EACT;AAIA,QAAM,UAAoB,CAAA;AAE1B,MAAI,KAAK,SAAS,UAAU;AAE1B,eAAW,UAAU,KAAK,YAAY;AACpC,cAAQ,KAAK,GAAG,OAAO,IAAI;AAAA,IAC7B;AACA,YAAQ,KAAK,GAAG,KAAK,OAAO,IAAI;AAAA,EAClC,OAAO;AAKL,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,WAAW,QAAQ,CAAC;AAC1B,MAAI,aAAa,OAAW,QAAO;AAGnC,MAAI,YAAY,GAAI,QAAO;AAE3B,QAAM,OAAO,KAAK,MAAM,WAAW,CAAC;AAEpC,aAAW,OAAO,SAAS;AACzB,QAAI,OAAO,GAAI,QAAO;AACtB,QAAI,KAAK,MAAM,MAAM,CAAC,MAAM,KAAM,QAAO;AAAA,EAC3C;AAIA,QAAM,SAAS,MAAM,CAAC,EAAE,KAAK,CAAC;AAC9B,aAAW,OAAO,SAAS;AACzB,UAAM,MAAM,MAAM;AAClB,WAAO,GAAG;AAAA,EACZ;AAGA,MAAI,OAAO,CAAC,IAAI,EAAG,QAAO;AAE1B,MAAI,OAAO,CAAC,IAAI,EAAG,QAAO;AAE1B,WAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,QAAI,OAAO,CAAC,IAAI,EAAG,QAAO;AAAA,EAC5B;AAKA,SAAO;AACT;AAEO,MAAM,0BAA0C;AAAA,EACrD;AAAA,EACA;AACF;AC9EA,MAAM,eAAe,CAAC,SAAkC;AACtD,SAAO,KAAK,SAAS;AACvB;AAEA,MAAM,cAA6B;AAAA,EACjC,QAAQ;AAAA,EACR,MAAM;AACR;AAEO,MAAM,oBAAoC;AAAA,EAC/C;AAAA,IACE,MAAM;AAAA,IACN,KAAK;AAAA,EAAA;AAAA,EAEP;AACF;ACZA,MAAM,uBAA6B;AAAA,EACjC,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,sBAAsB,CAAC,SAAkC;AAC7D,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,WAAW;AAAA,IAClC,CAAC,WAA8B,OAAO,SAAS;AAAA,EAAA;AAGjD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,EACT;AAWA,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,aAAS,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC/C,eAAS,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC/C,cAAM,KAAK,YAAY,CAAC;AACxB,cAAM,KAAK,YAAY,CAAC;AACxB,cAAM,KAAK,YAAY,CAAC;AAExB,YAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI;AAEvB,cAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,cAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,cAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AACtC,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AACtC,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AAGtC,cAAM,QAAQ,oBAAI,IAAI,CAAC,OAAO,OAAO,KAAK,CAAC;AAC3C,YAAI,MAAM,SAAS,EAAG;AAItB,YAAI,QAAQ,KAAK,QAAQ,KAAK,QAAQ,EAAG;AAGzC,cAAM,OAAO,YAAY;AACzB,cAAM,OAAO,YAAY;AACzB,cAAM,OAAO,YAAY;AAEzB,YAAI,SAAS,QAAQ,SAAS,MAAM;AAClC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,2BAA2C;AAAA,EACtD;AAAA,EACA;AACF;ACzEA,MAAM,uBAA6B;AAAA,EACjC,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,sBAAsB,CAAC,SAAkC;AAC7D,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,KAAK,WAAW;AAAA,IAC/B,CAAC,MAA4B,EAAE,SAAS,YAAY,EAAE,SAAS;AAAA,EAAA;AAGjE,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO;AAAA,EACT;AAGA,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,aAAS,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AAC5C,eAAS,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AAC5C,cAAM,KAAK,SAAS,CAAC;AACrB,cAAM,KAAK,SAAS,CAAC;AACrB,cAAM,KAAK,SAAS,CAAC;AAErB,YAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI;AAEvB,cAAM,MAAM,GAAG,KAAK,CAAC;AACrB,cAAM,MAAM,GAAG,KAAK,CAAC;AACrB,cAAM,MAAM,GAAG,KAAK,CAAC;AAGrB,YAAI,OAAO,MAAM,OAAO,MAAM,OAAO,GAAI;AAEzC,cAAM,QAAQ,KAAK,MAAM,MAAM,CAAC;AAChC,cAAM,QAAQ,KAAK,MAAM,MAAM,CAAC;AAChC,cAAM,QAAQ,KAAK,MAAM,MAAM,CAAC;AAGhC,cAAM,QAAQ,oBAAI,IAAI,CAAC,OAAO,OAAO,KAAK,CAAC;AAE3C,YAAI,MAAM,SAAS,EAAG;AAEtB,cAAM,OAAO,MAAM;AACnB,cAAM,OAAO,MAAM;AACnB,cAAM,OAAO,MAAM;AAGnB,YAAI,SAAS,QAAQ,SAAS,MAAM;AAClC,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,2BAA2C;AAAA,EACtD;AAAA,EACA;AACF;ACnEA,MAAM,mBAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,mBAAmB,CAAC,SAAkC;AAC1D,MAAI,KAAK,SAAS,UAAU;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,KAAK,WAAW;AAAA,IAClC,CAAC,WAA8B,OAAO,SAAS;AAAA,EAAA;AAGjD,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO;AAAA,EACT;AAQA,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,aAAS,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC/C,eAAS,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC/C,cAAM,KAAK,YAAY,CAAC;AACxB,cAAM,KAAK,YAAY,CAAC;AACxB,cAAM,KAAK,YAAY,CAAC;AAExB,YAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI;AAEvB,cAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,cAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,cAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AACtC,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AACtC,cAAM,QAAQ,KAAK,MAAM,YAAY,CAAC;AAGtC,YAAI,UAAU,SAAS,UAAU,MAAO;AAGxC,YAAI,QAAQ,EAAG;AAGf,cAAM,OAAO,YAAY;AACzB,cAAM,OAAO,YAAY;AACzB,cAAM,OAAO,YAAY;AAGzB,cAAM,OAAO,oBAAI,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC;AACvC,YAAI,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,GAAG;AAC7C,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,wBAAwC;AAAA,EACnD;AAAA,EACA;AACF;ACrEA,MAAM,eAAqB;AAAA,EACzB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,eAAe,CAAC,SAAkC;AACtD,MAAI;AACJ,MAAI,KAAK,SAAS,UAAU;AAC1B,aAAS,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAAA,EAC3C,WAAW,KAAK,SAAS,cAAc;AACrC,aAAS,KAAK;AAAA,EAChB,OAAO;AACL,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI;AAG5C,QAAM,WAAW,QAAQ,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,QAAQ,KAAK;AACzE,MAAI,CAAC,SAAU,QAAO;AAGtB,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC;AAGjD,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,cAAc,QAAQ,CAAC;AAC7B,MAAI,gBAAgB,OAAW,QAAO;AAEtC,QAAM,kBAAkB,gBAAgB,WAAW;AACnD,QAAM,gBAAgB,QAAQ;AAAA,IAC5B,CAAC,MAAM,gBAAgB,CAAC,MAAM;AAAA,EAAA;AAGhC,SAAO;AACT;AAEO,MAAM,oBAAoC;AAAA,EAC/C;AAAA,EACA;AACF;AC7CA,MAAM,gBAAsB;AAAA,EAC1B,MAAM;AAAA,EACN,KAAK;AAAA,IACH,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAEZ;AAEA,MAAM,gBAAgB,CAAC,SAAkC;AACvD,MAAI;AACJ,MAAI,KAAK,SAAS,UAAU;AAC1B,aAAS,CAAC,KAAK,QAAQ,GAAG,KAAK,UAAU;AAAA,EAC3C,WAAW,KAAK,SAAS,cAAc;AACrC,aAAS,KAAK;AAAA,EAChB,OAAO;AACL,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI;AAG5C,QAAM,WAAW,QAAQ,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,QAAQ,KAAK;AACzE,MAAI,SAAU,QAAO;AAGrB,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,SAAS,CAAC,CAAC;AAGjD,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,cAAc,QAAQ,CAAC;AAC7B,MAAI,gBAAgB,OAAW,QAAO;AAEtC,QAAM,kBAAkB,gBAAgB,WAAW;AACnD,QAAM,gBAAgB,QAAQ;AAAA,IAC5B,CAAC,MAAM,gBAAgB,CAAC,MAAM;AAAA,EAAA;AAGhC,SAAO;AACT;AAEO,MAAM,qBAAqC;AAAA,EAChD;AAAA,EACA;AACF;AC/CA,SAAS,wBACP,MACA,MACgB;AAChB,QAAM,aAA4B,EAAE,QAAQ,GAAG,MAAM,EAAA;AAErD,QAAM,QAAQ,CAAC,SAAkC;AAC/C,QAAI,KAAK,SAAS,SAAU,QAAO;AAEnC,eAAW,UAAU,KAAK,YAAY;AACpC,UAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AACxD,YAAI,OAAO,KAAK,CAAC,MAAM,MAAM;AAC3B,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,qBAAqB,EAAE,MAAM,KAAK,WAAA,GAAc,KAAK;AAC9D;AAEO,MAAM,iBAAiB,wBAAwB,QAAQ,QAAQ,IAAI;AACnE,MAAM,kBAAkB,wBAAwB,SAAS,QAAQ,KAAK;AACtE,MAAM,iBAAiB,wBAAwB,QAAQ,QAAQ,IAAI;AC7B1E,MAAM,aAAa;AAAA,EACjB,MAAM;AAAA,EACN,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAA;AAC1B;AAEO,MAAM,wBAAwC;AAAA,EACnD;AAAA,EACA,CAAC,MAAsB,YAAmC;AACxD,WAAO,QAAQ,YAAY,CAAC,CAAC,QAAQ;AAAA,EACvC;AACF;AC+CO,MAAM,uBAAyC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AClEA,MAAM,wBAAwB;AAWvB,SAAS,uBACd,MACA,SACY;AACZ,QAAM,SAA8B,CAAA;AAEpC,aAAWA,eAAc,sBAAsB;AAC7C,QAAIA,YAAW,YAAY,MAAM,OAAO,GAAG;AACzC,YAAM,QAAQA,YAAW,SAAS,MAAM,OAAO;AAC/C,UAAI,UAAU,EAAG;AACjB,aAAO,KAAK,CAACA,YAAW,KAAK,MAAM,KAAK,CAAC;AAAA,IAC3C;AAAA,EACF;AAQA,QAAM,aAAa,OAAO,KAAK,CAAC,CAAA,EAAG,GAAG,MAAM,OAAO,qBAAqB;AACxE,MAAI,YAAY;AACd,WAAO,OAAO,OAAO,CAAC,CAAA,EAAG,GAAG,MAAM,OAAO,qBAAqB;AAAA,EAChE;AAEA,SAAO;AACT;AAKA,SAAS,YACP,YACQ;AACR,SAAO,WAAW,OAAO,CAAC,KAAK,CAAA,EAAG,GAAG,MAAM,MAAM,KAAK,CAAC;AACzD;AASO,SAAS,WACd,OACA,UACA,QACA,QACA,aACA,gBACA,SACY;AACZ,QAAM,UAAwB;AAAA,IAC5B,UAAU,SAAS,KAAK;AAAA,IACxB;AAAA,IACA,QAAQ,WAAW,UAAa,UAAU,MAAM,IAAI,SAAS;AAAA,IAC7D,QAAQ,WAAW,UAAa,UAAU,MAAM,IAAI,SAAS;AAAA,IAC7D,aAAa,eAAe,CAAA;AAAA,IAC5B,gBAAgB,kBAAkB,CAAA;AAAA,IAClC;AAAA,EAAA;AAGF,QAAM,4BAA4B,mBAAmB,KAAK;AAE1D,MAAI,aAAyB,CAAA;AAC7B,MAAI,SAAS;AAEb,aAAW,QAAQ,2BAA2B;AAC5C,UAAM,gBAAgB,uBAAuB,MAAM,OAAO;AAC1D,UAAM,aAAa,YAAY,aAAa;AAE5C,QAAI,aAAa,QAAQ;AACvB,eAAS;AACT,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AACT;AClGA,MAAM,gBAAgB;AAEtB,MAAM,sBAAsB,IAAI,OAAO,KAAK,aAAa,KAAK;AAO9D,MAAM,yBAAyB,IAAI,aAAa,QAAQ,aAAa,aAAa,aAAa;AAC/F,MAAM,sBAAsB,IAAI,OAAO,IAAI,sBAAsB,IAAI;AAoB9D,SAAS,eAAe,OAA4C;AAGzE,UACG,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,GAAG,MAC1C,oBAAoB,KAAK,KAAK;AAElC;AAUO,SAAS,eAAe,OAAmC;AAChE,MAAI,CAAC,eAAe,KAAK,GAAG;AAC1B,UAAM,IAAI,MAAM,iCAAiC,KAAK,EAAE;AAAA,EAC1D;AACA,SAAO;AACT;AAOO,SAAS,OAAO,OAAoC;AACzD,SAAO,oBAAoB,KAAK,KAAK;AACvC;AAgBO,SAASC,oBAAkB,OAAwC;AACxE,QAAM,cAAwB,CAAA;AAC9B,QAAM,UAA6B,CAAA;AAEnC,MAAI,UAAU;AACd,MAAI,OAAoC;AAGxC,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,KAAK;AAChB,UAAI,SAAS;AACX,cAAM,IAAI,MAAM,mCAAmC;AACrD,UAAI,QAAQ,SAAS,EAAG,aAAY,KAAK,OAAO;AAEhD,gBAAU;AACV,aAAO;AAAA,IACT,WAAW,SAAS,KAAK;AACvB,UAAI,SAAS,OAAQ,OAAM,IAAI,MAAM,gCAAgC;AACrE,iBAAW;AAEX,cAAQ,KAAK,4BAA4B,eAAe,OAAO,CAAC,CAAC;AACjE,gBAAU;AACV,aAAO;AAAA,IACT,WAAW,SAAS,KAAK;AACvB,UAAI,SAAS;AACX,cAAM,IAAI,MAAM,sCAAsC;AACxD,UAAI,QAAQ,SAAS,EAAG,aAAY,KAAK,OAAO;AAEhD,gBAAU;AACV,aAAO;AAAA,IACT,WAAW,SAAS,KAAK;AACvB,UAAI,SAAS;AACX,cAAM,IAAI,MAAM,oCAAoC;AACtD,iBAAW;AAEX,cAAQ,KAAK,4BAA4B,eAAe,OAAO,CAAC,CAAC;AACjE,gBAAU;AACV,aAAO;AAAA,IACT,OAAO;AACL,iBAAW;AAAA,IACb;AAAA,EACF;AAGA,MAAI,QAAQ,SAAS,GAAG;AACtB,QAAI,SAAS,SAAU,OAAM,IAAI,MAAM,iCAAiC;AACxE,gBAAY,KAAK,OAAO;AAAA,EAC1B;AAGA,QAAM,iBAAiB,YAAY,KAAK,EAAE;AAC1C,QAAM,YAAY,sBAAsB,OAAO,cAAc,CAAC;AAE9D,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,EAAA;AAEJ;AAMA,SAAS,4BACP,OACiB;AACjB,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAAG;AAChD,WAAO;AACP,cAAU,MAAM,MAAM,GAAG,EAAE;AAAA,EAC7B,WAAW,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAAG;AACvD,WAAO;AACP,cAAU,MAAM,MAAM,GAAG,EAAE;AAAA,EAC7B,OAAO;AACL,UAAM,IAAI;AAAA,MACR,gCAAgC,KAAK;AAAA,IAAA;AAAA,EAEzC;AAGA,QAAM,MAAM,sBAAsB,OAAO,OAAO,CAAC;AACjD,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAGA,QAAM,QAAQ,IAAI;AAClB,QAAM,YAAY,IAAI,MAAM,CAAC,OAAO,OAAO,IAAI,CAAC,CAAC;AAGjD,MAAI,SAAS,SAAS;AACpB,QAAI,UAAU,KAAK,CAAC,WAAW;AAC7B,YAAM,IAAI,MAAM,kBAAkB,KAAK,8BAA8B;AAAA,IACvE;AACA,QAAI,CAAC,SAAS,GAAG,GAAG;AAClB,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,UAAM,SAAiB;AAAA,MACrB,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA;AAAA,IAAA;AAGR,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,KAAK,WAAW;AAE5B,QAAI,CAAC,SAAS,GAAG,GAAG;AAClB,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,UAAM,SAAiB;AAAA,MACrB,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,EAAE,MAAM,SAAS,WAAW,MAAM,MAAM,OAAA;AAAA;AAAA,IAAO;AAEvD,WAAO;AAAA,EACT,WAAW,UAAU,KAAK,WAAW;AAEnC,QAAI,CAAC,SAAS,GAAG,GAAG;AAClB,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,UAAM,SAAiB;AAAA,MACrB,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,EAAE,MAAM,SAAS,KAAK,MAAM,MAAM,OAAA;AAAA;AAAA,IAAO;AAEjD,WAAO;AAAA,EACT,WAAW,UAAU,GAAG;AAKtB,QAAI,CAAC,SAAS,GAAG,GAAG;AAClB,YAAM,IAAI,MAAM,2CAA2C;AAAA,IAC7D;AACA,UAAM,UAAmB;AAAA,MACvB,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA,MACN,MAAM,EAAE,MAAM,SAAS,KAAK,MAAM,MAAM,QAAA;AAAA;AAAA,IAAQ;AAElD,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR,iCAAiC,KAAK;AAAA,EAAA;AAE1C;AA4HO,SAAS,OAAO,OAA2B;AAChD,MAAI,CAAC,OAAO,KAAK,GAAG;AAClB,UAAM,IAAI,MAAM,wBAAwB,KAAK,EAAE;AAAA,EACjD;AACA,SAAO;AACT;AASO,SAAS,sBAAsB,MAA+B;AACnE,QAAM,SAAsB,CAAA;AAC5B,MAAI,iBAA2B,CAAA;AAE/B,aAAW,QAAQ,MAAM;AACvB,QAAI,QAAQ,OAAO,QAAQ,KAAK;AAC9B,qBAAe,KAAK,SAAS,MAAM,EAAE,CAAC;AAAA,IACxC,OAAO;AAEL,UAAI;AAEJ,cAAQ,MAAA;AAAA,QACN,KAAK;AACH,iBAAO,QAAQ;AACf;AAAA,QACF,KAAK;AACH,iBAAO,QAAQ;AACf;AAAA,QACF,KAAK;AACH,iBAAO,QAAQ;AACf;AAAA,QACF,KAAK;AACH,iBAAO,QAAQ;AACf;AAAA,QACF;AAEE,2BAAiB,CAAA;AACjB;AAAA,MAAA;AAGJ,iBAAW,OAAO,gBAAgB;AAChC,YAAI,SAAS,KAAK;AAEhB,cAAI,OAAO,KAAK,OAAO,GAAG;AACxB,mBAAO,KAAK,YAAY,OAAO,MAAM,CAAC,CAAC;AAAA,UACzC;AAAA,QACF,OAAO;AAEL,cAAI,OAAO,GAAG;AACZ,mBAAO,KAAK,YAAY,OAAO,MAAM,CAAC,CAAC;AAAA,UACzC;AAAA,QACF;AAAA,MACF;AACA,uBAAiB,CAAA;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;AC5ZO,SAAS,UAAU,OAAsB;AAC9C,QAAM,MAAM,sBAAsB,OAAO,KAAK,CAAC;AAC/C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,CAAA;AAAA,EAAC;AAEd;AASO,SAAS,kBAAkB,OAAsB;AAGtD,SAAOC,oBAA0B,eAAe,KAAK,CAAC;AACxD;AChCO,MAAM,UAAU;AAAA,EACrB,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA;AACX;AAGO,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AACd;AAGO,MAAM,YAAY;AAAA,EACvB,SAAS;AAAA;AAAA,EAET,iBAAiB;AACnB;AAGO,MAAM,WAAW;AAAA,EACtB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AAGT;AAIO,MAAM,YAAY;AAAA,EACvB,aAAa;AAAA,EACb,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AACjB;AAEO,MAAM,YAAY;AAAA,EACvB,aAAa;AAAA,EACb,eAAe;AAAA,EACf,aAAa;AAAA,EACb,eAAe;AACjB;AAGO,MAAM,iBAAiB;AACvB,MAAM,sBAAsB;AC7C5B,SAAS,wBAAkC;AAChD,QAAM,UAAqB;AAAA,IACzB,MAAM,QAAQ;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,EAAA;AAET,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf;AAAA,EAAA;AAEJ;ACXO,SAAS,qBAA+B;AAC7C,QAAM,UAAqB;AAAA,IACzB,MAAM,QAAQ;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,EAAA;AAET,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf;AAAA,EAAA;AAEJ;ACDA,MAAM,kBAAiC;AAAA,EACrC;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAI;AAAA,EAAK;AAC3C;AAKA,SAAS,KAAK,OAAmB;AAC/B,QAAM,KAAK,gBAAgB,KAAK,CAAC,MAAM,MAAM,KAAK;AAClD,MAAI,OAAO,QAAW;AACpB,UAAM,IAAI,MAAM,qBAAqB,KAAK,EAAE;AAAA,EAC9C;AACA,SAAO;AACT;AASO,SAAS,kBACd,MACA,SACA,SACU;AACV,QAAM,UAAU;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,EAAA;AAIT,aAAW,UAAU,KAAK,YAAY;AACpC,QAAI,KAAK;AACT,UAAM,iBAAiB,SAAS,OAAO,KAAK,CAAC,CAAC;AAE9C,QAAI,OAAO,SAAS,UAAU;AAE5B,YAAM,SAAS,iBACX,UAAU,cACV,UAAU;AACd,YAAM,WAAW,iBACb,UAAU,gBACV,UAAU;AAEd,WAAK;AAGL,UAAI,SAAS,CAAC,CAAC,OAAO;AACtB,UAAI,CAAC,UAAU,CAAC,QAAQ,SAAS;AAE/B,YAAI,OAAO,KAAK,SAAS,QAAQ,QAAQ,GAAG;AAC1C,mBAAS;AAAA,QACX;AAAA,MACF;AAEA,UAAI,QAAQ;AACV,aAAK;AAAA,MACP;AACA,cAAQ,UAAU;AAAA,IACpB,WAAW,OAAO,SAAS,UAAU;AAEnC,YAAM,SAAS,iBACX,UAAU,cACV,UAAU;AACd,YAAM,WAAW,iBACb,UAAU,gBACV,UAAU;AAEd,WAAK;AACL,UAAI,OAAO,MAAM;AACf,aAAK;AAAA,MACP;AACA,cAAQ,UAAU;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,UAAU,KAAK,OAAO,KAAK,CAAC;AAClC,MAAI,WAAW;AAEf,MACE,YAAY,QAAQ,QACpB,YAAY,QAAQ,SACpB,YAAY,QAAQ,MACpB;AACA,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,YAAY,QAAQ,QAAQ;AAC9B,gBAAY,UAAU;AAAA,EACxB;AACA,MAAI,YAAY,QAAQ,QAAQ;AAC9B,gBAAY,UAAU;AAAA,EACxB;AAGA,MAAI,WAAW,UAAU,iBAAiB;AACxC,eAAW,UAAU;AAAA,EACvB;AACA,UAAQ,SAAS;AAGjB,QAAM,YAAY,cAAc,MAAM,QAAQ,QAAQ;AACtD,MAAI,cAAc,UAAW,SAAQ,QAAQ,SAAS;AAAA,WAC7C,cAAc,UAAW,SAAQ,QAAQ,SAAS;AAAA,WAClD,cAAc,QAAS,SAAQ,QAAQ,SAAS;AAAA,eAC5C,QAAQ;AAGrB,MAAI,QAAQ,SAAS;AACnB,QAAI,CAAC,SAAS;AACZ,cAAQ,QAAQ,SAAS;AAAA,IAC3B;AAAA,EACF,OAAO;AACL,QAAI,QAAQ,UAAU;AACpB,cAAQ,QAAQ,SAAS;AAAA,IAC3B;AAAA,EACF;AAGA,MAAI,MACF,QAAQ,OACR,QAAQ,SACR,QAAQ,SACR,QAAQ,QACR,QAAQ;AAGV,MAAI,WAAW,QAAQ,SAAS;AAC9B,WAAO,EAAE,OAAO,gBAAgB,QAAA;AAAA,EAClC;AAGA,MAAI,QAAQ,MAAM,CAAC,QAAQ,WAAW,CAAC,QAAQ,UAAU;AACvD,UAAM;AAAA,EACR,OAAO;AACL,UAAM,KAAK,KAAK,MAAM,EAAE,IAAI;AAAA,EAC9B;AAEA,SAAO;AAAA,IACL,OAAO,KAAK,GAAG;AAAA,IACf;AAAA,EAAA;AAEJ;ACvJO,SAAS,YACd,MACA,SACA,UAAU,OACA;AAEV,MAAI,KAAK,SAAS,cAAc;AAC9B,WAAO,sBAAA;AAAA,EACT;AAGA,MAAI,KAAK,SAAS,WAAW;AAC3B,WAAO,mBAAA;AAAA,EACT;AAGA,SAAO,kBAAkB,MAAM,SAAS,OAAO;AACjD;AC7BO,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAC1B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAe3B,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,gBAAgB;AACtB,MAAM,cAAc;AAQpB,MAAM,mBAAmB;AC6BzB,MAAM,aAAa;AAAA;AAAA,EAExB,QAAQ;AAAA;AAAA,EAER,QAAQ;AAAA;AAAA,EAER,SAAS;AAAA;AAAA,EAET,QAAQ;AAAA;AAAA,EAER,WAAW;AAAA;AAAA,EAEX,SAAS;AAAA;AAAA,EAET,eAAe;AACjB;ACjCA,SAAS,QAAQ,QAAwB;AACvC,SAAO,KAAK,KAAK,SAAS,GAAG,IAAI;AACnC;AAYO,SAAS,oBAAoB,IAAQ,KAAqB;AAC/D,SAAO,KAAK,KAAK,IAAI,GAAG,IAAI,GAAG;AACjC;AAKO,SAAS,gBAAgB,SAAoC;AAClE,UAAQ,QAAQ,MAAA;AAAA,IACd,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO,QAAQ,OAAO,CAAC,IAAI,IAAI,QAAQ,OAAO,CAAC;AAAA,IACjD,KAAK;AACH,aAAO,QAAQ,SAAS;AAAA,EAAA;AAE9B;AASO,SAAS,cAAc,KAAa,YAAgC;AACzE,MAAI,OAAO,IAAI;AACb,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,aAAa;AACtB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,eAAe;AACxB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,YAAY;AACrB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,aAAa;AACtB,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,cAAc,cAAc,kBAAkB;AACvD,WAAO,WAAW;AAAA,EACpB;AACA,SAAO,WAAW;AACpB;AAQA,SAAS,mBAAmB,OAAkC;AAC5D,UAAQ,OAAA;AAAA,IACN,KAAK,WAAW;AACd,aAAO,qBAAqB;AAAA,IAC9B,KAAK,WAAW;AACd,aAAO;AAAA,IACT,KAAK,WAAW;AACd,aAAO;AAAA,IACT,KAAK,WAAW;AACd,aAAO;AAAA,IACT,KAAK,WAAW;AACd,aAAO;AAAA,IACT,KAAK,WAAW;AACd,aAAO;AAAA,IACT,KAAK,WAAW;AACd,aAAO;AAAA,EAAA;AAEb;AASA,SAAS,mBACP,OACA,QACc;AACd,SAAO;AAAA,IACL,UAAU,SAAS,KAAK;AAAA,IACxB,UAAU,OAAO;AAAA,IACjB,QAAQ,OAAO;AAAA,IACf,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO,WAAW,QAAQ;AAAA,IACjC,aAAa,OAAO;AAAA,IACpB,GAAI,OAAO,iBAAiB,EAAE,gBAAgB,OAAO,eAAA,IAAmB,CAAA;AAAA,EAAC;AAE7E;AAcO,SAAS,uBACd,OACA,QACa;AACb,QAAM,UAAU,mBAAmB,OAAO,MAAM;AAChD,QAAM,4BAA4B,mBAAmB,KAAK;AAC1D,MAAI,aAAiC;AACrC,MAAI,iBAAiB;AAErB,aAAW,QAAQ,2BAA2B;AAE5C,UAAM,aAAa,uBAAuB,MAAM,OAAO;AACvD,UAAM,YAAY,WAAW,OAAO,CAAC,KAAK,CAAA,EAAG,GAAG,MAAM,MAAM,KAAK,CAAC;AAGlE,QAAI,cAAc,EAAG;AAGrB,UAAM,UAAU,WAAW,KAAK,CAAC,CAAC,IAAI,MAAM,SAAS,OAAO;AAC5D,UAAM,WAAW,YAAY,MAAM,SAAS,OAAO;AAGnD,UAAM,OAAO,UAAU,OAAO,QAAQ,WAAW;AAGjD,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,QAAQ,gBAAgB,OAAO,OAAO;AAE5C,QAAI,QAAQ,gBAAgB;AAC1B,uBAAiB;AACjB,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,YAAA;AAAA,EACZ;AAEA,SAAO;AACT;AAKO,SAAS,2BACd,WACA,UACA,MACA,SACa;AACb,QAAM,WAAW,YAAY;AAC7B,QAAM,KAAK,SAAS;AAGpB,QAAM,gBAAgB,oBAAoB,IAAI,QAAQ;AAGtD,QAAM,aAAa,cAAc,UAAU,aAAa;AAGxD,QAAM,aAAa,mBAAmB,UAAU,KAAK;AAGrD,QAAM,UAAU,iBAAiB,YAAY,OAAO;AAEpD,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAKA,SAAS,iBACP,YACA,SACS;AACT,MAAI,QAAQ,SAAS;AACnB,QAAI,QAAQ,OAAO;AAEjB,YAAM,SAAS,QAAQ,aAAa,CAAC;AACrC,aAAO,EAAE,MAAM,YAAY,QAAQ,OAAA;AAAA,IACrC,OAAO;AAEL,YAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,YAAM,WAAW,QAAQ,aAAa,CAAC;AACvC,aAAO,EAAE,MAAM,WAAW,QAAQ,CAAC,UAAU,SAAS,EAAA;AAAA,IACxD;AAAA,EACF,OAAO;AAEL,QAAI,QAAQ,OAAO;AAEjB,YAAM,MAAM,QAAQ,aAAa,CAAC;AAClC,aAAO,EAAE,MAAM,OAAO,QAAQ,IAAA;AAAA,IAChC,OAAO;AAEL,YAAM,MAAM,QAAQ,aAAa,CAAC;AAClC,aAAO,EAAE,MAAM,OAAO,QAAQ,IAAA;AAAA,IAChC;AAAA,EACF;AACF;"}
1
+ {"version":3,"file":"index.js","sources":["../src/types.ts","../src/errors.ts","../src/utils/assertions.ts","../src/core/hai.ts","../src/core/tehai.ts","../src/core/mentsu.ts","../src/core/dora.ts","../src/core/machi.ts","../src/features/shanten/logic/chiitoitsu.ts","../src/features/shanten/logic/kokushi.ts","../src/features/shanten/logic/mentsu-te.ts","../src/features/shanten/index.ts","../src/features/machi/index.ts","../src/features/yaku/lib/structures/mentsu-te.ts","../src/features/yaku/lib/structures/chiitoitsu.ts","../src/features/yaku/lib/structures/kokushi.ts","../src/features/yaku/lib/structures/index.ts","../src/features/yaku/utils.ts","../src/features/yaku/factory.ts","../src/features/yaku/lib/definitions/tanyao.ts","../src/features/yaku/lib/definitions/pinfu.ts","../src/features/yaku/lib/definitions/iipeikou.ts","../src/features/yaku/lib/definitions/ryanpeikou.ts","../src/features/yaku/lib/definitions/sanankou.ts","../src/features/yaku/lib/definitions/suuankou.ts","../src/features/yaku/lib/definitions/sankantsu.ts","../src/features/yaku/lib/definitions/suukantsu.ts","../src/features/yaku/lib/definitions/toitoi.ts","../src/features/yaku/lib/definitions/chiitoitsu.ts","../src/features/yaku/lib/definitions/honchan.ts","../src/features/yaku/lib/definitions/junchan.ts","../src/features/yaku/lib/definitions/honroutou.ts","../src/features/yaku/lib/definitions/chinroutou.ts","../src/features/yaku/lib/definitions/shousangen.ts","../src/features/yaku/lib/definitions/daisangen.ts","../src/features/yaku/lib/definitions/tsuuiisou.ts","../src/features/yaku/lib/definitions/ryuuiisou.ts","../src/features/yaku/lib/definitions/shousuushii.ts","../src/features/yaku/lib/definitions/daisuushii.ts","../src/features/yaku/lib/definitions/chuuren-poutou.ts","../src/features/yaku/lib/definitions/kokushi.ts","../src/features/yaku/lib/definitions/sanshoku-doujun.ts","../src/features/yaku/lib/definitions/sanshoku-doukou.ts","../src/features/yaku/lib/definitions/ikkitsuukan.ts","../src/features/yaku/lib/definitions/honitsu.ts","../src/features/yaku/lib/definitions/chinitsu.ts","../src/features/yaku/lib/definitions/yakuhai.ts","../src/features/yaku/lib/definitions/menzen-tsumo.ts","../src/features/yaku/lib/definitions/index.ts","../src/features/yaku/index.ts","../src/features/parser/mspz.ts","../src/features/parser/index.ts","../src/features/score/lib/fu/constants.ts","../src/features/score/lib/fu/lib/chiitoitsu.ts","../src/features/score/lib/fu/lib/kokushi.ts","../src/features/score/lib/fu/lib/mentsu.ts","../src/features/score/lib/fu/index.ts","../src/features/score/constants.ts","../src/features/score/types.ts","../src/features/score/index.ts"],"sourcesContent":["/**\n * 牌種IDの定数セット\n *\n * - 0-8: 萬子 (ManZu)\n * - 9-17: 筒子 (PinZu)\n * - 18-26: 索子 (SouZu)\n * - 27-33: 字牌 (JiHai)\n */\nexport const HAI_KIND_IDS = [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,\n] as const;\n\n// Utility for fixed-length tuple\ntype TupleOf<\n T,\n N extends number,\n R extends unknown[] = [],\n> = R[\"length\"] extends N ? R : TupleOf<T, N, [T, ...R]>;\n\n/**\n * 牌の種類の総数 (34)\n */\nexport type HaiKindCount = (typeof HAI_KIND_IDS)[\"length\"];\n\n/**\n * 特定の種類の牌の所持数 (0-4枚)\n * 各種類の牌は最大4枚まで存在します。\n */\nexport type HaiQuantity = 0 | 1 | 2 | 3 | 4;\n\n/**\n * 全34種類の牌の所持数分布配列。\n * インデックスは HaiKindId (0-33) に対応します。\n * 各要素はその種類の牌の所持数 (0-4) です。\n *\n * @example\n * // \"113m 1z\" (MPSZ形式) に対応\n * const dist: HaiKindDistribution = [\n * 2, 0, 1, 0, 0, 0, 0, 0, 0, // 萬子 (1m x2, 3m x1)\n * 0, 0, 0, 0, 0, 0, 0, 0, 0, // 筒子\n * 0, 0, 0, 0, 0, 0, 0, 0, 0, // 索子\n * 1, 0, 0, 0, 0, 0, 0 // 字牌 (1z x1)\n * ];\n */\nexport type HaiKindDistribution = Readonly<TupleOf<HaiQuantity, HaiKindCount>>;\n\n/**\n * 物理的な牌の一意な識別子 (HaiId)\n *\n * 麻雀セットに含まれる136枚の牌それぞれに一意のIDが割り当てられます (0-135)。\n *\n * - 0-35: 萬子\n * - 36-71: 筒子\n * - 72-107: 索子\n * - 108-135: 字牌\n *\n * Branded Type を使用して通常の number と区別します。\n */\nexport type HaiId = number & { readonly __brand: \"HaiId\" };\n\n/**\n * 牌種ID (HaiKindId)\n *\n * 麻雀の34種類の牌を一意に識別するID。\n */\nexport type HaiKindId = (typeof HAI_KIND_IDS)[number];\n\n/**\n * 牌種 (HaiKind)\n *\n * 牌種IDに対応する定数定義。\n */\nexport const HaiKind = {\n ManZu1: 0,\n ManZu2: 1,\n ManZu3: 2,\n ManZu4: 3,\n ManZu5: 4,\n ManZu6: 5,\n ManZu7: 6,\n ManZu8: 7,\n ManZu9: 8,\n PinZu1: 9,\n PinZu2: 10,\n PinZu3: 11,\n PinZu4: 12,\n PinZu5: 13,\n PinZu6: 14,\n PinZu7: 15,\n PinZu8: 16,\n PinZu9: 17,\n SouZu1: 18,\n SouZu2: 19,\n SouZu3: 20,\n SouZu4: 21,\n SouZu5: 22,\n SouZu6: 23,\n SouZu7: 24,\n SouZu8: 25,\n SouZu9: 26,\n Ton: 27,\n Nan: 28,\n Sha: 29,\n Pei: 30,\n Haku: 31,\n Hatsu: 32,\n Chun: 33,\n} as const;\n\nexport type HaiKind = (typeof HaiKind)[keyof typeof HaiKind];\n\n/**\n * 牌種タイプ (HaiType)\n */\nexport const HaiType = {\n Manzu: \"Manzu\",\n Pinzu: \"Pinzu\",\n Souzu: \"Souzu\",\n Jihai: \"Jihai\",\n} as const;\n\nexport type HaiType = (typeof HaiType)[keyof typeof HaiType];\n\nexport type Suupai =\n | typeof HaiType.Manzu\n | typeof HaiType.Pinzu\n | typeof HaiType.Souzu;\n\nexport type Jihai = typeof HaiType.Jihai;\n\n/**\n * シャンテン数 (ShantenNumber)\n *\n * 和了までの手数 - 1 を表す数値。\n *\n * - 0: テンパイ (Tenpai) - 次の1手で和了できる状態\n * - 1: 1シャンテン - テンパイまであと1手\n * - ...\n * - 13: 理論上の最大値 (例: 13面待ちでない国士無双の初期状態など)\n *\n * ※ 本ライブラリでは、ツモる前の13枚の手牌に対する計算を前提とするため、\n * 和了 (-1) はこの型には含めない。\n */\nexport type ShantenNumber =\n | 0\n | 1\n | 2\n | 3\n | 4\n | 5\n | 6\n | 7\n | 8\n | 9\n | 10\n | 11\n | 12\n | 13;\n\n/**\n * 符 (Fu)\n *\n * 和了時の手牌構成から算出される符の値。\n *\n * - 20: 副底(基本符)\n * - 25: 七対子専用\n * - 30〜110: 10符刻みの値\n */\nexport type Fu = 20 | 25 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | 110;\n\n/**\n * 他家 (Tacha)\n *\n * 自分から見た他家の位置関係(相対席)。\n * 副露(鳴き)の発生元などを表現するために使用する。\n *\n * - 1: 下家 (Shimocha) - 右側\n * - 2: 対面 (Toimen) - 正面\n * - 3: 上家 (Kamicha) - 左側\n */\nexport const Tacha = {\n Shimocha: 1,\n Toimen: 2,\n Kamicha: 3,\n} as const;\n\nexport type Tacha = (typeof Tacha)[keyof typeof Tacha];\n\n/**\n * 副露種別 (FuroType)\n */\nexport const FuroType = {\n Chi: \"Chi\",\n Pon: \"Pon\",\n Daiminkan: \"Daiminkan\",\n Kakan: \"Kakan\",\n} as const;\n\nexport type FuroType = (typeof FuroType)[keyof typeof FuroType];\n\n/**\n * 副露 (Furo)\n *\n * 面子に対する「鳴き」のメタ情報。\n * ここでは副露を「自分の手牌が不足している面子を、他家が捨てた牌を取って完成させる行為」と定義し、\n * 暗槓(自力で4枚揃える行為)はここには含めない。\n *\n * 構成する牌自体はここには含めず、この型を持つ親(Mentsuなど)が保持することを想定する。\n */\nexport type Furo =\n | { readonly type: typeof FuroType.Chi; readonly from: Tacha }\n | { readonly type: typeof FuroType.Pon; readonly from: Tacha }\n | { readonly type: typeof FuroType.Daiminkan; readonly from: Tacha }\n | { readonly type: typeof FuroType.Kakan; readonly from: Tacha };\n\n/**\n * 面子種別 (MentsuType)\n */\nexport const MentsuType = {\n Shuntsu: \"Shuntsu\", // 順子 (123)\n Koutsu: \"Koutsu\", // 刻子 (111)\n Kantsu: \"Kantsu\", // 槓子 (1111)\n Toitsu: \"Toitsu\", // 対子 (11)\n Tatsu: \"Tatsu\", // 塔子 (12, 13)\n} as const;\n\nexport type MentsuType = (typeof MentsuType)[keyof typeof MentsuType];\n\n/**\n * 基本的な面子構造 (ジェネリック)\n *\n * 牌の型をジェネリクス `T` で抽象化することで、以下の両方のユースケースに対応します:\n * 1. `HaiKindId`: MPSZ形式の手牌をもとにシャンテン計算を行うなど、牌の種類のみに関心がある場合(抽象的な計算)。\n * 2. `HaiId`: 実際のゲームの牌譜など、牌の物理的なIDを処理対象とする場合(具象的な計算)。\n */\ninterface BaseMentsu<T extends HaiKindId | HaiId> {\n readonly type: MentsuType;\n /**\n * 構成する牌のリスト。\n *\n * 各面子型(Shuntsu等)において固定長タプル(例: `[T, T, T]`)として再定義することで、\n * 面子の種類ごとの正しい牌枚数(順子なら3枚、槓子なら4枚など)を型レベルで強制します。\n */\n hais: readonly T[];\n}\n\n/**\n * 順子 (Shuntsu)\n */\nexport type Shuntsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Shuntsu;\n readonly hais: readonly [T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 刻子 (Koutsu)\n */\nexport type Koutsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Koutsu;\n readonly hais: readonly [T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 槓子 (Kantsu)\n */\nexport type Kantsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Kantsu;\n readonly hais: readonly [T, T, T, T];\n readonly furo?: Furo;\n};\n\n/**\n * 対子 (Toitsu)\n */\nexport type Toitsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Toitsu;\n readonly hais: readonly [T, T];\n readonly furo?: never;\n};\n\n/**\n * 塔子 (Tatsu)\n */\nexport type Tatsu<T extends HaiKindId | HaiId = HaiKindId> = BaseMentsu<T> & {\n readonly type: typeof MentsuType.Tatsu;\n readonly hais: readonly [T, T];\n readonly furo?: never;\n};\n\n/**\n * 完成面子 (CompletedMentsu)\n * - 順子 (Shuntsu)\n * - 刻子 (Koutsu)\n * - 槓子 (Kantsu)\n */\nexport type CompletedMentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | Shuntsu<T>\n | Koutsu<T>\n | Kantsu<T>;\n\n/**\n * 未完成面子 (IncompletedMentsu)\n * - 対子 (Toitsu)\n * - 塔子 (Tatsu)\n */\nexport type IncompletedMentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | Toitsu<T>\n | Tatsu<T>;\n\n/**\n * 面子 (Mentsu)\n *\n * 広義の面子(ブロック)。指定がない場合は HaiKindId のリストを持つ。\n */\nexport type Mentsu<T extends HaiKindId | HaiId = HaiKindId> =\n | CompletedMentsu<T>\n | IncompletedMentsu<T>;\n\n/** 自風・場風を表す牌 (東・南・西・北) */\nexport type Kazehai =\n | typeof HaiKind.Ton\n | typeof HaiKind.Nan\n | typeof HaiKind.Sha\n | typeof HaiKind.Pei;\n\nexport type HaiIsYaocyu<ID extends HaiKindId | HaiId> = ID extends 0 | 8 | 9\n ? true\n : false;\n\n/**\n * 手牌 (Tehai)\n *\n * 純手牌と副露を合わせたもの。\n * @template T 牌の型 (HaiKindId | HaiId)\n */\nexport interface Tehai<T extends HaiKindId | HaiId = HaiKindId> {\n readonly closed: readonly T[];\n readonly exposed: readonly CompletedMentsu<T>[];\n}\n\n/**\n * ツモる前の手牌 (13枚)\n */\nexport type Tehai13<T extends HaiKindId | HaiId = HaiKindId> = Tehai<T>;\n\n/**\n * ツモった後の手牌 (14枚)\n */\nexport type Tehai14<T extends HaiKindId | HaiId = HaiKindId> = Tehai<T>;\n\n// ... types existing ...\n","/**\n * riichi-mahjong ライブラリの基本エラークラス\n * 全てのカスタムエラーはこのクラスを継承します。\n */\nexport class MahjongError extends Error {\n /**\n *\n */\n constructor(message: string) {\n super(message);\n this.name = \"MahjongError\";\n\n // TypeScriptでカスタムエラーを正しく動作させるためのハック\n // TypeScriptでカスタムエラーを正しく動作させるためのハック\n Object.setPrototypeOf(this, MahjongError.prototype);\n }\n}\n\n/**\n * ツモれなかった場合のエラー (少牌)\n * 手牌が規定枚数(13枚)より少ない場合にスローされます。\n */\nexport class ShoushaiError extends MahjongError {\n /**\n *\n */\n constructor(message = \"手牌が規定枚数(13枚)より少ないです。\") {\n super(message);\n this.name = \"ShoushaiError\";\n\n Object.setPrototypeOf(this, ShoushaiError.prototype);\n }\n}\n\n/**\n * 切り忘れの場合のエラー (多牌)\n * 手牌が規定枚数(13枚)より多い場合にスローされます。\n */\nexport class TahaiError extends MahjongError {\n /**\n *\n */\n constructor(message = \"手牌が規定枚数(13枚)より多いです。\") {\n super(message);\n this.name = \"TahaiError\";\n\n Object.setPrototypeOf(this, TahaiError.prototype);\n }\n}\n\n/**\n * 引数が不正な場合のエラー\n * 必要なパラメータが不足している、または不正な値の場合にスローされます。\n */\nexport class MahjongArgumentError extends MahjongError {\n /**\n *\n */\n constructor(message: string) {\n super(message);\n this.name = \"MahjongArgumentError\";\n\n Object.setPrototypeOf(this, MahjongArgumentError.prototype);\n }\n}\n\n/**\n * 牌IDが重複している場合のエラー\n * (物理的な牌IDは一意である必要があります)\n */\nexport class DuplicatedHaiIdError extends MahjongError {\n /**\n *\n */\n constructor(message = \"牌IDが重複しています。\") {\n super(message);\n this.name = \"DuplicatedHaiIdError\";\n\n Object.setPrototypeOf(this, DuplicatedHaiIdError.prototype);\n }\n}\n\n/**\n * 牌の枚数が不正な場合のエラー\n * (同種の牌は最大4枚までです)\n */\nexport class InvalidHaiQuantityError extends MahjongError {\n /**\n *\n */\n constructor(message = \"同種の牌が5枚以上存在します。\") {\n super(message);\n this.name = \"InvalidHaiQuantityError\";\n\n Object.setPrototypeOf(this, InvalidHaiQuantityError.prototype);\n }\n}\n\n/**\n * チョンボ(錯和)の基底エラークラス\n *\n * 不正な和了宣言に関するエラーの基底クラス。\n * 具体的なチョンボ種別(役なし、フリテン等)はサブクラスで定義する。\n */\nexport class ChomboError extends MahjongError {\n /**\n *\n */\n constructor(message = \"不正な和了です。\") {\n super(message);\n this.name = \"ChomboError\";\n\n Object.setPrototypeOf(this, ChomboError.prototype);\n }\n}\n\n/**\n * 役なし和了のエラー\n *\n * 和了形は成立しているが、役が一つも成立していない場合にスローされます。\n */\nexport class NoYakuError extends ChomboError {\n /**\n *\n */\n constructor(message = \"役が成立していません。\") {\n super(message);\n this.name = \"NoYakuError\";\n\n Object.setPrototypeOf(this, NoYakuError.prototype);\n }\n}\n","import { type HaiId, type HaiKindId } from \"../types\";\n\n/**\n * Checks if the array has exactly 2 elements and narrows the type to a tuple.\n */\nexport function isTuple2<T>(arr: readonly T[]): arr is readonly [T, T] {\n return arr.length === 2;\n}\n\n/**\n * Checks if the array has exactly 3 elements and narrows the type to a tuple.\n */\nexport function isTuple3<T>(arr: readonly T[]): arr is readonly [T, T, T] {\n return arr.length === 3;\n}\n\n/**\n * Checks if the array has exactly 4 elements and narrows the type to a tuple.\n */\nexport function isTuple4<T>(arr: readonly T[]): arr is readonly [T, T, T, T] {\n return arr.length === 4;\n}\n\n/**\n * Casts a number to HaiKindId safely (conceptually).\n * Use this only when you are sure the number is a valid HaiKindId.\n */\nexport function asHaiKindId(id: number): HaiKindId {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return id as HaiKindId;\n}\n\n/**\n * Casts a number to HaiId safely (conceptually).\n */\nexport function asHaiId(id: number): HaiId {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return id as HaiId;\n}\n","import { type HaiId, HaiKind, type HaiKindId, HaiType } from \"../types\";\nimport { asHaiKindId } from \"../utils/assertions\";\n\n/**\n * 牌種IDから牌種タイプを取得する\n */\nexport function kindIdToHaiType(kind: HaiKindId): HaiType {\n if (kind >= HaiKind.ManZu1 && kind <= HaiKind.ManZu9) {\n return HaiType.Manzu;\n }\n if (kind >= HaiKind.PinZu1 && kind <= HaiKind.PinZu9) {\n return HaiType.Pinzu;\n }\n if (kind >= HaiKind.SouZu1 && kind <= HaiKind.SouZu9) {\n return HaiType.Souzu;\n }\n return HaiType.Jihai;\n}\n\n/**\n * 物理牌IDから牌種IDを取得する\n * 0-35: 萬子 (36枚 = 9種 * 4枚) -> 0-8\n * 36-71: 筒子 (36枚) -> 9-17\n * 72-107: 索子 (36枚) -> 18-26\n * 108-135: 字牌 (28枚 = 7種 * 4枚) -> 27-33\n */\nexport function haiIdToKindId(id: HaiId): HaiKindId {\n if (id < 36) return asHaiKindId(Math.floor(id / 4));\n if (id < 72) return asHaiKindId(Math.floor((id - 36) / 4) + 9);\n if (id < 108) return asHaiKindId(Math.floor((id - 72) / 4) + 18);\n return asHaiKindId(Math.floor((id - 108) / 4) + 27);\n}\n\n/**\n * 牌種IDから数値(1-9)を取得する\n * 字牌の場合は undefined を返す\n */\nexport function haiKindToNumber(kind: HaiKindId): number | undefined {\n const type = kindIdToHaiType(kind);\n if (type === HaiType.Jihai) return undefined;\n\n if (type === HaiType.Manzu) return kind - HaiKind.ManZu1 + 1;\n if (type === HaiType.Pinzu) return kind - HaiKind.PinZu1 + 1;\n // if (kindIdToHaiType(kind) === HaiType.Souzu)\n return kind - HaiKind.SouZu1 + 1;\n}\n\n/**\n * 数牌かどうかを判定する\n */\nexport function isSuupai(kind: HaiKindId): boolean {\n return kindIdToHaiType(kind) !== HaiType.Jihai;\n}\n\n/**\n * 么九牌(1,9,字牌)の牌種IDセット\n */\nexport const YAOCHU_KIND_IDS = [\n HaiKind.ManZu1,\n HaiKind.ManZu9,\n HaiKind.PinZu1,\n HaiKind.PinZu9,\n HaiKind.SouZu1,\n HaiKind.SouZu9,\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n HaiKind.Haku,\n HaiKind.Hatsu,\n HaiKind.Chun,\n] as const;\n\n/**\n * 么九牌(1,9,字牌)かどうかを判定する\n */\nexport function isYaochu(kind: HaiKindId): boolean {\n return YAOCHU_KIND_IDS.some((k) => k === kind);\n}\n","import {\n DuplicatedHaiIdError,\n InvalidHaiQuantityError,\n ShoushaiError,\n TahaiError,\n} from \"../errors\";\nimport type {\n HaiId,\n HaiKindDistribution,\n HaiKindId,\n Tehai,\n Tehai13,\n Tehai14,\n} from \"../types\";\n\n/**\n * 手牌の有効枚数を計算します。\n * 副露(槓子含む)は一律3枚として計算します。\n */\nexport function calculateTehaiCount<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): number {\n return tehai.closed.length + tehai.exposed.length * 3;\n}\n\n/**\n * 牌種ごとの枚数をカウントします。\n */\nexport function countHaiKind(hais: readonly HaiKindId[]): HaiKindDistribution {\n const counts = Array.from({ length: 34 }, () => 0);\n for (const hai of hais) {\n counts[hai] = (counts[hai] ?? 0) + 1;\n }\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return counts as unknown as HaiKindDistribution;\n}\n\n/**\n * 手牌がTehai13(有効枚数13枚)であるか検証します。\n * @throws {ShoushaiError} 枚数が不足している場合\n * @throws {TahaiError} 枚数が超過している場合\n */\nexport function validateTehai13<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 13) {\n throw new ShoushaiError();\n }\n if (count > 13) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌がTehai14(有効枚数14枚)であるか検証します。\n * @throws {ShoushaiError} 枚数が不足している場合\n * @throws {TahaiError} 枚数が超過している場合\n * @throws {InvalidHaiQuantityError} 同一種の牌が5枚以上ある場合\n * @throws {DuplicatedHaiIdError} 物理牌モードでIDが重複している場合\n */\nexport function validateTehai14<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 14) {\n throw new ShoushaiError();\n }\n if (count > 14) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌がTehai13またはTehai14(有効枚数が13または14枚)であるか検証します。\n * シャンテン計算や待ち判定など、13枚/14枚の区別なく手牌として扱いたい場合に使用します。\n *\n * @throws {ShoushaiError} 枚数が不足している場合 (< 13)\n * @throws {TahaiError} 枚数が超過している場合 (> 14)\n * @throws {InvalidHaiQuantityError} 同一種の牌が5枚以上ある場合\n * @throws {DuplicatedHaiIdError} 物理牌モードでIDが重複している場合\n */\nexport function validateTehai<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const count = calculateTehaiCount(tehai);\n if (count < 13) {\n throw new ShoushaiError();\n }\n if (count > 14) {\n throw new TahaiError();\n }\n validateHaiConsistency(tehai);\n}\n\n/**\n * 手牌の整合性を検証します。\n * - 物理的な牌ID (`HaiId`) が使われている場合、重複チェックを行います。\n * - 牌種ID (`HaiKindId`) の場合(または変換後)、同一牌種が5枚以上ないかチェックします。\n *\n * @throws {DuplicatedHaiIdError}\n * @throws {InvalidHaiQuantityError}\n */\nfunction validateHaiConsistency<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): void {\n const allHais: number[] = [\n ...tehai.closed,\n ...tehai.exposed.flatMap((m) => m.hais),\n ];\n\n // 1. Check for physical HaiId usage (any id > 33)\n // HaiKindId is 0-33. Any value > 33 implies HaiId (0-135).\n // Note: Low HaiIds (0-33) are ambiguous, but if mix of high and low exists, it's HaiId.\n // If only low values exist, we can't strictly distinguish, but treating as KindId is safe\n // unless the user provided [0, 0] intending HaiId 0 and HaiId 0.\n // However, normally HaiKindId 0 is ManZu1.\n // Strategy: If MAX(id) > 33, treat as HaiId.\n const isHaiIdMode = allHais.some((h) => h > 33);\n\n if (isHaiIdMode) {\n // Check for duplicate HaiIds\n const uniqueIds = new Set(allHais);\n if (uniqueIds.size !== allHais.length) {\n throw new DuplicatedHaiIdError();\n }\n }\n\n // 2. Check for Kind quantity (max 4 per kind)\n const counts = new Map<number, number>();\n for (const hai of allHais) {\n // If HaiId mode, convert to KindId\n // If KindId mode, use as is\n // import { haiIdToKindId } from \"./hai\"; <--- Need to import or implement logic\n // Since we are in core/tehai, and core/hai depends on types.\n // Let's defer strict conversion.\n // For now, assume generic T validation behavior.\n // But we need `haiIdToKindId`.\n // Let's implement logic inline or use import.\n // Circular dependency risk? core/tehai -> core/hai.\n // core/hai imports types. core/tehai imports types. Should be fine.\n // But I need to import it at top of file.\n\n // Using inline logic to avoid circular deps if any (though likely safe)\n // 0-35 -> 0-8, etc.\n let kind: number = hai;\n if (hai > 33) {\n if (hai < 36) kind = Math.floor(hai / 4);\n else if (hai < 72) kind = Math.floor((hai - 36) / 4) + 9;\n else if (hai < 108) kind = Math.floor((hai - 72) / 4) + 18;\n else kind = Math.floor((hai - 108) / 4) + 27;\n }\n\n const current = counts.get(kind) ?? 0;\n if (current + 1 > 4) {\n throw new InvalidHaiQuantityError();\n }\n counts.set(kind, current + 1);\n }\n}\n\n/**\n * Type Guard for Tehai13\n */\nexport function isTehai13<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): tehai is Tehai13<T> {\n try {\n validateTehai13(tehai);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Type Guard for Tehai14\n */\nexport function isTehai14<T extends HaiKindId | HaiId>(\n tehai: Tehai<T>,\n): tehai is Tehai14<T> {\n try {\n validateTehai14(tehai);\n return true;\n } catch {\n return false;\n }\n}\n","import type { HaiKindId } from \"../types\";\nimport { haiKindToNumber, isSuupai, kindIdToHaiType } from \"./hai\";\nimport { isTuple2, isTuple3, isTuple4 } from \"../utils/assertions\";\n\n// バリデーションロジックは「HaiKindId の配列」に対して行うものと定義する。\n// HaiId を持つ Mentsu を検証したい場合は、呼び出し側で KindId に変換してから渡す必要がある。\n// ただし、利便性のために `convertHaiIdToKindId` ヘルパーを export する。\n\n/**\n * 順子かどうかを検証する\n\n */\nexport function isValidShuntsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple3(kindIds)) return false;\n\n const [a, b, c] = kindIds;\n if (!isSuupai(a) || !isSuupai(b) || !isSuupai(c)) return false;\n\n const typeA = kindIdToHaiType(a);\n const typeB = kindIdToHaiType(b);\n const typeC = kindIdToHaiType(c);\n\n if (typeA !== typeB || typeA !== typeC) return false;\n\n const numA = haiKindToNumber(a);\n const numB = haiKindToNumber(b);\n const numC = haiKindToNumber(c);\n\n if (numA === undefined || numB === undefined || numC === undefined)\n return false;\n\n // ソートして連続性をチェック\n const sorted = [numA, numB, numC].sort((x, y) => x - y);\n\n // Safe to access since we just created it with 3 elements\n // But strict check might complain about index access on array\n if (!isTuple3(sorted)) return false; // Should be always true\n return sorted[0] + 1 === sorted[1] && sorted[1] + 1 === sorted[2];\n}\n\n/**\n * 刻子かどうかを検証する\n\n */\nexport function isValidKoutsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple3(kindIds)) return false;\n const [a, b, c] = kindIds;\n return a === b && b === c;\n}\n\n/**\n * 槓子かどうかを検証する\n\n */\nexport function isValidKantsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple4(kindIds)) return false;\n const [a, b, c, d] = kindIds;\n return a === b && b === c && c === d;\n}\n\n/**\n * 対子かどうかを検証する\n\n */\nexport function isValidToitsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple2(kindIds)) return false;\n const [a, b] = kindIds;\n return a === b;\n}\n\n/**\n * 塔子かどうかを検証する\n\n */\nexport function isValidTatsu(kindIds: readonly HaiKindId[]): boolean {\n if (!isTuple2(kindIds)) return false;\n const [a, b] = kindIds;\n\n // 数牌でなければならない\n if (!isSuupai(a) || !isSuupai(b)) return false;\n\n // 同じ種類でなければならない\n const typeA = kindIdToHaiType(a);\n const typeB = kindIdToHaiType(b);\n if (typeA !== typeB) return false;\n\n const numA = haiKindToNumber(a);\n const numB = haiKindToNumber(b);\n\n if (numA === undefined || numB === undefined) return false;\n\n const diff = Math.abs(numA - numB);\n // 差が1 (ペンチャン/リャンメン) または 2 (カンチャン) ならOK\n return diff === 1 || diff === 2;\n}\n","import { HaiKind, type HaiKindId, type Tehai } from \"../types\";\nimport { kindIdToHaiType } from \"./hai\";\nimport { HaiType } from \"../types\";\nimport { asHaiKindId } from \"../utils/assertions\";\n\n/**\n * ドラ表示牌から次の牌(ドラ)を求める\n * @param indicator ドラ表示牌のID (HaiKindId)\n * @returns ドラ牌のID (HaiKindId)\n */\nexport function getDoraNext(indicator: HaiKindId): HaiKindId {\n const type = kindIdToHaiType(indicator);\n\n if (type === HaiType.Manzu) {\n if (indicator === HaiKind.ManZu9) return HaiKind.ManZu1;\n return asHaiKindId(indicator + 1);\n }\n\n if (type === HaiType.Pinzu) {\n if (indicator === HaiKind.PinZu9) return HaiKind.PinZu1;\n return asHaiKindId(indicator + 1);\n }\n\n if (type === HaiType.Souzu) {\n if (indicator === HaiKind.SouZu9) return HaiKind.SouZu1;\n return asHaiKindId(indicator + 1);\n }\n\n // Jihai\n // Ton(27) -> Nan(28) -> Sha(29) -> Pei(30) -> Ton(27)\n if (indicator === HaiKind.Pei) return HaiKind.Ton;\n if (indicator >= HaiKind.Ton && indicator < HaiKind.Pei) {\n return asHaiKindId(indicator + 1);\n }\n\n // Haku(31) -> Hatsu(32) -> Chun(33) -> Haku(31)\n if (indicator === HaiKind.Chun) return HaiKind.Haku;\n if (indicator >= HaiKind.Haku && indicator < HaiKind.Chun) {\n return asHaiKindId(indicator + 1);\n }\n\n // Should not happen for valid HaiKindId\n return indicator;\n}\n\n/**\n * 手牌に含まれるドラの数を数える\n * @param tehai 手牌\n * @param indicators ドラ表示牌のリスト\n * @returns ドラの総数\n */\nexport function countDora(\n tehai: Tehai,\n indicators: readonly HaiKindId[],\n): number {\n let count = 0;\n // Calculate actual dora hinds\n const doraHais = indicators.map(getDoraNext);\n\n // Count in closed hand\n for (const hai of tehai.closed) {\n for (const dora of doraHais) {\n if (hai === dora) count++;\n }\n }\n\n // Count in exposed mentsu\n for (const mentsu of tehai.exposed) {\n for (const hai of mentsu.hais) {\n for (const dora of doraHais) {\n if (hai === dora) count++;\n }\n }\n }\n\n // TODO: Add Akadora counting logic here\n\n return count;\n}\n","import type { HaiKindId, Shuntsu } from \"../types\";\nimport type { HouraStructure } from \"../features/yaku/types\";\n\n/** 待ちの形 */\nexport type MachiType =\n | \"Tanki\" // 単騎待ち\n | \"Shanpon\" // 双碰待ち (シャボ)\n | \"Ryanmen\" // 両面待ち\n | \"Kanchan\" // 嵌張待ち\n | \"Penchan\"; // 辺張待ち\n\n/**\n * 手牌構造と和了牌から待ちの形を判定する\n * @param hand 分解された手牌構造\n * @param agariHai 和了牌\n * @returns 待ちの形(判定できない、または Shanpon などの場合は undefined)\n */\nexport function classifyMachi(\n hand: HouraStructure,\n agariHai: HaiKindId,\n): MachiType | undefined {\n if (hand.type !== \"Mentsu\") return undefined;\n\n // 1. 雀頭での和了(単騎待ち)\n if (hand.jantou.hais.includes(agariHai)) {\n return \"Tanki\";\n }\n\n // 2. 順子・刻子・槓子での和了\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.type === \"Shuntsu\") {\n const machi = classifyShuntsuWait(mentsu, agariHai);\n if (machi) return machi;\n } else {\n // 3. 刻子・槓子での和了(双碰待ち)\n // 刻子の一部が和了牌=シャボ待ちで和了\n if (mentsu.hais.includes(agariHai)) {\n return \"Shanpon\";\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * 順子における待ちの形を判定する(内部ヘルパー)\n */\nfunction classifyShuntsuWait(\n shuntsu: Shuntsu,\n agariHai: HaiKindId,\n): MachiType | undefined {\n const { hais } = shuntsu;\n if (!hais.includes(agariHai)) return undefined;\n\n const [a, b, c] = hais; // 順子はソートされている前提\n\n if (agariHai === a) {\n // [Agari, b, c]\n const valC = c % 9;\n if (valC === 8) return \"Penchan\";\n return \"Ryanmen\";\n }\n\n if (agariHai === c) {\n // [a, b, Agari]\n const valA = a % 9;\n if (valA === 0) return \"Penchan\";\n return \"Ryanmen\";\n }\n\n if (agariHai === b) {\n // [a, Agari, c]\n return \"Kanchan\";\n }\n\n return undefined;\n}\n","import { countHaiKind, validateTehai13 } from \"../../../core/tehai\";\nimport type { Tehai13 } from \"../../../types\";\n\n/**\n * 七対子のシャンテン数を計算する\n *\n * @param tehai 手牌 (13枚)\n * @returns シャンテン数 (0: 聴牌, -1: 和了 - 理論上)\n */\nexport function calculateChiitoitsuShanten(tehai: Tehai13): number {\n // 防御的プログラミング (Defensive Programming):\n // 公開API(calculateShanten)側でもバリデーションが行われる想定だが(Facadeパターン)、\n // 内部整合性を保つため、ここでも独立してバリデーションを実施する。\n validateTehai13(tehai);\n\n // シャンテン数を計算する前にバリデーションを実行する\n // 七対子は門前のみ\n\n if (tehai.exposed.length > 0) {\n return Infinity;\n }\n\n // HaiId/HaiKindId の正規化は廃止。呼び出し元で HaiKindId を保証する。\n const haiCounts = countHaiKind(tehai.closed);\n\n let pairs = 0;\n let kinds = 0;\n\n for (const count of haiCounts) {\n if (count > 0) {\n kinds++;\n }\n if (count >= 2) {\n pairs++;\n }\n }\n\n let shanten = 6 - pairs;\n\n // 種類不足ペナルティ\n if (kinds < 7) {\n shanten += 7 - kinds;\n }\n\n return shanten;\n}\n","import type { Tehai13 } from \"../../../types\";\nimport { isYaochu } from \"../../../core/hai\";\nimport { countHaiKind, validateTehai13 } from \"../../../core/tehai\";\nimport { asHaiKindId } from \"../../../utils/assertions\";\n\n/**\n * 国士無双のシャンテン数を計算します。\n *\n * ルール:\n * - 13種類の么九牌(1,9,字牌)を各1枚ずつ揃える。\n * - そのうちのどれか1種類が対子(2枚)になっている必要がある。\n * - 門前限定。\n *\n * 計算式:\n * シャンテン数 = 13 - (么九牌の種類数) - (么九牌の対子があるか ? 1 : 0)\n *\n * @param tehai 手牌\n * @returns シャンテン数 (0: 聴牌, -1: 和了(理論上))。副露している場合は Infinity。\n */\nexport function calculateKokushiShanten(tehai: Tehai13): number {\n // 防御的プログラミング (Defensive Programming):\n // 公開API(calculateShanten)側でもバリデーションが行われる想定だが(Facadeパターン)、\n // 内部整合性を保つため、ここでも独立してバリデーションを実施する。\n validateTehai13(tehai);\n\n // 国士無双は門前のみ\n if (tehai.exposed.length > 0) {\n return Infinity;\n }\n\n const dist = countHaiKind(tehai.closed);\n\n // 么九牌の種類数をカウント\n // 同時に、么九牌の対子が存在するかもチェック\n let uniqueYaochuCount = 0;\n let hasYaochuPair = false;\n\n for (let i = 0; i < dist.length; i++) {\n const kind = asHaiKindId(i);\n if (!isYaochu(kind)) {\n continue;\n }\n\n const count = dist[i];\n if (count !== undefined && count > 0) {\n uniqueYaochuCount++;\n if (count >= 2) {\n hasYaochuPair = true;\n }\n }\n }\n\n const pairBonus = hasYaochuPair ? 1 : 0;\n\n // シャンテン数 = 13 - (種類の数) - (対子ボーナス)\n return 13 - uniqueYaochuCount - pairBonus;\n}\n","import type { Tehai } from \"../../../types\";\n\nimport { countHaiKind, validateTehai } from \"../../../core/tehai\";\n\n/**\n * 面子手(4面子1雀頭)のシャンテン数を計算する\n *\n * @param tehai 手牌 (13枚 or 14枚)\n * @returns シャンテン数\n */\nexport function calculateMentsuTeShanten(tehai: Tehai): number {\n // 防御的プログラミング\n validateTehai(tehai);\n\n const counts = countHaiKind(tehai.closed);\n // Mutation is required for the algorithm, so we convert to a mutable number array\n const mutableCounts: number[] = Array.from(counts);\n const exposedCount = tehai.exposed.length;\n\n // 基本シャンテン数 (8 - 2 * 面子数)\n let minShanten = 8 - 2 * exposedCount;\n\n // 1. 雀頭がある場合\n for (let i = 0; i < 34; i++) {\n if ((mutableCounts[i] ?? 0) >= 2) {\n mutableCounts[i] = (mutableCounts[i] ?? 0) - 2;\n const { m, t } = searchMentsu(mutableCounts);\n const currentMentsu = exposedCount + m;\n const effectiveTaatsu = Math.min(4 - currentMentsu, t);\n const shanten = 8 - 2 * currentMentsu - effectiveTaatsu - 1;\n minShanten = Math.min(minShanten, shanten);\n mutableCounts[i] = (mutableCounts[i] ?? 0) + 2;\n }\n }\n\n // 2. 雀頭がない場合\n {\n const { m, t } = searchMentsu(mutableCounts);\n const currentMentsu = exposedCount + m;\n const effectiveTaatsu = Math.min(4 - currentMentsu, t);\n const shanten = 8 - 2 * currentMentsu - effectiveTaatsu;\n minShanten = Math.min(minShanten, shanten);\n }\n\n return minShanten;\n}\n\n/**\n * 探索結果の型\n */\ninterface SearchResult {\n m: number;\n t: number;\n}\n\n/**\n * 面子と塔子の最大数を探索する\n */\nfunction searchMentsu(counts: readonly number[]): SearchResult {\n let maxScore = -1;\n let bestResult: SearchResult = { m: 0, t: 0 };\n // copies needed because we mutate counts\n const w = [...counts];\n\n const search = (index: number, m: number) => {\n // 34種類すべて見終わったら塔子を数える\n if (index >= 34) {\n const t = countTaatsu(w);\n const score = 2 * m + t;\n if (score > maxScore) {\n maxScore = score;\n bestResult = { m, t };\n }\n return;\n }\n\n // 牌がない場合は次に進む\n if ((w[index] ?? 0) === 0) {\n search(index + 1, m);\n return;\n }\n\n // A. 刻子 (3枚) の場合\n if ((w[index] ?? 0) >= 3) {\n w[index] = (w[index] ?? 0) - 3;\n search(index, m + 1);\n w[index] = (w[index] ?? 0) + 3;\n }\n\n // B. 順子 (3枚) の場合\n if (index < 27) {\n const mod = index % 9;\n if (mod < 7) {\n if (\n (w[index] ?? 0) > 0 &&\n (w[index + 1] ?? 0) > 0 &&\n (w[index + 2] ?? 0) > 0\n ) {\n w[index] = (w[index] ?? 0) - 1;\n w[index + 1] = (w[index + 1] ?? 0) - 1;\n w[index + 2] = (w[index + 2] ?? 0) - 1;\n search(index, m + 1);\n w[index] = (w[index] ?? 0) + 1;\n w[index + 1] = (w[index + 1] ?? 0) + 1;\n w[index + 2] = (w[index + 2] ?? 0) + 1;\n }\n }\n }\n\n // C. 面子として使わない場合\n search(index + 1, m);\n };\n\n search(0, 0);\n return bestResult;\n}\n\n/**\n * 残った牌から塔子(対子、両面、嵌張、辺張)の数を数える\n */\nfunction countTaatsu(counts: readonly number[]): number {\n let taatsu = 0;\n // countsのコピーを作成\n const w = [...counts];\n\n for (let i = 0; i < 34; i++) {\n if ((w[i] ?? 0) === 0) continue;\n\n // 順子系の塔子 (1枚 + 1枚)\n if (i < 27) {\n const mod = i % 9;\n // 辺張・両面 (i, i+1)\n if (mod < 8) {\n if ((w[i] ?? 0) > 0 && (w[i + 1] ?? 0) > 0) {\n w[i] = (w[i] ?? 0) - 1;\n w[i + 1] = (w[i + 1] ?? 0) - 1;\n taatsu++;\n }\n }\n\n // 嵌張 (i, i+2)\n if (mod < 7) {\n if ((w[i] ?? 0) > 0 && (w[i + 2] ?? 0) > 0) {\n w[i] = (w[i] ?? 0) - 1;\n w[i + 2] = (w[i + 2] ?? 0) - 1;\n taatsu++;\n }\n }\n }\n\n // 対子 (2枚)\n if ((w[i] ?? 0) >= 2) {\n w[i] = (w[i] ?? 0) - 2;\n taatsu++;\n }\n }\n return taatsu;\n}\n","import type { Tehai13 } from \"../../types\";\nimport { validateTehai13 } from \"../../core/tehai\";\nimport { calculateChiitoitsuShanten } from \"./logic/chiitoitsu\";\nimport { calculateKokushiShanten } from \"./logic/kokushi\";\nimport { calculateMentsuTeShanten } from \"./logic/mentsu-te\";\n\nexport {\n calculateChiitoitsuShanten,\n calculateKokushiShanten,\n calculateMentsuTeShanten,\n};\n\n/**\n * シャンテン数を計算します。\n * 面子手、七対子、国士無双のシャンテン数のうち最小値を返します。\n *\n * NOTE: 入力は必ず牌種ID (`HaiKindId`) である必要があります。\n * 物理牌ID (`HaiId`) を持っている場合は、事前に `haiIdToKindId` で変換してください。\n *\n * @param tehai 手牌\n * @returns シャンテン数\n */\nexport function calculateShanten(\n tehai: Tehai13,\n useChiitoitsu = true,\n useKokushi = true,\n): number {\n // Facadeパターン: 公開APIのエントリーポイントで入力を保証する\n validateTehai13(tehai);\n\n const chiitoitsuShanten = useChiitoitsu\n ? calculateChiitoitsuShanten(tehai)\n : Infinity;\n const kokushiShanten = useKokushi ? calculateKokushiShanten(tehai) : Infinity;\n const mentsuShanten = calculateMentsuTeShanten(tehai);\n\n return Math.min(chiitoitsuShanten, kokushiShanten, mentsuShanten);\n}\n","import type { HaiKindId, Tehai13 } from \"../../types\";\nimport { countHaiKind } from \"../../core/tehai\";\nimport { calculateMentsuTeShanten } from \"../shanten\";\n\n/**\n * 手牌の受け入れ(有効牌)を計算する。\n * 今回は面子手のみを対象とし、七対子や国士無双は考慮しない。\n *\n * @param tehai 手牌 (13枚)\n * @returns シャンテン数を進める牌のリスト\n */\nexport function getUkeire(tehai: Tehai13): HaiKindId[] {\n const currentShanten = calculateMentsuTeShanten(tehai);\n const ukeireList: HaiKindId[] = [];\n\n // 手牌の全ての牌(純手牌 + 副露)をカウント\n const allHais: HaiKindId[] = [\n ...tehai.closed,\n ...tehai.exposed.flatMap((m) => m.hais),\n ];\n const haiCounts = countHaiKind(allHais);\n\n // 全34種の牌について、1枚加えてシャンテン数が下がるか試す\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const tile = i as HaiKindId;\n\n // 4枚使い切っている牌種はスキップ(山に残っていない)\n if (haiCounts[tile] >= 4) {\n continue;\n }\n\n // 手牌のコピーを作成(副作用を防ぐため、常に新しいオブジェクトで試行)\n // Tehai13に1枚足すので、厳密にはTehai14として扱う必要があるが、\n // calculateMentsuTeShanten は Tehai<HaiKindId> を受け付けるので、\n // 構造的に { closed, exposed } が適合していればOK。\n // ただし、Tehai13型に準拠したオブジェクトに1枚足すと枚数オーバーになるため、\n // バリデーションを通過させるために Tehai14 として構築するか、\n // calculateMentsuTeShanten 側が Generics で受け入れる点を利用する。\n\n // 配列のコピーを作成\n const newClosed = [...tehai.closed, tile];\n const newTehai = {\n closed: newClosed,\n exposed: tehai.exposed,\n };\n\n // シャンテン数を計算\n // ここでバリデーションエラーが出ないように、calculateMentsuTeShanten 側は validateTehai を使用している。\n const newShanten = calculateMentsuTeShanten(newTehai);\n\n if (newShanten < currentShanten) {\n ukeireList.push(tile);\n }\n }\n\n return ukeireList;\n}\n","import type { CompletedMentsu } from \"../../../../types\";\nimport type { MentsuHouraStructure } from \"../../types\";\nimport { validateTehai14, countHaiKind } from \"../../../../core/tehai\";\nimport { isTuple4 } from \"../../../../utils/assertions\";\nimport type { Tehai14, HaiKindId, Shuntsu, Koutsu } from \"../../../../types\";\n\n/**\n * 手牌を標準形(4面子1雀頭)に構造化する。\n * 七対子や国士無双は対象外。\n *\n * 【役判定について】\n * この関数は純粋に「4面子1雀頭」の形になっているかのみを検証します。\n * 役が成立しているかどうか(和了できるかどうか)は判定しません。\n * そのため、役なし(Yakunashi)の手牌であっても構造的に整合していれば結果を返します。\n *\n * 【戻り値が配列である理由について】\n * 麻雀の手牌は、同じ牌構成であっても複数の解釈(多義性)が成立する場合があります。\n * 例: `111222333m`\n * - 三暗刻 (111 + 222 + 333)\n * - 三連刻/一盃口 (123 + 123 + 123)\n *\n * このように成立する役が変わる可能性があるため、可能な全ての構造化パターンをリストとして返します。\n * 利用側は、これらのパターンのうち最も高得点となるものを選択する必要があります。\n *\n * @param tehai 和了形の手牌\n * @returns 可能な構造化パターンのリスト。構造化できない場合は空配列。\n */\nexport function getHouraStructuresForMentsuTe(\n tehai: Tehai14,\n): MentsuHouraStructure[] {\n validateTehai14(tehai);\n\n // HaiKindDistributionはreadonlyなので、可変配列に複製する\n const counts = [...countHaiKind(tehai.closed)];\n const results: MentsuHouraStructure[] = [];\n\n // 1. 雀頭候補を探す\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n if ((counts[kind] ?? 0) >= 2) {\n // 雀頭抜き出し\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! -= 2;\n\n // 残りの牌で面子分解\n const requiredMentsuCount = 4 - tehai.exposed.length;\n const subResults = decomposeClosedMentsu(counts, requiredMentsuCount);\n\n // subResultsには閉じた部分で見つかった面子のリストが含まれる\n for (const closedMentsu of subResults) {\n // 副露面子と結合して完全な構成を作成する\n const fullMentsuList = [...closedMentsu, ...tehai.exposed];\n\n // 4面子であることを確認(ロジック上は保証されているはずだが、念のため)\n if (isTuple4(fullMentsuList)) {\n results.push({\n type: \"Mentsu\",\n fourMentsu: fullMentsuList,\n jantou: { type: \"Toitsu\", hais: [kind, kind] },\n });\n }\n }\n\n // バックトラック\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! += 2;\n }\n }\n\n return results;\n}\n\n/**\n * 閉じた手牌の残りを面子に分解する再帰関数\n */\nfunction decomposeClosedMentsu(\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n counts: number[],\n requiredCount: number,\n): CompletedMentsu[][] {\n if (requiredCount === 0) {\n // 全ての牌が使用されたか確認\n const remaining = counts.reduce((acc, c) => acc + c, 0);\n return remaining === 0 ? [[]] : [];\n }\n\n // 面子の重複順列を防ぎ決定論的な順序を強制するため、カウントが0より大きい最初の牌を見つける\n let firstIndex = -1;\n for (let i = 0; i < 34; i++) {\n if ((counts[i] ?? 0) > 0) {\n firstIndex = i;\n break;\n }\n }\n\n if (firstIndex === -1) {\n // Should not happen if requiredCount > 0, unless invalid hand\n return [];\n }\n\n const results: CompletedMentsu[][] = [];\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = firstIndex as HaiKindId;\n\n // 刻子を試す\n if ((counts[kind] ?? 0) >= 3) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! -= 3;\n const tails = decomposeClosedMentsu(counts, requiredCount - 1);\n const koutsu: Koutsu = { type: \"Koutsu\", hais: [kind, kind, kind] };\n for (const tail of tails) {\n results.push([koutsu, ...tail]);\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[kind]! += 3; // バックトラック\n }\n\n // 順子を試す\n // 数牌(0-26)かつ7を超えない(n, n+1, n+2を作れる)場合のみ有効\n if (kind < 27 && kind % 9 <= 6) {\n const k1 = kind;\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const k2 = (kind + 1) as HaiKindId;\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const k3 = (kind + 2) as HaiKindId;\n\n if ((counts[k2] ?? 0) > 0 && (counts[k3] ?? 0) > 0) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k1]! -= 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k2]! -= 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k3]! -= 1;\n\n const tails = decomposeClosedMentsu(counts, requiredCount - 1);\n const shuntsu: Shuntsu = { type: \"Shuntsu\", hais: [k1, k2, k3] };\n for (const tail of tails) {\n results.push([shuntsu, ...tail]);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k1]! += 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k2]! += 1;\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n counts[k3]! += 1; // バックトラック\n }\n }\n\n return results;\n}\n","import type { Tehai14, HaiKindId, Toitsu } from \"../../../../types\";\nimport type { ChiitoitsuHouraStructure } from \"../../types\";\nimport { countHaiKind } from \"../../../../core/tehai\";\n\n/**\n * 手牌を七対子(7つの対子)として構造化する。\n */\nexport function getHouraStructuresForChiitoitsu(\n tehai: Tehai14,\n): ChiitoitsuHouraStructure[] {\n // 七対子は門前のみ(定義によっては鳴きも許容する場合があるが、一般的には門前)\n if (tehai.exposed.length > 0) return [];\n\n const counts = countHaiKind(tehai.closed);\n const pairs: Toitsu[] = [];\n\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n const count = counts[kind];\n\n if (count === 2) {\n pairs.push({ type: \"Toitsu\", hais: [kind, kind] });\n } else if (count === 4) {\n // 4枚使いの七対子を認めるか(ローカルルール次第だが、通常は認めない)\n // ここでは標準的なルールに従い、4枚あっても2対子とはみなさない実装とする\n // ※4枚使い七対子を実装する場合は pairs.push(...) を2回行う\n return [];\n } else if (count > 0) {\n // 2枚でない牌がある場合は七対子不成立\n return [];\n }\n }\n\n if (pairs.length !== 7) return [];\n\n return [\n {\n type: \"Chiitoitsu\",\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n pairs: pairs as unknown as [\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n Toitsu,\n ],\n },\n ];\n}\n","import type { Tehai14, HaiKindId } from \"../../../../types\";\nimport type { KokushiHouraStructure } from \"../../types\";\nimport { countHaiKind } from \"../../../../core/tehai\";\nimport { isYaochu } from \"../../../../core/hai\";\n\n/**\n * 手牌を国士無双(13種の么九牌+雀頭)として構造化する。\n */\nexport function getHouraStructuresForKokushi(\n tehai: Tehai14,\n): KokushiHouraStructure[] {\n // 国士無双は門前のみ\n if (tehai.exposed.length > 0) return [];\n\n const counts = countHaiKind(tehai.closed);\n const yaochuList: HaiKindId[] = [];\n let jantou: HaiKindId | undefined;\n\n for (let i = 0; i < 34; i++) {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const kind = i as HaiKindId;\n\n const count = counts[kind];\n\n if (count > 0) {\n if (!isYaochu(kind)) return []; // 么九牌以外が含まれていれば不成立\n\n if (count === 1) {\n yaochuList.push(kind);\n } else if (count === 2) {\n if (jantou !== undefined) return []; // 雀頭が既に存在すれば不成立(複数棋の雀頭候補)\n jantou = kind;\n yaochuList.push(kind);\n } else {\n return []; // 3枚以上あれば不成立\n }\n }\n }\n\n if (yaochuList.length !== 13 || jantou === undefined) return [];\n\n return [\n {\n type: \"Kokushi\",\n yaochu: yaochuList,\n jantou,\n },\n ];\n}\n","import type { Tehai14 } from \"../../../../types\";\nimport type { HouraStructure } from \"../../types\";\nimport { getHouraStructuresForMentsuTe } from \"./mentsu-te\";\nimport { getHouraStructuresForChiitoitsu } from \"./chiitoitsu\";\nimport { getHouraStructuresForKokushi } from \"./kokushi\";\n\nexport * from \"./mentsu-te\";\nexport * from \"./chiitoitsu\";\nexport * from \"./kokushi\";\n\n/**\n * 手牌をすべての可能な和了形に構造化する。\n * 面子手、七対子、国士無双の全ての可能性を探索する。\n */\nexport function getHouraStructures(tehai: Tehai14): HouraStructure[] {\n return [\n ...getHouraStructuresForMentsuTe(tehai),\n ...getHouraStructuresForChiitoitsu(tehai),\n ...getHouraStructuresForKokushi(tehai),\n ];\n}\n","import {\n HaiKind,\n type HaiKindId,\n type Kazehai,\n type Tehai14,\n} from \"../../types\";\n\n/**\n * 手牌が門前(メンゼン)かどうかを判定する。\n *\n * 門前の定義:\n * - 明刻、明順、明槓などの「晒し」が含まれていないこと。\n * - 暗槓は門前として扱う。\n *\n * @param tehai 判定対象の手牌\n * @returns 門前であれば true、そうでなければ false\n */\nexport function isMenzen(tehai: Tehai14): boolean {\n // exposed(副露ブロック)が空なら門前\n if (tehai.exposed.length === 0) {\n return true;\n }\n\n // 副露ブロックがある場合、全てが「暗槓」であれば門前とみなす\n // 暗槓の定義: typeが\"Kantsu\"かつfuro情報を持たない(現状のデータ構造における定義)\n return tehai.exposed.every((m) => {\n return m.type === \"Kantsu\" && !m.furo;\n });\n}\n/**\n * 指定された牌が風牌かどうかを判定する。\n *\n * @param id 判定対象の牌種ID\n * @returns 風牌(東・南・西・北)であれば true\n */\nexport function isKazehai(id: HaiKindId): id is Kazehai {\n return (\n id === HaiKind.Ton ||\n id === HaiKind.Nan ||\n id === HaiKind.Sha ||\n id === HaiKind.Pei\n );\n}\n","import type { YakuHanConfig, YakuName, HouraStructure } from \"./types\";\nimport { YakuDefinition, HouraContext } from \"./types\";\n\n/**\n * 役定義を作成するファクトリ関数\n *\n * @param yaku 役の設定情報(名前、翻数など)\n * @param check 役の成立条件を判定する関数 (真偽値を返す)\n * @returns YakuDefinition (isSatisfied, getHansu を持つ)\n */\nexport function createYakuDefinition(\n yaku: Readonly<{ name: YakuName; han: YakuHanConfig }>,\n check: (hand: HouraStructure, context: HouraContext) => boolean,\n): YakuDefinition {\n return {\n yaku,\n isSatisfied: check,\n getHansu: (hand, context) => {\n if (!check(hand, context)) {\n return 0;\n }\n return context.isMenzen ? yaku.han.closed : yaku.han.open;\n },\n };\n}\n","import { isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst TANYAO_YAKU: Yaku = {\n name: \"Tanyao\",\n han: {\n open: 1,\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkTanyao = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n // 雀頭のチェック\n if (isYaochu(hand.jantou.hais[0])) return false;\n\n // 面子のチェック\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.hais.some(isYaochu)) return false;\n }\n\n return true;\n};\n\nexport const tanyaoDefinition: YakuDefinition = createYakuDefinition(\n TANYAO_YAKU,\n checkTanyao,\n);\n","import { HaiKind, type HaiKindId } from \"../../../../types\";\n\nimport type {\n HouraStructure,\n Yaku,\n HouraContext,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport type { Shuntsu } from \"../../../../types\";\nimport { classifyMachi } from \"../../../../core/machi\";\nimport { MahjongArgumentError } from \"../../../../errors\";\nimport { createYakuDefinition } from \"../../factory\";\n\nconst PINFU_YAKU: Yaku = {\n name: \"Pinfu\",\n han: {\n open: 0,\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkPinfu: (hand: HouraStructure, context: HouraContext) => boolean = (\n hand,\n context,\n) => {\n // 1. 門前であること\n if (!context.isMenzen) return false;\n if (hand.type !== \"Mentsu\") return false;\n\n // 2. 雀頭が役牌でないこと\n // 三元牌、場風、自風が含まれていないことを確認\n const jantouKind = hand.jantou.hais[0];\n\n if (context.bakaze === undefined || context.jikaze === undefined) {\n throw new MahjongArgumentError(\n \"Pinfu check requires bakaze and jikaze in context\",\n );\n }\n\n const yakuhaiList: HaiKindId[] = [\n HaiKind.Haku,\n HaiKind.Hatsu,\n HaiKind.Chun,\n context.bakaze,\n context.jikaze,\n ];\n\n if (yakuhaiList.includes(jantouKind)) return false;\n\n // 3. 全て順子であること\n if (!hand.fourMentsu.every((m): m is Shuntsu => m.type === \"Shuntsu\"))\n return false;\n\n // 4. 両面待ちであること\n const waitType = classifyMachi(hand, context.agariHai);\n\n return waitType === \"Ryanmen\";\n};\n\nexport const pinfuDefinition: YakuDefinition = createYakuDefinition(\n PINFU_YAKU,\n checkPinfu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst IIPEIKO_YAKU: Yaku = {\n name: \"Iipeikou\",\n han: {\n open: 0, // 門前限定\n closed: 1,\n } satisfies YakuHanConfig,\n};\n\nconst checkIipeikou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n // 順子が2つ未満なら一盃口はあり得ない\n if (shuntsuList.length < 2) {\n return false;\n }\n\n // 同一順子のペア数をカウントする\n // 二盃口(ペア数 >= 2)の場合は上位互換のため一盃口不成立\n const shuntsuCounts = new Map<number, number>();\n for (const shuntsu of shuntsuList) {\n const key = shuntsu.hais[0];\n const currentCount = shuntsuCounts.get(key) ?? 0;\n shuntsuCounts.set(key, currentCount + 1);\n }\n\n let pairCount = 0;\n for (const count of shuntsuCounts.values()) {\n pairCount += Math.floor(count / 2);\n }\n\n // 一盃口: ちょうど1ペア(二盃口の除外)\n return pairCount === 1;\n};\n\nexport const iipeikouDefinition: YakuDefinition = createYakuDefinition(\n IIPEIKO_YAKU,\n checkIipeikou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst RYANPEIKOU_YAKU: Yaku = {\n name: \"Ryanpeikou\",\n han: {\n open: 0, // 門前限定\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkRyanpeikou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n // 順子が4つなければ二盃口はあり得ない\n if (shuntsuList.length < 4) {\n return false;\n }\n\n // 各順子の出現数をカウントする\n // Shuntsuは [T, T, T] で、先頭の牌IDが同じなら同じ順子とみなす\n const shuntsuCounts = new Map<number, number>();\n\n for (const shuntsu of shuntsuList) {\n const key = shuntsu.hais[0];\n const currentCount = shuntsuCounts.get(key) ?? 0;\n shuntsuCounts.set(key, currentCount + 1);\n }\n\n let pairCount = 0;\n for (const count of shuntsuCounts.values()) {\n // 同じ順子が2つで1ペア。4つなら2ペア。\n pairCount += Math.floor(count / 2);\n }\n\n return pairCount >= 2;\n};\n\nexport const ryanpeikouDefinition: YakuDefinition = createYakuDefinition(\n RYANPEIKOU_YAKU,\n checkRyanpeikou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst SANANKOU_YAKU: Yaku = {\n name: \"Sanankou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanankou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n let ankouCount = 0;\n\n for (const triplet of triplets) {\n // 副露している刻子は暗刻ではない\n if (triplet.furo) continue;\n\n const isAgariHaiInTriplet = triplet.hais.includes(context.agariHai);\n\n // ロン和了の場合、和了牌を含む刻子は明刻扱いとなる(シャボ待ちの場合)。\n // ただし、単騎待ちの場合は暗刻扱いとなる。\n // 単騎待ちかどうかの判定: 雀頭の牌が和了牌と同じかどうか\n const isTanki = hand.jantou.hais[0] === context.agariHai;\n\n if (context.isTsumo) {\n // ツモなら、副露していなければ全て暗刻\n ankouCount++;\n } else {\n // ロン和了の場合\n if (isAgariHaiInTriplet) {\n // 和了牌を含む刻子の場合\n if (isTanki) {\n // 単騎待ちなら暗刻\n ankouCount++;\n } else {\n // シャボ(等の)待ちでロンした場合は明刻扱いなのでカウントしない\n }\n } else {\n // 和了牌を含まない刻子は暗刻\n ankouCount++;\n }\n }\n }\n\n return ankouCount >= 3;\n};\n\nexport const sanankouDefinition: YakuDefinition = createYakuDefinition(\n SANANKOU_YAKU,\n checkSanankou,\n);\n","import type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst SUUANKOU_YAKU: Yaku = {\n name: \"Suuankou\",\n han: {\n open: 0, // 門前限定(構造上必然的にそうなるが、定義としても門前)\n closed: 13, // 通常役満(13) または ダブル役満(26)\n } satisfies YakuHanConfig,\n};\n\nconst checkSuuankou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n let ankouCount = 0;\n\n for (const triplet of triplets) {\n // 副露している刻子は暗刻ではない\n if (triplet.furo) continue;\n\n const isAgariHaiInTriplet = triplet.hais.includes(context.agariHai);\n\n const isTanki = hand.jantou.hais[0] === context.agariHai;\n\n if (context.isTsumo) {\n // ツモなら、副露していなければ全て暗刻\n ankouCount++;\n } else {\n // ロン和了の場合\n if (isAgariHaiInTriplet) {\n // 和了牌を含む刻子(シャボ待ちロン)は明刻\n // 単騎待ちロンなら暗刻\n if (isTanki) {\n ankouCount++;\n }\n } else {\n // 和了牌を含まない刻子は暗刻\n ankouCount++;\n }\n }\n }\n\n return ankouCount === 4;\n};\n\n// 四暗刻はダブル役満(単騎待ち)判定が必要なため、\n// createYakuDefinitionのデフォルトのgetHansuロジックをオーバーライドするか、\n// このcheck関数内で判定してフラグを渡すなどの工夫が必要。\n// しかし createYakuDefinition は boolean を返す check 関数しか受け取らない。\n// ここでは factory 側を拡張するのではなく、この定義でカスタム実装を行うか、\n// あるいは factory を通さずに直接 YakuDefinition を作る。\n// factoryの改修はスコープ外のため、ここではオブジェクトリテラルで定義を作成する。\n\nexport const suuankouDefinition: YakuDefinition = {\n yaku: SUUANKOU_YAKU,\n isSatisfied: (hand, context) => checkSuuankou(hand, context),\n getHansu: (hand, context) => {\n if (!checkSuuankou(hand, context)) return 0;\n\n // 四暗刻単騎の判定\n const isTanki =\n hand.type === \"Mentsu\" && hand.jantou.hais[0] === context.agariHai;\n\n // 単騎待ちならダブル役満(26)\n if (isTanki) {\n return 26;\n }\n\n return 13;\n },\n};\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANKANTSU_YAKU: Yaku = {\n name: \"Sankantsu\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSankantsu = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 槓子を抽出\n const kantsuList = hand.fourMentsu.filter((m) => m.type === \"Kantsu\");\n\n // 2. 槓子が3つ以上あれば成立\n return kantsuList.length >= 3;\n};\n\nexport const sankantsuDefinition: YakuDefinition = createYakuDefinition(\n SANKANTSU_YAKU,\n checkSankantsu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SUUKANTSU_YAKU: Yaku = {\n name: \"Suukantsu\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkSuukantsu = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 槓子を抽出\n const kantsuList = hand.fourMentsu.filter((m) => m.type === \"Kantsu\");\n\n // 2. 槓子が4つあれば成立\n return kantsuList.length === 4;\n};\n\nexport const suukantsuDefinition: YakuDefinition = createYakuDefinition(\n SUUKANTSU_YAKU,\n checkSuukantsu,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst TOITOI_YAKU: Yaku = {\n name: \"Toitoi\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkToitoi = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 全ての面子が刻子(Koutsu)または槓子(Kantsu)であることを確認\n return hand.fourMentsu.every(\n (mentsu) => mentsu.type === \"Koutsu\" || mentsu.type === \"Kantsu\",\n );\n};\n\nexport const toitoiDefinition: YakuDefinition = createYakuDefinition(\n TOITOI_YAKU,\n checkToitoi,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHIITOITSU_YAKU: Yaku = {\n name: \"Chiitoitsu\",\n han: {\n open: 0, // 門前限定\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkChiitoitsu = (hand: HouraStructure): boolean => {\n return hand.type === \"Chiitoitsu\";\n};\n\nexport const chiitoitsuDefinition: YakuDefinition = createYakuDefinition(\n CHIITOITSU_YAKU,\n checkChiitoitsu,\n);\n","import { isYaochu, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONCHAN_YAKU: Yaku = {\n name: \"Honchan\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonchan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 1. 全ての面子・雀頭に么九牌(1・9・字牌)が含まれること\n const allHasYaochu = allBlocks.every((block) =>\n block.hais.some((k) => isYaochu(k)),\n );\n if (!allHasYaochu) return false;\n\n // 2. 少なくとも1つの順子が含まれること(混老頭の除外)\n const hasShuntsu = hand.fourMentsu.some((m) => m.type === \"Shuntsu\");\n if (!hasShuntsu) return false;\n\n // 3. 少なくとも1つの字牌が含まれること(純全帯幺九の除外)\n const hasJihai = allBlocks.some((block) =>\n block.hais.some((k) => kindIdToHaiType(k) === HaiType.Jihai),\n );\n if (!hasJihai) return false;\n\n return true;\n};\n\nexport const honchanDefinition: YakuDefinition = createYakuDefinition(\n HONCHAN_YAKU,\n checkHonchan,\n);\n","import { isSuupai, isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst JUNCHAN_YAKU: Yaku = {\n name: \"Junchan\",\n han: {\n open: 2,\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkJunchan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 1. 全ての面子・雀頭に老頭牌(1・9)が含まれること\n // isYaochu(k) && isSuupai(k) で「字牌を除く么九牌」=「老頭牌」となる\n const allHasRoutou = allBlocks.every((block) =>\n block.hais.some((k) => isYaochu(k) && isSuupai(k)),\n );\n if (!allHasRoutou) return false;\n\n // 2. 少なくとも1つの順子が含まれること(清老頭の除外)\n const hasShuntsu = hand.fourMentsu.some((m) => m.type === \"Shuntsu\");\n if (!hasShuntsu) return false;\n\n return true;\n};\n\nexport const junchanDefinition: YakuDefinition = createYakuDefinition(\n JUNCHAN_YAKU,\n checkJunchan,\n);\n","import { isYaochu, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONROUTOU_YAKU: Yaku = {\n name: \"Honroutou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonroutou = (hand: HouraStructure): boolean => {\n let blocks;\n\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // 1. 全ての牌が么九牌(1・9・字牌)であること\n // これにより順子(123など)が含まれる可能性も排除される(2,3は么九牌ではないため)\n const allYaochu = blocks.every((block) =>\n block.hais.every((k) => isYaochu(k)),\n );\n if (!allYaochu) return false;\n\n // 2. 少なくとも1つの字牌が含まれること(清老頭の除外)\n const hasJihai = blocks.some((block) =>\n block.hais.some((k) => kindIdToHaiType(k) === HaiType.Jihai),\n );\n if (!hasJihai) return false;\n\n return true;\n};\n\nexport const honroutouDefinition: YakuDefinition = createYakuDefinition(\n HONROUTOU_YAKU,\n checkHonroutou,\n);\n","import { isSuupai, isYaochu } from \"../../../../core/hai\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHINROUTOU_YAKU: Yaku = {\n name: \"Chinroutou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkChinroutou = (hand: HouraStructure): boolean => {\n // 老頭牌は6種類(1m,9m,1p,9p,1s,9s)しかないため、七対子(7種)は成立しない\n if (hand.type !== \"Mentsu\") return false;\n\n const allBlocks = [hand.jantou, ...hand.fourMentsu];\n\n // 全てが老頭牌(字牌以外の么九牌)で構成されていること\n const allRoutou = allBlocks.every((block) =>\n block.hais.every((k) => isYaochu(k) && isSuupai(k)),\n );\n if (!allRoutou) return false;\n\n return true;\n};\n\nexport const chinroutouDefinition: YakuDefinition = createYakuDefinition(\n CHINROUTOU_YAKU,\n checkChinroutou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst SHOUSANGEN_YAKU: Yaku = {\n name: \"Shousangen\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkShousangen = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const sangenpai: HaiKindId[] = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];\n\n // 1. 三元牌の刻子・槓子をカウント\n let sangenKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (sangenpai.includes(triplet.hais[0])) {\n sangenKoutsuCount++;\n }\n }\n\n // 2. 三元牌の雀頭があるかチェック\n const isSangenJantou = sangenpai.includes(hand.jantou.hais[0]);\n\n // 小三元の条件: 三元牌の刻子が2つ かつ 三元牌の雀頭が1つ\n return sangenKoutsuCount === 2 && isSangenJantou;\n};\n\nexport const shousangenDefinition: YakuDefinition = createYakuDefinition(\n SHOUSANGEN_YAKU,\n checkShousangen,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst DAISANGEN_YAKU: Yaku = {\n name: \"Daisangen\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkDaisangen = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const sangenpai: HaiKindId[] = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];\n\n // 1. 三元牌の刻子・槓子をカウント\n let sangenKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (sangenpai.includes(triplet.hais[0])) {\n sangenKoutsuCount++;\n }\n }\n\n // 大三元の条件: 三元牌の刻子が3つ全てあること\n return sangenKoutsuCount === 3;\n};\n\nexport const daisangenDefinition: YakuDefinition = createYakuDefinition(\n DAISANGEN_YAKU,\n checkDaisangen,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind } from \"../../../../types\";\n\nconst TSUUIISOU_YAKU: Yaku = {\n name: \"Tsuuiisou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst isJihai = (id: number): boolean => {\n return id >= HaiKind.Ton && id <= HaiKind.Chun;\n};\n\nconst checkTsuuiisou = (hand: HouraStructure): boolean => {\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else if (hand.type === \"Chiitoitsu\") {\n // 七対子の場合\n for (const pair of hand.pairs) {\n allHais.push(...pair.hais);\n }\n } else {\n // 国士無双など(国士は字一色にはなり得ないが、構造上は考慮)\n return false;\n }\n\n // 全ての牌が字牌であれば成立\n return allHais.every(isJihai);\n};\n\nexport const tsuuiisouDefinition: YakuDefinition = createYakuDefinition(\n TSUUIISOU_YAKU,\n checkTsuuiisou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind } from \"../../../../types\";\n\nconst RYUUIISOU_YAKU: Yaku = {\n name: \"Ryuuiisou\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst GREEN_TILES = new Set<number>([\n HaiKind.SouZu2,\n HaiKind.SouZu3,\n HaiKind.SouZu4,\n HaiKind.SouZu6,\n HaiKind.SouZu8,\n HaiKind.Hatsu,\n]);\n\nconst isGreen = (id: number): boolean => {\n return GREEN_TILES.has(id);\n};\n\nconst checkRyuuiisou = (hand: HouraStructure): boolean => {\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else if (hand.type === \"Chiitoitsu\") {\n // 七対子の場合\n for (const pair of hand.pairs) {\n allHais.push(...pair.hais);\n }\n } else {\n // 国士無双など\n return false;\n }\n\n // 全ての牌が緑色牌であれば成立\n return allHais.every(isGreen);\n};\n\nexport const ryuuiisouDefinition: YakuDefinition = createYakuDefinition(\n RYUUIISOU_YAKU,\n checkRyuuiisou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst SHOUSUUSHII_YAKU: Yaku = {\n name: \"Shousuushii\",\n han: {\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkShousuushii = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const windTiles: HaiKindId[] = [\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n ];\n\n // 1. 風牌の刻子・槓子をカウント\n let windKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (windTiles.includes(triplet.hais[0])) {\n windKoutsuCount++;\n }\n }\n\n // 2. 風牌の雀頭があるかチェック\n const isWindJantou = windTiles.includes(hand.jantou.hais[0]);\n\n // 小四喜の条件: 風牌の刻子が3つ かつ 風牌の雀頭が1つ\n // (合計で4種類の風牌が揃うことになる。例: 東東東 南南南 西西西 北北)\n return windKoutsuCount === 3 && isWindJantou;\n};\n\nexport const shousuushiiDefinition: YakuDefinition = createYakuDefinition(\n SHOUSUUSHII_YAKU,\n checkShousuushii,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HaiKind, type HaiKindId } from \"../../../../types\";\n\nconst DAISUUSHII_YAKU: Yaku = {\n name: \"Daisuushii\",\n han: {\n // TODO: ダブル役満(26翻)とするかはルールによるため、一旦通常の役満として実装\n open: 13,\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkDaisuushii = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const windTiles: HaiKindId[] = [\n HaiKind.Ton,\n HaiKind.Nan,\n HaiKind.Sha,\n HaiKind.Pei,\n ];\n\n // 1. 風牌の刻子・槓子をカウント\n let windKoutsuCount = 0;\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n for (const triplet of triplets) {\n if (windTiles.includes(triplet.hais[0])) {\n windKoutsuCount++;\n }\n }\n\n // 大四喜の条件: 風牌の刻子が4つ全てあること\n return windKoutsuCount === 4;\n};\n\nexport const daisuushiiDefinition: YakuDefinition = createYakuDefinition(\n DAISUUSHII_YAKU,\n checkDaisuushii,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\nimport { HouraContext } from \"../../types\";\n\nconst CHUUREN_POUTOU_YAKU: Yaku = {\n name: \"ChuurenPoutou\",\n han: {\n open: 0, // 門前限定\n closed: 13,\n } satisfies YakuHanConfig,\n};\n\nconst checkChuurenPoutou = (\n hand: HouraStructure,\n context: HouraContext,\n): boolean => {\n // 1. 門前でなければならない\n if (!context.isMenzen) {\n return false;\n }\n\n // 構造は Mentsu 手のみ(基本的には)\n // 構造解析結果がどうあれ、元の手牌構成が九蓮宝燈の形かどうかを確認する\n const allHais: number[] = [];\n\n if (hand.type === \"Mentsu\") {\n // 面子手の場合\n for (const mentsu of hand.fourMentsu) {\n allHais.push(...mentsu.hais);\n }\n allHais.push(...hand.jantou.hais);\n } else {\n // 九蓮宝燈は通常、面子手の特殊形として扱われることが多いが、\n // 構造解析器が Mentsu として解釈できない場合も考慮すべきか?\n // 一旦 Mentsu 型として解釈されていることを前提とする\n // (九蓮宝燈は 111+234+567+8999+α のように分解可能なので Mentsu になるはず)\n return false;\n }\n\n // 2. 混一色チェック(全て同じ色、字牌なし)\n if (allHais.length === 0) return false;\n const firstHai = allHais[0];\n if (firstHai === undefined) return false;\n\n // 字牌が含まれていたらNG\n if (firstHai >= 27) return false;\n\n const suit = Math.floor(firstHai / 9); // 0, 1, 2\n\n for (const hai of allHais) {\n if (hai >= 27) return false; // 字牌混入\n if (Math.floor(hai / 9) !== suit) return false; // 色混在\n }\n\n // 3. 数牌のカウントチェック\n // 1が3枚以上, 9が3枚以上, 2-8が1枚以上\n const counts = Array(9).fill(0);\n for (const hai of allHais) {\n const num = hai % 9; // 0-8\n counts[num]++;\n }\n\n // 1 (index 0) >= 3\n if (counts[0] < 3) return false;\n // 9 (index 8) >= 3\n if (counts[8] < 3) return false;\n // 2-8 (index 1-7) >= 1\n for (let i = 1; i <= 7; i++) {\n if (counts[i] < 1) return false;\n }\n\n // 合計14枚で上記を満たしていれば、必ず九蓮宝燈の形になる\n // (3+3+7 = 13枚が必須パーツで、残り1枚は何でもよいため)\n\n return true;\n};\n\nexport const chuurenPoutouDefinition: YakuDefinition = createYakuDefinition(\n CHUUREN_POUTOU_YAKU,\n checkChuurenPoutou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst checkKokushi = (hand: HouraStructure): boolean => {\n return hand.type === \"Kokushi\";\n};\n\nconst KOKUSHI_HAN: YakuHanConfig = {\n closed: 13,\n open: 0,\n};\n\nexport const kokushiDefinition: YakuDefinition = createYakuDefinition(\n {\n name: \"KokushiMusou\",\n han: KOKUSHI_HAN,\n },\n checkKokushi,\n);\n","import { createYakuDefinition } from \"../../factory\";\n\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANSHOKU_DOUJUN_YAKU: Yaku = {\n name: \"SanshokuDoujun\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanshokuDoujun = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n if (shuntsuList.length < 3) {\n return false;\n }\n\n // 順子リストから3つの組み合わせを全てチェックし、三色同順の条件を満たすものを探す\n // 条件:\n // 1. 3つの順子がそれぞれ異なる色(萬子、筒子、索子)であること\n // 2. 3つの順子の構成数字が同じであること(例: 123m, 123p, 123s)\n\n // ヘルパーロジック:\n // HaiKindId の範囲: 0-8 (ManZu), 9-17 (PinZu), 18-26 (SouZu)\n // 9で割った商が色(0, 1, 2)、余りが数値(0-8)を表す\n\n for (let i = 0; i < shuntsuList.length; i++) {\n for (let j = i + 1; j < shuntsuList.length; j++) {\n for (let k = j + 1; k < shuntsuList.length; k++) {\n const s1 = shuntsuList[i];\n const s2 = shuntsuList[j];\n const s3 = shuntsuList[k];\n\n if (!s1 || !s2 || !s3) continue;\n\n const firstHai1 = s1.hais[0];\n const firstHai2 = s2.hais[0];\n const firstHai3 = s3.hais[0];\n\n const suit1 = Math.floor(firstHai1 / 9);\n const suit2 = Math.floor(firstHai2 / 9);\n const suit3 = Math.floor(firstHai3 / 9);\n\n // 異なる色(0, 1, 2)でなければならない\n const suits = new Set([suit1, suit2, suit3]);\n if (suits.size !== 3) continue;\n\n // 全て数牌(0, 1, 2)でなければならない\n // ※通常、Shuntsuに字牌は含まれないが、念のためチェック\n if (suit1 > 2 || suit2 > 2 || suit3 > 2) continue;\n\n // 数値(インデックス)が一致するかチェック\n const num1 = firstHai1 % 9;\n const num2 = firstHai2 % 9;\n const num3 = firstHai3 % 9;\n\n if (num1 === num2 && num2 === num3) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const sanshokuDoujunDefinition: YakuDefinition = createYakuDefinition(\n SANSHOKU_DOUJUN_YAKU,\n checkSanshokuDoujun,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Kantsu,\n Koutsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst SANSHOKU_DOUKOU_YAKU: Yaku = {\n name: \"SanshokuDoukou\",\n han: {\n open: 2,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkSanshokuDoukou = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n // 1. 刻子・槓子を抽出\n const triplets = hand.fourMentsu.filter(\n (m): m is Koutsu | Kantsu => m.type === \"Koutsu\" || m.type === \"Kantsu\",\n );\n\n if (triplets.length < 3) {\n return false;\n }\n\n // 2. 刻子の組み合わせ(3つ)をチェック\n for (let i = 0; i < triplets.length; i++) {\n for (let j = i + 1; j < triplets.length; j++) {\n for (let k = j + 1; k < triplets.length; k++) {\n const t1 = triplets[i];\n const t2 = triplets[j];\n const t3 = triplets[k];\n\n if (!t1 || !t2 || !t3) continue;\n\n const id1 = t1.hais[0];\n const id2 = t2.hais[0];\n const id3 = t3.hais[0];\n\n // 字牌が含まれていたら対象外 (字牌ID >= 27)\n if (id1 >= 27 || id2 >= 27 || id3 >= 27) continue;\n\n const suit1 = Math.floor(id1 / 9);\n const suit2 = Math.floor(id2 / 9);\n const suit3 = Math.floor(id3 / 9);\n\n // ※ 0:萬子, 1:筒子, 2:索子\n const suits = new Set([suit1, suit2, suit3]);\n // 異なる3色でなければならない\n if (suits.size !== 3) continue;\n\n const num1 = id1 % 9;\n const num2 = id2 % 9;\n const num3 = id3 % 9;\n\n // 同じ数字でなければならない\n if (num1 === num2 && num2 === num3) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const sanshokuDoukouDefinition: YakuDefinition = createYakuDefinition(\n SANSHOKU_DOUKOU_YAKU,\n checkSanshokuDoukou,\n);\n","import { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Shuntsu,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst IKKITSUUKAN_YAKU: Yaku = {\n name: \"Ikkitsuukan\",\n han: {\n open: 1,\n closed: 2,\n } satisfies YakuHanConfig,\n};\n\nconst checkIkkitsuukan = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") {\n return false;\n }\n\n const shuntsuList = hand.fourMentsu.filter(\n (mentsu): mentsu is Shuntsu => mentsu.type === \"Shuntsu\",\n );\n\n if (shuntsuList.length < 3) {\n return false;\n }\n\n // 順子リストから3つの組み合わせを全てチェックし、一気通貫の条件を満たすものを探す\n // 条件:\n // 1. 3つの順子が全て同じ色(萬子、筒子、索子のいずれか)であること\n // 2. 3つの順子がそれぞれ 1-2-3, 4-5-6, 7-8-9 であること\n // (インデックスの余りが 0, 3, 6 となること)\n\n for (let i = 0; i < shuntsuList.length; i++) {\n for (let j = i + 1; j < shuntsuList.length; j++) {\n for (let k = j + 1; k < shuntsuList.length; k++) {\n const s1 = shuntsuList[i];\n const s2 = shuntsuList[j];\n const s3 = shuntsuList[k];\n\n if (!s1 || !s2 || !s3) continue;\n\n const firstHai1 = s1.hais[0];\n const firstHai2 = s2.hais[0];\n const firstHai3 = s3.hais[0];\n\n const suit1 = Math.floor(firstHai1 / 9);\n const suit2 = Math.floor(firstHai2 / 9);\n const suit3 = Math.floor(firstHai3 / 9);\n\n // 全て同じ色でなければならない\n if (suit1 !== suit2 || suit2 !== suit3) continue;\n\n // 字牌が含まれていないかチェック(念のため)\n if (suit1 > 2) continue;\n\n // 数値(インデックス)を取得\n const num1 = firstHai1 % 9;\n const num2 = firstHai2 % 9;\n const num3 = firstHai3 % 9;\n\n // 0 (1-2-3), 3 (4-5-6), 6 (7-8-9) が揃っていれば成立\n const nums = new Set([num1, num2, num3]);\n if (nums.has(0) && nums.has(3) && nums.has(6)) {\n return true;\n }\n }\n }\n }\n\n return false;\n};\n\nexport const ikkitsuukanDefinition: YakuDefinition = createYakuDefinition(\n IKKITSUUKAN_YAKU,\n checkIkkitsuukan,\n);\n","import { isSuupai, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst HONITSU_YAKU: Yaku = {\n name: \"Honitsu\",\n han: {\n open: 2,\n closed: 3,\n } satisfies YakuHanConfig,\n};\n\nconst checkHonitsu = (hand: HouraStructure): boolean => {\n let blocks;\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // ブロック内の全ての牌をフラットな配列にする\n const allHais = blocks.flatMap((b) => b.hais);\n\n // 1. 字牌が少なくとも1つ含まれること(清一色の除外)\n const hasJihai = allHais.some((k) => kindIdToHaiType(k) === HaiType.Jihai);\n if (!hasJihai) return false;\n\n // 2. 数牌が全て同じ種類であること\n const suupais = allHais.filter((k) => isSuupai(k));\n\n // 数牌が含まれていない場合は字一色(または不成立)なので、ホンイツではない\n if (suupais.length === 0) return false;\n\n const firstSuupai = suupais[0];\n if (firstSuupai === undefined) return false;\n\n const firstSuupaiType = kindIdToHaiType(firstSuupai);\n const isAllSameType = suupais.every(\n (k) => kindIdToHaiType(k) === firstSuupaiType,\n );\n\n return isAllSameType;\n};\n\nexport const honitsuDefinition: YakuDefinition = createYakuDefinition(\n HONITSU_YAKU,\n checkHonitsu,\n);\n","import { isSuupai, kindIdToHaiType } from \"../../../../core/hai\";\nimport { HaiType } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n Yaku,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nconst CHINITSU_YAKU: Yaku = {\n name: \"Chinitsu\",\n han: {\n open: 5,\n closed: 6,\n } satisfies YakuHanConfig,\n};\n\nconst checkChinitsu = (hand: HouraStructure): boolean => {\n let blocks;\n if (hand.type === \"Mentsu\") {\n blocks = [hand.jantou, ...hand.fourMentsu];\n } else if (hand.type === \"Chiitoitsu\") {\n blocks = hand.pairs;\n } else {\n return false;\n }\n\n // ブロック内の全ての牌をフラットな配列にする\n const allHais = blocks.flatMap((b) => b.hais);\n\n // 1. 字牌が含まれていないこと\n const hasJihai = allHais.some((k) => kindIdToHaiType(k) === HaiType.Jihai);\n if (hasJihai) return false;\n\n // 2. 数牌が全て同じ種類であること\n const suupais = allHais.filter((k) => isSuupai(k));\n\n // 数牌が含まれていない(字一色想定だが上記でJihaiチェック済みなので事実上ありえない)場合は不成立\n if (suupais.length === 0) return false;\n\n const firstSuupai = suupais[0];\n if (firstSuupai === undefined) return false;\n\n const firstSuupaiType = kindIdToHaiType(firstSuupai);\n const isAllSameType = suupais.every(\n (k) => kindIdToHaiType(k) === firstSuupaiType,\n );\n\n return isAllSameType;\n};\n\nexport const chinitsuDefinition: YakuDefinition = createYakuDefinition(\n CHINITSU_YAKU,\n checkChinitsu,\n);\n","import { HaiKind, HaiKindId } from \"../../../../types\";\nimport { createYakuDefinition } from \"../../factory\";\nimport type {\n HouraStructure,\n YakuDefinition,\n YakuHanConfig,\n} from \"../../types\";\n\nfunction createYakuhaiDefinition(\n name: \"Haku\" | \"Hatsu\" | \"Chun\",\n tile: HaiKindId,\n): YakuDefinition {\n const HAN_CONFIG: YakuHanConfig = { closed: 1, open: 1 };\n\n const check = (hand: HouraStructure): boolean => {\n if (hand.type !== \"Mentsu\") return false;\n\n for (const mentsu of hand.fourMentsu) {\n if (mentsu.type === \"Koutsu\" || mentsu.type === \"Kantsu\") {\n if (mentsu.hais[0] === tile) {\n return true;\n }\n }\n }\n return false;\n };\n\n return createYakuDefinition({ name, han: HAN_CONFIG }, check);\n}\n\nexport const hakuDefinition = createYakuhaiDefinition(\"Haku\", HaiKind.Haku);\nexport const hatsuDefinition = createYakuhaiDefinition(\"Hatsu\", HaiKind.Hatsu);\nexport const chunDefinition = createYakuhaiDefinition(\"Chun\", HaiKind.Chun);\n","import type { YakuDefinition, HouraStructure, HouraContext } from \"../../types\";\nimport { createYakuDefinition } from \"../../factory\";\n\nconst definition = {\n name: \"MenzenTsumo\",\n han: { open: 0, closed: 1 },\n} as const;\n\nexport const menzenTsumoDefinition: YakuDefinition = createYakuDefinition(\n definition,\n (hand: HouraStructure, context: HouraContext): boolean => {\n return context.isMenzen && !!context.isTsumo;\n },\n);\n","import { tanyaoDefinition } from \"./tanyao\";\nimport { pinfuDefinition } from \"./pinfu\";\nimport { iipeikouDefinition } from \"./iipeikou\";\nimport { ryanpeikouDefinition } from \"./ryanpeikou\";\nimport { sanankouDefinition } from \"./sanankou\";\nimport { suuankouDefinition } from \"./suuankou\";\nimport { sankantsuDefinition } from \"./sankantsu\";\nimport { suukantsuDefinition } from \"./suukantsu\";\nimport { toitoiDefinition } from \"./toitoi\";\nimport { chiitoitsuDefinition } from \"./chiitoitsu\";\nimport { honchanDefinition } from \"./honchan\";\nimport { junchanDefinition } from \"./junchan\";\nimport { honroutouDefinition } from \"./honroutou\";\nimport { chinroutouDefinition } from \"./chinroutou\";\nimport { shousangenDefinition } from \"./shousangen\";\nimport { daisangenDefinition } from \"./daisangen\";\nimport { tsuuiisouDefinition } from \"./tsuuiisou\";\nimport { ryuuiisouDefinition } from \"./ryuuiisou\";\nimport { shousuushiiDefinition } from \"./shousuushii\";\nimport { daisuushiiDefinition } from \"./daisuushii\";\nimport { chuurenPoutouDefinition } from \"./chuuren-poutou\";\nimport { kokushiDefinition } from \"./kokushi\";\nimport { sanshokuDoujunDefinition } from \"./sanshoku-doujun\";\nimport { sanshokuDoukouDefinition } from \"./sanshoku-doukou\";\nimport { ikkitsuukanDefinition } from \"./ikkitsuukan\";\nimport { honitsuDefinition } from \"./honitsu\";\nimport { chinitsuDefinition } from \"./chinitsu\";\nimport { hakuDefinition, hatsuDefinition, chunDefinition } from \"./yakuhai\";\nimport { menzenTsumoDefinition } from \"./menzen-tsumo\";\nimport type { YakuDefinition } from \"../../types\";\n\nexport * from \"./tanyao\";\nexport * from \"./pinfu\";\nexport * from \"./iipeikou\";\nexport * from \"./ryanpeikou\";\nexport * from \"./sanankou\";\nexport * from \"./suuankou\";\nexport * from \"./sankantsu\";\nexport * from \"./suukantsu\";\nexport * from \"./toitoi\";\nexport * from \"./chiitoitsu\";\nexport * from \"./honchan\";\nexport * from \"./junchan\";\nexport * from \"./honroutou\";\nexport * from \"./chinroutou\";\nexport * from \"./shousangen\";\nexport * from \"./daisangen\";\nexport * from \"./tsuuiisou\";\nexport * from \"./ryuuiisou\";\nexport * from \"./shousuushii\";\nexport * from \"./daisuushii\";\nexport * from \"./chuuren-poutou\";\nexport * from \"./kokushi\";\nexport * from \"./sanshoku-doujun\";\nexport * from \"./sanshoku-doukou\";\nexport * from \"./ikkitsuukan\";\nexport * from \"./honitsu\";\nexport * from \"./chinitsu\";\nexport * from \"./yakuhai\";\n\nexport const ALL_YAKU_DEFINITIONS: YakuDefinition[] = [\n tanyaoDefinition,\n pinfuDefinition,\n iipeikouDefinition,\n ryanpeikouDefinition,\n sanankouDefinition,\n suuankouDefinition,\n sankantsuDefinition,\n suukantsuDefinition,\n toitoiDefinition,\n chiitoitsuDefinition,\n honchanDefinition,\n junchanDefinition,\n honroutouDefinition,\n chinroutouDefinition,\n shousangenDefinition,\n daisangenDefinition,\n tsuuiisouDefinition,\n ryuuiisouDefinition,\n shousuushiiDefinition,\n daisuushiiDefinition,\n chuurenPoutouDefinition,\n kokushiDefinition,\n sanshokuDoujunDefinition,\n sanshokuDoukouDefinition,\n ikkitsuukanDefinition,\n honitsuDefinition,\n chinitsuDefinition,\n hakuDefinition,\n hatsuDefinition,\n chunDefinition,\n menzenTsumoDefinition,\n];\n","import type { Tehai14, HaiKindId } from \"../../types\";\nimport type { YakuResult, YakuName, Hansu, HouraStructure } from \"./types\";\n\nimport { getHouraStructures } from \"./lib/structures\";\nimport { isMenzen, isKazehai } from \"./utils\";\nimport { ALL_YAKU_DEFINITIONS } from \"./lib/definitions\";\nimport type { HouraContext } from \"./types\";\n\nexport type {\n HouraStructure,\n YakuResult,\n YakuName,\n Hansu,\n TehaiYaku,\n YakuHanConfig,\n Yakuhai,\n} from \"./types\";\nexport * from \"./lib\";\n\n/**\n * 役満の翻数閾値\n *\n * 麻雀において役満は13翻以上と定義される。\n * 数え役満(通常役の合計が13翻以上)とは異なり、\n * ここでは個々の役が単体で持つ翻数が13以上であるかを判定する基準として使用する。\n */\nconst YAKUMAN_HAN_THRESHOLD = 13;\n\n/**\n * 単一の和了構造に対して役を判定する\n *\n * 役満が成立している場合、通常役は除外して役満のみを返す。\n *\n * @param hand 和了構造(面子手、七対子、国士無双のいずれか)\n * @param context 和了コンテキスト\n * @returns 成立した役と翻数のリスト\n */\nexport function detectYakuForStructure(\n hand: HouraStructure,\n context: HouraContext,\n): YakuResult {\n const result: [YakuName, Hansu][] = [];\n\n for (const definition of ALL_YAKU_DEFINITIONS) {\n if (definition.isSatisfied(hand, context)) {\n const hansu = definition.getHansu(hand, context);\n if (hansu === 0) continue;\n result.push([definition.yaku.name, hansu]);\n }\n }\n\n // 一般ルール: 役満(13翻以上)が成立した場合、役満未満の通常役は複合しない。\n // 複数の役満が同時に成立した場合(例: 字一色 + 大四喜)、全ての役満を返す。\n //\n // NOTE: 複数役満の同時成立をダブル役満・トリプル役満として合算する特殊ルールには\n // 現在未対応。現在の実装では個々の役満をそのまま返しており、合算してダブル役満等\n // として扱う処理は行っていない。\n const hasYakuman = result.some(([, han]) => han >= YAKUMAN_HAN_THRESHOLD);\n if (hasYakuman) {\n return result.filter(([, han]) => han >= YAKUMAN_HAN_THRESHOLD);\n }\n\n return result;\n}\n\n/**\n * YakuResult から総翻数を計算する\n */\nfunction getTotalHan(\n yakuResult: readonly (readonly [YakuName, Hansu])[],\n): number {\n return yakuResult.reduce((sum, [, han]) => sum + han, 0);\n}\n\n/**\n * 手牌の構造役を検出する\n *\n * @param tehai 判定対象の手牌\n * @param agariHai 和了牌\n * @returns 成立した役と翻数のリスト(最も高得点となる解釈の結果)\n */\nexport function detectYaku(\n tehai: Tehai14,\n agariHai: HaiKindId,\n bakaze?: HaiKindId,\n jikaze?: HaiKindId,\n doraMarkers?: readonly HaiKindId[],\n uraDoraMarkers?: readonly HaiKindId[],\n isTsumo?: boolean,\n): YakuResult {\n const context: HouraContext = {\n isMenzen: isMenzen(tehai),\n agariHai,\n bakaze: bakaze !== undefined && isKazehai(bakaze) ? bakaze : undefined,\n jikaze: jikaze !== undefined && isKazehai(jikaze) ? jikaze : undefined,\n doraMarkers: doraMarkers ?? [],\n uraDoraMarkers: uraDoraMarkers ?? [],\n isTsumo,\n };\n\n const structuralInterpretations = getHouraStructures(tehai);\n\n let bestResult: YakuResult = [];\n let maxHan = -1;\n\n for (const hand of structuralInterpretations) {\n const currentResult = detectYakuForStructure(hand, context);\n const currentHan = getTotalHan(currentResult);\n\n if (currentHan > maxHan) {\n maxHan = currentHan;\n bestResult = currentResult;\n }\n }\n\n return bestResult;\n}\n","import { asHaiKindId, isTuple3, isTuple4 } from \"../../utils/assertions\";\nimport { ShoushaiError, TahaiError } from \"../../errors\";\nimport {\n CompletedMentsu,\n FuroType,\n HaiId,\n HaiKind,\n HaiKindDistribution,\n HaiKindId,\n Kantsu,\n Koutsu,\n MentsuType,\n Shuntsu,\n Tacha,\n} from \"../../types\";\nimport { haiIdToKindId, haiKindToNumber } from \"../../core/hai\";\n\n// 1つ以上の数字 + 1つのサフィックス (m, p, s, z)\nconst BLOCK_PATTERN = \"\\\\d+[mpsz]\";\n// 標準的なMSPZ: ブロックの繰り返し (空文字列も許容)\nconst STANDARD_MSPZ_REGEX = new RegExp(`^(${BLOCK_PATTERN})*$`);\n\n// 拡張パート: [...] または (...) で囲まれたブロック (囲みの中も同様のブロック構造)\n// 注: 現在のパーサー実装では、[] の中は単純な BLOCK_PATTERN の連続を許容しているか?\n// parseMentsuString: mspzStringToHaiKindIds を呼んでいる。\n// mspzStringToHaiKindIds: \\d+[mpsz] をパースする。\n// したがって、[] の中身も BLOCK_PATTERN の繰り返しであるべき。\nconst EXTENDED_BLOCK_PATTERN = `(${BLOCK_PATTERN}|\\\\[(${BLOCK_PATTERN})+\\\\]|\\\\((${BLOCK_PATTERN})+\\\\))`;\nconst EXTENDED_MSPZ_REGEX = new RegExp(`^${EXTENDED_BLOCK_PATTERN}*$`);\n\n/**\n * 標準的なMSPZ形式の文字列(拡張記法を含まない)\n */\nexport type MspzString = string & { readonly __brand: \"MspzString\" };\n\n/**\n * 拡張MSPZ形式の文字列\n * 通常のMSPZに加え、`[...]` (副露) や `(...)` (暗槓) を含むことができます。\n */\nexport type ExtendedMspzString = string & {\n readonly __brand: \"ExtendedMspzString\";\n};\n\n/**\n * 文字列が拡張MSPZ形式(`[` または `(` を含み、かつ正しい書式)かどうかを判定します。\n * @param input 判定対象の文字列\n * @returns 拡張MSPZ形式であれば true\n */\nexport function isExtendedMspz(input: string): input is ExtendedMspzString {\n // ブラケットを含む、かつ拡張書式にマッチする場合のみ true\n // (ブラケットを含まない適正なMSPZは、ここでの定義上 ExtendedMspzString とはみなさない = MspzString と区別する)\n return (\n (input.includes(\"[\") || input.includes(\"(\")) &&\n EXTENDED_MSPZ_REGEX.test(input)\n );\n}\n\n/**\n * 文字列を ExtendedMspzString 型として扱います。\n * 拡張MSPZ形式であることを検証します。\n *\n * @param input 変換対象の文字列\n * @throws {Error} 拡張MSPZ形式でない場合\n * @returns ExtendedMspzString\n */\nexport function asExtendedMspz(input: string): ExtendedMspzString {\n if (!isExtendedMspz(input)) {\n throw new Error(`Invalid Extended MSPZ string: ${input}`);\n }\n return input;\n}\n\n/**\n * 文字列が標準的なMSPZ形式(拡張記法を含まず、かつ正しい書式)かどうかを判定します。\n * @param input 判定対象の文字列\n * @returns 標準MSPZ形式であれば true\n */\nexport function isMspz(input: string): input is MspzString {\n return STANDARD_MSPZ_REGEX.test(input);\n}\n\n/**\n * 拡張MSPZ解析結果\n */\nexport interface ExtendedMspzParseResult {\n readonly closed: readonly HaiKindId[];\n readonly exposed: readonly CompletedMentsu[];\n}\n\n/**\n * 拡張MSPZ形式の文字列を解析して、純手牌と副露のリストに変換します。\n *\n * @param input 拡張MSPZ形式の文字列\n * @returns 解析結果オブジェクト\n */\nexport function parseExtendedMspz(input: string): ExtendedMspzParseResult {\n const closedParts: string[] = [];\n const exposed: CompletedMentsu[] = [];\n\n let current = \"\";\n let mode: \"closed\" | \"open\" | \"ankan\" = \"closed\";\n\n // 文字単位でパース\n for (const char of input) {\n if (char === \"[\") {\n if (mode !== \"closed\")\n throw new Error(\"Nested brackets are not supported\");\n if (current.length > 0) closedParts.push(current);\n // current = \"\"; // OLD\n current = \"[\"; // NEW: Start capturing with bracket\n mode = \"open\";\n } else if (char === \"]\") {\n if (mode !== \"open\") throw new Error(\"Unexpected closing bracket ']'\");\n current += \"]\"; // NEW: End capturing with bracket\n // exposed.push(parseMentsuString(current, \"open\")); // OLD\n exposed.push(parseMentsuFromExtendedMspz(asExtendedMspz(current))); // NEW\n current = \"\";\n mode = \"closed\";\n } else if (char === \"(\") {\n if (mode !== \"closed\")\n throw new Error(\"Nested parentheses are not supported\");\n if (current.length > 0) closedParts.push(current);\n // current = \"\"; // OLD\n current = \"(\"; // NEW\n mode = \"ankan\";\n } else if (char === \")\") {\n if (mode !== \"ankan\")\n throw new Error(\"Unexpected closing parenthesis ')'\");\n current += \")\"; // NEW\n // exposed.push(parseMentsuString(current, \"ankan\")); // OLD\n exposed.push(parseMentsuFromExtendedMspz(asExtendedMspz(current))); // NEW\n current = \"\";\n mode = \"closed\";\n } else {\n current += char;\n }\n }\n\n // 残りのclosed部分\n if (current.length > 0) {\n if (mode !== \"closed\") throw new Error(\"Unclosed bracket or parenthesis\");\n closedParts.push(current);\n }\n\n // closed部分を結合してパース\n const fullClosedMspz = closedParts.join(\"\");\n const closedIds = parseMspzToHaiKindIds(asMspz(fullClosedMspz));\n\n return {\n closed: closedIds,\n exposed: exposed,\n };\n}\n\n/**\n * 副露・暗槓のブロック文字列(例: \"[123m]\", \"(11z)\")を解析してCompletedMentsuを生成する内部関数\n * 括弧の種類から副露か暗槓かを自動判定します。\n */\nfunction parseMentsuFromExtendedMspz(\n block: ExtendedMspzString,\n): CompletedMentsu {\n let mode: \"open\" | \"ankan\";\n let content: string;\n\n if (block.startsWith(\"[\") && block.endsWith(\"]\")) {\n mode = \"open\";\n content = block.slice(1, -1);\n } else if (block.startsWith(\"(\") && block.endsWith(\")\")) {\n mode = \"ankan\";\n content = block.slice(1, -1);\n } else {\n throw new Error(\n `Invalid Extended MSPZ block: ${block} (must be [...] or (...))`,\n );\n }\n\n // 中身は標準MSPZ形式である必要がある\n const ids = parseMspzToHaiKindIds(asMspz(content));\n if (ids.length === 0) {\n throw new Error(\"Empty mentsu specification\");\n }\n\n // 枚数チェック & 種類判定\n const count = ids.length;\n const isAllSame = ids.every((id) => id === ids[0]);\n\n // 暗槓 (Ankan)\n if (mode === \"ankan\") {\n if (count !== 4 || !isAllSame) {\n throw new Error(`Invalid Ankan: ${block} (must be 4 identical tiles)`);\n }\n if (!isTuple4(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const kantsu: Kantsu = {\n type: MentsuType.Kantsu,\n hais: ids,\n // Ankan has no furo info (or minimal)\n };\n return kantsu;\n }\n\n // 副露 (Open)\n if (count === 4 && isAllSame) {\n // Daiminkan\n if (!isTuple4(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const kantsu: Kantsu = {\n type: MentsuType.Kantsu,\n hais: ids,\n furo: { type: FuroType.Daiminkan, from: Tacha.Toimen }, // Default\n };\n return kantsu;\n } else if (count === 3 && isAllSame) {\n // Pon\n if (!isTuple3(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const koutsu: Koutsu = {\n type: MentsuType.Koutsu,\n hais: ids,\n furo: { type: FuroType.Pon, from: Tacha.Toimen }, // Default\n };\n return koutsu;\n } else if (count === 3) {\n // Chi (Should check continuity, strictly speaking but relying on user for now or implicit check)\n // Minimal check: sorted? mspzStringToHaiKindIds sorts by default?\n // mspzStringToHaiKindIds does NOT sort across different suits, but \"123m\" results in sorted array.\n // Let's assume valid sequence for now.\n if (!isTuple3(ids)) {\n throw new Error(\"Internal Error: ids length check mismatch\");\n }\n const shuntsu: Shuntsu = {\n type: MentsuType.Shuntsu,\n hais: ids,\n furo: { type: FuroType.Chi, from: Tacha.Kamicha }, // Default\n };\n return shuntsu;\n }\n\n throw new Error(\n `Invalid Mentsu specification: ${block} (must be 3 or 4 tiles)`,\n );\n}\n\n/**\n * 13枚の牌種ID配列を 34種の牌種分布(所持数分布)に変換します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function createDistribution(\n hais: readonly HaiKindId[],\n): HaiKindDistribution {\n if (hais.length < 13) {\n throw new ShoushaiError(\n `Invalid number of tiles: expected 13, got ${hais.length}`,\n );\n }\n if (hais.length > 13) {\n throw new TahaiError(\n `Invalid number of tiles: expected 13, got ${hais.length}`,\n );\n }\n\n const counts = Array.from({ length: 34 }, () => 0);\n\n for (const kind of hais) {\n counts[kind] = (counts[kind] ?? 0) + 1;\n }\n\n // Tupleへの変換はアサーションが必要だが、生成ロジックが保証しているため安全\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return counts as unknown as HaiKindDistribution;\n}\n\n/**\n * 13枚の牌ID配列を 34種の牌種分布(所持数分布)に変換します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function haiIdsToDistribution(\n // Branded type makes linter think it's mutable object, but it's primitive number.\n // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types\n hais: readonly HaiId[],\n): HaiKindDistribution {\n const kinds = hais.map(haiIdToKindId);\n return createDistribution(kinds);\n}\n\n/**\n * 13枚の牌種ID配列を MSPZ形式の文字列(例: \"123m456p...\")に変換します。\n * すべての牌をソートして表記します。\n * @throws {ShoushaiError} 牌の数が13枚より少ない場合\n * @throws {TahaiError} 牌の数が13枚より多い場合\n */\nexport function haiKindIdsToMspzString(hais: readonly HaiKindId[]): string {\n const counts = createDistribution(hais);\n let result = \"\";\n\n // 萬子\n const manzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.ManZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) manzu.push(num);\n }\n }\n if (manzu.length > 0) {\n result += manzu.join(\"\") + \"m\";\n }\n\n // 筒子\n const pinzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.PinZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) pinzu.push(num);\n }\n }\n if (pinzu.length > 0) {\n result += pinzu.join(\"\") + \"p\";\n }\n\n // 索子\n const souzu: number[] = [];\n for (let i = 0; i < 9; i++) {\n const kind = asHaiKindId(HaiKind.SouZu1 + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n const num = haiKindToNumber(kind);\n if (num !== undefined) souzu.push(num);\n }\n }\n if (souzu.length > 0) {\n result += souzu.join(\"\") + \"s\";\n }\n\n // 字牌\n const jihai: number[] = [];\n for (let i = 0; i < 7; i++) {\n const kind = asHaiKindId(HaiKind.Ton + i);\n const count = counts[kind];\n for (let j = 0; j < count; j++) {\n // 字牌は 1-7 で表すことが多い\n const num = i + 1;\n jihai.push(num);\n }\n }\n if (jihai.length > 0) {\n result += jihai.join(\"\") + \"z\";\n }\n\n return result;\n}\n\n/**\n * 文字列を MspzString 型として扱います。\n * 標準的なMSPZ形式(拡張記法を含まない)であることを検証します。\n *\n * @param input 変換対象の文字列\n * @throws {Error} 拡張記法が含まれている場合\n * @returns MspzString\n */\nexport function asMspz(input: string): MspzString {\n if (!isMspz(input)) {\n throw new Error(`Invalid MSPZ string: ${input}`);\n }\n return input;\n}\n\n/**\n * MSPZ形式の文字列(例: \"123m456p\")を解析して HaiKindId の配列に変換します。\n * 主にテストデータの作成用途で使用します。\n *\n * @param mspz MSPZ形式の文字列\n * @returns HaiKindId の配列\n */\nexport function parseMspzToHaiKindIds(mspz: MspzString): HaiKindId[] {\n const result: HaiKindId[] = [];\n let currentNumbers: number[] = [];\n\n for (const char of mspz) {\n if (char >= \"0\" && char <= \"9\") {\n currentNumbers.push(parseInt(char, 10));\n } else {\n // Suffix handling\n let base: HaiKindId | undefined;\n\n switch (char) {\n case \"m\":\n base = HaiKind.ManZu1;\n break;\n case \"p\":\n base = HaiKind.PinZu1;\n break;\n case \"s\":\n base = HaiKind.SouZu1;\n break;\n case \"z\":\n base = HaiKind.Ton;\n break;\n default:\n // 無視する\n currentNumbers = [];\n continue;\n }\n\n for (const num of currentNumbers) {\n if (char === \"z\") {\n // 字牌: 1=東(27), ... 7=中(33)\n if (num >= 1 && num <= 7) {\n result.push(asHaiKindId(base + num - 1));\n }\n } else {\n // 数牌: 1-9\n if (num >= 1) {\n result.push(asHaiKindId(base + num - 1));\n }\n }\n }\n currentNumbers = [];\n }\n }\n\n return result;\n}\n","import {\n parseExtendedMspz as internalParseExtendedMspz,\n parseMspzToHaiKindIds,\n asMspz,\n asExtendedMspz,\n} from \"./mspz\";\nimport type { Tehai } from \"../../types\";\n\nexport type { MspzString, ExtendedMspzString } from \"./mspz\";\nexport { isExtendedMspz } from \"./mspz\";\n\n/**\n * 標準的なMSPZ文字列(例: \"123m456p...\")を解析して手牌オブジェクトを生成します。\n * 副露牌は含まれません。\n *\n * @param input MSPZ形式の文字列\n * @returns 手牌オブジェクト\n */\nexport function parseMspz(input: string): Tehai {\n const ids = parseMspzToHaiKindIds(asMspz(input));\n return {\n closed: ids,\n exposed: [],\n };\n}\n\n/**\n * 拡張MSPZ文字列(例: \"123m[123p]...\")を解析して手牌オブジェクトを生成します。\n * 副露牌(`[...]`)や暗槓(`(...)`)を含めることができます。\n *\n * @param input 拡張MSPZ形式の文字列\n * @returns 手牌オブジェクト\n */\nexport function parseExtendedMspz(input: string): Tehai {\n // parseExtendedMspz returns { closed: HaiKindId[], exposed: CompletedMentsu[] }\n // which is compatible with Tehai interface.\n return internalParseExtendedMspz(asExtendedMspz(input));\n}\n","/**\n * 符計算に関する定数定義\n */\n\n// 符底 (FuTei)\nexport const FU_BASE = {\n NORMAL: 20,\n CHIITOITSU: 25,\n KOKUSHI: 20, // 便宜上\n} as const;\n\n// 和了符 (AgariFu)\nexport const FU_AGARI = {\n TSUMO: 2,\n MENZEN_RON: 10,\n} as const;\n\n// 雀頭符 (JantouFu)\nexport const FU_JANTOU = {\n YAKUHAI: 2,\n // 連風牌の扱い(設定により4符または2符)\n DOUBLE_WIND_CAP: 2,\n} as const;\n\n// 待ち符 (MachiFu)\nexport const FU_MACHI = {\n KANCHAN: 2,\n PENCHAN: 2,\n TANKI: 2,\n RYANMEN: 0,\n SHANPON: 0,\n} as const;\n\n// 面子符 (MentsuFu)\n// [Yaochu/Suupai]_[Open/Closed]\nexport const FU_KOUTSU = {\n SUUPAI_OPEN: 2,\n SUUPAI_CLOSED: 4,\n YAOCHU_OPEN: 4,\n YAOCHU_CLOSED: 8,\n} as const;\n\nexport const FU_KANTSU = {\n SUUPAI_OPEN: 8,\n SUUPAI_CLOSED: 16,\n YAOCHU_OPEN: 16,\n YAOCHU_CLOSED: 32,\n} as const;\n\n// 例外処理用定数\nexport const FU_PINFU_TSUMO = 20;\nexport const FU_OPEN_PINFU_GLAZE = 30; // 喰いタン平和形などの20符切り上げ\n","import type { FuResult, FuDetails } from \"../types\";\nimport { FU_BASE } from \"../constants\";\n\n/**\n * 七対子の符を計算する\n */\nexport function calculateChiitoitsuFu(): FuResult {\n const details: FuDetails = {\n base: FU_BASE.CHIITOITSU,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n return {\n total: FU_BASE.CHIITOITSU,\n details,\n };\n}\n","import type { FuResult, FuDetails } from \"../types\";\nimport { FU_BASE } from \"../constants\";\n\n/**\n * 国士無双の符を計算する\n * (点数計算上は役満固定だが、便宜上符底のみを返す)\n */\nexport function calculateKokushiFu(): FuResult {\n const details: FuDetails = {\n base: FU_BASE.KOKUSHI,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n return {\n total: FU_BASE.KOKUSHI,\n details,\n };\n}\n","import type { FuResult } from \"../types\";\nimport type { HouraContext } from \"../../../../yaku/types\";\nimport type { MentsuHouraStructure } from \"../../../../yaku/types\";\nimport { isYaochu } from \"../../../../../core/hai\";\nimport { HaiKind, type Fu } from \"../../../../../types\";\nimport { classifyMachi } from \"../../../../../core/machi\";\nimport {\n FU_BASE,\n FU_KOUTSU,\n FU_KANTSU,\n FU_JANTOU,\n FU_MACHI,\n FU_AGARI,\n FU_PINFU_TSUMO,\n FU_OPEN_PINFU_GLAZE,\n} from \"../constants\";\n\n/** 有効な符の値 */\nconst VALID_FU_VALUES: readonly Fu[] = [\n 20, 25, 30, 40, 50, 60, 70, 80, 90, 100, 110,\n];\n\n/**\n * 数値を Fu 型に変換する(無効な値の場合はエラー)\n */\nfunction toFu(value: number): Fu {\n const fu = VALID_FU_VALUES.find((f) => f === value);\n if (fu === undefined) {\n throw new Error(`Invalid fu value: ${value}`);\n }\n return fu;\n}\n\n/**\n * 面子手の符を計算する\n *\n * @param hand 面子手の構造\n * @param context 和了コンテキスト\n * @param isPinfu 平和成立フラグ\n */\nexport function calculateMentsuFu(\n hand: MentsuHouraStructure,\n context: HouraContext,\n isPinfu: boolean,\n): FuResult {\n const details = {\n base: FU_BASE.NORMAL,\n mentsu: 0,\n jantou: 0,\n machi: 0,\n agari: 0,\n };\n\n // 1. 面子符 (MentsuFu)\n for (const mentsu of hand.fourMentsu) {\n let fu = 0;\n const isYaochuMentsu = isYaochu(mentsu.hais[0]);\n\n if (mentsu.type === \"Koutsu\") {\n // 刻子\n const openFu = isYaochuMentsu\n ? FU_KOUTSU.YAOCHU_OPEN\n : FU_KOUTSU.SUUPAI_OPEN;\n const closedFu = isYaochuMentsu\n ? FU_KOUTSU.YAOCHU_CLOSED\n : FU_KOUTSU.SUUPAI_CLOSED;\n\n fu = closedFu;\n\n // 明刻判定\n let isOpen = !!mentsu.furo;\n if (!isOpen && !context.isTsumo) {\n // ロン和了で、かつその牌を含む刻子であれば明刻扱い\n if (mentsu.hais.includes(context.agariHai)) {\n isOpen = true;\n }\n }\n\n if (isOpen) {\n fu = openFu;\n }\n details.mentsu += fu;\n } else if (mentsu.type === \"Kantsu\") {\n // 槓子\n const openFu = isYaochuMentsu\n ? FU_KANTSU.YAOCHU_OPEN\n : FU_KANTSU.SUUPAI_OPEN;\n const closedFu = isYaochuMentsu\n ? FU_KANTSU.YAOCHU_CLOSED\n : FU_KANTSU.SUUPAI_CLOSED;\n\n fu = closedFu;\n if (mentsu.furo) {\n fu = openFu;\n }\n details.mentsu += fu;\n }\n }\n\n // 2. 雀頭符 (JantouFu)\n const headHai = hand.jantou.hais[0];\n let jantouFu = 0;\n\n if (\n headHai === HaiKind.Haku ||\n headHai === HaiKind.Hatsu ||\n headHai === HaiKind.Chun\n ) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n if (headHai === context.bakaze) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n if (headHai === context.jikaze) {\n jantouFu += FU_JANTOU.YAKUHAI;\n }\n\n // 連風牌の加算上限\n if (jantouFu > FU_JANTOU.DOUBLE_WIND_CAP) {\n jantouFu = FU_JANTOU.DOUBLE_WIND_CAP;\n }\n details.jantou = jantouFu;\n\n // 3. 待ち符 (MachiFu)\n const machiType = classifyMachi(hand, context.agariHai);\n if (machiType === \"Kanchan\") details.machi = FU_MACHI.KANCHAN;\n else if (machiType === \"Penchan\") details.machi = FU_MACHI.PENCHAN;\n else if (machiType === \"Tanki\") details.machi = FU_MACHI.TANKI;\n else details.machi = 0; // Ryanmen, Shanpon\n\n // 4. 和了符 (AgariFu)\n if (context.isTsumo) {\n if (!isPinfu) {\n details.agari = FU_AGARI.TSUMO;\n }\n } else {\n if (context.isMenzen) {\n details.agari = FU_AGARI.MENZEN_RON;\n }\n }\n\n // 合計\n let sum =\n details.base +\n details.mentsu +\n details.jantou +\n details.machi +\n details.agari;\n\n // 平和ツモ例外\n if (isPinfu && context.isTsumo) {\n return { total: FU_PINFU_TSUMO, details };\n }\n\n // 切り上げ (喰いタン平和形など)\n if (sum === 20 && !context.isTsumo && !context.isMenzen) {\n sum = FU_OPEN_PINFU_GLAZE;\n } else {\n sum = Math.ceil(sum / 10) * 10;\n }\n\n return {\n total: toFu(sum),\n details,\n };\n}\n","import type { HouraStructure, HouraContext } from \"../../../yaku/types\";\nimport type { FuResult } from \"./types\";\nimport { calculateChiitoitsuFu } from \"./lib/chiitoitsu\";\nimport { calculateKokushiFu } from \"./lib/kokushi\";\nimport { calculateMentsuFu } from \"./lib/mentsu\";\n\n/**\n * 手牌の符を計算する (calculateFu)\n *\n * @param hand 和了形の手牌構造\n * @param context 和了コンテキスト (場風、自風、ツモ/ロン等)\n * @param isPinfu 平和が成立しているかどうか (平和ツモ20符例外の適用に必要)\n * @returns 符計算結果\n */\nexport function calculateFu(\n hand: HouraStructure,\n context: HouraContext,\n isPinfu = false,\n): FuResult {\n // 1. 七対子 (ChiiToitsu)\n if (hand.type === \"Chiitoitsu\") {\n return calculateChiitoitsuFu();\n }\n\n // 2. 国士無双 (Kokushi)\n if (hand.type === \"Kokushi\") {\n return calculateKokushiFu();\n }\n\n // 3. 面子手 (Mentsu)\n return calculateMentsuFu(hand, context, isPinfu);\n}\n","export const SCORE_OYA_MULTIPLIER = 1.5; // 親は1.5倍\n\nexport const SCORE_BASE_MANGAN = 2000;\nexport const SCORE_BASE_HANEMAN = 3000;\nexport const SCORE_BASE_BAIMAN = 4000;\nexport const SCORE_BASE_SANBAIMAN = 6000;\nexport const SCORE_BASE_YAKUMAN = 8000;\n\n/**\n * 満貫以上の判定基準となる翻数\n *\n * \"5翻\" であれば符数に関わらず満貫以上が確定するため 5 を設定しています。\n *\n * Q: 4翻は満貫ではないのか?\n * A: 4翻でも符数が高ければ(40符以上など)満貫になりますが、以下のようなケースでは満貫(8000点)に届きません。\n * - 七対子 (25符): 6400点\n * - 門前ツモ・愚形など (30符): 7900点 (※切り上げ満貫なしの場合)\n * - 鳴き手・ロン (30符): 7700点 (※切り上げ満貫なしの場合)\n *\n * そのため、4翻以下の場合は計算による基本点が基準値(2000)を超えたかどうかで判定します。\n */\nexport const HAN_MANGAN = 5;\nexport const HAN_HANEMAN = 6;\nexport const HAN_BAIMAN = 8;\nexport const HAN_SANBAIMAN = 11;\nexport const HAN_YAKUMAN = 13;\n\nexport const HAS_YAKUMAN = 13; // 便宜上の翻数\nexport const HAS_DOUBLE_YAKUMAN = 26;\n\n// 切り上げ満貫の閾値 (30符4翻 = 1920 -> 2000? 60符3翻=1920)\n// 一般的には 2000点(子) / 3000点(親) が満貫の最低点(ベース)\n// 符計算による基本点が 2000 を超えたら満貫\nexport const BASE_SCORE_LIMIT = 2000;\n","import type { Fu, HaiKindId, Kazehai } from \"../../types\";\nimport type { HouraContext } from \"../yaku/types\";\n\n/**\n * 点数計算用コンテキスト (ScoreContext)\n *\n * HouraContext を拡張し、点数計算に必要な追加情報を持つ。\n *\n * HouraContext は役判定に特化しており、isOya は役の成立・翻数に影響しない。\n * isOya は支払い計算(親ロン/子ロン、親ツモ/子ツモの倍率差)にのみ必要なため、\n * 点数計算用のコンテキストとして分離している。\n */\nexport interface ScoreContext extends HouraContext {\n /** 和了者が親かどうか */\n readonly isOya: boolean;\n}\n\nexport interface ScoreCalculationConfig {\n /** 和了牌 */\n agariHai: HaiKindId;\n /** ツモ和了かどうか (必須) */\n isTsumo: boolean;\n /** 自風 (必須) */\n jikaze: Kazehai;\n /** 場風 (必須) */\n bakaze: Kazehai;\n /** ドラ表示牌 (必須、なければ空配列) */\n doraMarkers: readonly HaiKindId[];\n /** 裏ドラ表示牌 (任意) */\n uraDoraMarkers?: readonly HaiKindId[];\n}\n\n/** ロン和了時の支払い */\nexport interface Ron {\n type: \"ron\";\n /** 振り込んだプレイヤーが支払う点数 */\n amount: number;\n}\n\n/** 子のツモ和了時の支払い */\nexport interface KoTsumo {\n type: \"koTsumo\";\n /** [子の支払い, 親の支払い] */\n readonly amount: readonly [number, number];\n}\n\n/** 親のツモ和了時の支払い */\nexport interface OyaTsumo {\n type: \"oyaTsumo\";\n /** 子全員が支払う点数(オール) */\n amount: number;\n}\n\n/** 支払い情報 */\nexport type Payment = Ron | KoTsumo | OyaTsumo;\n\n/**\n * 点数レベル (ScoreLevel)\n *\n * 翻数と基本点から決まる点数の区分。\n * 満貫以上では符に関係なく固定の基本点が適用される。\n */\nexport const ScoreLevel = {\n /** 満貫未満(通常計算) */\n Normal: \"Normal\",\n /** 満貫(5翻、または基本点2000以上) */\n Mangan: \"Mangan\",\n /** 跳満(6-7翻) */\n Haneman: \"Haneman\",\n /** 倍満(8-10翻) */\n Baiman: \"Baiman\",\n /** 三倍満(11-12翻) */\n Sanbaiman: \"Sanbaiman\",\n /** 役満(13翻以上) */\n Yakuman: \"Yakuman\",\n /** ダブル役満(26翻以上、または役満複合) */\n DoubleYakuman: \"DoubleYakuman\",\n} as const;\n\nexport type ScoreLevel = (typeof ScoreLevel)[keyof typeof ScoreLevel];\n\nexport interface ScoreResult {\n han: number;\n fu: Fu;\n scoreLevel: ScoreLevel;\n payment: Payment;\n}\n","import { type Tehai14, type Fu, HaiKind } from \"../../types\";\nimport { NoYakuError } from \"../../errors\";\nimport { countDora } from \"../../core/dora\";\nimport { getHouraStructures } from \"../yaku/lib/structures\";\nimport { detectYakuForStructure } from \"../yaku\";\nimport { calculateFu } from \"./lib/fu\";\nimport type { FuResult } from \"./lib/fu/types\";\nimport { isMenzen } from \"../yaku/utils\";\nimport {\n BASE_SCORE_LIMIT,\n HAN_BAIMAN,\n HAN_HANEMAN,\n HAN_MANGAN,\n HAN_SANBAIMAN,\n HAN_YAKUMAN,\n SCORE_BASE_BAIMAN,\n SCORE_BASE_HANEMAN,\n SCORE_BASE_MANGAN,\n SCORE_BASE_SANBAIMAN,\n SCORE_BASE_YAKUMAN,\n} from \"./constants\";\nimport {\n ScoreLevel,\n type ScoreCalculationConfig,\n type ScoreContext,\n type ScoreResult,\n type Payment,\n type Ron,\n type KoTsumo,\n type OyaTsumo,\n} from \"./types\";\nexport type {\n ScoreCalculationConfig,\n ScoreResult,\n Payment,\n Ron,\n KoTsumo,\n OyaTsumo,\n};\nexport { ScoreLevel };\n\n/**\n * 100点単位で切り上げる\n */\nfunction ceil100(points: number): number {\n return Math.ceil(points / 100) * 100;\n}\n\n/**\n * 基本点を計算する\n * 基本点 = 符 × 2^(2+翻)\n *\n * 基本点は子がツモ和了したときの他の子1人あたりの支払い点数に相当する。\n * - 子ツモ: 子の支払い = 基本点, 親の支払い = 基本点 × 2\n * - 子ロン: 支払い = 基本点 × 4\n * - 親ツモ: 各子の支払い = 基本点 × 2\n * - 親ロン: 支払い = 基本点 × 6\n */\nexport function calculateBasePoints(fu: Fu, han: number): number {\n return fu * Math.pow(2, 2 + han);\n}\n\n/**\n * 支払い情報から和了者が受け取る総点数を計算する\n */\nexport function getPaymentTotal(payment: Readonly<Payment>): number {\n switch (payment.type) {\n case \"ron\":\n return payment.amount;\n case \"koTsumo\":\n return payment.amount[0] * 2 + payment.amount[1];\n case \"oyaTsumo\":\n return payment.amount * 3;\n }\n}\n\n/**\n * 翻数と基本点から点数レベルを判定する\n *\n * @param han 翻数\n * @param basePoints 基本点(符 × 2^(2+翻))\n * @returns 点数レベル\n */\nexport function getScoreLevel(han: number, basePoints: number): ScoreLevel {\n if (han >= 26) {\n return ScoreLevel.DoubleYakuman;\n }\n if (han >= HAN_YAKUMAN) {\n return ScoreLevel.Yakuman;\n }\n if (han >= HAN_SANBAIMAN) {\n return ScoreLevel.Sanbaiman;\n }\n if (han >= HAN_BAIMAN) {\n return ScoreLevel.Baiman;\n }\n if (han >= HAN_HANEMAN) {\n return ScoreLevel.Haneman;\n }\n if (han >= HAN_MANGAN || basePoints >= BASE_SCORE_LIMIT) {\n return ScoreLevel.Mangan;\n }\n return ScoreLevel.Normal;\n}\n\n/**\n * 点数レベルに対応する基本点を取得する\n *\n * @param level 点数レベル\n * @returns 基本点(Normal の場合は null)\n */\nfunction getLimitBasePoints(level: ScoreLevel): number | null {\n switch (level) {\n case ScoreLevel.DoubleYakuman:\n return SCORE_BASE_YAKUMAN * 2;\n case ScoreLevel.Yakuman:\n return SCORE_BASE_YAKUMAN;\n case ScoreLevel.Sanbaiman:\n return SCORE_BASE_SANBAIMAN;\n case ScoreLevel.Baiman:\n return SCORE_BASE_BAIMAN;\n case ScoreLevel.Haneman:\n return SCORE_BASE_HANEMAN;\n case ScoreLevel.Mangan:\n return SCORE_BASE_MANGAN;\n case ScoreLevel.Normal:\n return null;\n }\n}\n\n/**\n * 点数計算用コンテキストを作成する\n *\n * @param tehai 手牌 (14枚)\n * @param config 点数計算の設定\n * @returns 点数計算用コンテキスト\n */\nfunction createScoreContext(\n tehai: Tehai14,\n config: Readonly<ScoreCalculationConfig>,\n): ScoreContext {\n return {\n isMenzen: isMenzen(tehai),\n agariHai: config.agariHai,\n bakaze: config.bakaze,\n jikaze: config.jikaze,\n isTsumo: config.isTsumo,\n isOya: config.jikaze === HaiKind.Ton,\n doraMarkers: config.doraMarkers,\n ...(config.uraDoraMarkers ? { uraDoraMarkers: config.uraDoraMarkers } : {}),\n };\n}\n\n/**\n * 手牌とコンテキストから点数を計算する(公開API)\n *\n * 手牌の構造解析を行い、最も高点となる解釈を採用して点数を返します。\n *\n * 注: 同一手牌で「翻数が高いが符が低い解釈」と「翻数が低いが符が高い解釈」が\n * 両立するケースは実質的に存在しないため、翻数最大の解釈を採用しています。\n *\n * @param tehai 手牌 (14枚)\n * @param config 点数計算の設定 (場風、自風、ドラなど)\n * @returns 点数計算結果\n */\nexport function calculateScoreForTehai(\n tehai: Tehai14,\n config: Readonly<ScoreCalculationConfig>,\n): ScoreResult {\n const context = createScoreContext(tehai, config);\n const structuralInterpretations = getHouraStructures(tehai);\n let bestResult: ScoreResult | null = null;\n let maxTotalPoints = -1;\n\n for (const hand of structuralInterpretations) {\n // 1. 役の判定\n const yakuResult = detectYakuForStructure(hand, context);\n const yakuHansu = yakuResult.reduce((sum, [, han]) => sum + han, 0);\n\n // 役がない場合はこの構造は不成立\n if (yakuHansu === 0) continue;\n\n // 2. 符の計算\n const isPinfu = yakuResult.some(([name]) => name === \"Pinfu\");\n const fuResult = calculateFu(hand, context, isPinfu);\n\n // 3. ドラの計算\n const dora = countDora(tehai, context.doraMarkers);\n\n // 4. 点数計算\n const result = calculateScoreFromHanAndFu(\n yakuHansu,\n fuResult,\n dora,\n context,\n );\n const total = getPaymentTotal(result.payment);\n\n if (total > maxTotalPoints) {\n maxTotalPoints = total;\n bestResult = result;\n }\n }\n\n if (!bestResult) {\n throw new NoYakuError();\n }\n\n return bestResult;\n}\n\n/**\n * 基本的な点数計算ロジック (内部用・テスト用)\n */\nexport function calculateScoreFromHanAndFu(\n yakuHansu: number,\n fuResult: Readonly<FuResult>,\n dora: number,\n context: Readonly<ScoreContext>,\n): ScoreResult {\n const totalHan = yakuHansu + dora;\n const fu = fuResult.total;\n\n // 基本点の計算\n const rawBasePoints = calculateBasePoints(fu, totalHan);\n\n // 点数レベルの判定\n const scoreLevel = getScoreLevel(totalHan, rawBasePoints);\n\n // 満貫以上なら固定の基本点、それ以外は計算値を使用\n const basePoints = getLimitBasePoints(scoreLevel) ?? rawBasePoints;\n\n // 支払い計算\n const payment = calculatePayment(basePoints, context);\n\n return {\n han: totalHan,\n fu: fu,\n scoreLevel,\n payment,\n };\n}\n\n/**\n * 基本点から支払い情報を計算する\n */\nfunction calculatePayment(\n basePoints: number,\n context: Readonly<ScoreContext>,\n): Payment {\n if (context.isTsumo) {\n if (context.isOya) {\n // 親ツモ: オール (基本点 * 2)\n const allPay = ceil100(basePoints * 2);\n return { type: \"oyaTsumo\", amount: allPay };\n } else {\n // 子ツモ: 親の支払い = 基本点 * 2, 子の支払い = 基本点 * 1\n const parentPay = ceil100(basePoints * 2);\n const childPay = ceil100(basePoints * 1);\n return { type: \"koTsumo\", amount: [childPay, parentPay] };\n }\n } else {\n // ロン和了\n if (context.isOya) {\n // 親ロン: 基本点 * 6\n const pay = ceil100(basePoints * 6);\n return { type: \"ron\", amount: pay };\n } else {\n // 子ロン: 基本点 * 4\n const pay = ceil100(basePoints * 4);\n return { type: \"ron\", amount: pay };\n }\n }\n}\n"],"names":["definition","parseExtendedMspz","internalParseExtendedMspz"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pai-forge/riichi-mahjong",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"