mj-simulator 0.1.0

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.
Files changed (85) hide show
  1. package/data/honor-table.bin +0 -0
  2. package/data/suit-table.bin +0 -0
  3. package/dist/cli.d.ts +1 -0
  4. package/dist/cli.js +278 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/core/constants.d.ts +13 -0
  7. package/dist/core/constants.js +19 -0
  8. package/dist/core/constants.js.map +1 -0
  9. package/dist/core/hand.d.ts +37 -0
  10. package/dist/core/hand.js +105 -0
  11. package/dist/core/hand.js.map +1 -0
  12. package/dist/core/remaining.d.ts +12 -0
  13. package/dist/core/remaining.js +26 -0
  14. package/dist/core/remaining.js.map +1 -0
  15. package/dist/core/tile.d.ts +45 -0
  16. package/dist/core/tile.js +140 -0
  17. package/dist/core/tile.js.map +1 -0
  18. package/dist/ev/ev-calculator.d.ts +10 -0
  19. package/dist/ev/ev-calculator.js +257 -0
  20. package/dist/ev/ev-calculator.js.map +1 -0
  21. package/dist/ev/types.d.ts +16 -0
  22. package/dist/ev/types.js +2 -0
  23. package/dist/ev/types.js.map +1 -0
  24. package/dist/index.d.ts +27 -0
  25. package/dist/index.js +27 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/init.d.ts +11 -0
  28. package/dist/init.js +24 -0
  29. package/dist/init.js.map +1 -0
  30. package/dist/mc/simulator.d.ts +18 -0
  31. package/dist/mc/simulator.js +11 -0
  32. package/dist/mc/simulator.js.map +1 -0
  33. package/dist/mc/wasm-bridge.d.ts +30 -0
  34. package/dist/mc/wasm-bridge.js +56 -0
  35. package/dist/mc/wasm-bridge.js.map +1 -0
  36. package/dist/models/danger.d.ts +34 -0
  37. package/dist/models/danger.js +147 -0
  38. package/dist/models/danger.js.map +1 -0
  39. package/dist/models/evaluation.d.ts +31 -0
  40. package/dist/models/evaluation.js +46 -0
  41. package/dist/models/evaluation.js.map +1 -0
  42. package/dist/scoring/decomposer.d.ts +7 -0
  43. package/dist/scoring/decomposer.js +106 -0
  44. package/dist/scoring/decomposer.js.map +1 -0
  45. package/dist/scoring/fu.d.ts +5 -0
  46. package/dist/scoring/fu.js +47 -0
  47. package/dist/scoring/fu.js.map +1 -0
  48. package/dist/scoring/score-calculator.d.ts +11 -0
  49. package/dist/scoring/score-calculator.js +164 -0
  50. package/dist/scoring/score-calculator.js.map +1 -0
  51. package/dist/scoring/types.d.ts +38 -0
  52. package/dist/scoring/types.js +2 -0
  53. package/dist/scoring/types.js.map +1 -0
  54. package/dist/scoring/wait-type.d.ts +11 -0
  55. package/dist/scoring/wait-type.js +65 -0
  56. package/dist/scoring/wait-type.js.map +1 -0
  57. package/dist/scoring/yaku.d.ts +7 -0
  58. package/dist/scoring/yaku.js +490 -0
  59. package/dist/scoring/yaku.js.map +1 -0
  60. package/dist/shanten/init.d.ts +9 -0
  61. package/dist/shanten/init.js +26 -0
  62. package/dist/shanten/init.js.map +1 -0
  63. package/dist/shanten/lookup-table.d.ts +37 -0
  64. package/dist/shanten/lookup-table.js +228 -0
  65. package/dist/shanten/lookup-table.js.map +1 -0
  66. package/dist/shanten/shanten-calculator.d.ts +18 -0
  67. package/dist/shanten/shanten-calculator.js +101 -0
  68. package/dist/shanten/shanten-calculator.js.map +1 -0
  69. package/dist/shanten/ukeire.d.ts +35 -0
  70. package/dist/shanten/ukeire.js +103 -0
  71. package/dist/shanten/ukeire.js.map +1 -0
  72. package/dist/types.d.ts +17 -0
  73. package/dist/types.js +2 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/worker-client.d.ts +42 -0
  76. package/dist/worker-client.js +83 -0
  77. package/dist/worker-client.js.map +1 -0
  78. package/dist/worker.d.ts +1 -0
  79. package/dist/worker.js +67 -0
  80. package/dist/worker.js.map +1 -0
  81. package/node_modules/mj-mc-wasm/mj_mc_wasm.d.ts +57 -0
  82. package/node_modules/mj-mc-wasm/mj_mc_wasm.js +235 -0
  83. package/node_modules/mj-mc-wasm/mj_mc_wasm_bg.wasm +0 -0
  84. package/node_modules/mj-mc-wasm/package.json +15 -0
  85. package/package.json +44 -0
@@ -0,0 +1,140 @@
1
+ import { SUIT_NUM, JIHAI_OFFSET, YAOCHU_IDS, RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID, } from "./constants.js";
2
+ const SUIT_CHARS = ["m", "p", "s", "z"];
3
+ const JIHAI_NAMES = ["東", "南", "西", "北", "白", "發", "中"];
4
+ const yaochiSet = new Set(YAOCHU_IDS);
5
+ /**
6
+ * 赤ドラかどうかを返す
7
+ */
8
+ export function isRedTile(id) {
9
+ return id === RED_MANZU_ID || id === RED_PINZU_ID || id === RED_SOUZU_ID;
10
+ }
11
+ /**
12
+ * 赤ドラIDを対応する通常の5に変換する(通常牌はそのまま返す)
13
+ */
14
+ export function toNormalTile(id) {
15
+ if (id === RED_MANZU_ID)
16
+ return 4; // 5m
17
+ if (id === RED_PINZU_ID)
18
+ return 13; // 5p
19
+ if (id === RED_SOUZU_ID)
20
+ return 22; // 5s
21
+ return id;
22
+ }
23
+ /**
24
+ * 牌IDの色インデックスを返す (0=萬子, 1=筒子, 2=索子, 3=字牌)
25
+ */
26
+ export function getSuitIndex(id) {
27
+ if (id === RED_MANZU_ID)
28
+ return 0;
29
+ if (id === RED_PINZU_ID)
30
+ return 1;
31
+ if (id === RED_SOUZU_ID)
32
+ return 2;
33
+ if (id < SUIT_NUM)
34
+ return 0;
35
+ if (id < SUIT_NUM * 2)
36
+ return 1;
37
+ if (id < SUIT_NUM * 3)
38
+ return 2;
39
+ return 3;
40
+ }
41
+ /**
42
+ * 牌IDの色を返す
43
+ */
44
+ export function getSuit(id) {
45
+ return SUIT_CHARS[getSuitIndex(id)];
46
+ }
47
+ /**
48
+ * 牌IDの数字を返す (1-9, 字牌は1-7, 赤ドラは5)
49
+ */
50
+ export function getNum(id) {
51
+ if (id >= RED_MANZU_ID && id <= RED_SOUZU_ID)
52
+ return 5;
53
+ if (id >= JIHAI_OFFSET)
54
+ return id - JIHAI_OFFSET + 1;
55
+ return (id % SUIT_NUM) + 1;
56
+ }
57
+ /**
58
+ * 牌IDが么九牌かどうかを返す
59
+ */
60
+ export function isYaochu(id) {
61
+ return yaochiSet.has(id);
62
+ }
63
+ /**
64
+ * 牌IDを文字列に変換する (例: 0 → "1m", 27 → "東", 34 → "0m")
65
+ */
66
+ export function tileToString(id) {
67
+ if (id === RED_MANZU_ID)
68
+ return "0m";
69
+ if (id === RED_PINZU_ID)
70
+ return "0p";
71
+ if (id === RED_SOUZU_ID)
72
+ return "0s";
73
+ if (id >= JIHAI_OFFSET) {
74
+ return JIHAI_NAMES[id - JIHAI_OFFSET];
75
+ }
76
+ return `${getNum(id)}${getSuit(id)}`;
77
+ }
78
+ /**
79
+ * ドラ表示牌から実際のドラ牌を返す
80
+ * 数牌: 1→2→...→9→1 (循環)
81
+ * 風牌: 東→南→西→北→東 (循環)
82
+ * 三元牌: 白→發→中→白 (循環)
83
+ */
84
+ export function nextTile(id) {
85
+ const normalId = toNormalTile(id);
86
+ if (normalId < JIHAI_OFFSET) {
87
+ // 数牌: 同色内で循環 (1→2→...→9→1)
88
+ const suitOffset = Math.floor(normalId / SUIT_NUM) * SUIT_NUM;
89
+ const num = normalId - suitOffset; // 0-8
90
+ return suitOffset + (num + 1) % SUIT_NUM;
91
+ }
92
+ const jihaiIndex = normalId - JIHAI_OFFSET; // 0-6
93
+ if (jihaiIndex < 4) {
94
+ // 風牌: 東(0)→南(1)→西(2)→北(3)→東(0)
95
+ return JIHAI_OFFSET + (jihaiIndex + 1) % 4;
96
+ }
97
+ // 三元牌: 白(4)→發(5)→中(6)→白(4)
98
+ return JIHAI_OFFSET + 4 + (jihaiIndex - 4 + 1) % 3;
99
+ }
100
+ /**
101
+ * 牌表記文字列をパースして牌ID配列を返す
102
+ * 例: "123m456p" → [0, 1, 2, 12, 13, 14]
103
+ * "東南白" → [27, 28, 32]
104
+ */
105
+ export function parseTiles(notation) {
106
+ const tiles = [];
107
+ const buffer = []; // 数字バッファ
108
+ for (let i = 0; i < notation.length; i++) {
109
+ const ch = notation[i];
110
+ // 数字 (0-9)
111
+ if (ch >= "0" && ch <= "9") {
112
+ buffer.push(parseInt(ch));
113
+ continue;
114
+ }
115
+ // 色指定文字
116
+ const suitIdx = SUIT_CHARS.indexOf(ch);
117
+ if (suitIdx >= 0) {
118
+ const offset = suitIdx === 3 ? JIHAI_OFFSET : suitIdx * SUIT_NUM;
119
+ for (const num of buffer) {
120
+ if (num === 0 && suitIdx < 3) {
121
+ // 赤ドラ: 0m → 34, 0p → 35, 0s → 36
122
+ tiles.push(RED_MANZU_ID + suitIdx);
123
+ }
124
+ else {
125
+ tiles.push(offset + num - 1);
126
+ }
127
+ }
128
+ buffer.length = 0;
129
+ continue;
130
+ }
131
+ // 漢字の字牌
132
+ const jihaiIdx = JIHAI_NAMES.indexOf(ch);
133
+ if (jihaiIdx >= 0) {
134
+ tiles.push(JIHAI_OFFSET + jihaiIdx);
135
+ continue;
136
+ }
137
+ }
138
+ return tiles;
139
+ }
140
+ //# sourceMappingURL=tile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tile.js","sourceRoot":"","sources":["../../src/core/tile.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,GACb,MAAM,gBAAgB,CAAC;AAQxB,MAAM,UAAU,GAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpD,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAExD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAS,UAAU,CAAC,CAAC;AAE9C;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY,IAAI,EAAE,KAAK,YAAY,CAAC;AAC3E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,CAAC,CAAC,CAAE,KAAK;IACzC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,EAAE,CAAC,CAAC,KAAK;IACzC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,EAAE,CAAC,CAAC,KAAK;IACzC,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE,GAAG,QAAQ;QAAE,OAAO,CAAC,CAAC;IAC5B,IAAI,EAAE,GAAG,QAAQ,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,EAAE,GAAG,QAAQ,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,OAAO,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM,CAAC,EAAU;IAC/B,IAAI,EAAE,IAAI,YAAY,IAAI,EAAE,IAAI,YAAY;QAAE,OAAO,CAAC,CAAC;IACvD,IAAI,EAAE,IAAI,YAAY;QAAE,OAAO,EAAE,GAAG,YAAY,GAAG,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,EAAE,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,EAAE,IAAI,YAAY,EAAE,CAAC;QACvB,OAAO,WAAW,CAAC,EAAE,GAAG,YAAY,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;QAC5B,2BAA2B;QAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,QAAQ,CAAC;QAC9D,MAAM,GAAG,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC,MAAM;QACzC,OAAO,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAC3C,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC,CAAC,MAAM;IAClD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,+BAA+B;QAC/B,OAAO,YAAY,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IACD,2BAA2B;IAC3B,OAAO,YAAY,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAa,EAAE,CAAC,CAAC,SAAS;IAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAEvB,WAAW;QACX,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,QAAQ;QACR,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,EAAc,CAAC,CAAC;QACnD,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC;YACjE,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;gBACzB,IAAI,GAAG,KAAK,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAC7B,iCAAiC;oBACjC,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC;gBACrC,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;YACD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QAED,QAAQ;QACR,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { HandArray } from "../core/hand.js";
2
+ import type { EVOptions, EVResult } from "./types.js";
3
+ /**
4
+ * 13枚手牌の期待値を計算する (ツモ待ち状態)
5
+ */
6
+ export declare function calculateEV13(hand: HandArray, options: EVOptions): number;
7
+ /**
8
+ * 14枚手牌の各打牌候補の期待値を計算する
9
+ */
10
+ export declare function calculateEV14(hand: HandArray, options: EVOptions): EVResult[];
@@ -0,0 +1,257 @@
1
+ import { NUM_TILE_TYPES_WITH_RED } from "../core/constants.js";
2
+ import { handToNotation } from "../core/hand.js";
3
+ import { calculateShanten, calculateShantenRegular, calculateShantenChiitoi } from "../shanten/shanten-calculator.js";
4
+ import { calculateScore } from "../scoring/score-calculator.js";
5
+ /**
6
+ * 13枚手牌の期待値を計算する (ツモ待ち状態)
7
+ */
8
+ export function calculateEV13(hand, options) {
9
+ const maxTurns = options.maxTurns ?? 18;
10
+ const currentTurn = options.currentTurn ?? 1;
11
+ const maxShanten = options.maxShanten ?? 2;
12
+ const turnsLeft = maxTurns - currentTurn + 1;
13
+ const memoReg = new Map();
14
+ const memoChi = new Map();
15
+ // 向聴戻しを評価するため、現在のシャンテン数+1まで探索を許可
16
+ const overallShanten = calculateShanten(hand);
17
+ const effectiveMaxShanten = Math.max(maxShanten, overallShanten + 1);
18
+ return calcEV13Unified(hand, options.remaining, turnsLeft, options.context, effectiveMaxShanten, memoReg, memoChi);
19
+ }
20
+ /**
21
+ * 14枚手牌の各打牌候補の期待値を計算する
22
+ */
23
+ export function calculateEV14(hand, options) {
24
+ const maxTurns = options.maxTurns ?? 18;
25
+ const currentTurn = options.currentTurn ?? 1;
26
+ const maxShanten = options.maxShanten ?? 2;
27
+ const turnsLeft = maxTurns - currentTurn + 1;
28
+ const memoReg = new Map();
29
+ const memoChi = new Map();
30
+ const shanten14Reg = calculateShantenRegular(hand);
31
+ const shanten14Chi = calculateShantenChiitoi(hand);
32
+ const overallShanten14 = Math.min(shanten14Reg, shanten14Chi);
33
+ // 向聴戻しを評価するため、現在のシャンテン数+1まで探索を許可
34
+ const effectiveMaxShanten = Math.max(maxShanten, overallShanten14 + 1);
35
+ const results = [];
36
+ for (let d = 0; d < NUM_TILE_TYPES_WITH_RED; d++) {
37
+ if (hand[d] <= 0)
38
+ continue;
39
+ hand[d]--;
40
+ const shantenAfter = calculateShanten(hand);
41
+ const shantenRegAfter = calculateShantenRegular(hand);
42
+ const shantenChiitoiAfter = calculateShantenChiitoi(hand);
43
+ // ルートごとに向聴維持 or 1段階戻しかつeffectiveMaxShanten以内かを判定
44
+ // 両ルートとも条件を満たさなければスキップ
45
+ const regValid = shantenRegAfter <= shanten14Reg + 1 && shantenRegAfter <= effectiveMaxShanten;
46
+ const chiValid = shantenChiitoiAfter <= shanten14Chi + 1 && shantenChiitoiAfter <= effectiveMaxShanten;
47
+ if (!regValid && !chiValid) {
48
+ hand[d]++;
49
+ continue;
50
+ }
51
+ // 受入枚数計算(通常手・七対子それぞれのルートで改善する牌を検出)
52
+ let acceptCount = 0;
53
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
54
+ if (options.remaining[t] <= 0)
55
+ continue;
56
+ if (t === d && options.remaining[t] <= 0)
57
+ continue;
58
+ hand[t]++;
59
+ const improves = (regValid && calculateShantenRegular(hand) < shantenRegAfter) ||
60
+ (chiValid && calculateShantenChiitoi(hand) < shantenChiitoiAfter);
61
+ if (improves) {
62
+ acceptCount += options.remaining[t];
63
+ }
64
+ hand[t]--;
65
+ }
66
+ const ev = calcEV13Unified(hand, options.remaining, turnsLeft, options.context, effectiveMaxShanten, memoReg, memoChi);
67
+ results.push({ discard: d, shantenAfter, acceptCount, ev });
68
+ hand[d]++;
69
+ }
70
+ // EV降順ソート
71
+ results.sort((a, b) => b.ev - a.ev);
72
+ return results;
73
+ }
74
+ /**
75
+ * 13枚手牌の期待値を統合型で計算する
76
+ * 両ルートの有効牌を統合収集し、不ツモの連鎖をループ展開。
77
+ * ツモ後の再帰は独立ルート(calcEV13Route)にフォールバック。
78
+ * テンパイ手は独立ルートに直接フォールバック(リーチ前提、手変わりなし)。
79
+ */
80
+ function calcEV13Unified(hand, remaining, turnsLeft, context, maxShanten, memoReg, memoChi) {
81
+ if (turnsLeft <= 0)
82
+ return 0;
83
+ const shantenReg = calculateShantenRegular(hand);
84
+ const shantenChi = calculateShantenChiitoi(hand);
85
+ const overallShanten = Math.min(shantenReg, shantenChi);
86
+ if (overallShanten > maxShanten)
87
+ return 0;
88
+ if (overallShanten < 0)
89
+ return 0;
90
+ // テンパイ手は独立ルートにフォールバック(リーチ前提、手変わりなし)
91
+ if (overallShanten === 0) {
92
+ return Math.max(calcEV13Route(hand, remaining, turnsLeft, context, maxShanten, memoReg, "regular"), calcEV13Route(hand, remaining, turnsLeft, context, maxShanten, memoChi, "chiitoi"));
93
+ }
94
+ // 有効牌を両ルートから統合収集
95
+ const trackReg = shantenReg <= maxShanten;
96
+ const trackChi = shantenChi <= maxShanten;
97
+ const acceptTiles = [];
98
+ let acceptCount = 0;
99
+ let totalRemaining = 0;
100
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
101
+ if (remaining[t] <= 0)
102
+ continue;
103
+ totalRemaining += remaining[t];
104
+ }
105
+ if (totalRemaining <= 0)
106
+ return 0;
107
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
108
+ if (remaining[t] <= 0)
109
+ continue;
110
+ hand[t]++;
111
+ const improvesReg = trackReg && calculateShantenRegular(hand) < shantenReg;
112
+ const improvesChi = trackChi && calculateShantenChiitoi(hand) < shantenChi;
113
+ if (improvesReg || improvesChi) {
114
+ acceptTiles.push({ tile: t, count: remaining[t], improvesReg, improvesChi });
115
+ acceptCount += remaining[t];
116
+ }
117
+ hand[t]--;
118
+ }
119
+ if (acceptCount === 0)
120
+ return 0;
121
+ const pAccept = acceptCount / totalRemaining;
122
+ // 不ツモの連鎖をループで展開
123
+ // EV = Σ_{k=0}^{T-1} p × (1-p)^k × evAccept(T-k)
124
+ let result = 0;
125
+ let pNotDrawPow = 1; // (1-p)^k
126
+ for (let k = 0; k < turnsLeft; k++) {
127
+ const remainingTurns = turnsLeft - k - 1;
128
+ // 有効牌ツモ時のEV (加重平均)
129
+ let evAccept = 0;
130
+ for (const { tile: t, count, improvesReg, improvesChi } of acceptTiles) {
131
+ hand[t]++;
132
+ remaining[t]--;
133
+ // 改善したルートで最善打牌を選択し、独立ルートで再帰
134
+ let bestEv = 0;
135
+ if (improvesReg) {
136
+ const s14Reg = calculateShantenRegular(hand);
137
+ for (let d = 0; d < NUM_TILE_TYPES_WITH_RED; d++) {
138
+ if (hand[d] <= 0)
139
+ continue;
140
+ hand[d]--;
141
+ if (calculateShantenRegular(hand) <= s14Reg) {
142
+ const ev = calcEV13Route(hand, remaining, remainingTurns, context, maxShanten, memoReg, "regular");
143
+ if (ev > bestEv)
144
+ bestEv = ev;
145
+ }
146
+ hand[d]++;
147
+ }
148
+ }
149
+ if (improvesChi) {
150
+ const s14Chi = calculateShantenChiitoi(hand);
151
+ for (let d = 0; d < NUM_TILE_TYPES_WITH_RED; d++) {
152
+ if (hand[d] <= 0)
153
+ continue;
154
+ hand[d]--;
155
+ if (calculateShantenChiitoi(hand) <= s14Chi) {
156
+ const ev = calcEV13Route(hand, remaining, remainingTurns, context, maxShanten, memoChi, "chiitoi");
157
+ if (ev > bestEv)
158
+ bestEv = ev;
159
+ }
160
+ hand[d]++;
161
+ }
162
+ }
163
+ evAccept += (count / acceptCount) * bestEv;
164
+ remaining[t]++;
165
+ hand[t]--;
166
+ }
167
+ result += pAccept * pNotDrawPow * evAccept;
168
+ pNotDrawPow *= (1 - pAccept);
169
+ }
170
+ return result;
171
+ }
172
+ /**
173
+ * 13枚手牌の期待値を特定ルート(通常手or七対子)で再帰計算する
174
+ */
175
+ function calcEV13Route(hand, remaining, turnsLeft, context, maxShanten, memo, route) {
176
+ if (turnsLeft <= 0)
177
+ return 0;
178
+ const shantenFn = route === "regular" ? calculateShantenRegular : calculateShantenChiitoi;
179
+ const routeShanten = shantenFn(hand);
180
+ if (routeShanten > maxShanten)
181
+ return 0;
182
+ if (routeShanten < 0)
183
+ return 0;
184
+ const key = handToNotation(hand) + ":" + turnsLeft;
185
+ const cached = memo.get(key);
186
+ if (cached !== undefined)
187
+ return cached;
188
+ // 有効牌とその枚数を収集
189
+ const acceptTiles = [];
190
+ let acceptCount = 0;
191
+ let totalRemaining = 0;
192
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
193
+ if (remaining[t] <= 0)
194
+ continue;
195
+ totalRemaining += remaining[t];
196
+ }
197
+ if (totalRemaining <= 0) {
198
+ memo.set(key, 0);
199
+ return 0;
200
+ }
201
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
202
+ if (remaining[t] <= 0)
203
+ continue;
204
+ hand[t]++;
205
+ if (shantenFn(hand) < routeShanten) {
206
+ acceptTiles.push({ tile: t, count: remaining[t] });
207
+ acceptCount += remaining[t];
208
+ }
209
+ hand[t]--;
210
+ }
211
+ if (acceptCount === 0) {
212
+ memo.set(key, 0);
213
+ return 0;
214
+ }
215
+ const pAccept = acceptCount / totalRemaining;
216
+ // 有効牌ツモ時のEV (加重平均)
217
+ let weightedEv = 0;
218
+ for (const { tile: t, count } of acceptTiles) {
219
+ hand[t]++;
220
+ remaining[t]--;
221
+ // overall シャンテンで和了判定
222
+ const overallShanten = calculateShanten(hand);
223
+ if (routeShanten === 0 && overallShanten === -1) {
224
+ // 和了 → スコア計算
225
+ const riichiContext = { ...context, riichi: true };
226
+ const score = calculateScore(hand, t, riichiContext);
227
+ if (score) {
228
+ weightedEv += (count / acceptCount) * score.totalPoints;
229
+ }
230
+ }
231
+ else {
232
+ // シャンテン改善 → 最善打牌を選択
233
+ let bestEv = 0;
234
+ const shanten14 = shantenFn(hand);
235
+ for (let d = 0; d < NUM_TILE_TYPES_WITH_RED; d++) {
236
+ if (hand[d] <= 0)
237
+ continue;
238
+ hand[d]--;
239
+ const sAfter = shantenFn(hand);
240
+ // 向聴戻しの打牌はスキップ(枝刈り)
241
+ if (sAfter <= shanten14) {
242
+ const ev = calcEV13Route(hand, remaining, turnsLeft - 1, context, maxShanten, memo, route);
243
+ if (ev > bestEv)
244
+ bestEv = ev;
245
+ }
246
+ hand[d]++;
247
+ }
248
+ weightedEv += (count / acceptCount) * bestEv;
249
+ }
250
+ remaining[t]++;
251
+ hand[t]--;
252
+ }
253
+ const result = pAccept * weightedEv + (1 - pAccept) * calcEV13Route(hand, remaining, turnsLeft - 1, context, maxShanten, memo, route);
254
+ memo.set(key, result);
255
+ return result;
256
+ }
257
+ //# sourceMappingURL=ev-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ev-calculator.js","sourceRoot":"","sources":["../../src/ev/ev-calculator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { TileId } from "../core/tile.js";
2
+ import type { HandArray } from "../core/hand.js";
3
+ import type { GameContext } from "../scoring/types.js";
4
+ export interface EVOptions {
5
+ context: GameContext;
6
+ remaining: HandArray;
7
+ maxTurns?: number;
8
+ currentTurn?: number;
9
+ maxShanten?: number;
10
+ }
11
+ export interface EVResult {
12
+ discard: TileId;
13
+ shantenAfter: number;
14
+ acceptCount: number;
15
+ ev: number;
16
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/ev/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ export { NUM_TILE_TYPES, NUM_TILE_TYPES_WITH_RED, SUIT_NUM, HONOR_NUM, NUM_SUITS, MANZU_OFFSET, PINZU_OFFSET, SOUZU_OFFSET, JIHAI_OFFSET, YAOCHU_IDS, RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID } from "./core/constants.js";
2
+ export { NORMAL_FIVE_IDS, RED_IDS, maxTilesForId, buildDefaultRemaining } from "./core/remaining.js";
3
+ export { tileToString, parseTiles, getSuit, getSuitIndex, getNum, isYaochu, isRedTile, toNormalTile, nextTile } from "./core/tile.js";
4
+ export type { TileId, TileSuit } from "./core/tile.js";
5
+ export { createHand, handFromTiles, handFromNotation, handToString, handToNotation, countTiles, splitToSuits } from "./core/hand.js";
6
+ export type { HandArray } from "./core/hand.js";
7
+ export { calculateShanten, calculateShantenRegular, calculateShantenChiitoi, calculateShantenKokushi } from "./shanten/shanten-calculator.js";
8
+ export { calculateUkeire13, calculateUkeire14, mergeRedAcceptTiles } from "./shanten/ukeire.js";
9
+ export type { UkeireOptions, UkeireResult } from "./types.js";
10
+ export { setSuitTable, setHonorTable, isInitialized } from "./shanten/lookup-table.js";
11
+ export { initFromUrl } from "./shanten/init.js";
12
+ export { decomposeHand } from "./scoring/decomposer.js";
13
+ export { determineWaitType, determineWaitTypes } from "./scoring/wait-type.js";
14
+ export { evaluateYaku } from "./scoring/yaku.js";
15
+ export { calculateFu } from "./scoring/fu.js";
16
+ export { calculateScore } from "./scoring/score-calculator.js";
17
+ export type { MentsuType, Mentsu, RegularDecomposition, ChiitoiDecomposition, HandDecomposition, WaitType, YakuResult, GameContext, ScoreResult } from "./scoring/types.js";
18
+ export { calculateEV13, calculateEV14 } from "./ev/ev-calculator.js";
19
+ export type { EVOptions, EVResult } from "./ev/types.js";
20
+ export { init } from "./init.js";
21
+ export { simulateDiscards } from "./mc/simulator.js";
22
+ export type { MCOptions, MCResult } from "./mc/simulator.js";
23
+ export { TENPAI_PROB_BY_TURN, AVG_DEAL_IN_LOSS, tileDanger, getTenpaiProb, getAvgDealInLoss } from "./models/danger.js";
24
+ export { evaluateDiscards } from "./models/evaluation.js";
25
+ export type { EvalOptions, EvalResult } from "./models/evaluation.js";
26
+ export { WorkerClient } from "./worker-client.js";
27
+ export type { WorkerClientOptions } from "./worker-client.js";
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ // ライブラリエントリポイント
2
+ export { NUM_TILE_TYPES, NUM_TILE_TYPES_WITH_RED, SUIT_NUM, HONOR_NUM, NUM_SUITS, MANZU_OFFSET, PINZU_OFFSET, SOUZU_OFFSET, JIHAI_OFFSET, YAOCHU_IDS, RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID } from "./core/constants.js";
3
+ export { NORMAL_FIVE_IDS, RED_IDS, maxTilesForId, buildDefaultRemaining } from "./core/remaining.js";
4
+ export { tileToString, parseTiles, getSuit, getSuitIndex, getNum, isYaochu, isRedTile, toNormalTile, nextTile } from "./core/tile.js";
5
+ export { createHand, handFromTiles, handFromNotation, handToString, handToNotation, countTiles, splitToSuits } from "./core/hand.js";
6
+ export { calculateShanten, calculateShantenRegular, calculateShantenChiitoi, calculateShantenKokushi } from "./shanten/shanten-calculator.js";
7
+ export { calculateUkeire13, calculateUkeire14, mergeRedAcceptTiles } from "./shanten/ukeire.js";
8
+ export { setSuitTable, setHonorTable, isInitialized } from "./shanten/lookup-table.js";
9
+ export { initFromUrl } from "./shanten/init.js";
10
+ // Scoring
11
+ export { decomposeHand } from "./scoring/decomposer.js";
12
+ export { determineWaitType, determineWaitTypes } from "./scoring/wait-type.js";
13
+ export { evaluateYaku } from "./scoring/yaku.js";
14
+ export { calculateFu } from "./scoring/fu.js";
15
+ export { calculateScore } from "./scoring/score-calculator.js";
16
+ // EV
17
+ export { calculateEV13, calculateEV14 } from "./ev/ev-calculator.js";
18
+ // Init (統合初期化)
19
+ export { init } from "./init.js";
20
+ // MC Simulation
21
+ export { simulateDiscards } from "./mc/simulator.js";
22
+ // Models
23
+ export { TENPAI_PROB_BY_TURN, AVG_DEAL_IN_LOSS, tileDanger, getTenpaiProb, getAvgDealInLoss } from "./models/danger.js";
24
+ export { evaluateDiscards } from "./models/evaluation.js";
25
+ // Worker Client
26
+ export { WorkerClient } from "./worker-client.js";
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC5N,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEtI,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAErI,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC9I,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEhG,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D,KAAK;AACL,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGrE,eAAe;AACf,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,gBAAgB;AAChB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,SAAS;AACT,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG1D,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { initFromUrl } from "./shanten/init.js";
2
+ /**
3
+ * 統合初期化関数
4
+ * シャンテンテーブル(ブラウザ時はURL指定)を初期化する
5
+ * テーブル初期化後、WASM MCも初期化する
6
+ */
7
+ export declare function init(options?: {
8
+ suitTableUrl?: string;
9
+ honorTableUrl?: string;
10
+ wasmUrl?: string;
11
+ }): Promise<void>;
package/dist/init.js ADDED
@@ -0,0 +1,24 @@
1
+ import { initFromUrl } from "./shanten/init.js";
2
+ import { isInitialized, getSuitTable, getHonorTable } from "./shanten/lookup-table.js";
3
+ import { initMCWasm } from "./mc/wasm-bridge.js";
4
+ export { initFromUrl } from "./shanten/init.js";
5
+ /**
6
+ * 統合初期化関数
7
+ * シャンテンテーブル(ブラウザ時はURL指定)を初期化する
8
+ * テーブル初期化後、WASM MCも初期化する
9
+ */
10
+ export async function init(options) {
11
+ if (options?.suitTableUrl && options?.honorTableUrl) {
12
+ await initFromUrl(options.suitTableUrl, options.honorTableUrl);
13
+ }
14
+ // テーブルが初期化されていればWASM MCも初期化
15
+ if (isInitialized()) {
16
+ try {
17
+ await initMCWasm(getSuitTable(), getHonorTable(), options?.wasmUrl);
18
+ }
19
+ catch {
20
+ // WASM MC初期化失敗はサイレントに無視
21
+ }
22
+ }
23
+ }
24
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAI1B;IACC,IAAI,OAAO,EAAE,YAAY,IAAI,OAAO,EAAE,aAAa,EAAE,CAAC;QACpD,MAAM,WAAW,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACjE,CAAC;IAED,4BAA4B;IAC5B,IAAI,aAAa,EAAE,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,UAAU,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * MCシミュレーション ラッパー(WASM版専用)
3
+ */
4
+ import type { HandArray } from "../core/hand.js";
5
+ import type { GameContext } from "../scoring/types.js";
6
+ import { type MCResult } from "./wasm-bridge.js";
7
+ export type { MCResult } from "./wasm-bridge.js";
8
+ export interface MCOptions {
9
+ context: GameContext;
10
+ remaining: HandArray;
11
+ turnsLeft: number;
12
+ numSims?: number;
13
+ useRiichi?: boolean;
14
+ }
15
+ /**
16
+ * 14枚手牌の各打牌候補をMCシミュレーションで評価(WASM版)
17
+ */
18
+ export declare function simulateDiscards(hand: HandArray, options: MCOptions): MCResult[];
@@ -0,0 +1,11 @@
1
+ import { wasmSimulateDiscards } from "./wasm-bridge.js";
2
+ /**
3
+ * 14枚手牌の各打牌候補をMCシミュレーションで評価(WASM版)
4
+ */
5
+ export function simulateDiscards(hand, options) {
6
+ const numSims = options.numSims ?? 3000;
7
+ const useRiichi = options.useRiichi ?? true;
8
+ const uraDoraCount = options.context.uraDoraCount ?? 1;
9
+ return wasmSimulateDiscards(hand, options.remaining, options.turnsLeft, numSims, useRiichi, options.context.seatWind, options.context.roundWind, options.context.doraIndicators, options.context.useRedDora, options.context.riichi, uraDoraCount);
10
+ }
11
+ //# sourceMappingURL=simulator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulator.js","sourceRoot":"","sources":["../../src/mc/simulator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAiB,MAAM,kBAAkB,CAAC;AAYvE;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAe,EACf,OAAkB;IAElB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC;IAEvD,OAAO,oBAAoB,CACzB,IAAI,EACJ,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,SAAS,EACjB,OAAO,EACP,SAAS,EACT,OAAO,CAAC,OAAO,CAAC,QAAQ,EACxB,OAAO,CAAC,OAAO,CAAC,SAAS,EACzB,OAAO,CAAC,OAAO,CAAC,cAAc,EAC9B,OAAO,CAAC,OAAO,CAAC,UAAU,EAC1B,OAAO,CAAC,OAAO,CAAC,MAAM,EACtB,YAAY,CACb,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * WASM MCブリッジ
3
+ * Rust WASM モジュールの初期化を担当
4
+ * スコア計算はRust内で完結(JSコールバック不要)
5
+ */
6
+ import type { TileId } from "../core/tile.js";
7
+ import type { HandArray } from "../core/hand.js";
8
+ export interface MCResult {
9
+ discard: TileId;
10
+ shantenAfter: number;
11
+ acceptCount: number;
12
+ ev: number;
13
+ winRate: number;
14
+ avgPoints: number;
15
+ }
16
+ /**
17
+ * WASM MC モジュールを初期化する
18
+ * @param suitTable 数牌テーブル
19
+ * @param honorTable 字牌テーブル
20
+ * @param wasmUrl ブラウザ用: WASMバイナリのURL。指定時はfetchで非同期初期化。省略時はNode.jsファイル読み込み。
21
+ */
22
+ export declare function initMCWasm(suitTable: Int8Array, honorTable: Int8Array, wasmUrl?: string): Promise<void>;
23
+ /**
24
+ * WASM MCが利用可能かどうか
25
+ */
26
+ export declare function isWasmMCAvailable(): boolean;
27
+ /**
28
+ * WASM版simulateDiscardsを呼び出す
29
+ */
30
+ export declare function wasmSimulateDiscards(hand: HandArray, remaining: HandArray, turnsLeft: number, numSims: number, useRiichi: boolean, seatWind: number, roundWind: number, doraIndicators: number[], useRedDora: boolean, initialRiichi: boolean, uraDoraCount: number): MCResult[];