@pai-forge/riichi-mahjong 0.3.5 → 0.3.6
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.d.ts +20 -28
- package/dist/index.js +873 -343
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -203,6 +203,481 @@ const YAOCHU_KIND_IDS = [
|
|
|
203
203
|
function isYaochu(kind) {
|
|
204
204
|
return YAOCHU_KIND_IDS.some((k) => k === kind);
|
|
205
205
|
}
|
|
206
|
+
const defaultErrorConfig = {
|
|
207
|
+
withStackTrace: false
|
|
208
|
+
};
|
|
209
|
+
const createNeverThrowError = (message, result, config = defaultErrorConfig) => {
|
|
210
|
+
const data = result.isOk() ? { type: "Ok", value: result.value } : { type: "Err", value: result.error };
|
|
211
|
+
const maybeStack = config.withStackTrace ? new Error().stack : void 0;
|
|
212
|
+
return {
|
|
213
|
+
data,
|
|
214
|
+
message,
|
|
215
|
+
stack: maybeStack
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
219
|
+
function adopt(value) {
|
|
220
|
+
return value instanceof P ? value : new P(function(resolve) {
|
|
221
|
+
resolve(value);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return new (P || (P = Promise))(function(resolve, reject) {
|
|
225
|
+
function fulfilled(value) {
|
|
226
|
+
try {
|
|
227
|
+
step(generator.next(value));
|
|
228
|
+
} catch (e) {
|
|
229
|
+
reject(e);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function rejected(value) {
|
|
233
|
+
try {
|
|
234
|
+
step(generator["throw"](value));
|
|
235
|
+
} catch (e) {
|
|
236
|
+
reject(e);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function step(result) {
|
|
240
|
+
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
241
|
+
}
|
|
242
|
+
step((generator = generator.apply(thisArg, [])).next());
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function __values(o) {
|
|
246
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
247
|
+
if (m) return m.call(o);
|
|
248
|
+
if (o && typeof o.length === "number") return {
|
|
249
|
+
next: function() {
|
|
250
|
+
if (o && i >= o.length) o = void 0;
|
|
251
|
+
return { value: o && o[i++], done: !o };
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
255
|
+
}
|
|
256
|
+
function __await(v) {
|
|
257
|
+
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
|
258
|
+
}
|
|
259
|
+
function __asyncGenerator(thisArg, _arguments, generator) {
|
|
260
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
261
|
+
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
262
|
+
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function() {
|
|
263
|
+
return this;
|
|
264
|
+
}, i;
|
|
265
|
+
function awaitReturn(f) {
|
|
266
|
+
return function(v) {
|
|
267
|
+
return Promise.resolve(v).then(f, reject);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function verb(n, f) {
|
|
271
|
+
if (g[n]) {
|
|
272
|
+
i[n] = function(v) {
|
|
273
|
+
return new Promise(function(a, b) {
|
|
274
|
+
q.push([n, v, a, b]) > 1 || resume(n, v);
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
if (f) i[n] = f(i[n]);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function resume(n, v) {
|
|
281
|
+
try {
|
|
282
|
+
step(g[n](v));
|
|
283
|
+
} catch (e) {
|
|
284
|
+
settle(q[0][3], e);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function step(r) {
|
|
288
|
+
r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);
|
|
289
|
+
}
|
|
290
|
+
function fulfill(value) {
|
|
291
|
+
resume("next", value);
|
|
292
|
+
}
|
|
293
|
+
function reject(value) {
|
|
294
|
+
resume("throw", value);
|
|
295
|
+
}
|
|
296
|
+
function settle(f, v) {
|
|
297
|
+
if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function __asyncDelegator(o) {
|
|
301
|
+
var i, p;
|
|
302
|
+
return i = {}, verb("next"), verb("throw", function(e) {
|
|
303
|
+
throw e;
|
|
304
|
+
}), verb("return"), i[Symbol.iterator] = function() {
|
|
305
|
+
return this;
|
|
306
|
+
}, i;
|
|
307
|
+
function verb(n, f) {
|
|
308
|
+
i[n] = o[n] ? function(v) {
|
|
309
|
+
return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v;
|
|
310
|
+
} : f;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function __asyncValues(o) {
|
|
314
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
315
|
+
var m = o[Symbol.asyncIterator], i;
|
|
316
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function() {
|
|
317
|
+
return this;
|
|
318
|
+
}, i);
|
|
319
|
+
function verb(n) {
|
|
320
|
+
i[n] = o[n] && function(v) {
|
|
321
|
+
return new Promise(function(resolve, reject) {
|
|
322
|
+
v = o[n](v), settle(resolve, reject, v.done, v.value);
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function settle(resolve, reject, d, v) {
|
|
327
|
+
Promise.resolve(v).then(function(v2) {
|
|
328
|
+
resolve({ value: v2, done: d });
|
|
329
|
+
}, reject);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
333
|
+
var e = new Error(message);
|
|
334
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
335
|
+
};
|
|
336
|
+
class ResultAsync {
|
|
337
|
+
constructor(res) {
|
|
338
|
+
this._promise = res;
|
|
339
|
+
}
|
|
340
|
+
static fromSafePromise(promise) {
|
|
341
|
+
const newPromise = promise.then((value) => new Ok(value));
|
|
342
|
+
return new ResultAsync(newPromise);
|
|
343
|
+
}
|
|
344
|
+
static fromPromise(promise, errorFn) {
|
|
345
|
+
const newPromise = promise.then((value) => new Ok(value)).catch((e) => new Err(errorFn(e)));
|
|
346
|
+
return new ResultAsync(newPromise);
|
|
347
|
+
}
|
|
348
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
349
|
+
static fromThrowable(fn, errorFn) {
|
|
350
|
+
return (...args) => {
|
|
351
|
+
return new ResultAsync((() => __awaiter(this, void 0, void 0, function* () {
|
|
352
|
+
try {
|
|
353
|
+
return new Ok(yield fn(...args));
|
|
354
|
+
} catch (error) {
|
|
355
|
+
return new Err(errorFn ? errorFn(error) : error);
|
|
356
|
+
}
|
|
357
|
+
}))());
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
static combine(asyncResultList) {
|
|
361
|
+
return combineResultAsyncList(asyncResultList);
|
|
362
|
+
}
|
|
363
|
+
static combineWithAllErrors(asyncResultList) {
|
|
364
|
+
return combineResultAsyncListWithAllErrors(asyncResultList);
|
|
365
|
+
}
|
|
366
|
+
map(f) {
|
|
367
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
368
|
+
if (res.isErr()) {
|
|
369
|
+
return new Err(res.error);
|
|
370
|
+
}
|
|
371
|
+
return new Ok(yield f(res.value));
|
|
372
|
+
})));
|
|
373
|
+
}
|
|
374
|
+
andThrough(f) {
|
|
375
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
376
|
+
if (res.isErr()) {
|
|
377
|
+
return new Err(res.error);
|
|
378
|
+
}
|
|
379
|
+
const newRes = yield f(res.value);
|
|
380
|
+
if (newRes.isErr()) {
|
|
381
|
+
return new Err(newRes.error);
|
|
382
|
+
}
|
|
383
|
+
return new Ok(res.value);
|
|
384
|
+
})));
|
|
385
|
+
}
|
|
386
|
+
andTee(f) {
|
|
387
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
388
|
+
if (res.isErr()) {
|
|
389
|
+
return new Err(res.error);
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
yield f(res.value);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
}
|
|
395
|
+
return new Ok(res.value);
|
|
396
|
+
})));
|
|
397
|
+
}
|
|
398
|
+
orTee(f) {
|
|
399
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
400
|
+
if (res.isOk()) {
|
|
401
|
+
return new Ok(res.value);
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
yield f(res.error);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
}
|
|
407
|
+
return new Err(res.error);
|
|
408
|
+
})));
|
|
409
|
+
}
|
|
410
|
+
mapErr(f) {
|
|
411
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
412
|
+
if (res.isOk()) {
|
|
413
|
+
return new Ok(res.value);
|
|
414
|
+
}
|
|
415
|
+
return new Err(yield f(res.error));
|
|
416
|
+
})));
|
|
417
|
+
}
|
|
418
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
419
|
+
andThen(f) {
|
|
420
|
+
return new ResultAsync(this._promise.then((res) => {
|
|
421
|
+
if (res.isErr()) {
|
|
422
|
+
return new Err(res.error);
|
|
423
|
+
}
|
|
424
|
+
const newValue = f(res.value);
|
|
425
|
+
return newValue instanceof ResultAsync ? newValue._promise : newValue;
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
429
|
+
orElse(f) {
|
|
430
|
+
return new ResultAsync(this._promise.then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
431
|
+
if (res.isErr()) {
|
|
432
|
+
return f(res.error);
|
|
433
|
+
}
|
|
434
|
+
return new Ok(res.value);
|
|
435
|
+
})));
|
|
436
|
+
}
|
|
437
|
+
match(ok2, _err) {
|
|
438
|
+
return this._promise.then((res) => res.match(ok2, _err));
|
|
439
|
+
}
|
|
440
|
+
unwrapOr(t) {
|
|
441
|
+
return this._promise.then((res) => res.unwrapOr(t));
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* @deprecated will be removed in 9.0.0.
|
|
445
|
+
*
|
|
446
|
+
* You can use `safeTry` without this method.
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* safeTry(async function* () {
|
|
450
|
+
* const okValue = yield* yourResult
|
|
451
|
+
* })
|
|
452
|
+
* ```
|
|
453
|
+
* Emulates Rust's `?` operator in `safeTry`'s body. See also `safeTry`.
|
|
454
|
+
*/
|
|
455
|
+
safeUnwrap() {
|
|
456
|
+
return __asyncGenerator(this, arguments, function* safeUnwrap_1() {
|
|
457
|
+
return yield __await(yield __await(yield* __asyncDelegator(__asyncValues(yield __await(this._promise.then((res) => res.safeUnwrap()))))));
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
// Makes ResultAsync implement PromiseLike<Result>
|
|
461
|
+
then(successCallback, failureCallback) {
|
|
462
|
+
return this._promise.then(successCallback, failureCallback);
|
|
463
|
+
}
|
|
464
|
+
[Symbol.asyncIterator]() {
|
|
465
|
+
return __asyncGenerator(this, arguments, function* _a() {
|
|
466
|
+
const result = yield __await(this._promise);
|
|
467
|
+
if (result.isErr()) {
|
|
468
|
+
yield yield __await(errAsync(result.error));
|
|
469
|
+
}
|
|
470
|
+
return yield __await(result.value);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function errAsync(err2) {
|
|
475
|
+
return new ResultAsync(Promise.resolve(new Err(err2)));
|
|
476
|
+
}
|
|
477
|
+
const combineResultList = (resultList) => {
|
|
478
|
+
let acc = ok([]);
|
|
479
|
+
for (const result of resultList) {
|
|
480
|
+
if (result.isErr()) {
|
|
481
|
+
acc = err(result.error);
|
|
482
|
+
break;
|
|
483
|
+
} else {
|
|
484
|
+
acc.map((list) => list.push(result.value));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return acc;
|
|
488
|
+
};
|
|
489
|
+
const combineResultAsyncList = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
|
|
490
|
+
const combineResultListWithAllErrors = (resultList) => {
|
|
491
|
+
let acc = ok([]);
|
|
492
|
+
for (const result of resultList) {
|
|
493
|
+
if (result.isErr() && acc.isErr()) {
|
|
494
|
+
acc.error.push(result.error);
|
|
495
|
+
} else if (result.isErr() && acc.isOk()) {
|
|
496
|
+
acc = err([result.error]);
|
|
497
|
+
} else if (result.isOk() && acc.isOk()) {
|
|
498
|
+
acc.value.push(result.value);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return acc;
|
|
502
|
+
};
|
|
503
|
+
const combineResultAsyncListWithAllErrors = (asyncResultList) => ResultAsync.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultListWithAllErrors);
|
|
504
|
+
var Result;
|
|
505
|
+
(function(Result2) {
|
|
506
|
+
function fromThrowable(fn, errorFn) {
|
|
507
|
+
return (...args) => {
|
|
508
|
+
try {
|
|
509
|
+
const result = fn(...args);
|
|
510
|
+
return ok(result);
|
|
511
|
+
} catch (e) {
|
|
512
|
+
return err(errorFn ? errorFn(e) : e);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
Result2.fromThrowable = fromThrowable;
|
|
517
|
+
function combine(resultList) {
|
|
518
|
+
return combineResultList(resultList);
|
|
519
|
+
}
|
|
520
|
+
Result2.combine = combine;
|
|
521
|
+
function combineWithAllErrors(resultList) {
|
|
522
|
+
return combineResultListWithAllErrors(resultList);
|
|
523
|
+
}
|
|
524
|
+
Result2.combineWithAllErrors = combineWithAllErrors;
|
|
525
|
+
})(Result || (Result = {}));
|
|
526
|
+
function ok(value) {
|
|
527
|
+
return new Ok(value);
|
|
528
|
+
}
|
|
529
|
+
function err(err2) {
|
|
530
|
+
return new Err(err2);
|
|
531
|
+
}
|
|
532
|
+
class Ok {
|
|
533
|
+
constructor(value) {
|
|
534
|
+
this.value = value;
|
|
535
|
+
}
|
|
536
|
+
isOk() {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
isErr() {
|
|
540
|
+
return !this.isOk();
|
|
541
|
+
}
|
|
542
|
+
map(f) {
|
|
543
|
+
return ok(f(this.value));
|
|
544
|
+
}
|
|
545
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
546
|
+
mapErr(_f) {
|
|
547
|
+
return ok(this.value);
|
|
548
|
+
}
|
|
549
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
550
|
+
andThen(f) {
|
|
551
|
+
return f(this.value);
|
|
552
|
+
}
|
|
553
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
554
|
+
andThrough(f) {
|
|
555
|
+
return f(this.value).map((_value) => this.value);
|
|
556
|
+
}
|
|
557
|
+
andTee(f) {
|
|
558
|
+
try {
|
|
559
|
+
f(this.value);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
}
|
|
562
|
+
return ok(this.value);
|
|
563
|
+
}
|
|
564
|
+
orTee(_f) {
|
|
565
|
+
return ok(this.value);
|
|
566
|
+
}
|
|
567
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
568
|
+
orElse(_f) {
|
|
569
|
+
return ok(this.value);
|
|
570
|
+
}
|
|
571
|
+
asyncAndThen(f) {
|
|
572
|
+
return f(this.value);
|
|
573
|
+
}
|
|
574
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
575
|
+
asyncAndThrough(f) {
|
|
576
|
+
return f(this.value).map(() => this.value);
|
|
577
|
+
}
|
|
578
|
+
asyncMap(f) {
|
|
579
|
+
return ResultAsync.fromSafePromise(f(this.value));
|
|
580
|
+
}
|
|
581
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
582
|
+
unwrapOr(_v) {
|
|
583
|
+
return this.value;
|
|
584
|
+
}
|
|
585
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
586
|
+
match(ok2, _err) {
|
|
587
|
+
return ok2(this.value);
|
|
588
|
+
}
|
|
589
|
+
safeUnwrap() {
|
|
590
|
+
const value = this.value;
|
|
591
|
+
return (function* () {
|
|
592
|
+
return value;
|
|
593
|
+
})();
|
|
594
|
+
}
|
|
595
|
+
_unsafeUnwrap(_) {
|
|
596
|
+
return this.value;
|
|
597
|
+
}
|
|
598
|
+
_unsafeUnwrapErr(config) {
|
|
599
|
+
throw createNeverThrowError("Called `_unsafeUnwrapErr` on an Ok", this, config);
|
|
600
|
+
}
|
|
601
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias, require-yield
|
|
602
|
+
*[Symbol.iterator]() {
|
|
603
|
+
return this.value;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
class Err {
|
|
607
|
+
constructor(error) {
|
|
608
|
+
this.error = error;
|
|
609
|
+
}
|
|
610
|
+
isOk() {
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
isErr() {
|
|
614
|
+
return !this.isOk();
|
|
615
|
+
}
|
|
616
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
617
|
+
map(_f) {
|
|
618
|
+
return err(this.error);
|
|
619
|
+
}
|
|
620
|
+
mapErr(f) {
|
|
621
|
+
return err(f(this.error));
|
|
622
|
+
}
|
|
623
|
+
andThrough(_f) {
|
|
624
|
+
return err(this.error);
|
|
625
|
+
}
|
|
626
|
+
andTee(_f) {
|
|
627
|
+
return err(this.error);
|
|
628
|
+
}
|
|
629
|
+
orTee(f) {
|
|
630
|
+
try {
|
|
631
|
+
f(this.error);
|
|
632
|
+
} catch (e) {
|
|
633
|
+
}
|
|
634
|
+
return err(this.error);
|
|
635
|
+
}
|
|
636
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
637
|
+
andThen(_f) {
|
|
638
|
+
return err(this.error);
|
|
639
|
+
}
|
|
640
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
|
641
|
+
orElse(f) {
|
|
642
|
+
return f(this.error);
|
|
643
|
+
}
|
|
644
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
645
|
+
asyncAndThen(_f) {
|
|
646
|
+
return errAsync(this.error);
|
|
647
|
+
}
|
|
648
|
+
asyncAndThrough(_f) {
|
|
649
|
+
return errAsync(this.error);
|
|
650
|
+
}
|
|
651
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
652
|
+
asyncMap(_f) {
|
|
653
|
+
return errAsync(this.error);
|
|
654
|
+
}
|
|
655
|
+
unwrapOr(v) {
|
|
656
|
+
return v;
|
|
657
|
+
}
|
|
658
|
+
match(_ok, err2) {
|
|
659
|
+
return err2(this.error);
|
|
660
|
+
}
|
|
661
|
+
safeUnwrap() {
|
|
662
|
+
const error = this.error;
|
|
663
|
+
return (function* () {
|
|
664
|
+
yield err(error);
|
|
665
|
+
throw new Error("Do not use this generator out of `safeTry`");
|
|
666
|
+
})();
|
|
667
|
+
}
|
|
668
|
+
_unsafeUnwrap(config) {
|
|
669
|
+
throw createNeverThrowError("Called `_unsafeUnwrap` on an Err", this, config);
|
|
670
|
+
}
|
|
671
|
+
_unsafeUnwrapErr(_) {
|
|
672
|
+
return this.error;
|
|
673
|
+
}
|
|
674
|
+
*[Symbol.iterator]() {
|
|
675
|
+
const self = this;
|
|
676
|
+
yield self;
|
|
677
|
+
return self;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
Result.fromThrowable;
|
|
206
681
|
function calculateTehaiCount(tehai) {
|
|
207
682
|
return tehai.closed.length + tehai.exposed.length * 3;
|
|
208
683
|
}
|
|
@@ -213,41 +688,47 @@ function countHaiKind(hais) {
|
|
|
213
688
|
}
|
|
214
689
|
return counts;
|
|
215
690
|
}
|
|
216
|
-
function
|
|
691
|
+
function validateTehai13(tehai) {
|
|
217
692
|
const count = calculateTehaiCount(tehai);
|
|
218
693
|
if (count < 13) {
|
|
219
|
-
|
|
694
|
+
return err(new ShoushaiError());
|
|
220
695
|
}
|
|
221
696
|
if (count > 13) {
|
|
222
|
-
|
|
697
|
+
return err(new TahaiError());
|
|
223
698
|
}
|
|
224
|
-
validateHaiConsistency(tehai);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
699
|
+
const consRes = validateHaiConsistency(tehai);
|
|
700
|
+
if (consRes.isErr()) {
|
|
701
|
+
return err(consRes.error);
|
|
702
|
+
}
|
|
703
|
+
return ok(tehai);
|
|
228
704
|
}
|
|
229
|
-
function
|
|
705
|
+
function validateTehai14(tehai) {
|
|
230
706
|
const count = calculateTehaiCount(tehai);
|
|
231
707
|
if (count < 14) {
|
|
232
|
-
|
|
708
|
+
return err(new ShoushaiError());
|
|
233
709
|
}
|
|
234
710
|
if (count > 14) {
|
|
235
|
-
|
|
711
|
+
return err(new TahaiError());
|
|
236
712
|
}
|
|
237
|
-
validateHaiConsistency(tehai);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
713
|
+
const consRes = validateHaiConsistency(tehai);
|
|
714
|
+
if (consRes.isErr()) {
|
|
715
|
+
return err(consRes.error);
|
|
716
|
+
}
|
|
717
|
+
return ok(tehai);
|
|
241
718
|
}
|
|
242
719
|
function validateTehai(tehai) {
|
|
243
720
|
const count = calculateTehaiCount(tehai);
|
|
244
721
|
if (count < 13) {
|
|
245
|
-
|
|
722
|
+
return err(new ShoushaiError());
|
|
246
723
|
}
|
|
247
724
|
if (count > 14) {
|
|
248
|
-
|
|
725
|
+
return err(new TahaiError());
|
|
249
726
|
}
|
|
250
|
-
validateHaiConsistency(tehai);
|
|
727
|
+
const consRes = validateHaiConsistency(tehai);
|
|
728
|
+
if (consRes.isErr()) {
|
|
729
|
+
return err(consRes.error);
|
|
730
|
+
}
|
|
731
|
+
return ok(tehai);
|
|
251
732
|
}
|
|
252
733
|
function validateHaiConsistency(tehai) {
|
|
253
734
|
const allHais = [
|
|
@@ -258,7 +739,7 @@ function validateHaiConsistency(tehai) {
|
|
|
258
739
|
if (isHaiIdMode) {
|
|
259
740
|
const uniqueIds = new Set(allHais);
|
|
260
741
|
if (uniqueIds.size !== allHais.length) {
|
|
261
|
-
|
|
742
|
+
return err(new DuplicatedHaiIdError());
|
|
262
743
|
}
|
|
263
744
|
}
|
|
264
745
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -266,26 +747,17 @@ function validateHaiConsistency(tehai) {
|
|
|
266
747
|
const kind = isHaiIdMode ? haiIdToKindId(hai) : hai;
|
|
267
748
|
const current = counts.get(kind) ?? 0;
|
|
268
749
|
if (current + 1 > 4) {
|
|
269
|
-
|
|
750
|
+
return err(new InvalidHaiQuantityError());
|
|
270
751
|
}
|
|
271
752
|
counts.set(kind, current + 1);
|
|
272
753
|
}
|
|
754
|
+
return ok(void 0);
|
|
273
755
|
}
|
|
274
756
|
function isTehai13(tehai) {
|
|
275
|
-
|
|
276
|
-
validateTehai13(tehai);
|
|
277
|
-
return true;
|
|
278
|
-
} catch {
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
757
|
+
return validateTehai13(tehai).isOk();
|
|
281
758
|
}
|
|
282
759
|
function isTehai14(tehai) {
|
|
283
|
-
|
|
284
|
-
validateTehai14(tehai);
|
|
285
|
-
return true;
|
|
286
|
-
} catch {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
760
|
+
return validateTehai14(tehai).isOk();
|
|
289
761
|
}
|
|
290
762
|
function isValidShuntsu(kindIds) {
|
|
291
763
|
if (!isTuple3(kindIds)) return false;
|
|
@@ -544,11 +1016,12 @@ function countTaatsu(counts) {
|
|
|
544
1016
|
return taatsu;
|
|
545
1017
|
}
|
|
546
1018
|
function calculateShanten(tehai, useChiitoitsu = true, useKokushi = true) {
|
|
547
|
-
validateTehai13(tehai);
|
|
1019
|
+
const valRes = validateTehai13(tehai);
|
|
1020
|
+
if (valRes.isErr()) return err(valRes.error);
|
|
548
1021
|
const chiitoitsuShanten = useChiitoitsu ? calculateChiitoitsuShanten(tehai) : Infinity;
|
|
549
1022
|
const kokushiShanten = useKokushi ? calculateKokushiShanten(tehai) : Infinity;
|
|
550
1023
|
const mentsuShanten = calculateMentsuTeShanten(tehai);
|
|
551
|
-
return Math.min(chiitoitsuShanten, kokushiShanten, mentsuShanten);
|
|
1024
|
+
return ok(Math.min(chiitoitsuShanten, kokushiShanten, mentsuShanten));
|
|
552
1025
|
}
|
|
553
1026
|
function getUkeire(tehai) {
|
|
554
1027
|
const currentShanten = calculateMentsuTeShanten(tehai);
|
|
@@ -765,17 +1238,68 @@ function analyzeIshokuPattern(hand) {
|
|
|
765
1238
|
suupaiSuit: isAllSameType ? firstSuupaiType : void 0
|
|
766
1239
|
};
|
|
767
1240
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1241
|
+
class YakuBuilder {
|
|
1242
|
+
/**
|
|
1243
|
+
*
|
|
1244
|
+
*/
|
|
1245
|
+
constructor(yaku) {
|
|
1246
|
+
this.yaku = yaku;
|
|
1247
|
+
}
|
|
1248
|
+
predicates = [];
|
|
1249
|
+
hanCalculator;
|
|
1250
|
+
/**
|
|
1251
|
+
*
|
|
1252
|
+
*/
|
|
1253
|
+
require(predicate) {
|
|
1254
|
+
this.predicates.push(predicate);
|
|
1255
|
+
return this;
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
*
|
|
1259
|
+
*/
|
|
1260
|
+
menzenOnly() {
|
|
1261
|
+
this.predicates.push((hand, context) => context.isMenzen);
|
|
1262
|
+
return this;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
*
|
|
1266
|
+
*/
|
|
1267
|
+
dynamicHan(calculator) {
|
|
1268
|
+
this.hanCalculator = calculator;
|
|
1269
|
+
return this;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
*
|
|
1273
|
+
*/
|
|
1274
|
+
build() {
|
|
1275
|
+
const yaku = this.yaku;
|
|
1276
|
+
const predicates = this.predicates;
|
|
1277
|
+
const calculator = this.hanCalculator;
|
|
1278
|
+
return {
|
|
1279
|
+
yaku,
|
|
1280
|
+
isSatisfied(hand, context) {
|
|
1281
|
+
return predicates.every((pred) => pred(hand, context));
|
|
1282
|
+
},
|
|
1283
|
+
getHansu(hand, context) {
|
|
1284
|
+
if (!this.isSatisfied(hand, context)) {
|
|
1285
|
+
return 0;
|
|
1286
|
+
}
|
|
1287
|
+
if (calculator) {
|
|
1288
|
+
return calculator(hand, context);
|
|
1289
|
+
}
|
|
1290
|
+
return context.isMenzen ? yaku.han.closed : yaku.han.open;
|
|
775
1291
|
}
|
|
776
|
-
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function createYaku(name, closedHan, openHan = 0) {
|
|
1296
|
+
return new YakuBuilder({
|
|
1297
|
+
name,
|
|
1298
|
+
han: {
|
|
1299
|
+
closed: closedHan,
|
|
1300
|
+
open: openHan
|
|
777
1301
|
}
|
|
778
|
-
};
|
|
1302
|
+
});
|
|
779
1303
|
}
|
|
780
1304
|
const TANYAO_YAKU = {
|
|
781
1305
|
name: "Tanyao",
|
|
@@ -792,10 +1316,11 @@ const checkTanyao = (hand) => {
|
|
|
792
1316
|
}
|
|
793
1317
|
return true;
|
|
794
1318
|
};
|
|
795
|
-
const tanyaoDefinition =
|
|
796
|
-
TANYAO_YAKU,
|
|
797
|
-
|
|
798
|
-
|
|
1319
|
+
const tanyaoDefinition = createYaku(
|
|
1320
|
+
TANYAO_YAKU.name,
|
|
1321
|
+
TANYAO_YAKU.han.closed,
|
|
1322
|
+
TANYAO_YAKU.han.open
|
|
1323
|
+
).require(checkTanyao).build();
|
|
799
1324
|
const PINFU_YAKU = {
|
|
800
1325
|
name: "Pinfu",
|
|
801
1326
|
han: {
|
|
@@ -825,10 +1350,11 @@ const checkPinfu = (hand, context) => {
|
|
|
825
1350
|
const waitType = classifyMachi(hand, context.agariHai);
|
|
826
1351
|
return waitType === "Ryanmen";
|
|
827
1352
|
};
|
|
828
|
-
const pinfuDefinition =
|
|
829
|
-
PINFU_YAKU,
|
|
830
|
-
|
|
831
|
-
|
|
1353
|
+
const pinfuDefinition = createYaku(
|
|
1354
|
+
PINFU_YAKU.name,
|
|
1355
|
+
PINFU_YAKU.han.closed,
|
|
1356
|
+
PINFU_YAKU.han.open
|
|
1357
|
+
).require(checkPinfu).build();
|
|
832
1358
|
const IIPEIKO_YAKU = {
|
|
833
1359
|
name: "Iipeikou",
|
|
834
1360
|
han: {
|
|
@@ -841,10 +1367,11 @@ const checkIipeikou = (hand) => {
|
|
|
841
1367
|
const pairCount = countShuntsuPairs(hand);
|
|
842
1368
|
return pairCount === 1;
|
|
843
1369
|
};
|
|
844
|
-
const iipeikouDefinition =
|
|
845
|
-
IIPEIKO_YAKU,
|
|
846
|
-
|
|
847
|
-
|
|
1370
|
+
const iipeikouDefinition = createYaku(
|
|
1371
|
+
IIPEIKO_YAKU.name,
|
|
1372
|
+
IIPEIKO_YAKU.han.closed,
|
|
1373
|
+
IIPEIKO_YAKU.han.open
|
|
1374
|
+
).require(checkIipeikou).build();
|
|
848
1375
|
const RYANPEIKOU_YAKU = {
|
|
849
1376
|
name: "Ryanpeikou",
|
|
850
1377
|
han: {
|
|
@@ -866,20 +1393,14 @@ const checkRyanpeikou = (hand) => {
|
|
|
866
1393
|
const pairCount = countShuntsuPairs(hand);
|
|
867
1394
|
return pairCount >= 2;
|
|
868
1395
|
};
|
|
869
|
-
const ryanpeikouDefinition =
|
|
870
|
-
RYANPEIKOU_YAKU,
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
han: {
|
|
876
|
-
open: 2,
|
|
877
|
-
closed: 2
|
|
878
|
-
}
|
|
879
|
-
};
|
|
880
|
-
const checkSanankou = (hand, context) => {
|
|
1396
|
+
const ryanpeikouDefinition = createYaku(
|
|
1397
|
+
RYANPEIKOU_YAKU.name,
|
|
1398
|
+
RYANPEIKOU_YAKU.han.closed,
|
|
1399
|
+
RYANPEIKOU_YAKU.han.open
|
|
1400
|
+
).require(checkRyanpeikou).build();
|
|
1401
|
+
const countAnkou = (hand, context) => {
|
|
881
1402
|
if (hand.type !== "Mentsu") {
|
|
882
|
-
return
|
|
1403
|
+
return 0;
|
|
883
1404
|
}
|
|
884
1405
|
const triplets = hand.fourMentsu.filter(
|
|
885
1406
|
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
@@ -901,12 +1422,69 @@ const checkSanankou = (hand, context) => {
|
|
|
901
1422
|
}
|
|
902
1423
|
}
|
|
903
1424
|
}
|
|
1425
|
+
return ankouCount;
|
|
1426
|
+
};
|
|
1427
|
+
const countSpecificKoutsu = (hand, targetKinds) => {
|
|
1428
|
+
if (hand.type !== "Mentsu") {
|
|
1429
|
+
return 0;
|
|
1430
|
+
}
|
|
1431
|
+
let count = 0;
|
|
1432
|
+
const triplets = hand.fourMentsu.filter(
|
|
1433
|
+
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
1434
|
+
);
|
|
1435
|
+
for (const triplet of triplets) {
|
|
1436
|
+
if (targetKinds.includes(triplet.hais[0])) {
|
|
1437
|
+
count++;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
return count;
|
|
1441
|
+
};
|
|
1442
|
+
const isAllHaisMatch = (hand, predicate) => {
|
|
1443
|
+
if (hand.type === "Mentsu") {
|
|
1444
|
+
const allHais = [
|
|
1445
|
+
...hand.fourMentsu.flatMap((m) => m.hais),
|
|
1446
|
+
...hand.jantou.hais
|
|
1447
|
+
];
|
|
1448
|
+
return allHais.every(predicate);
|
|
1449
|
+
} else if (hand.type === "Chiitoitsu") {
|
|
1450
|
+
const allHais = hand.pairs.flatMap((p) => p.hais);
|
|
1451
|
+
return allHais.every(predicate);
|
|
1452
|
+
} else {
|
|
1453
|
+
return hand.yaochu.every(predicate);
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
const getShuntsuCombinations3 = (shuntsuList) => {
|
|
1457
|
+
const combos = [];
|
|
1458
|
+
for (let i = 0; i < shuntsuList.length; i++) {
|
|
1459
|
+
for (let j = i + 1; j < shuntsuList.length; j++) {
|
|
1460
|
+
for (let k = j + 1; k < shuntsuList.length; k++) {
|
|
1461
|
+
const s1 = shuntsuList[i];
|
|
1462
|
+
const s2 = shuntsuList[j];
|
|
1463
|
+
const s3 = shuntsuList[k];
|
|
1464
|
+
if (s1 && s2 && s3) {
|
|
1465
|
+
combos.push([s1, s2, s3]);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return combos;
|
|
1471
|
+
};
|
|
1472
|
+
const SANANKOU_YAKU = {
|
|
1473
|
+
name: "Sanankou",
|
|
1474
|
+
han: {
|
|
1475
|
+
open: 2,
|
|
1476
|
+
closed: 2
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
const checkSanankou = (hand, context) => {
|
|
1480
|
+
const ankouCount = countAnkou(hand, context);
|
|
904
1481
|
return ankouCount >= 3;
|
|
905
1482
|
};
|
|
906
|
-
const sanankouDefinition =
|
|
907
|
-
SANANKOU_YAKU,
|
|
908
|
-
|
|
909
|
-
|
|
1483
|
+
const sanankouDefinition = createYaku(
|
|
1484
|
+
SANANKOU_YAKU.name,
|
|
1485
|
+
SANANKOU_YAKU.han.closed,
|
|
1486
|
+
SANANKOU_YAKU.han.open
|
|
1487
|
+
).require(checkSanankou).build();
|
|
910
1488
|
const SUUANKOU_YAKU = {
|
|
911
1489
|
name: "Suuankou",
|
|
912
1490
|
han: {
|
|
@@ -917,29 +1495,7 @@ const SUUANKOU_YAKU = {
|
|
|
917
1495
|
}
|
|
918
1496
|
};
|
|
919
1497
|
const checkSuuankou = (hand, context) => {
|
|
920
|
-
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
const triplets = hand.fourMentsu.filter(
|
|
924
|
-
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
925
|
-
);
|
|
926
|
-
let ankouCount = 0;
|
|
927
|
-
for (const triplet of triplets) {
|
|
928
|
-
if (triplet.furo) continue;
|
|
929
|
-
const isAgariHaiInTriplet = triplet.hais.includes(context.agariHai);
|
|
930
|
-
const isTanki = hand.jantou.hais[0] === context.agariHai;
|
|
931
|
-
if (context.isTsumo) {
|
|
932
|
-
ankouCount++;
|
|
933
|
-
} else {
|
|
934
|
-
if (isAgariHaiInTriplet) {
|
|
935
|
-
if (isTanki) {
|
|
936
|
-
ankouCount++;
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
ankouCount++;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
}
|
|
1498
|
+
const ankouCount = countAnkou(hand, context);
|
|
943
1499
|
return ankouCount === 4;
|
|
944
1500
|
};
|
|
945
1501
|
const suuankouDefinition = {
|
|
@@ -968,10 +1524,11 @@ const checkSankantsu = (hand) => {
|
|
|
968
1524
|
const kantsuList = hand.fourMentsu.filter((m) => m.type === "Kantsu");
|
|
969
1525
|
return kantsuList.length >= 3;
|
|
970
1526
|
};
|
|
971
|
-
const sankantsuDefinition =
|
|
972
|
-
SANKANTSU_YAKU,
|
|
973
|
-
|
|
974
|
-
|
|
1527
|
+
const sankantsuDefinition = createYaku(
|
|
1528
|
+
SANKANTSU_YAKU.name,
|
|
1529
|
+
SANKANTSU_YAKU.han.closed,
|
|
1530
|
+
SANKANTSU_YAKU.han.open
|
|
1531
|
+
).require(checkSankantsu).build();
|
|
975
1532
|
const SUUKANTSU_YAKU = {
|
|
976
1533
|
name: "Suukantsu",
|
|
977
1534
|
han: {
|
|
@@ -986,10 +1543,11 @@ const checkSuukantsu = (hand) => {
|
|
|
986
1543
|
const kantsuList = hand.fourMentsu.filter((m) => m.type === "Kantsu");
|
|
987
1544
|
return kantsuList.length === 4;
|
|
988
1545
|
};
|
|
989
|
-
const suukantsuDefinition =
|
|
990
|
-
SUUKANTSU_YAKU,
|
|
991
|
-
|
|
992
|
-
|
|
1546
|
+
const suukantsuDefinition = createYaku(
|
|
1547
|
+
SUUKANTSU_YAKU.name,
|
|
1548
|
+
SUUKANTSU_YAKU.han.closed,
|
|
1549
|
+
SUUKANTSU_YAKU.han.open
|
|
1550
|
+
).require(checkSuukantsu).build();
|
|
993
1551
|
const TOITOI_YAKU = {
|
|
994
1552
|
name: "Toitoi",
|
|
995
1553
|
han: {
|
|
@@ -1005,10 +1563,11 @@ const checkToitoi = (hand) => {
|
|
|
1005
1563
|
(mentsu) => mentsu.type === "Koutsu" || mentsu.type === "Kantsu"
|
|
1006
1564
|
);
|
|
1007
1565
|
};
|
|
1008
|
-
const toitoiDefinition =
|
|
1009
|
-
TOITOI_YAKU,
|
|
1010
|
-
|
|
1011
|
-
|
|
1566
|
+
const toitoiDefinition = createYaku(
|
|
1567
|
+
TOITOI_YAKU.name,
|
|
1568
|
+
TOITOI_YAKU.han.closed,
|
|
1569
|
+
TOITOI_YAKU.han.open
|
|
1570
|
+
).require(checkToitoi).build();
|
|
1012
1571
|
const CHIITOITSU_YAKU = {
|
|
1013
1572
|
name: "Chiitoitsu",
|
|
1014
1573
|
han: {
|
|
@@ -1020,10 +1579,11 @@ const CHIITOITSU_YAKU = {
|
|
|
1020
1579
|
const checkChiitoitsu = (hand) => {
|
|
1021
1580
|
return hand.type === "Chiitoitsu";
|
|
1022
1581
|
};
|
|
1023
|
-
const chiitoitsuDefinition =
|
|
1024
|
-
CHIITOITSU_YAKU,
|
|
1025
|
-
|
|
1026
|
-
|
|
1582
|
+
const chiitoitsuDefinition = createYaku(
|
|
1583
|
+
CHIITOITSU_YAKU.name,
|
|
1584
|
+
CHIITOITSU_YAKU.han.closed,
|
|
1585
|
+
CHIITOITSU_YAKU.han.open
|
|
1586
|
+
).require(checkChiitoitsu).build();
|
|
1027
1587
|
const HONCHAN_YAKU = {
|
|
1028
1588
|
name: "Honchan",
|
|
1029
1589
|
han: {
|
|
@@ -1046,10 +1606,11 @@ const checkHonchan = (hand) => {
|
|
|
1046
1606
|
if (!hasJihai) return false;
|
|
1047
1607
|
return true;
|
|
1048
1608
|
};
|
|
1049
|
-
const honchanDefinition =
|
|
1050
|
-
HONCHAN_YAKU,
|
|
1051
|
-
|
|
1052
|
-
|
|
1609
|
+
const honchanDefinition = createYaku(
|
|
1610
|
+
HONCHAN_YAKU.name,
|
|
1611
|
+
HONCHAN_YAKU.han.closed,
|
|
1612
|
+
HONCHAN_YAKU.han.open
|
|
1613
|
+
).require(checkHonchan).build();
|
|
1053
1614
|
const JUNCHAN_YAKU = {
|
|
1054
1615
|
name: "Junchan",
|
|
1055
1616
|
han: {
|
|
@@ -1068,10 +1629,11 @@ const checkJunchan = (hand) => {
|
|
|
1068
1629
|
if (!hasShuntsu) return false;
|
|
1069
1630
|
return true;
|
|
1070
1631
|
};
|
|
1071
|
-
const junchanDefinition =
|
|
1072
|
-
JUNCHAN_YAKU,
|
|
1073
|
-
|
|
1074
|
-
|
|
1632
|
+
const junchanDefinition = createYaku(
|
|
1633
|
+
JUNCHAN_YAKU.name,
|
|
1634
|
+
JUNCHAN_YAKU.han.closed,
|
|
1635
|
+
JUNCHAN_YAKU.han.open
|
|
1636
|
+
).require(checkJunchan).build();
|
|
1075
1637
|
const HONROUTOU_YAKU = {
|
|
1076
1638
|
name: "Honroutou",
|
|
1077
1639
|
han: {
|
|
@@ -1098,10 +1660,11 @@ const checkHonroutou = (hand) => {
|
|
|
1098
1660
|
if (!hasJihai) return false;
|
|
1099
1661
|
return true;
|
|
1100
1662
|
};
|
|
1101
|
-
const honroutouDefinition =
|
|
1102
|
-
HONROUTOU_YAKU,
|
|
1103
|
-
|
|
1104
|
-
|
|
1663
|
+
const honroutouDefinition = createYaku(
|
|
1664
|
+
HONROUTOU_YAKU.name,
|
|
1665
|
+
HONROUTOU_YAKU.han.closed,
|
|
1666
|
+
HONROUTOU_YAKU.han.open
|
|
1667
|
+
).require(checkHonroutou).build();
|
|
1105
1668
|
const CHINROUTOU_YAKU = {
|
|
1106
1669
|
name: "Chinroutou",
|
|
1107
1670
|
han: {
|
|
@@ -1118,10 +1681,11 @@ const checkChinroutou = (hand) => {
|
|
|
1118
1681
|
if (!allRoutou) return false;
|
|
1119
1682
|
return true;
|
|
1120
1683
|
};
|
|
1121
|
-
const chinroutouDefinition =
|
|
1122
|
-
CHINROUTOU_YAKU,
|
|
1123
|
-
|
|
1124
|
-
|
|
1684
|
+
const chinroutouDefinition = createYaku(
|
|
1685
|
+
CHINROUTOU_YAKU.name,
|
|
1686
|
+
CHINROUTOU_YAKU.han.closed,
|
|
1687
|
+
CHINROUTOU_YAKU.han.open
|
|
1688
|
+
).require(checkChinroutou).build();
|
|
1125
1689
|
const SHOUSANGEN_YAKU = {
|
|
1126
1690
|
name: "Shousangen",
|
|
1127
1691
|
han: {
|
|
@@ -1134,22 +1698,15 @@ const checkShousangen = (hand) => {
|
|
|
1134
1698
|
return false;
|
|
1135
1699
|
}
|
|
1136
1700
|
const sangenpai = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];
|
|
1137
|
-
|
|
1138
|
-
const triplets = hand.fourMentsu.filter(
|
|
1139
|
-
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
1140
|
-
);
|
|
1141
|
-
for (const triplet of triplets) {
|
|
1142
|
-
if (sangenpai.includes(triplet.hais[0])) {
|
|
1143
|
-
sangenKoutsuCount++;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1701
|
+
const sangenKoutsuCount = countSpecificKoutsu(hand, sangenpai);
|
|
1146
1702
|
const isSangenJantou = sangenpai.includes(hand.jantou.hais[0]);
|
|
1147
1703
|
return sangenKoutsuCount === 2 && isSangenJantou;
|
|
1148
1704
|
};
|
|
1149
|
-
const shousangenDefinition =
|
|
1150
|
-
SHOUSANGEN_YAKU,
|
|
1151
|
-
|
|
1152
|
-
|
|
1705
|
+
const shousangenDefinition = createYaku(
|
|
1706
|
+
SHOUSANGEN_YAKU.name,
|
|
1707
|
+
SHOUSANGEN_YAKU.han.closed,
|
|
1708
|
+
SHOUSANGEN_YAKU.han.open
|
|
1709
|
+
).require(checkShousangen).build();
|
|
1153
1710
|
const DAISANGEN_YAKU = {
|
|
1154
1711
|
name: "Daisangen",
|
|
1155
1712
|
han: {
|
|
@@ -1158,25 +1715,15 @@ const DAISANGEN_YAKU = {
|
|
|
1158
1715
|
}
|
|
1159
1716
|
};
|
|
1160
1717
|
const checkDaisangen = (hand) => {
|
|
1161
|
-
if (hand.type !== "Mentsu") {
|
|
1162
|
-
return false;
|
|
1163
|
-
}
|
|
1164
1718
|
const sangenpai = [HaiKind.Haku, HaiKind.Hatsu, HaiKind.Chun];
|
|
1165
|
-
|
|
1166
|
-
const triplets = hand.fourMentsu.filter(
|
|
1167
|
-
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
1168
|
-
);
|
|
1169
|
-
for (const triplet of triplets) {
|
|
1170
|
-
if (sangenpai.includes(triplet.hais[0])) {
|
|
1171
|
-
sangenKoutsuCount++;
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1719
|
+
const sangenKoutsuCount = countSpecificKoutsu(hand, sangenpai);
|
|
1174
1720
|
return sangenKoutsuCount === 3;
|
|
1175
1721
|
};
|
|
1176
|
-
const daisangenDefinition =
|
|
1177
|
-
DAISANGEN_YAKU,
|
|
1178
|
-
|
|
1179
|
-
|
|
1722
|
+
const daisangenDefinition = createYaku(
|
|
1723
|
+
DAISANGEN_YAKU.name,
|
|
1724
|
+
DAISANGEN_YAKU.han.closed,
|
|
1725
|
+
DAISANGEN_YAKU.han.open
|
|
1726
|
+
).require(checkDaisangen).build();
|
|
1180
1727
|
const TSUUIISOU_YAKU = {
|
|
1181
1728
|
name: "Tsuuiisou",
|
|
1182
1729
|
han: {
|
|
@@ -1188,25 +1735,13 @@ const isJihai = (id) => {
|
|
|
1188
1735
|
return id >= HaiKind.Ton && id <= HaiKind.Chun;
|
|
1189
1736
|
};
|
|
1190
1737
|
const checkTsuuiisou = (hand) => {
|
|
1191
|
-
|
|
1192
|
-
if (hand.type === "Mentsu") {
|
|
1193
|
-
for (const mentsu of hand.fourMentsu) {
|
|
1194
|
-
allHais.push(...mentsu.hais);
|
|
1195
|
-
}
|
|
1196
|
-
allHais.push(...hand.jantou.hais);
|
|
1197
|
-
} else if (hand.type === "Chiitoitsu") {
|
|
1198
|
-
for (const pair of hand.pairs) {
|
|
1199
|
-
allHais.push(...pair.hais);
|
|
1200
|
-
}
|
|
1201
|
-
} else {
|
|
1202
|
-
return false;
|
|
1203
|
-
}
|
|
1204
|
-
return allHais.every(isJihai);
|
|
1738
|
+
return isAllHaisMatch(hand, isJihai);
|
|
1205
1739
|
};
|
|
1206
|
-
const tsuuiisouDefinition =
|
|
1207
|
-
TSUUIISOU_YAKU,
|
|
1208
|
-
|
|
1209
|
-
|
|
1740
|
+
const tsuuiisouDefinition = createYaku(
|
|
1741
|
+
TSUUIISOU_YAKU.name,
|
|
1742
|
+
TSUUIISOU_YAKU.han.closed,
|
|
1743
|
+
TSUUIISOU_YAKU.han.open
|
|
1744
|
+
).require(checkTsuuiisou).build();
|
|
1210
1745
|
const RYUUIISOU_YAKU = {
|
|
1211
1746
|
name: "Ryuuiisou",
|
|
1212
1747
|
han: {
|
|
@@ -1226,25 +1761,13 @@ const isGreen = (id) => {
|
|
|
1226
1761
|
return GREEN_TILES.has(id);
|
|
1227
1762
|
};
|
|
1228
1763
|
const checkRyuuiisou = (hand) => {
|
|
1229
|
-
|
|
1230
|
-
if (hand.type === "Mentsu") {
|
|
1231
|
-
for (const mentsu of hand.fourMentsu) {
|
|
1232
|
-
allHais.push(...mentsu.hais);
|
|
1233
|
-
}
|
|
1234
|
-
allHais.push(...hand.jantou.hais);
|
|
1235
|
-
} else if (hand.type === "Chiitoitsu") {
|
|
1236
|
-
for (const pair of hand.pairs) {
|
|
1237
|
-
allHais.push(...pair.hais);
|
|
1238
|
-
}
|
|
1239
|
-
} else {
|
|
1240
|
-
return false;
|
|
1241
|
-
}
|
|
1242
|
-
return allHais.every(isGreen);
|
|
1764
|
+
return isAllHaisMatch(hand, isGreen);
|
|
1243
1765
|
};
|
|
1244
|
-
const ryuuiisouDefinition =
|
|
1245
|
-
RYUUIISOU_YAKU,
|
|
1246
|
-
|
|
1247
|
-
|
|
1766
|
+
const ryuuiisouDefinition = createYaku(
|
|
1767
|
+
RYUUIISOU_YAKU.name,
|
|
1768
|
+
RYUUIISOU_YAKU.han.closed,
|
|
1769
|
+
RYUUIISOU_YAKU.han.open
|
|
1770
|
+
).require(checkRyuuiisou).build();
|
|
1248
1771
|
const SHOUSUUSHII_YAKU = {
|
|
1249
1772
|
name: "Shousuushii",
|
|
1250
1773
|
han: {
|
|
@@ -1262,22 +1785,15 @@ const checkShousuushii = (hand) => {
|
|
|
1262
1785
|
HaiKind.Sha,
|
|
1263
1786
|
HaiKind.Pei
|
|
1264
1787
|
];
|
|
1265
|
-
|
|
1266
|
-
const triplets = hand.fourMentsu.filter(
|
|
1267
|
-
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
1268
|
-
);
|
|
1269
|
-
for (const triplet of triplets) {
|
|
1270
|
-
if (windTiles.includes(triplet.hais[0])) {
|
|
1271
|
-
windKoutsuCount++;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1788
|
+
const windKoutsuCount = countSpecificKoutsu(hand, windTiles);
|
|
1274
1789
|
const isWindJantou = windTiles.includes(hand.jantou.hais[0]);
|
|
1275
1790
|
return windKoutsuCount === 3 && isWindJantou;
|
|
1276
1791
|
};
|
|
1277
|
-
const shousuushiiDefinition =
|
|
1278
|
-
SHOUSUUSHII_YAKU,
|
|
1279
|
-
|
|
1280
|
-
|
|
1792
|
+
const shousuushiiDefinition = createYaku(
|
|
1793
|
+
SHOUSUUSHII_YAKU.name,
|
|
1794
|
+
SHOUSUUSHII_YAKU.han.closed,
|
|
1795
|
+
SHOUSUUSHII_YAKU.han.open
|
|
1796
|
+
).require(checkShousuushii).build();
|
|
1281
1797
|
const DAISUUSHII_YAKU = {
|
|
1282
1798
|
name: "Daisuushii",
|
|
1283
1799
|
han: {
|
|
@@ -1287,30 +1803,20 @@ const DAISUUSHII_YAKU = {
|
|
|
1287
1803
|
}
|
|
1288
1804
|
};
|
|
1289
1805
|
const checkDaisuushii = (hand) => {
|
|
1290
|
-
if (hand.type !== "Mentsu") {
|
|
1291
|
-
return false;
|
|
1292
|
-
}
|
|
1293
1806
|
const windTiles = [
|
|
1294
1807
|
HaiKind.Ton,
|
|
1295
1808
|
HaiKind.Nan,
|
|
1296
1809
|
HaiKind.Sha,
|
|
1297
1810
|
HaiKind.Pei
|
|
1298
1811
|
];
|
|
1299
|
-
|
|
1300
|
-
const triplets = hand.fourMentsu.filter(
|
|
1301
|
-
(m) => m.type === "Koutsu" || m.type === "Kantsu"
|
|
1302
|
-
);
|
|
1303
|
-
for (const triplet of triplets) {
|
|
1304
|
-
if (windTiles.includes(triplet.hais[0])) {
|
|
1305
|
-
windKoutsuCount++;
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1812
|
+
const windKoutsuCount = countSpecificKoutsu(hand, windTiles);
|
|
1308
1813
|
return windKoutsuCount === 4;
|
|
1309
1814
|
};
|
|
1310
|
-
const daisuushiiDefinition =
|
|
1311
|
-
DAISUUSHII_YAKU,
|
|
1312
|
-
|
|
1313
|
-
|
|
1815
|
+
const daisuushiiDefinition = createYaku(
|
|
1816
|
+
DAISUUSHII_YAKU.name,
|
|
1817
|
+
DAISUUSHII_YAKU.han.closed,
|
|
1818
|
+
DAISUUSHII_YAKU.han.open
|
|
1819
|
+
).require(checkDaisuushii).build();
|
|
1314
1820
|
const CHUUREN_POUTOU_YAKU = {
|
|
1315
1821
|
name: "ChuurenPoutou",
|
|
1316
1822
|
han: {
|
|
@@ -1353,24 +1859,22 @@ const checkChuurenPoutou = (hand, context) => {
|
|
|
1353
1859
|
}
|
|
1354
1860
|
return true;
|
|
1355
1861
|
};
|
|
1356
|
-
const chuurenPoutouDefinition =
|
|
1357
|
-
CHUUREN_POUTOU_YAKU,
|
|
1358
|
-
|
|
1359
|
-
|
|
1862
|
+
const chuurenPoutouDefinition = createYaku(
|
|
1863
|
+
CHUUREN_POUTOU_YAKU.name,
|
|
1864
|
+
CHUUREN_POUTOU_YAKU.han.closed,
|
|
1865
|
+
CHUUREN_POUTOU_YAKU.han.open
|
|
1866
|
+
).require(checkChuurenPoutou).build();
|
|
1360
1867
|
const checkKokushi = (hand) => {
|
|
1361
1868
|
return hand.type === "Kokushi";
|
|
1362
1869
|
};
|
|
1363
1870
|
const KOKUSHI_HAN = {
|
|
1364
|
-
closed: 13
|
|
1365
|
-
open: 0
|
|
1871
|
+
closed: 13
|
|
1366
1872
|
};
|
|
1367
|
-
const kokushiDefinition =
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
checkKokushi
|
|
1373
|
-
);
|
|
1873
|
+
const kokushiDefinition = createYaku(
|
|
1874
|
+
"KokushiMusou",
|
|
1875
|
+
KOKUSHI_HAN.closed,
|
|
1876
|
+
0
|
|
1877
|
+
).require(checkKokushi).build();
|
|
1374
1878
|
const SANSHOKU_DOUJUN_YAKU = {
|
|
1375
1879
|
name: "SanshokuDoujun",
|
|
1376
1880
|
han: {
|
|
@@ -1388,37 +1892,30 @@ const checkSanshokuDoujun = (hand) => {
|
|
|
1388
1892
|
if (shuntsuList.length < 3) {
|
|
1389
1893
|
return false;
|
|
1390
1894
|
}
|
|
1391
|
-
for (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
if (suit1 > 2 || suit2 > 2 || suit3 > 2) continue;
|
|
1407
|
-
const num1 = firstHai1 % 9;
|
|
1408
|
-
const num2 = firstHai2 % 9;
|
|
1409
|
-
const num3 = firstHai3 % 9;
|
|
1410
|
-
if (num1 === num2 && num2 === num3) {
|
|
1411
|
-
return true;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1895
|
+
for (const [s1, s2, s3] of getShuntsuCombinations3(shuntsuList)) {
|
|
1896
|
+
const firstHai1 = s1.hais[0];
|
|
1897
|
+
const firstHai2 = s2.hais[0];
|
|
1898
|
+
const firstHai3 = s3.hais[0];
|
|
1899
|
+
const suit1 = Math.floor(firstHai1 / 9);
|
|
1900
|
+
const suit2 = Math.floor(firstHai2 / 9);
|
|
1901
|
+
const suit3 = Math.floor(firstHai3 / 9);
|
|
1902
|
+
const suits = /* @__PURE__ */ new Set([suit1, suit2, suit3]);
|
|
1903
|
+
if (suits.size !== 3) continue;
|
|
1904
|
+
if (suit1 > 2 || suit2 > 2 || suit3 > 2) continue;
|
|
1905
|
+
const num1 = firstHai1 % 9;
|
|
1906
|
+
const num2 = firstHai2 % 9;
|
|
1907
|
+
const num3 = firstHai3 % 9;
|
|
1908
|
+
if (num1 === num2 && num2 === num3) {
|
|
1909
|
+
return true;
|
|
1414
1910
|
}
|
|
1415
1911
|
}
|
|
1416
1912
|
return false;
|
|
1417
1913
|
};
|
|
1418
|
-
const sanshokuDoujunDefinition =
|
|
1419
|
-
SANSHOKU_DOUJUN_YAKU,
|
|
1420
|
-
|
|
1421
|
-
|
|
1914
|
+
const sanshokuDoujunDefinition = createYaku(
|
|
1915
|
+
SANSHOKU_DOUJUN_YAKU.name,
|
|
1916
|
+
SANSHOKU_DOUJUN_YAKU.han.closed,
|
|
1917
|
+
SANSHOKU_DOUJUN_YAKU.han.open
|
|
1918
|
+
).require(checkSanshokuDoujun).build();
|
|
1422
1919
|
const SANSHOKU_DOUKOU_YAKU = {
|
|
1423
1920
|
name: "SanshokuDoukou",
|
|
1424
1921
|
han: {
|
|
@@ -1463,10 +1960,11 @@ const checkSanshokuDoukou = (hand) => {
|
|
|
1463
1960
|
}
|
|
1464
1961
|
return false;
|
|
1465
1962
|
};
|
|
1466
|
-
const sanshokuDoukouDefinition =
|
|
1467
|
-
SANSHOKU_DOUKOU_YAKU,
|
|
1468
|
-
|
|
1469
|
-
|
|
1963
|
+
const sanshokuDoukouDefinition = createYaku(
|
|
1964
|
+
SANSHOKU_DOUKOU_YAKU.name,
|
|
1965
|
+
SANSHOKU_DOUKOU_YAKU.han.closed,
|
|
1966
|
+
SANSHOKU_DOUKOU_YAKU.han.open
|
|
1967
|
+
).require(checkSanshokuDoukou).build();
|
|
1470
1968
|
const IKKITSUUKAN_YAKU = {
|
|
1471
1969
|
name: "Ikkitsuukan",
|
|
1472
1970
|
han: {
|
|
@@ -1484,37 +1982,30 @@ const checkIkkitsuukan = (hand) => {
|
|
|
1484
1982
|
if (shuntsuList.length < 3) {
|
|
1485
1983
|
return false;
|
|
1486
1984
|
}
|
|
1487
|
-
for (
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
const num1 = firstHai1 % 9;
|
|
1503
|
-
const num2 = firstHai2 % 9;
|
|
1504
|
-
const num3 = firstHai3 % 9;
|
|
1505
|
-
const nums = /* @__PURE__ */ new Set([num1, num2, num3]);
|
|
1506
|
-
if (nums.has(0) && nums.has(3) && nums.has(6)) {
|
|
1507
|
-
return true;
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1985
|
+
for (const [s1, s2, s3] of getShuntsuCombinations3(shuntsuList)) {
|
|
1986
|
+
const firstHai1 = s1.hais[0];
|
|
1987
|
+
const firstHai2 = s2.hais[0];
|
|
1988
|
+
const firstHai3 = s3.hais[0];
|
|
1989
|
+
const suit1 = Math.floor(firstHai1 / 9);
|
|
1990
|
+
const suit2 = Math.floor(firstHai2 / 9);
|
|
1991
|
+
const suit3 = Math.floor(firstHai3 / 9);
|
|
1992
|
+
if (suit1 !== suit2 || suit2 !== suit3) continue;
|
|
1993
|
+
if (suit1 > 2) continue;
|
|
1994
|
+
const num1 = firstHai1 % 9;
|
|
1995
|
+
const num2 = firstHai2 % 9;
|
|
1996
|
+
const num3 = firstHai3 % 9;
|
|
1997
|
+
const nums = /* @__PURE__ */ new Set([num1, num2, num3]);
|
|
1998
|
+
if (nums.has(0) && nums.has(3) && nums.has(6)) {
|
|
1999
|
+
return true;
|
|
1510
2000
|
}
|
|
1511
2001
|
}
|
|
1512
2002
|
return false;
|
|
1513
2003
|
};
|
|
1514
|
-
const ikkitsuukanDefinition =
|
|
1515
|
-
IKKITSUUKAN_YAKU,
|
|
1516
|
-
|
|
1517
|
-
|
|
2004
|
+
const ikkitsuukanDefinition = createYaku(
|
|
2005
|
+
IKKITSUUKAN_YAKU.name,
|
|
2006
|
+
IKKITSUUKAN_YAKU.han.closed,
|
|
2007
|
+
IKKITSUUKAN_YAKU.han.open
|
|
2008
|
+
).require(checkIkkitsuukan).build();
|
|
1518
2009
|
const HONITSU_YAKU = {
|
|
1519
2010
|
name: "Honitsu",
|
|
1520
2011
|
han: {
|
|
@@ -1527,10 +2018,11 @@ const checkHonitsu = (hand) => {
|
|
|
1527
2018
|
if (result === void 0) return false;
|
|
1528
2019
|
return result.hasJihai && result.suupaiSuit !== void 0;
|
|
1529
2020
|
};
|
|
1530
|
-
const honitsuDefinition =
|
|
1531
|
-
HONITSU_YAKU,
|
|
1532
|
-
|
|
1533
|
-
|
|
2021
|
+
const honitsuDefinition = createYaku(
|
|
2022
|
+
HONITSU_YAKU.name,
|
|
2023
|
+
HONITSU_YAKU.han.closed,
|
|
2024
|
+
HONITSU_YAKU.han.open
|
|
2025
|
+
).require(checkHonitsu).build();
|
|
1534
2026
|
const CHINITSU_YAKU = {
|
|
1535
2027
|
name: "Chinitsu",
|
|
1536
2028
|
han: {
|
|
@@ -1543,10 +2035,11 @@ const checkChinitsu = (hand) => {
|
|
|
1543
2035
|
if (result === void 0) return false;
|
|
1544
2036
|
return !result.hasJihai && result.suupaiSuit !== void 0;
|
|
1545
2037
|
};
|
|
1546
|
-
const chinitsuDefinition =
|
|
1547
|
-
CHINITSU_YAKU,
|
|
1548
|
-
|
|
1549
|
-
|
|
2038
|
+
const chinitsuDefinition = createYaku(
|
|
2039
|
+
CHINITSU_YAKU.name,
|
|
2040
|
+
CHINITSU_YAKU.han.closed,
|
|
2041
|
+
CHINITSU_YAKU.han.open
|
|
2042
|
+
).require(checkChinitsu).build();
|
|
1550
2043
|
function createYakuhaiDefinition(name, tile) {
|
|
1551
2044
|
const HAN_CONFIG = { closed: 1, open: 1 };
|
|
1552
2045
|
const check = (hand) => {
|
|
@@ -1560,7 +2053,11 @@ function createYakuhaiDefinition(name, tile) {
|
|
|
1560
2053
|
}
|
|
1561
2054
|
return false;
|
|
1562
2055
|
};
|
|
1563
|
-
return
|
|
2056
|
+
return createYaku(
|
|
2057
|
+
name,
|
|
2058
|
+
HAN_CONFIG.closed,
|
|
2059
|
+
HAN_CONFIG.open
|
|
2060
|
+
).require(check).build();
|
|
1564
2061
|
}
|
|
1565
2062
|
const hakuDefinition = createYakuhaiDefinition("Haku", HaiKind.Haku);
|
|
1566
2063
|
const hatsuDefinition = createYakuhaiDefinition("Hatsu", HaiKind.Hatsu);
|
|
@@ -1569,12 +2066,13 @@ const definition = {
|
|
|
1569
2066
|
name: "MenzenTsumo",
|
|
1570
2067
|
han: { open: 0, closed: 1 }
|
|
1571
2068
|
};
|
|
1572
|
-
const menzenTsumoDefinition =
|
|
1573
|
-
definition,
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
2069
|
+
const menzenTsumoDefinition = createYaku(
|
|
2070
|
+
definition.name,
|
|
2071
|
+
definition.han.closed,
|
|
2072
|
+
definition.han.open
|
|
2073
|
+
).require((hand, context) => {
|
|
2074
|
+
return context.isMenzen && !!context.isTsumo;
|
|
2075
|
+
}).build();
|
|
1578
2076
|
const ALL_YAKU_DEFINITIONS = [
|
|
1579
2077
|
tanyaoDefinition,
|
|
1580
2078
|
pinfuDefinition,
|
|
@@ -1659,9 +2157,9 @@ function isExtendedMspz(input) {
|
|
|
1659
2157
|
}
|
|
1660
2158
|
function asExtendedMspz(input) {
|
|
1661
2159
|
if (!isExtendedMspz(input)) {
|
|
1662
|
-
|
|
2160
|
+
return err(new MspzParseError(`Invalid Extended MSPZ string: ${input}`));
|
|
1663
2161
|
}
|
|
1664
|
-
return input;
|
|
2162
|
+
return ok(input);
|
|
1665
2163
|
}
|
|
1666
2164
|
function isMspz(input) {
|
|
1667
2165
|
return STANDARD_MSPZ_REGEX.test(input);
|
|
@@ -1674,28 +2172,36 @@ function parseExtendedMspz$1(input) {
|
|
|
1674
2172
|
for (const char of input) {
|
|
1675
2173
|
if (char === "[") {
|
|
1676
2174
|
if (mode !== "closed")
|
|
1677
|
-
|
|
2175
|
+
return err(new MspzParseError("Nested brackets are not supported"));
|
|
1678
2176
|
if (current.length > 0) closedParts.push(current);
|
|
1679
2177
|
current = "[";
|
|
1680
2178
|
mode = "open";
|
|
1681
2179
|
} else if (char === "]") {
|
|
1682
2180
|
if (mode !== "open")
|
|
1683
|
-
|
|
2181
|
+
return err(new MspzParseError("Unexpected closing bracket ']'"));
|
|
1684
2182
|
current += "]";
|
|
1685
|
-
|
|
2183
|
+
const extMspzRes = asExtendedMspz(current);
|
|
2184
|
+
if (extMspzRes.isErr()) return err(extMspzRes.error);
|
|
2185
|
+
const mentsuRes = parseMentsuFromExtendedMspz(extMspzRes.value);
|
|
2186
|
+
if (mentsuRes.isErr()) return err(mentsuRes.error);
|
|
2187
|
+
exposed.push(mentsuRes.value);
|
|
1686
2188
|
current = "";
|
|
1687
2189
|
mode = "closed";
|
|
1688
2190
|
} else if (char === "(") {
|
|
1689
2191
|
if (mode !== "closed")
|
|
1690
|
-
|
|
2192
|
+
return err(new MspzParseError("Nested parentheses are not supported"));
|
|
1691
2193
|
if (current.length > 0) closedParts.push(current);
|
|
1692
2194
|
current = "(";
|
|
1693
2195
|
mode = "ankan";
|
|
1694
2196
|
} else if (char === ")") {
|
|
1695
2197
|
if (mode !== "ankan")
|
|
1696
|
-
|
|
2198
|
+
return err(new MspzParseError("Unexpected closing parenthesis ')'"));
|
|
1697
2199
|
current += ")";
|
|
1698
|
-
|
|
2200
|
+
const extMspzRes = asExtendedMspz(current);
|
|
2201
|
+
if (extMspzRes.isErr()) return err(extMspzRes.error);
|
|
2202
|
+
const mentsuRes = parseMentsuFromExtendedMspz(extMspzRes.value);
|
|
2203
|
+
if (mentsuRes.isErr()) return err(mentsuRes.error);
|
|
2204
|
+
exposed.push(mentsuRes.value);
|
|
1699
2205
|
current = "";
|
|
1700
2206
|
mode = "closed";
|
|
1701
2207
|
} else {
|
|
@@ -1704,15 +2210,17 @@ function parseExtendedMspz$1(input) {
|
|
|
1704
2210
|
}
|
|
1705
2211
|
if (current.length > 0) {
|
|
1706
2212
|
if (mode !== "closed")
|
|
1707
|
-
|
|
2213
|
+
return err(new MspzParseError("Unclosed bracket or parenthesis"));
|
|
1708
2214
|
closedParts.push(current);
|
|
1709
2215
|
}
|
|
1710
2216
|
const fullClosedMspz = closedParts.join("");
|
|
1711
|
-
const
|
|
1712
|
-
return
|
|
2217
|
+
const mspzRes = asMspz(fullClosedMspz);
|
|
2218
|
+
if (mspzRes.isErr()) return err(mspzRes.error);
|
|
2219
|
+
const closedIds = parseMspzToHaiKindIds(mspzRes.value);
|
|
2220
|
+
return ok({
|
|
1713
2221
|
closed: closedIds,
|
|
1714
2222
|
exposed
|
|
1715
|
-
};
|
|
2223
|
+
});
|
|
1716
2224
|
}
|
|
1717
2225
|
function parseMentsuFromExtendedMspz(block) {
|
|
1718
2226
|
let mode;
|
|
@@ -1724,35 +2232,45 @@ function parseMentsuFromExtendedMspz(block) {
|
|
|
1724
2232
|
mode = "ankan";
|
|
1725
2233
|
content = block.slice(1, -1);
|
|
1726
2234
|
} else {
|
|
1727
|
-
|
|
1728
|
-
|
|
2235
|
+
return err(
|
|
2236
|
+
new MspzParseError(
|
|
2237
|
+
`Invalid Extended MSPZ block: ${block} (must be [...] or (...))`
|
|
2238
|
+
)
|
|
1729
2239
|
);
|
|
1730
2240
|
}
|
|
1731
|
-
const
|
|
2241
|
+
const mspzRes = asMspz(content);
|
|
2242
|
+
if (mspzRes.isErr()) return err(mspzRes.error);
|
|
2243
|
+
const ids = parseMspzToHaiKindIds(mspzRes.value);
|
|
1732
2244
|
if (ids.length === 0) {
|
|
1733
|
-
|
|
2245
|
+
return err(new MspzParseError("Empty mentsu specification"));
|
|
1734
2246
|
}
|
|
1735
2247
|
const count = ids.length;
|
|
1736
2248
|
const isAllSame = ids.every((id) => id === ids[0]);
|
|
1737
2249
|
if (mode === "ankan") {
|
|
1738
2250
|
if (count !== 4 || !isAllSame) {
|
|
1739
|
-
|
|
1740
|
-
|
|
2251
|
+
return err(
|
|
2252
|
+
new MspzParseError(
|
|
2253
|
+
`Invalid Ankan: ${block} (must be 4 identical tiles)`
|
|
2254
|
+
)
|
|
1741
2255
|
);
|
|
1742
2256
|
}
|
|
1743
2257
|
if (!isTuple4(ids)) {
|
|
1744
|
-
|
|
2258
|
+
return err(
|
|
2259
|
+
new MspzParseError("Internal Error: ids length check mismatch")
|
|
2260
|
+
);
|
|
1745
2261
|
}
|
|
1746
2262
|
const kantsu = {
|
|
1747
2263
|
type: MentsuType.Kantsu,
|
|
1748
2264
|
hais: ids
|
|
1749
2265
|
// Ankan has no furo info (or minimal)
|
|
1750
2266
|
};
|
|
1751
|
-
return kantsu;
|
|
2267
|
+
return ok(kantsu);
|
|
1752
2268
|
}
|
|
1753
2269
|
if (count === 4 && isAllSame) {
|
|
1754
2270
|
if (!isTuple4(ids)) {
|
|
1755
|
-
|
|
2271
|
+
return err(
|
|
2272
|
+
new MspzParseError("Internal Error: ids length check mismatch")
|
|
2273
|
+
);
|
|
1756
2274
|
}
|
|
1757
2275
|
const kantsu = {
|
|
1758
2276
|
type: MentsuType.Kantsu,
|
|
@@ -1760,10 +2278,12 @@ function parseMentsuFromExtendedMspz(block) {
|
|
|
1760
2278
|
furo: { type: FuroType.Daiminkan, from: Tacha.Toimen }
|
|
1761
2279
|
// Default
|
|
1762
2280
|
};
|
|
1763
|
-
return kantsu;
|
|
2281
|
+
return ok(kantsu);
|
|
1764
2282
|
} else if (count === 3 && isAllSame) {
|
|
1765
2283
|
if (!isTuple3(ids)) {
|
|
1766
|
-
|
|
2284
|
+
return err(
|
|
2285
|
+
new MspzParseError("Internal Error: ids length check mismatch")
|
|
2286
|
+
);
|
|
1767
2287
|
}
|
|
1768
2288
|
const koutsu = {
|
|
1769
2289
|
type: MentsuType.Koutsu,
|
|
@@ -1771,10 +2291,12 @@ function parseMentsuFromExtendedMspz(block) {
|
|
|
1771
2291
|
furo: { type: FuroType.Pon, from: Tacha.Toimen }
|
|
1772
2292
|
// Default
|
|
1773
2293
|
};
|
|
1774
|
-
return koutsu;
|
|
2294
|
+
return ok(koutsu);
|
|
1775
2295
|
} else if (count === 3) {
|
|
1776
2296
|
if (!isTuple3(ids)) {
|
|
1777
|
-
|
|
2297
|
+
return err(
|
|
2298
|
+
new MspzParseError("Internal Error: ids length check mismatch")
|
|
2299
|
+
);
|
|
1778
2300
|
}
|
|
1779
2301
|
const shuntsu = {
|
|
1780
2302
|
type: MentsuType.Shuntsu,
|
|
@@ -1782,17 +2304,19 @@ function parseMentsuFromExtendedMspz(block) {
|
|
|
1782
2304
|
furo: { type: FuroType.Chi, from: Tacha.Kamicha }
|
|
1783
2305
|
// Default
|
|
1784
2306
|
};
|
|
1785
|
-
return shuntsu;
|
|
2307
|
+
return ok(shuntsu);
|
|
1786
2308
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
2309
|
+
return err(
|
|
2310
|
+
new MspzParseError(
|
|
2311
|
+
`Invalid Mentsu specification: ${block} (must be 3 or 4 tiles)`
|
|
2312
|
+
)
|
|
1789
2313
|
);
|
|
1790
2314
|
}
|
|
1791
2315
|
function asMspz(input) {
|
|
1792
2316
|
if (!isMspz(input)) {
|
|
1793
|
-
|
|
2317
|
+
return err(new MspzParseError(`Invalid MSPZ string: ${input}`));
|
|
1794
2318
|
}
|
|
1795
|
-
return input;
|
|
2319
|
+
return ok(input);
|
|
1796
2320
|
}
|
|
1797
2321
|
function parseMspzToHaiKindIds(mspz) {
|
|
1798
2322
|
const result = [];
|
|
@@ -1836,14 +2360,20 @@ function parseMspzToHaiKindIds(mspz) {
|
|
|
1836
2360
|
return result;
|
|
1837
2361
|
}
|
|
1838
2362
|
function parseMspz(input) {
|
|
1839
|
-
const
|
|
1840
|
-
return
|
|
2363
|
+
const mspzRes = asMspz(input);
|
|
2364
|
+
if (mspzRes.isErr()) return err(mspzRes.error);
|
|
2365
|
+
const ids = parseMspzToHaiKindIds(mspzRes.value);
|
|
2366
|
+
return ok({
|
|
1841
2367
|
closed: ids,
|
|
1842
2368
|
exposed: []
|
|
1843
|
-
};
|
|
2369
|
+
});
|
|
1844
2370
|
}
|
|
1845
2371
|
function parseExtendedMspz(input) {
|
|
1846
|
-
|
|
2372
|
+
const extMspzRes = asExtendedMspz(input);
|
|
2373
|
+
if (extMspzRes.isErr()) return err(extMspzRes.error);
|
|
2374
|
+
const parsedRes = parseExtendedMspz$1(extMspzRes.value);
|
|
2375
|
+
if (parsedRes.isErr()) return err(parsedRes.error);
|
|
2376
|
+
return ok(parsedRes.value);
|
|
1847
2377
|
}
|
|
1848
2378
|
const FU_BASE = {
|
|
1849
2379
|
NORMAL: 20,
|
|
@@ -2192,8 +2722,6 @@ export {
|
|
|
2192
2722
|
Tacha,
|
|
2193
2723
|
TahaiError,
|
|
2194
2724
|
YAOCHU_KIND_IDS,
|
|
2195
|
-
assertTehai13,
|
|
2196
|
-
assertTehai14,
|
|
2197
2725
|
calculateScoreForTehai,
|
|
2198
2726
|
calculateShanten,
|
|
2199
2727
|
classifyMachi,
|
|
@@ -2220,6 +2748,8 @@ export {
|
|
|
2220
2748
|
kindIdToHaiType,
|
|
2221
2749
|
parseExtendedMspz,
|
|
2222
2750
|
parseMspz,
|
|
2223
|
-
validateTehai
|
|
2751
|
+
validateTehai,
|
|
2752
|
+
validateTehai13,
|
|
2753
|
+
validateTehai14
|
|
2224
2754
|
};
|
|
2225
2755
|
//# sourceMappingURL=index.js.map
|