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,228 @@
1
+ import { SUIT_NUM, HONOR_NUM } from "../core/constants.js";
2
+ /**
3
+ * ルックアップテーブル
4
+ *
5
+ * 各色の牌パターンごとに、面子数 m (0-4) に対して2つの値を保持:
6
+ * table[index * 10 + m * 2] = 面子m個抽出後の最大部分ブロック数(雀頭なし)
7
+ * table[index * 10 + m * 2 + 1] = 面子m個抽出後の最大部分ブロック数(雀頭あり, 不可なら-1)
8
+ */
9
+ let suitTable = null;
10
+ let honorTable = null;
11
+ /**
12
+ * 数牌用テーブルを取得する(遅延生成)
13
+ */
14
+ export function getSuitTable() {
15
+ if (!suitTable)
16
+ suitTable = generateTable(SUIT_NUM, true);
17
+ return suitTable;
18
+ }
19
+ /**
20
+ * 字牌用テーブルを取得する(遅延生成)
21
+ */
22
+ export function getHonorTable() {
23
+ if (!honorTable)
24
+ honorTable = generateTable(HONOR_NUM, false);
25
+ return honorTable;
26
+ }
27
+ /**
28
+ * 外部から数牌テーブルを注入する(Webアプリ向け)
29
+ */
30
+ export function setSuitTable(table) {
31
+ suitTable = table;
32
+ }
33
+ /**
34
+ * 外部から字牌テーブルを注入する(Webアプリ向け)
35
+ */
36
+ export function setHonorTable(table) {
37
+ honorTable = table;
38
+ }
39
+ /**
40
+ * テーブルが初期化済みかどうかを返す
41
+ */
42
+ export function isInitialized() {
43
+ return suitTable !== null && honorTable !== null;
44
+ }
45
+ /**
46
+ * テーブルをリセットする(テスト用)
47
+ * @internal
48
+ */
49
+ export function _resetTables() {
50
+ suitTable = null;
51
+ honorTable = null;
52
+ }
53
+ /**
54
+ * 牌パターンからテーブルインデックスを計算する
55
+ */
56
+ export function toIndex(suitHand) {
57
+ let idx = 0;
58
+ for (let i = 0; i < suitHand.length; i++) {
59
+ idx = idx * 5 + suitHand[i];
60
+ }
61
+ return idx;
62
+ }
63
+ /**
64
+ * テーブルから値を参照する
65
+ * @param table テーブル
66
+ * @param index パターンインデックス
67
+ * @param mentsu 面子数 (0-4)
68
+ * @param withJantou 雀頭ありかどうか
69
+ */
70
+ export function lookupTable(table, index, mentsu, withJantou) {
71
+ return table[index * 10 + mentsu * 2 + (withJantou ? 1 : 0)];
72
+ }
73
+ /**
74
+ * テーブルを生成する
75
+ */
76
+ function generateTable(suitSize, allowSequence) {
77
+ const tableSize = Math.pow(5, suitSize);
78
+ const table = new Int8Array(tableSize * 10);
79
+ // -1 で初期化(面子抽出不可能を示す)
80
+ table.fill(-1);
81
+ // 全有効パターンを列挙して計算
82
+ const hand = new Array(suitSize).fill(0);
83
+ function enumerate(pos, total) {
84
+ if (pos === suitSize) {
85
+ const index = toIndex(hand);
86
+ for (let m = 0; m <= 4; m++) {
87
+ const [noJ, withJ] = solve(hand, m, suitSize, allowSequence);
88
+ table[index * 10 + m * 2] = noJ;
89
+ table[index * 10 + m * 2 + 1] = withJ;
90
+ }
91
+ return;
92
+ }
93
+ for (let v = 0; v <= 4; v++) {
94
+ if (total + v > 14)
95
+ break;
96
+ hand[pos] = v;
97
+ enumerate(pos + 1, total + v);
98
+ }
99
+ hand[pos] = 0;
100
+ }
101
+ enumerate(0, 0);
102
+ return table;
103
+ }
104
+ /**
105
+ * 指定面子数を抽出した場合の最大部分ブロック数を求める
106
+ * @returns [雀頭なし最大, 雀頭あり最大(-1=不可)]
107
+ */
108
+ function solve(hand, targetMentsu, suitSize, allowSequence) {
109
+ let bestNoJ = -1;
110
+ let bestWithJ = -1;
111
+ let found = false;
112
+ const work = hand.slice();
113
+ function extractMentsu(pos, extracted) {
114
+ if (extracted === targetMentsu) {
115
+ found = true;
116
+ // 残り牌から部分ブロックをカウント
117
+ const noJ = countPartials(work, suitSize, false, allowSequence);
118
+ const withJ = countPartials(work, suitSize, true, allowSequence);
119
+ if (noJ > bestNoJ)
120
+ bestNoJ = noJ;
121
+ if (withJ > bestWithJ)
122
+ bestWithJ = withJ;
123
+ return;
124
+ }
125
+ // 最小の非ゼロ位置を探す (pos 以降)
126
+ let start = pos;
127
+ while (start < suitSize && work[start] === 0)
128
+ start++;
129
+ if (start >= suitSize)
130
+ return; // 抽出不可
131
+ // 刻子
132
+ if (work[start] >= 3) {
133
+ work[start] -= 3;
134
+ extractMentsu(start, extracted + 1);
135
+ work[start] += 3;
136
+ }
137
+ // 順子
138
+ if (allowSequence && start + 2 < suitSize && work[start] >= 1 && work[start + 1] >= 1 && work[start + 2] >= 1) {
139
+ work[start]--;
140
+ work[start + 1]--;
141
+ work[start + 2]--;
142
+ extractMentsu(start, extracted + 1);
143
+ work[start]++;
144
+ work[start + 1]++;
145
+ work[start + 2]++;
146
+ }
147
+ // この位置を面子に使わない(スキップ)
148
+ // 牌はそのまま残す(partial blockカウント時に使用される)
149
+ extractMentsu(start + 1, extracted);
150
+ }
151
+ extractMentsu(0, 0);
152
+ return [bestNoJ, bestWithJ];
153
+ }
154
+ /**
155
+ * 残り牌から部分ブロック(塔子・対子)の最大数をバックトラックで求める
156
+ * @param tryJantou trueなら対子1つを雀頭として使用するパターンも探索
157
+ * @returns 最大部分ブロック数 (tryJantou時、雀頭不可なら-1)
158
+ */
159
+ function countPartials(hand, suitSize, tryJantou, allowSequence) {
160
+ const work = hand.slice();
161
+ if (!tryJantou) {
162
+ return countPartialsBacktrack(work, 0, suitSize, allowSequence);
163
+ }
164
+ // 雀頭ありの場合: 各対子を雀頭に割り当てて探索
165
+ let best = -1;
166
+ for (let i = 0; i < suitSize; i++) {
167
+ if (work[i] >= 2) {
168
+ work[i] -= 2;
169
+ const partial = countPartialsBacktrack(work, 0, suitSize, allowSequence);
170
+ if (partial > best)
171
+ best = partial;
172
+ work[i] += 2;
173
+ }
174
+ }
175
+ return best;
176
+ }
177
+ /**
178
+ * バックトラックで部分ブロックの最大数を求める
179
+ */
180
+ function countPartialsBacktrack(work, pos, suitSize, allowSequence) {
181
+ // 最小の非ゼロ位置を探す
182
+ while (pos < suitSize && work[pos] === 0)
183
+ pos++;
184
+ if (pos >= suitSize)
185
+ return 0;
186
+ let best = 0;
187
+ // 対子
188
+ if (work[pos] >= 2) {
189
+ work[pos] -= 2;
190
+ const v = 1 + countPartialsBacktrack(work, pos, suitSize, allowSequence);
191
+ if (v > best)
192
+ best = v;
193
+ work[pos] += 2;
194
+ }
195
+ if (allowSequence) {
196
+ // 塔子 (隣接)
197
+ if (pos + 1 < suitSize && work[pos] >= 1 && work[pos + 1] >= 1) {
198
+ work[pos]--;
199
+ work[pos + 1]--;
200
+ const v = 1 + countPartialsBacktrack(work, pos, suitSize, allowSequence);
201
+ if (v > best)
202
+ best = v;
203
+ work[pos]++;
204
+ work[pos + 1]++;
205
+ }
206
+ // 嵌張 (1つ飛び)
207
+ if (pos + 2 < suitSize && work[pos] >= 1 && work[pos + 2] >= 1) {
208
+ work[pos]--;
209
+ work[pos + 2]--;
210
+ const v = 1 + countPartialsBacktrack(work, pos, suitSize, allowSequence);
211
+ if (v > best)
212
+ best = v;
213
+ work[pos]++;
214
+ work[pos + 2]++;
215
+ }
216
+ }
217
+ // スキップ(この位置の牌を部分ブロックに使わない)
218
+ {
219
+ const saved = work[pos];
220
+ work[pos] = 0;
221
+ const v = countPartialsBacktrack(work, pos + 1, suitSize, allowSequence);
222
+ if (v > best)
223
+ best = v;
224
+ work[pos] = saved;
225
+ }
226
+ return best;
227
+ }
228
+ //# sourceMappingURL=lookup-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-table.js","sourceRoot":"","sources":["../../src/shanten/lookup-table.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { HandArray } from "../core/hand.js";
2
+ /**
3
+ * 通常手(4面子1雀頭)のシャンテン数を計算する
4
+ */
5
+ export declare function calculateShantenRegular(hand: HandArray): number;
6
+ /**
7
+ * 七対子のシャンテン数を計算する
8
+ * 赤ドラは通常の5と同種として扱う
9
+ */
10
+ export declare function calculateShantenChiitoi(hand: HandArray): number;
11
+ /**
12
+ * 国士無双のシャンテン数を計算する
13
+ */
14
+ export declare function calculateShantenKokushi(hand: HandArray): number;
15
+ /**
16
+ * シャンテン数を計算する(通常手・七対子・国士無双の最小値)
17
+ */
18
+ export declare function calculateShanten(hand: HandArray): number;
@@ -0,0 +1,101 @@
1
+ import { NUM_TILE_TYPES, YAOCHU_IDS, RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID } from "../core/constants.js";
2
+ import { splitToSuits } from "../core/hand.js";
3
+ import { getSuitTable, getHonorTable, toIndex, lookupTable } from "./lookup-table.js";
4
+ /**
5
+ * 通常手(4面子1雀頭)のシャンテン数を計算する
6
+ */
7
+ export function calculateShantenRegular(hand) {
8
+ const suits = splitToSuits(hand);
9
+ const sTable = getSuitTable();
10
+ const hTable = getHonorTable();
11
+ // 各色のインデックスを事前計算
12
+ const indices = [
13
+ toIndex(suits[0]),
14
+ toIndex(suits[1]),
15
+ toIndex(suits[2]),
16
+ toIndex(suits[3]),
17
+ ];
18
+ const tables = [sTable, sTable, sTable, hTable];
19
+ let best = 8;
20
+ // 面子配分 (m0, m1, m2, m3) の全パターン列挙
21
+ for (let m0 = 0; m0 <= 4; m0++) {
22
+ for (let m1 = 0; m0 + m1 <= 4; m1++) {
23
+ for (let m2 = 0; m0 + m1 + m2 <= 4; m2++) {
24
+ for (let m3 = 0; m0 + m1 + m2 + m3 <= 4; m3++) {
25
+ const M = m0 + m1 + m2 + m3;
26
+ const ms = [m0, m1, m2, m3];
27
+ // 各色のテーブル参照(雀頭なし)
28
+ const n0 = lookupTable(tables[0], indices[0], m0, false);
29
+ const n1 = lookupTable(tables[1], indices[1], m1, false);
30
+ const n2 = lookupTable(tables[2], indices[2], m2, false);
31
+ const n3 = lookupTable(tables[3], indices[3], m3, false);
32
+ // いずれかの色で面子抽出不可能ならスキップ
33
+ if (n0 < 0 || n1 < 0 || n2 < 0 || n3 < 0)
34
+ continue;
35
+ // ケース1: 雀頭なし
36
+ const P = n0 + n1 + n2 + n3;
37
+ const remain = 4 - M;
38
+ const s1 = 8 - 2 * M - Math.min(P, remain);
39
+ if (s1 < best)
40
+ best = s1;
41
+ // ケース2: 雀頭あり(各色から1つずつ試す)
42
+ const noJ = [n0, n1, n2, n3];
43
+ for (let k = 0; k < 4; k++) {
44
+ const wJ = lookupTable(tables[k], indices[k], ms[k], true);
45
+ if (wJ < 0)
46
+ continue; // この色から雀頭不可
47
+ const P2 = wJ + P - noJ[k];
48
+ const s2 = 8 - 2 * M - Math.min(P2, remain) - 1;
49
+ if (s2 < best)
50
+ best = s2;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return best;
57
+ }
58
+ /**
59
+ * 七対子のシャンテン数を計算する
60
+ * 赤ドラは通常の5と同種として扱う
61
+ */
62
+ export function calculateShantenChiitoi(hand) {
63
+ let pairs = 0;
64
+ let kinds = 0;
65
+ for (let i = 0; i < NUM_TILE_TYPES; i++) {
66
+ let count = hand[i];
67
+ // 通常の5に赤ドラをマージ
68
+ if (i === 4)
69
+ count += hand[RED_MANZU_ID] ?? 0;
70
+ if (i === 13)
71
+ count += hand[RED_PINZU_ID] ?? 0;
72
+ if (i === 22)
73
+ count += hand[RED_SOUZU_ID] ?? 0;
74
+ if (count >= 2)
75
+ pairs++;
76
+ if (count >= 1)
77
+ kinds++;
78
+ }
79
+ return 6 - pairs + Math.max(0, 7 - kinds);
80
+ }
81
+ /**
82
+ * 国士無双のシャンテン数を計算する
83
+ */
84
+ export function calculateShantenKokushi(hand) {
85
+ let kinds = 0;
86
+ let hasPair = false;
87
+ for (const id of YAOCHU_IDS) {
88
+ if (hand[id] >= 1)
89
+ kinds++;
90
+ if (hand[id] >= 2)
91
+ hasPair = true;
92
+ }
93
+ return 13 - kinds - (hasPair ? 1 : 0);
94
+ }
95
+ /**
96
+ * シャンテン数を計算する(通常手・七対子・国士無双の最小値)
97
+ */
98
+ export function calculateShanten(hand) {
99
+ return Math.min(calculateShantenRegular(hand), calculateShantenChiitoi(hand), calculateShantenKokushi(hand));
100
+ }
101
+ //# sourceMappingURL=shanten-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shanten-calculator.js","sourceRoot":"","sources":["../../src/shanten/shanten-calculator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { HandArray } from "../core/hand.js";
2
+ import { TileId } from "../core/tile.js";
3
+ import { UkeireOptions, UkeireResult } from "../types.js";
4
+ /**
5
+ * 13枚手牌の有効牌(受入牌)を計算する
6
+ *
7
+ * @param hand 13枚の手牌
8
+ * @param remainingTiles 残り牌(省略時はデフォルト)
9
+ * @param options オプション(remainingTiles 省略時のみ有効)
10
+ * @returns 受入牌の一覧と合計枚数
11
+ */
12
+ export declare function calculateUkeire13(hand: HandArray, remainingTiles?: HandArray, options?: UkeireOptions): {
13
+ acceptTiles: TileId[];
14
+ acceptCount: number;
15
+ };
16
+ /**
17
+ * 14枚手牌の打牌別受入解析
18
+ *
19
+ * @param hand 14枚の手牌
20
+ * @param remainingTiles 残り牌(省略時はデフォルト)
21
+ * @param options オプション(remainingTiles 省略時のみ有効)
22
+ * @returns 各打牌の受入結果(シャンテン数昇順、同数なら受入枚数降順)
23
+ */
24
+ export declare function calculateUkeire14(hand: HandArray, remainingTiles?: HandArray, options?: UkeireOptions): UkeireResult[];
25
+ /**
26
+ * 受入牌リストの赤ドラを通常5にマージして表示用に返す
27
+ *
28
+ * @param acceptTiles 受入牌ID一覧
29
+ * @param remaining 残り牌配列
30
+ * @returns マージ済みの牌と枚数の配列(牌IDソート済み)
31
+ */
32
+ export declare function mergeRedAcceptTiles(acceptTiles: TileId[], remaining: HandArray): {
33
+ tile: TileId;
34
+ count: number;
35
+ }[];
@@ -0,0 +1,103 @@
1
+ import { NUM_TILE_TYPES_WITH_RED, RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID } from "../core/constants.js";
2
+ import { toNormalTile } from "../core/tile.js";
3
+ import { calculateShanten } from "./shanten-calculator.js";
4
+ /** 通常の5のID一覧 */
5
+ const NORMAL_FIVE_IDS = new Set([4, 13, 22]);
6
+ /** 赤ドラのID一覧 */
7
+ const RED_IDS = new Set([RED_MANZU_ID, RED_PINZU_ID, RED_SOUZU_ID]);
8
+ /**
9
+ * 残り牌のデフォルト計算
10
+ * 赤あり: 通常5=3枚, 赤5=1枚
11
+ * 赤なし: 通常5=4枚, 赤5=0枚
12
+ */
13
+ function defaultRemaining(hand, useRedDora = true) {
14
+ const remaining = new Array(NUM_TILE_TYPES_WITH_RED);
15
+ for (let i = 0; i < NUM_TILE_TYPES_WITH_RED; i++) {
16
+ if (RED_IDS.has(i)) {
17
+ remaining[i] = useRedDora ? 1 - (hand[i] ?? 0) : 0;
18
+ }
19
+ else if (NORMAL_FIVE_IDS.has(i)) {
20
+ remaining[i] = (useRedDora ? 3 : 4) - hand[i];
21
+ }
22
+ else {
23
+ remaining[i] = 4 - hand[i];
24
+ }
25
+ }
26
+ return remaining;
27
+ }
28
+ /**
29
+ * 13枚手牌の有効牌(受入牌)を計算する
30
+ *
31
+ * @param hand 13枚の手牌
32
+ * @param remainingTiles 残り牌(省略時はデフォルト)
33
+ * @param options オプション(remainingTiles 省略時のみ有効)
34
+ * @returns 受入牌の一覧と合計枚数
35
+ */
36
+ export function calculateUkeire13(hand, remainingTiles, options) {
37
+ const shanten = calculateShanten(hand);
38
+ const useRedDora = options?.useRedDora ?? true;
39
+ const remaining = remainingTiles ?? defaultRemaining(hand, useRedDora);
40
+ const acceptTiles = [];
41
+ let acceptCount = 0;
42
+ for (let t = 0; t < NUM_TILE_TYPES_WITH_RED; t++) {
43
+ if (remaining[t] <= 0)
44
+ continue;
45
+ hand[t]++;
46
+ if (calculateShanten(hand) < shanten) {
47
+ acceptTiles.push(t);
48
+ acceptCount += remaining[t];
49
+ }
50
+ hand[t]--;
51
+ }
52
+ return { acceptTiles, acceptCount };
53
+ }
54
+ /**
55
+ * 14枚手牌の打牌別受入解析
56
+ *
57
+ * @param hand 14枚の手牌
58
+ * @param remainingTiles 残り牌(省略時はデフォルト)
59
+ * @param options オプション(remainingTiles 省略時のみ有効)
60
+ * @returns 各打牌の受入結果(シャンテン数昇順、同数なら受入枚数降順)
61
+ */
62
+ export function calculateUkeire14(hand, remainingTiles, options) {
63
+ const results = [];
64
+ const useRedDora = options?.useRedDora ?? true;
65
+ for (let d = 0; d < NUM_TILE_TYPES_WITH_RED; d++) {
66
+ if (hand[d] <= 0)
67
+ continue;
68
+ hand[d]--;
69
+ const shantenAfter = calculateShanten(hand);
70
+ let remaining;
71
+ if (remainingTiles) {
72
+ remaining = remainingTiles;
73
+ }
74
+ else {
75
+ remaining = defaultRemaining(hand, useRedDora);
76
+ remaining[d]--; // 切った牌は河に行くので山から1枚減る
77
+ }
78
+ const { acceptTiles, acceptCount } = calculateUkeire13(hand, remaining);
79
+ results.push({ discard: d, shantenAfter, acceptTiles, acceptCount });
80
+ hand[d]++;
81
+ }
82
+ // シャンテン数昇順 → 同シャンテンなら受入枚数降順
83
+ results.sort((a, b) => a.shantenAfter - b.shantenAfter || b.acceptCount - a.acceptCount);
84
+ return results;
85
+ }
86
+ /**
87
+ * 受入牌リストの赤ドラを通常5にマージして表示用に返す
88
+ *
89
+ * @param acceptTiles 受入牌ID一覧
90
+ * @param remaining 残り牌配列
91
+ * @returns マージ済みの牌と枚数の配列(牌IDソート済み)
92
+ */
93
+ export function mergeRedAcceptTiles(acceptTiles, remaining) {
94
+ const map = new Map();
95
+ for (const t of acceptTiles) {
96
+ const normal = toNormalTile(t);
97
+ map.set(normal, (map.get(normal) ?? 0) + remaining[t]);
98
+ }
99
+ return Array.from(map.entries())
100
+ .sort(([a], [b]) => a - b)
101
+ .map(([tile, count]) => ({ tile, count }));
102
+ }
103
+ //# sourceMappingURL=ukeire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ukeire.js","sourceRoot":"","sources":["../../src/shanten/ukeire.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { TileId } from "./core/tile.js";
2
+ /** 受入解析オプション */
3
+ export interface UkeireOptions {
4
+ /** 赤ドラを使うか (デフォルト: true) */
5
+ useRedDora?: boolean;
6
+ }
7
+ /** 受入解析結果(1打牌分) */
8
+ export interface UkeireResult {
9
+ /** 打牌 */
10
+ discard: TileId;
11
+ /** 打牌後のシャンテン数 */
12
+ shantenAfter: number;
13
+ /** 受入牌一覧 */
14
+ acceptTiles: TileId[];
15
+ /** 受入枚数 */
16
+ acceptCount: number;
17
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import type { HandArray } from "./core/hand.js";
2
+ import type { TileId } from "./core/tile.js";
3
+ import type { UkeireOptions, UkeireResult } from "./types.js";
4
+ import type { EVOptions, EVResult } from "./ev/types.js";
5
+ import type { MCOptions, MCResult } from "./mc/simulator.js";
6
+ import type { EvalOptions, EvalResult } from "./models/evaluation.js";
7
+ export interface WorkerClientOptions {
8
+ /** Worker スクリプトの URL または Worker インスタンス */
9
+ worker: Worker | string;
10
+ /** 数牌テーブルの URL */
11
+ suitTableUrl: string;
12
+ /** 字牌テーブルの URL */
13
+ honorTableUrl: string;
14
+ /** WASM バイナリの URL */
15
+ wasmUrl: string;
16
+ }
17
+ export declare class WorkerClient {
18
+ private worker;
19
+ private pending;
20
+ private initPromise;
21
+ constructor(options: WorkerClientOptions);
22
+ /** 初期化完了を待つ */
23
+ ready(): Promise<void>;
24
+ /** シャンテン数計算 */
25
+ shanten(hand: HandArray): Promise<number>;
26
+ /** 13枚手牌の受入解析 */
27
+ ukeire13(hand: HandArray, remaining?: HandArray, options?: UkeireOptions): Promise<{
28
+ acceptTiles: TileId[];
29
+ acceptCount: number;
30
+ }>;
31
+ /** 14枚手牌の打牌別受入解析 */
32
+ ukeire14(hand: HandArray, remaining?: HandArray, options?: UkeireOptions): Promise<UkeireResult[]>;
33
+ /** 14枚手牌の各打牌候補の期待値計算 */
34
+ ev14(hand: HandArray, options: EVOptions): Promise<EVResult[]>;
35
+ /** MCシミュレーション */
36
+ simulate(hand: HandArray, options: MCOptions): Promise<MCResult[]>;
37
+ /** MC + 総合評価 */
38
+ evaluate(hand: HandArray, mcOptions: MCOptions, evalOptions: EvalOptions): Promise<EvalResult[]>;
39
+ /** Worker 終了 */
40
+ terminate(): void;
41
+ private request;
42
+ }
@@ -0,0 +1,83 @@
1
+ let nextId = 0;
2
+ export class WorkerClient {
3
+ worker;
4
+ pending = new Map();
5
+ initPromise;
6
+ constructor(options) {
7
+ this.worker = typeof options.worker === "string"
8
+ ? new Worker(options.worker, { type: "module" })
9
+ : options.worker;
10
+ this.worker.onmessage = (e) => {
11
+ const { type, requestId, payload } = e.data;
12
+ const req = this.pending.get(requestId);
13
+ if (!req)
14
+ return;
15
+ this.pending.delete(requestId);
16
+ if (type === "error") {
17
+ req.reject(new Error(payload.message));
18
+ }
19
+ else {
20
+ req.resolve(payload);
21
+ }
22
+ };
23
+ this.worker.onerror = (e) => {
24
+ // すべての pending リクエストを reject
25
+ const err = new Error(e.message ?? "Worker error");
26
+ for (const req of this.pending.values()) {
27
+ req.reject(err);
28
+ }
29
+ this.pending.clear();
30
+ };
31
+ // 自動初期化
32
+ this.initPromise = this.request("init", {
33
+ suitTableUrl: options.suitTableUrl,
34
+ honorTableUrl: options.honorTableUrl,
35
+ wasmUrl: options.wasmUrl,
36
+ });
37
+ }
38
+ /** 初期化完了を待つ */
39
+ ready() {
40
+ return this.initPromise;
41
+ }
42
+ /** シャンテン数計算 */
43
+ async shanten(hand) {
44
+ return this.request("shanten", { hand });
45
+ }
46
+ /** 13枚手牌の受入解析 */
47
+ async ukeire13(hand, remaining, options) {
48
+ return this.request("ukeire13", { hand, remaining, options });
49
+ }
50
+ /** 14枚手牌の打牌別受入解析 */
51
+ async ukeire14(hand, remaining, options) {
52
+ return this.request("ukeire14", { hand, remaining, options });
53
+ }
54
+ /** 14枚手牌の各打牌候補の期待値計算 */
55
+ async ev14(hand, options) {
56
+ return this.request("ev", { hand, options });
57
+ }
58
+ /** MCシミュレーション */
59
+ async simulate(hand, options) {
60
+ return this.request("simulate", { hand, options });
61
+ }
62
+ /** MC + 総合評価 */
63
+ async evaluate(hand, mcOptions, evalOptions) {
64
+ return this.request("evaluate", { hand, mcOptions, evalOptions });
65
+ }
66
+ /** Worker 終了 */
67
+ terminate() {
68
+ this.worker.terminate();
69
+ const err = new Error("Worker terminated");
70
+ for (const req of this.pending.values()) {
71
+ req.reject(err);
72
+ }
73
+ this.pending.clear();
74
+ }
75
+ request(type, payload) {
76
+ const requestId = String(nextId++);
77
+ return new Promise((resolve, reject) => {
78
+ this.pending.set(requestId, { resolve, reject });
79
+ this.worker.postMessage({ type, requestId, payload });
80
+ });
81
+ }
82
+ }
83
+ //# sourceMappingURL=worker-client.js.map