mahjong_engine 1.0.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.
- package/.github/workflows/qlty-coverage.yml +36 -0
- package/dist/BlockDivider.d.ts +11 -0
- package/dist/BlockDivider.d.ts.map +1 -0
- package/dist/BlockDivider.js +114 -0
- package/dist/BlockDivider.js.map +1 -0
- package/dist/BlockHais.d.ts +26 -0
- package/dist/BlockHais.d.ts.map +1 -0
- package/dist/BlockHais.js +77 -0
- package/dist/BlockHais.js.map +1 -0
- package/dist/BlockHaisList.d.ts +19 -0
- package/dist/BlockHaisList.d.ts.map +1 -0
- package/dist/BlockHaisList.js +85 -0
- package/dist/BlockHaisList.js.map +1 -0
- package/dist/Hai.d.ts +16 -0
- package/dist/Hai.d.ts.map +1 -0
- package/dist/Hai.js +71 -0
- package/dist/Hai.js.map +1 -0
- package/dist/Hais.d.ts +15 -0
- package/dist/Hais.d.ts.map +1 -0
- package/dist/Hais.js +44 -0
- package/dist/Hais.js.map +1 -0
- package/dist/IMentsu.d.ts +20 -0
- package/dist/IMentsu.d.ts.map +1 -0
- package/dist/IMentsu.js +3 -0
- package/dist/IMentsu.js.map +1 -0
- package/dist/MachiCalculator.d.ts +7 -0
- package/dist/MachiCalculator.d.ts.map +1 -0
- package/dist/MachiCalculator.js +25 -0
- package/dist/MachiCalculator.js.map +1 -0
- package/dist/MahjongConsts.d.ts +39 -0
- package/dist/MahjongConsts.d.ts.map +1 -0
- package/dist/MahjongConsts.js +54 -0
- package/dist/MahjongConsts.js.map +1 -0
- package/dist/Meld.d.ts +25 -0
- package/dist/Meld.d.ts.map +1 -0
- package/dist/Meld.js +72 -0
- package/dist/Meld.js.map +1 -0
- package/dist/Melds.d.ts +9 -0
- package/dist/Melds.d.ts.map +1 -0
- package/dist/Melds.js +18 -0
- package/dist/Melds.js.map +1 -0
- package/dist/MentsuAnalyzer.d.ts +11 -0
- package/dist/MentsuAnalyzer.d.ts.map +1 -0
- package/dist/MentsuAnalyzer.js +23 -0
- package/dist/MentsuAnalyzer.js.map +1 -0
- package/dist/PlayerContext.d.ts +28 -0
- package/dist/PlayerContext.d.ts.map +1 -0
- package/dist/PlayerContext.js +28 -0
- package/dist/PlayerContext.js.map +1 -0
- package/dist/PlayerHand.d.ts +12 -0
- package/dist/PlayerHand.d.ts.map +1 -0
- package/dist/PlayerHand.js +28 -0
- package/dist/PlayerHand.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/tensuu/FuCalculator.d.ts +15 -0
- package/dist/tensuu/FuCalculator.d.ts.map +1 -0
- package/dist/tensuu/FuCalculator.js +104 -0
- package/dist/tensuu/FuCalculator.js.map +1 -0
- package/dist/tensuu/FuDetail.d.ts +8 -0
- package/dist/tensuu/FuDetail.d.ts.map +1 -0
- package/dist/tensuu/FuDetail.js +15 -0
- package/dist/tensuu/FuDetail.js.map +1 -0
- package/dist/tensuu/ScoreResolver.d.ts +12 -0
- package/dist/tensuu/ScoreResolver.d.ts.map +1 -0
- package/dist/tensuu/ScoreResolver.js +33 -0
- package/dist/tensuu/ScoreResolver.js.map +1 -0
- package/dist/tensuu/ScoreResult.d.ts +11 -0
- package/dist/tensuu/ScoreResult.d.ts.map +1 -0
- package/dist/tensuu/ScoreResult.js +19 -0
- package/dist/tensuu/ScoreResult.js.map +1 -0
- package/dist/tensuu/TensuuCalculator.d.ts +13 -0
- package/dist/tensuu/TensuuCalculator.d.ts.map +1 -0
- package/dist/tensuu/TensuuCalculator.js +39 -0
- package/dist/tensuu/TensuuCalculator.js.map +1 -0
- package/dist/tensuu/TensuuResult.d.ts +15 -0
- package/dist/tensuu/TensuuResult.d.ts.map +1 -0
- package/dist/tensuu/TensuuResult.js +19 -0
- package/dist/tensuu/TensuuResult.js.map +1 -0
- package/dist/tileDefs.d.ts +42 -0
- package/dist/tileDefs.d.ts.map +1 -0
- package/dist/tileDefs.js +23 -0
- package/dist/tileDefs.js.map +1 -0
- package/dist/yaku/NormalYakuChecker.d.ts +7 -0
- package/dist/yaku/NormalYakuChecker.d.ts.map +1 -0
- package/dist/yaku/NormalYakuChecker.js +37 -0
- package/dist/yaku/NormalYakuChecker.js.map +1 -0
- package/dist/yaku/YakuChecker.d.ts +7 -0
- package/dist/yaku/YakuChecker.d.ts.map +1 -0
- package/dist/yaku/YakuChecker.js +27 -0
- package/dist/yaku/YakuChecker.js.map +1 -0
- package/dist/yaku/YakuCheckerBase.d.ts +33 -0
- package/dist/yaku/YakuCheckerBase.d.ts.map +1 -0
- package/dist/yaku/YakuCheckerBase.js +154 -0
- package/dist/yaku/YakuCheckerBase.js.map +1 -0
- package/dist/yaku/YakuContext.d.ts +14 -0
- package/dist/yaku/YakuContext.d.ts.map +1 -0
- package/dist/yaku/YakuContext.js +21 -0
- package/dist/yaku/YakuContext.js.map +1 -0
- package/dist/yaku/YakumanChecker.d.ts +7 -0
- package/dist/yaku/YakumanChecker.d.ts.map +1 -0
- package/dist/yaku/YakumanChecker.js +36 -0
- package/dist/yaku/YakumanChecker.js.map +1 -0
- package/dist/yaku/index.d.ts +6 -0
- package/dist/yaku/index.d.ts.map +1 -0
- package/dist/yaku/index.js +79 -0
- package/dist/yaku/index.js.map +1 -0
- package/dist/yaku/normal/ChinitsuChecker.d.ts +9 -0
- package/dist/yaku/normal/ChinitsuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/ChinitsuChecker.js +24 -0
- package/dist/yaku/normal/ChinitsuChecker.js.map +1 -0
- package/dist/yaku/normal/ChitoitsuChecker.d.ts +8 -0
- package/dist/yaku/normal/ChitoitsuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/ChitoitsuChecker.js +20 -0
- package/dist/yaku/normal/ChitoitsuChecker.js.map +1 -0
- package/dist/yaku/normal/DoukouChecker.d.ts +8 -0
- package/dist/yaku/normal/DoukouChecker.d.ts.map +1 -0
- package/dist/yaku/normal/DoukouChecker.js +17 -0
- package/dist/yaku/normal/DoukouChecker.js.map +1 -0
- package/dist/yaku/normal/HonchanChecker.d.ts +8 -0
- package/dist/yaku/normal/HonchanChecker.d.ts.map +1 -0
- package/dist/yaku/normal/HonchanChecker.js +22 -0
- package/dist/yaku/normal/HonchanChecker.js.map +1 -0
- package/dist/yaku/normal/HonitsuChecker.d.ts +8 -0
- package/dist/yaku/normal/HonitsuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/HonitsuChecker.js +20 -0
- package/dist/yaku/normal/HonitsuChecker.js.map +1 -0
- package/dist/yaku/normal/HonrotoChecker.d.ts +8 -0
- package/dist/yaku/normal/HonrotoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/HonrotoChecker.js +21 -0
- package/dist/yaku/normal/HonrotoChecker.js.map +1 -0
- package/dist/yaku/normal/IipekoChecker.d.ts +8 -0
- package/dist/yaku/normal/IipekoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/IipekoChecker.js +18 -0
- package/dist/yaku/normal/IipekoChecker.js.map +1 -0
- package/dist/yaku/normal/IttsuuChecker.d.ts +8 -0
- package/dist/yaku/normal/IttsuuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/IttsuuChecker.js +25 -0
- package/dist/yaku/normal/IttsuuChecker.js.map +1 -0
- package/dist/yaku/normal/JunchanChecker.d.ts +8 -0
- package/dist/yaku/normal/JunchanChecker.d.ts.map +1 -0
- package/dist/yaku/normal/JunchanChecker.js +19 -0
- package/dist/yaku/normal/JunchanChecker.js.map +1 -0
- package/dist/yaku/normal/KazehaiChecker.d.ts +11 -0
- package/dist/yaku/normal/KazehaiChecker.d.ts.map +1 -0
- package/dist/yaku/normal/KazehaiChecker.js +24 -0
- package/dist/yaku/normal/KazehaiChecker.js.map +1 -0
- package/dist/yaku/normal/MenzenTsumoChecker.d.ts +8 -0
- package/dist/yaku/normal/MenzenTsumoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/MenzenTsumoChecker.js +16 -0
- package/dist/yaku/normal/MenzenTsumoChecker.js.map +1 -0
- package/dist/yaku/normal/PinfuChecker.d.ts +8 -0
- package/dist/yaku/normal/PinfuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/PinfuChecker.js +30 -0
- package/dist/yaku/normal/PinfuChecker.js.map +1 -0
- package/dist/yaku/normal/RenpuuhaiChecker.d.ts +8 -0
- package/dist/yaku/normal/RenpuuhaiChecker.d.ts.map +1 -0
- package/dist/yaku/normal/RenpuuhaiChecker.js +22 -0
- package/dist/yaku/normal/RenpuuhaiChecker.js.map +1 -0
- package/dist/yaku/normal/RyanpekoChecker.d.ts +8 -0
- package/dist/yaku/normal/RyanpekoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/RyanpekoChecker.js +18 -0
- package/dist/yaku/normal/RyanpekoChecker.js.map +1 -0
- package/dist/yaku/normal/SanankoChecker.d.ts +8 -0
- package/dist/yaku/normal/SanankoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/SanankoChecker.js +17 -0
- package/dist/yaku/normal/SanankoChecker.js.map +1 -0
- package/dist/yaku/normal/SankantsuChecker.d.ts +8 -0
- package/dist/yaku/normal/SankantsuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/SankantsuChecker.js +17 -0
- package/dist/yaku/normal/SankantsuChecker.js.map +1 -0
- package/dist/yaku/normal/SanshokuChecker.d.ts +8 -0
- package/dist/yaku/normal/SanshokuChecker.d.ts.map +1 -0
- package/dist/yaku/normal/SanshokuChecker.js +17 -0
- package/dist/yaku/normal/SanshokuChecker.js.map +1 -0
- package/dist/yaku/normal/ShosangenChecker.d.ts +8 -0
- package/dist/yaku/normal/ShosangenChecker.d.ts.map +1 -0
- package/dist/yaku/normal/ShosangenChecker.js +20 -0
- package/dist/yaku/normal/ShosangenChecker.js.map +1 -0
- package/dist/yaku/normal/TanyaoChecker.d.ts +8 -0
- package/dist/yaku/normal/TanyaoChecker.d.ts.map +1 -0
- package/dist/yaku/normal/TanyaoChecker.js +18 -0
- package/dist/yaku/normal/TanyaoChecker.js.map +1 -0
- package/dist/yaku/normal/ToitoiChecker.d.ts +8 -0
- package/dist/yaku/normal/ToitoiChecker.d.ts.map +1 -0
- package/dist/yaku/normal/ToitoiChecker.js +17 -0
- package/dist/yaku/normal/ToitoiChecker.js.map +1 -0
- package/dist/yaku/normal/YakuhaiChecker.d.ts +9 -0
- package/dist/yaku/normal/YakuhaiChecker.d.ts.map +1 -0
- package/dist/yaku/normal/YakuhaiChecker.js +22 -0
- package/dist/yaku/normal/YakuhaiChecker.js.map +1 -0
- package/dist/yaku/yakuman/ChinrotoChecker.d.ts +6 -0
- package/dist/yaku/yakuman/ChinrotoChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/ChinrotoChecker.js +13 -0
- package/dist/yaku/yakuman/ChinrotoChecker.js.map +1 -0
- package/dist/yaku/yakuman/Churen9Checker.d.ts +10 -0
- package/dist/yaku/yakuman/Churen9Checker.d.ts.map +1 -0
- package/dist/yaku/yakuman/Churen9Checker.js +17 -0
- package/dist/yaku/yakuman/Churen9Checker.js.map +1 -0
- package/dist/yaku/yakuman/ChurenChecker.d.ts +7 -0
- package/dist/yaku/yakuman/ChurenChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/ChurenChecker.js +33 -0
- package/dist/yaku/yakuman/ChurenChecker.js.map +1 -0
- package/dist/yaku/yakuman/DaisangenChecker.d.ts +6 -0
- package/dist/yaku/yakuman/DaisangenChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/DaisangenChecker.js +15 -0
- package/dist/yaku/yakuman/DaisangenChecker.js.map +1 -0
- package/dist/yaku/yakuman/DaisushiChecker.d.ts +8 -0
- package/dist/yaku/yakuman/DaisushiChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/DaisushiChecker.js +20 -0
- package/dist/yaku/yakuman/DaisushiChecker.js.map +1 -0
- package/dist/yaku/yakuman/ExtendedYakumanChecker.d.ts +9 -0
- package/dist/yaku/yakuman/ExtendedYakumanChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/ExtendedYakumanChecker.js +18 -0
- package/dist/yaku/yakuman/ExtendedYakumanChecker.js.map +1 -0
- package/dist/yaku/yakuman/Kokushi13Checker.d.ts +10 -0
- package/dist/yaku/yakuman/Kokushi13Checker.d.ts.map +1 -0
- package/dist/yaku/yakuman/Kokushi13Checker.js +17 -0
- package/dist/yaku/yakuman/Kokushi13Checker.js.map +1 -0
- package/dist/yaku/yakuman/KokushiChecker.d.ts +7 -0
- package/dist/yaku/yakuman/KokushiChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/KokushiChecker.js +18 -0
- package/dist/yaku/yakuman/KokushiChecker.js.map +1 -0
- package/dist/yaku/yakuman/RyuisoChecker.d.ts +6 -0
- package/dist/yaku/yakuman/RyuisoChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/RyuisoChecker.js +17 -0
- package/dist/yaku/yakuman/RyuisoChecker.js.map +1 -0
- package/dist/yaku/yakuman/ShosushiChecker.d.ts +6 -0
- package/dist/yaku/yakuman/ShosushiChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/ShosushiChecker.js +15 -0
- package/dist/yaku/yakuman/ShosushiChecker.js.map +1 -0
- package/dist/yaku/yakuman/SuankoChecker.d.ts +7 -0
- package/dist/yaku/yakuman/SuankoChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/SuankoChecker.js +17 -0
- package/dist/yaku/yakuman/SuankoChecker.js.map +1 -0
- package/dist/yaku/yakuman/SuankoTankiChecker.d.ts +8 -0
- package/dist/yaku/yakuman/SuankoTankiChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/SuankoTankiChecker.js +28 -0
- package/dist/yaku/yakuman/SuankoTankiChecker.js.map +1 -0
- package/dist/yaku/yakuman/SukantsuChecker.d.ts +6 -0
- package/dist/yaku/yakuman/SukantsuChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/SukantsuChecker.js +12 -0
- package/dist/yaku/yakuman/SukantsuChecker.js.map +1 -0
- package/dist/yaku/yakuman/TsuisoChecker.d.ts +6 -0
- package/dist/yaku/yakuman/TsuisoChecker.d.ts.map +1 -0
- package/dist/yaku/yakuman/TsuisoChecker.js +13 -0
- package/dist/yaku/yakuman/TsuisoChecker.js.map +1 -0
- package/jest.config.js +8 -0
- package/modules/BlockDivider.ts +129 -0
- package/modules/BlockHais.ts +92 -0
- package/modules/BlockHaisList.ts +96 -0
- package/modules/Hai.ts +67 -0
- package/modules/Hais.ts +49 -0
- package/modules/IMentsu.ts +20 -0
- package/modules/MachiCalculator.ts +25 -0
- package/modules/MahjongConsts.ts +46 -0
- package/modules/Meld.ts +85 -0
- package/modules/Melds.ts +19 -0
- package/modules/MentsuAnalyzer.ts +37 -0
- package/modules/PlayerContext.ts +39 -0
- package/modules/PlayerHand.ts +32 -0
- package/modules/index.ts +11 -0
- package/modules/tensuu/FuCalculator.ts +118 -0
- package/modules/tensuu/FuDetail.ts +13 -0
- package/modules/tensuu/ScoreResolver.ts +35 -0
- package/modules/tensuu/ScoreResult.ts +24 -0
- package/modules/tensuu/TensuuCalculator.ts +40 -0
- package/modules/tensuu/TensuuResult.ts +21 -0
- package/modules/tileDefs.ts +24 -0
- package/modules/yaku/NormalYakuChecker.ts +31 -0
- package/modules/yaku/YakuChecker.ts +29 -0
- package/modules/yaku/YakuCheckerBase.ts +180 -0
- package/modules/yaku/YakuContext.ts +25 -0
- package/modules/yaku/YakumanChecker.ts +34 -0
- package/modules/yaku/index.ts +83 -0
- package/modules/yaku/normal/ChinitsuChecker.ts +25 -0
- package/modules/yaku/normal/ChitoitsuChecker.ts +20 -0
- package/modules/yaku/normal/DoukouChecker.ts +16 -0
- package/modules/yaku/normal/HonchanChecker.ts +24 -0
- package/modules/yaku/normal/HonitsuChecker.ts +20 -0
- package/modules/yaku/normal/HonrotoChecker.ts +23 -0
- package/modules/yaku/normal/IipekoChecker.ts +17 -0
- package/modules/yaku/normal/IttsuuChecker.ts +32 -0
- package/modules/yaku/normal/JunchanChecker.ts +20 -0
- package/modules/yaku/normal/KazehaiChecker.ts +25 -0
- package/modules/yaku/normal/MenzenTsumoChecker.ts +15 -0
- package/modules/yaku/normal/PinfuChecker.ts +26 -0
- package/modules/yaku/normal/RenpuuhaiChecker.ts +21 -0
- package/modules/yaku/normal/RyanpekoChecker.ts +17 -0
- package/modules/yaku/normal/SanankoChecker.ts +17 -0
- package/modules/yaku/normal/SankantsuChecker.ts +16 -0
- package/modules/yaku/normal/SanshokuChecker.ts +16 -0
- package/modules/yaku/normal/ShosangenChecker.ts +21 -0
- package/modules/yaku/normal/TanyaoChecker.ts +18 -0
- package/modules/yaku/normal/ToitoiChecker.ts +17 -0
- package/modules/yaku/normal/YakuhaiChecker.ts +22 -0
- package/modules/yaku/yakuman/ChinrotoChecker.ts +11 -0
- package/modules/yaku/yakuman/Churen9Checker.ts +16 -0
- package/modules/yaku/yakuman/ChurenChecker.ts +31 -0
- package/modules/yaku/yakuman/DaisangenChecker.ts +14 -0
- package/modules/yaku/yakuman/DaisushiChecker.ts +21 -0
- package/modules/yaku/yakuman/ExtendedYakumanChecker.ts +18 -0
- package/modules/yaku/yakuman/Kokushi13Checker.ts +16 -0
- package/modules/yaku/yakuman/KokushiChecker.ts +15 -0
- package/modules/yaku/yakuman/RyuisoChecker.ts +16 -0
- package/modules/yaku/yakuman/ShosushiChecker.ts +13 -0
- package/modules/yaku/yakuman/SuankoChecker.ts +14 -0
- package/modules/yaku/yakuman/SuankoTankiChecker.ts +28 -0
- package/modules/yaku/yakuman/SukantsuChecker.ts +9 -0
- package/modules/yaku/yakuman/TsuisoChecker.ts +10 -0
- package/package.json +28 -0
- package/tests/fu.test.ts +44 -0
- package/tests/machi/chinitsu.ts +137 -0
- package/tests/machi/chitoitsu.ts +12 -0
- package/tests/machi/kokushi.ts +22 -0
- package/tests/machi/maisuu.ts +77 -0
- package/tests/machi.test.ts +100 -0
- package/tests/machiType.test.ts +93 -0
- package/tests/score.test.ts +27 -0
- package/tests/tehaiCaseRunner.ts +57 -0
- package/tests/tensuu/fu.ts +272 -0
- package/tests/tensuu/score.ts +46 -0
- package/tests/tensuu/tensuuNum.ts +292 -0
- package/tests/tensuuFromNum.test.ts +15 -0
- package/tests/testConsts.ts +42 -0
- package/tests/toBlock.test.ts +155 -0
- package/tests/yaku/normal.ts +1046 -0
- package/tests/yaku/yakuman.ts +367 -0
- package/tests/yaku.test.ts +28 -0
- package/tsconfig.json +23 -0
package/modules/Hai.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { HaiType } from "./MahjongConsts";
|
|
2
|
+
import { TILE } from "./tileDefs";
|
|
3
|
+
|
|
4
|
+
export class Hai{
|
|
5
|
+
private readonly id: number;
|
|
6
|
+
|
|
7
|
+
constructor(id: number){
|
|
8
|
+
this.id = id;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get type(): HaiType{
|
|
12
|
+
if((TILE.MANZU as readonly number[]).includes(this.id)) return HaiType.MANZU;
|
|
13
|
+
if((TILE.PINZU as readonly number[]).includes(this.id)) return HaiType.PINZU;
|
|
14
|
+
if((TILE.SOUZU as readonly number[]).includes(this.id)) return HaiType.SOUZU;
|
|
15
|
+
if((TILE.JIHAI as readonly number[]).includes(this.id)) return HaiType.JIHAI;
|
|
16
|
+
return HaiType.BACK;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get num(): number{
|
|
20
|
+
if(this.type == HaiType.MANZU) return this.id;
|
|
21
|
+
if(this.type == HaiType.PINZU) return this.id - 9;
|
|
22
|
+
if(this.type == HaiType.SOUZU) return this.id - 18;
|
|
23
|
+
if(this.type == HaiType.JIHAI) return this.id;
|
|
24
|
+
return TILE.BACK;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get imageUrl(): string{
|
|
28
|
+
//萬子
|
|
29
|
+
if(this.type === HaiType.MANZU) return `m_${this.num}.png`;
|
|
30
|
+
//筒子
|
|
31
|
+
if(this.type === HaiType.PINZU) return `p_${this.num}.png`;
|
|
32
|
+
//索子
|
|
33
|
+
if(this.type === HaiType.SOUZU) return `s_${this.num}.png`;
|
|
34
|
+
//字牌
|
|
35
|
+
if(this.type === HaiType.JIHAI) return `j_${this.num - 27}.png`;
|
|
36
|
+
|
|
37
|
+
return `back.png`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isRoutouHai(): boolean {
|
|
41
|
+
return (this.num === 1 || this.num === 9) && this.isNumberHai();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
isNumberHai(): boolean {
|
|
45
|
+
return this.type === HaiType.MANZU || this.type === HaiType.PINZU || this.type === HaiType.SOUZU;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
isJihai(): boolean {
|
|
49
|
+
return this.type === HaiType.JIHAI;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
isYaochuHai(): boolean {
|
|
53
|
+
return this.isRoutouHai() || this.isJihai();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
isChuchanHai(): boolean {
|
|
57
|
+
return !this.isYaochuHai();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getId(): number {
|
|
61
|
+
return this.id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
clone(): Hai {
|
|
65
|
+
return new Hai(this.id);
|
|
66
|
+
}
|
|
67
|
+
}
|
package/modules/Hais.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {Hai} from './Hai';
|
|
2
|
+
|
|
3
|
+
export class Hais{
|
|
4
|
+
private readonly hais: Hai[];
|
|
5
|
+
|
|
6
|
+
constructor(ids: number[] = []){
|
|
7
|
+
this.hais = ids.map(id => new Hai(id));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[Symbol.iterator]() { return this.hais[Symbol.iterator](); }
|
|
11
|
+
|
|
12
|
+
//末尾に追加
|
|
13
|
+
push(id: number){
|
|
14
|
+
this.hais.push(new Hai(id));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//末尾を取り除く
|
|
18
|
+
pop(): Hai | undefined {
|
|
19
|
+
return this.hais.pop()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//任意の牌を取り除く
|
|
23
|
+
remove(id: number) {
|
|
24
|
+
const index = this.hais.findIndex(h => h.getId() === id);
|
|
25
|
+
if(index !== -1) this.hais.splice(index, 1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//牌を昇順に並び替える
|
|
29
|
+
sort() {
|
|
30
|
+
this.hais.sort((a, b) => a.getId() - b.getId());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//指定された牌がいくつあるか数える
|
|
34
|
+
count(id: number): number {
|
|
35
|
+
return this.hais.filter(h => h.getId() === id).length;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getHais(): Hai[] {
|
|
39
|
+
return this.hais;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get ids(): number[] {
|
|
43
|
+
return this.hais.map(h => h.getId());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get length(): number {
|
|
47
|
+
return this.hais.length;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BlockType, MeldType } from "./MahjongConsts";
|
|
2
|
+
import { Hai } from "./Hai";
|
|
3
|
+
import { Wind } from "./tileDefs";
|
|
4
|
+
|
|
5
|
+
export interface IMentsu {
|
|
6
|
+
getType(): BlockType | MeldType;
|
|
7
|
+
getHais(): Hai[];
|
|
8
|
+
hasRoutouHai(): boolean;
|
|
9
|
+
hasJihai(): boolean;
|
|
10
|
+
isShuntsu(): boolean;
|
|
11
|
+
isKotsuOrKantsu(): boolean;
|
|
12
|
+
isDragon(): boolean;
|
|
13
|
+
isSingleWind(playerWind: Wind): boolean;
|
|
14
|
+
isDoubleWind(playerWind: Wind, roundWind: Wind): boolean
|
|
15
|
+
isYakuhai(playerWind: Wind, roundWind: Wind): boolean
|
|
16
|
+
containsHai(...haiIds: number[]): boolean;
|
|
17
|
+
clone(): IMentsu;
|
|
18
|
+
get minHai(): Hai;
|
|
19
|
+
get maxHai(): Hai;
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Hai } from './Hai';
|
|
2
|
+
import { BlockDivider } from './BlockDivider';
|
|
3
|
+
import { PAI_TYPE_NUM } from './MahjongConsts';
|
|
4
|
+
|
|
5
|
+
export class MachiCalculator{
|
|
6
|
+
private readonly hais: Hai[];
|
|
7
|
+
|
|
8
|
+
constructor(hais: Hai[]){
|
|
9
|
+
this.hais = hais.map(h => h.clone()).sort((a, b) => a.getId() - b.getId());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
calculate(): number[] {
|
|
13
|
+
const machi: number[] = [];
|
|
14
|
+
|
|
15
|
+
for(let i = 1; i <= PAI_TYPE_NUM; i++){
|
|
16
|
+
const testhais = [...this.hais, new Hai(i)];
|
|
17
|
+
const divided = new BlockDivider(testhais).divide();
|
|
18
|
+
if(divided.length > 0){
|
|
19
|
+
machi.push(i);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return machi;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//手配の数
|
|
2
|
+
export const TEHAI_NUMBER: number = 14;
|
|
3
|
+
//牌の種類数
|
|
4
|
+
export const PAI_TYPE_NUM: number = 34;
|
|
5
|
+
|
|
6
|
+
//鳴きのタイプ
|
|
7
|
+
export enum MeldType{
|
|
8
|
+
CHI = "chi",
|
|
9
|
+
PON = "pon",
|
|
10
|
+
ANKAN = "ankan",
|
|
11
|
+
MINKAN = "minkan"
|
|
12
|
+
}
|
|
13
|
+
//牌のタイプ
|
|
14
|
+
export enum HaiType{
|
|
15
|
+
MANZU,
|
|
16
|
+
PINZU,
|
|
17
|
+
SOUZU,
|
|
18
|
+
JIHAI,
|
|
19
|
+
BACK
|
|
20
|
+
}
|
|
21
|
+
//ブロックのタイプ
|
|
22
|
+
export enum BlockType{
|
|
23
|
+
JANTO = "janto",
|
|
24
|
+
KOTSU = "kotsu",
|
|
25
|
+
SHUNTSU = "shuntsu",
|
|
26
|
+
CHITOI = "chitoi",
|
|
27
|
+
KOKUSHI = "kokushi"
|
|
28
|
+
}
|
|
29
|
+
//待ちのタイプ
|
|
30
|
+
export enum MachiType{
|
|
31
|
+
TANKI,
|
|
32
|
+
SHANPON,
|
|
33
|
+
RYANMEN,
|
|
34
|
+
KANCHAN,
|
|
35
|
+
PENCHAN,
|
|
36
|
+
}
|
|
37
|
+
//アガリ時に付随する条件式の役
|
|
38
|
+
export enum WinEvent{
|
|
39
|
+
NONE,
|
|
40
|
+
TENHO,
|
|
41
|
+
CHIHO,
|
|
42
|
+
RINSHAN,
|
|
43
|
+
CHANKAN,
|
|
44
|
+
HAITEI,
|
|
45
|
+
HOUTEI
|
|
46
|
+
}
|
package/modules/Meld.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Hai } from './Hai';
|
|
2
|
+
import { MeldType } from './MahjongConsts';
|
|
3
|
+
import { IMentsu } from './IMentsu';
|
|
4
|
+
import { TILE, Wind } from "./tileDefs";
|
|
5
|
+
|
|
6
|
+
export class Meld implements IMentsu {
|
|
7
|
+
private readonly hais: Hai[];
|
|
8
|
+
private readonly type: MeldType;
|
|
9
|
+
|
|
10
|
+
constructor(hais: Hai[], type: MeldType){
|
|
11
|
+
this.hais = hais;
|
|
12
|
+
this.type = type;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static from(haiId: number, type: MeldType): Meld {
|
|
16
|
+
const hai = new Hai(haiId);
|
|
17
|
+
if(type === MeldType.PON){
|
|
18
|
+
return new Meld([hai.clone(), hai.clone(), hai.clone()], type);
|
|
19
|
+
}
|
|
20
|
+
if(type === MeldType.CHI){
|
|
21
|
+
return new Meld([hai.clone(), new Hai(haiId + 1), new Hai(haiId + 2)], type);
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
return new Meld([hai.clone(), hai.clone(), hai.clone(), hai.clone()], type);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getHais(): Hai[] {
|
|
28
|
+
return this.hais;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getType(): MeldType {
|
|
32
|
+
return this.type;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
hasRoutouHai(): boolean {
|
|
36
|
+
return this.hais.some(h => h.isRoutouHai());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
hasJihai(): boolean {
|
|
40
|
+
return this.hais.some(h => h.isJihai());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isShuntsu(): boolean {
|
|
44
|
+
return this.type === MeldType.CHI;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isKotsuOrKantsu(): boolean {
|
|
48
|
+
return this.type === MeldType.PON || this.type === MeldType.ANKAN || this.type === MeldType.MINKAN;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
isDragon(): boolean {
|
|
52
|
+
return this.containsHai(TILE.DRAGON.WHITE.id, TILE.DRAGON.GREEN.id, TILE.DRAGON.RED.id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
isSingleWind(targetWind: Wind): boolean {
|
|
56
|
+
return this.containsHai(targetWind.id);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
isDoubleWind(playerWind: Wind, roundWind: Wind): boolean {
|
|
60
|
+
return this.isSingleWind(playerWind) && this.isSingleWind(roundWind);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isYakuhai(playerWind: Wind, roundWind: Wind): boolean {
|
|
64
|
+
return this.isDragon() ||
|
|
65
|
+
this.isSingleWind(playerWind) ||
|
|
66
|
+
this.isSingleWind(roundWind) ||
|
|
67
|
+
this.isDoubleWind(playerWind, roundWind);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
containsHai(...haiIds: number[]): boolean {
|
|
71
|
+
return this.hais.some(h => haiIds.includes(h.getId()));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
clone(): Meld {
|
|
75
|
+
return new Meld(this.hais.map(h => h.clone()), this.type);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get minHai(): Hai {
|
|
79
|
+
return this.hais.reduce((min, h) => h.num < min.num ? h : min);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get maxHai(): Hai {
|
|
83
|
+
return this.hais.reduce((max, h) => h.num > max.num ? h : max);
|
|
84
|
+
}
|
|
85
|
+
}
|
package/modules/Melds.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {Meld} from "./Meld";
|
|
2
|
+
|
|
3
|
+
export class Melds {
|
|
4
|
+
private readonly melds: Meld[] = [];
|
|
5
|
+
|
|
6
|
+
constructor(melds: Meld[] = []) {
|
|
7
|
+
this.melds = melds;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[Symbol.iterator]() { return this.melds[Symbol.iterator](); }
|
|
11
|
+
|
|
12
|
+
add(meld: Meld){
|
|
13
|
+
this.melds.push(meld);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get length() {
|
|
17
|
+
return this.melds.length;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { IMentsu } from './IMentsu';
|
|
2
|
+
import { Wind } from './tileDefs';
|
|
3
|
+
|
|
4
|
+
export class MentsuAnalyzer {
|
|
5
|
+
private readonly allMentsu: IMentsu[];
|
|
6
|
+
constructor(block: IMentsu[], melds: IMentsu[]){
|
|
7
|
+
this.allMentsu = [...block, ...melds];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getAll(): IMentsu[]{
|
|
11
|
+
return this.allMentsu;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
hasSingleWindMentsu(
|
|
15
|
+
filter: (m: IMentsu) => boolean,
|
|
16
|
+
requiredWind: Wind,
|
|
17
|
+
forbiddenWind: Wind
|
|
18
|
+
): boolean {
|
|
19
|
+
return this.allMentsu.some(m => filter(m) && m.isSingleWind(requiredWind) && !m.isSingleWind(forbiddenWind));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
hasDoubleWindMentsu(
|
|
23
|
+
filter: (m: IMentsu) => boolean,
|
|
24
|
+
playerWind: Wind,
|
|
25
|
+
roundWind: Wind
|
|
26
|
+
): boolean {
|
|
27
|
+
return this.allMentsu.some(m => filter(m) && m.isDoubleWind(playerWind, roundWind));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
hasYakuhaiMentsu(
|
|
31
|
+
filter: (m: IMentsu) => boolean,
|
|
32
|
+
playerWind: Wind,
|
|
33
|
+
roundWind: Wind
|
|
34
|
+
): boolean {
|
|
35
|
+
return this.allMentsu.some(m => filter(m) && m.isYakuhai(playerWind, roundWind));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Hai } from "./Hai";
|
|
2
|
+
import { WinEvent } from "./MahjongConsts";
|
|
3
|
+
import { Wind } from "./tileDefs";
|
|
4
|
+
|
|
5
|
+
interface UserOptions {
|
|
6
|
+
agariHai: Hai
|
|
7
|
+
isTsumo: boolean;
|
|
8
|
+
playerWind: Wind;
|
|
9
|
+
roundWind: Wind;
|
|
10
|
+
event?: WinEvent;
|
|
11
|
+
riichi?: boolean;
|
|
12
|
+
daburii?: boolean;
|
|
13
|
+
ippatsu?: boolean;
|
|
14
|
+
kuitan?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class PlayerContext{
|
|
18
|
+
agariHai: Hai;
|
|
19
|
+
isTsumo: boolean;
|
|
20
|
+
playerWind: Wind;
|
|
21
|
+
roundWind: Wind;
|
|
22
|
+
event: WinEvent;
|
|
23
|
+
riichi: boolean;
|
|
24
|
+
daburii: boolean;
|
|
25
|
+
ippatsu: boolean;
|
|
26
|
+
kuitan: boolean;
|
|
27
|
+
|
|
28
|
+
constructor(options: UserOptions) {
|
|
29
|
+
this.agariHai = options.agariHai;
|
|
30
|
+
this.isTsumo = options.isTsumo;
|
|
31
|
+
this.playerWind = options.playerWind;
|
|
32
|
+
this.roundWind = options.roundWind;
|
|
33
|
+
this.event = options.event ?? WinEvent.NONE;
|
|
34
|
+
this.riichi = options.riichi ?? false;
|
|
35
|
+
this.daburii = options.daburii ?? false;
|
|
36
|
+
this.ippatsu = options.ippatsu ?? false;
|
|
37
|
+
this.kuitan = options.kuitan ?? false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Hai } from './Hai';
|
|
2
|
+
import { Meld } from './Meld';
|
|
3
|
+
import { MeldType } from './MahjongConsts';
|
|
4
|
+
|
|
5
|
+
export class PlayerHand {
|
|
6
|
+
private readonly tehai: Hai[];
|
|
7
|
+
private readonly furo: Meld[];
|
|
8
|
+
|
|
9
|
+
constructor(tehai: Hai[], furo: Meld[]){
|
|
10
|
+
this.tehai = tehai;
|
|
11
|
+
this.furo = furo;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getTehai(): Hai[] {
|
|
15
|
+
return this.tehai;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getFuro(): Meld[] {
|
|
19
|
+
return this.furo;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
isMenzen(){
|
|
23
|
+
return this.furo.every(meld => meld.getType() === MeldType.ANKAN);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getAllTiles(): Hai[] {
|
|
27
|
+
const hais = this.tehai.map(h => h.clone());
|
|
28
|
+
const meldHais = this.furo.flatMap(m => m.getHais().map(h => h.clone()));
|
|
29
|
+
return [...hais, ...meldHais];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
}
|
package/modules/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { Hai } from './Hai.js';
|
|
2
|
+
export { Hais } from './Hais.js';
|
|
3
|
+
export { Melds } from './Melds.js';
|
|
4
|
+
export { BlockDivider } from './BlockDivider.js';
|
|
5
|
+
export { PlayerHand } from './PlayerHand.js';
|
|
6
|
+
export { PlayerContext } from './PlayerContext.js';
|
|
7
|
+
export { ScoreResolver } from './tensuu/ScoreResolver.js';
|
|
8
|
+
export { YakuContext } from './yaku/YakuContext.js';
|
|
9
|
+
export { YakuChecker } from './yaku/YakuChecker.js';
|
|
10
|
+
export { TILE } from './tileDefs.js';
|
|
11
|
+
export { ScoreResult } from './tensuu/ScoreResult.js';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BlockHais } from '../BlockHais';
|
|
2
|
+
import { BlockType, MachiType, MeldType } from '../MahjongConsts';
|
|
3
|
+
import { MentsuAnalyzer } from '../MentsuAnalyzer';
|
|
4
|
+
import { YakuContext } from '../yaku/YakuContext';
|
|
5
|
+
import { FuDetail } from './FuDetail';
|
|
6
|
+
|
|
7
|
+
type MentsuFuSpec = {
|
|
8
|
+
yaochu: {name: string, fu: number};
|
|
9
|
+
chuchan: {name: string, fu: number};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const MENTSU_FU_TABLE: Record<string, MentsuFuSpec> = {
|
|
13
|
+
[BlockType.KOTSU]: {
|
|
14
|
+
yaochu: {name: "么九牌暗刻", fu: 8},
|
|
15
|
+
chuchan: {name: "中張牌暗刻", fu: 4}
|
|
16
|
+
},
|
|
17
|
+
[MeldType.PON]: {
|
|
18
|
+
yaochu: {name: "么九牌明刻", fu: 4},
|
|
19
|
+
chuchan: {name: "中張牌明刻", fu: 2}
|
|
20
|
+
},
|
|
21
|
+
[MeldType.MINKAN]: {
|
|
22
|
+
yaochu: {name: "么九牌明槓", fu: 16},
|
|
23
|
+
chuchan: {name: "中張牌明槓", fu: 8}
|
|
24
|
+
},
|
|
25
|
+
[MeldType.ANKAN]: {
|
|
26
|
+
yaochu: {name: "么九牌暗槓", fu: 32},
|
|
27
|
+
chuchan: {name: "中張牌暗槓", fu: 16}
|
|
28
|
+
}
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export class FuCalculator{
|
|
32
|
+
private readonly context: YakuContext;
|
|
33
|
+
private readonly yaku: Map<string, number>;
|
|
34
|
+
|
|
35
|
+
constructor(context: YakuContext, yaku: Map<string, number>){
|
|
36
|
+
this.context = context;
|
|
37
|
+
this.yaku = yaku;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private calcSpecialFu(): FuDetail | null{
|
|
41
|
+
if(this.yaku.has("平和") && this.context.ctx.isTsumo) return new FuDetail("平和ツモ", 20);
|
|
42
|
+
if(this.yaku.has("七対子")) return new FuDetail("七対子", 25);
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private calcBaseFu(): FuDetail[]{
|
|
48
|
+
const baseFu: FuDetail[] = [new FuDetail("符底", 20)];
|
|
49
|
+
const isTsumo = this.context.ctx.isTsumo;
|
|
50
|
+
|
|
51
|
+
if(isTsumo) baseFu.push(new FuDetail("ツモ", 2));
|
|
52
|
+
if(!isTsumo && this.context.hand.isMenzen()) baseFu.push(new FuDetail("面前ロン", 10));
|
|
53
|
+
|
|
54
|
+
return baseFu;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private calcMachuFu(): FuDetail[]{
|
|
58
|
+
const machiFu: FuDetail[] = [];
|
|
59
|
+
const machiType = this.calcMachiType();
|
|
60
|
+
if(machiType.has(MachiType.TANKI)) machiFu.push(new FuDetail("単騎待ち", 2));
|
|
61
|
+
else if(machiType.has(MachiType.KANCHAN)) machiFu.push(new FuDetail("嵌張待ち", 2));
|
|
62
|
+
else if(machiType.has(MachiType.PENCHAN)) machiFu.push(new FuDetail("辺張待ち", 2));
|
|
63
|
+
|
|
64
|
+
return machiFu;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private calcJantoFu(analyzer: MentsuAnalyzer): FuDetail[]{
|
|
68
|
+
const JantoFu: FuDetail[] = [];
|
|
69
|
+
const pw = this.context.ctx.playerWind;
|
|
70
|
+
const rw = this.context.ctx.roundWind;
|
|
71
|
+
const hasYakuhaiJanto = analyzer.hasYakuhaiMentsu(m => m instanceof BlockHais && m.isJanto(), pw, rw);
|
|
72
|
+
|
|
73
|
+
if(!hasYakuhaiJanto) return JantoFu;
|
|
74
|
+
|
|
75
|
+
for(const m of analyzer.getAll()){
|
|
76
|
+
if(m.getType() === BlockType.JANTO){
|
|
77
|
+
JantoFu.push(new FuDetail("役牌雀頭", 2, m));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return JantoFu;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private calcMentsuFu(analyzer: MentsuAnalyzer): FuDetail[]{
|
|
85
|
+
const MentsuFu: FuDetail[] = [];
|
|
86
|
+
|
|
87
|
+
for(const m of analyzer.getAll()){
|
|
88
|
+
const table = MENTSU_FU_TABLE[m.getType()];
|
|
89
|
+
if(!table) continue;
|
|
90
|
+
|
|
91
|
+
const key = m.minHai.isYaochuHai() ? "yaochu" : "chuchan";
|
|
92
|
+
const spec = table[key];
|
|
93
|
+
|
|
94
|
+
MentsuFu.push(new FuDetail(spec.name, spec.fu, m));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return MentsuFu;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
calcFu(): FuDetail[]{
|
|
101
|
+
const special = this.calcSpecialFu();
|
|
102
|
+
if(special) return [special];
|
|
103
|
+
|
|
104
|
+
const analyzer = new MentsuAnalyzer(this.context.block.getBlockHais(), this.context.melds);
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
...this.calcBaseFu(),
|
|
108
|
+
...this.calcMachuFu(),
|
|
109
|
+
...this.calcJantoFu(analyzer),
|
|
110
|
+
...this.calcMentsuFu(analyzer),
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private calcMachiType(): Set<MachiType>{
|
|
115
|
+
const agariHaiId = this.context.ctx.agariHai.getId();
|
|
116
|
+
return this.context.block.calcMachiType(agariHaiId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IMentsu } from "../IMentsu";
|
|
2
|
+
|
|
3
|
+
export class FuDetail{
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly fu: number;
|
|
6
|
+
readonly mentsu?: IMentsu;
|
|
7
|
+
|
|
8
|
+
constructor(name: string, fu: number, mentsu?: IMentsu){
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.fu = fu;
|
|
11
|
+
this.mentsu = mentsu;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { YakuContext } from '../yaku/YakuContext';
|
|
2
|
+
import { TensuuCalculator } from './TensuuCalculator';
|
|
3
|
+
import { FuCalculator } from './FuCalculator';
|
|
4
|
+
import { FuDetail } from './FuDetail';
|
|
5
|
+
import { ScoreResult } from './ScoreResult';
|
|
6
|
+
|
|
7
|
+
export class ScoreResolver{
|
|
8
|
+
private readonly context: YakuContext;
|
|
9
|
+
private readonly yaku: Map<string, number>;
|
|
10
|
+
constructor(context: YakuContext, yaku: Map<string, number>){
|
|
11
|
+
this.context = context;
|
|
12
|
+
this.yaku = yaku;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private calcHonsuu(): number{
|
|
16
|
+
return [...this.yaku.values()].reduce((sum, val) => sum + val, 0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private countFusuu(detail: FuDetail[]): number{
|
|
20
|
+
return detail.reduce((sum, val) => sum + val.fu, 0);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private ceilFusuu(fu: number): number{
|
|
24
|
+
return fu === 25 ? 25 : Math.ceil(fu / 10) * 10;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
resolve(){
|
|
28
|
+
const han = this.calcHonsuu();
|
|
29
|
+
const fuDetail = new FuCalculator(this.context, this.yaku).calcFu();
|
|
30
|
+
const fuBasic = this.countFusuu(fuDetail);
|
|
31
|
+
const fuCeiled = this.ceilFusuu(fuBasic);
|
|
32
|
+
const tensuu = TensuuCalculator.calcTensuu(han, fuCeiled);
|
|
33
|
+
return new ScoreResult(han, fuBasic, fuCeiled, tensuu, fuDetail);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FuDetail } from "./FuDetail";
|
|
2
|
+
import { TensuuResult } from "./TensuuResult";
|
|
3
|
+
|
|
4
|
+
export class ScoreResult{
|
|
5
|
+
public readonly han: number;
|
|
6
|
+
public readonly fuBasic: number;
|
|
7
|
+
public readonly fuCeiled: number;
|
|
8
|
+
public readonly tensuu: TensuuResult;
|
|
9
|
+
public readonly fuDetail: FuDetail[];
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
han: number,
|
|
13
|
+
fuBasic: number,
|
|
14
|
+
fuCeiled: number,
|
|
15
|
+
tensuu: TensuuResult,
|
|
16
|
+
fuDetail: FuDetail[]
|
|
17
|
+
){
|
|
18
|
+
this.han = han;
|
|
19
|
+
this.fuBasic = fuBasic;
|
|
20
|
+
this.fuCeiled = fuCeiled;
|
|
21
|
+
this.tensuu = tensuu;
|
|
22
|
+
this.fuDetail = fuDetail;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { TensuuResult } from './TensuuResult';
|
|
2
|
+
|
|
3
|
+
export class TensuuCalculator{
|
|
4
|
+
static readonly BASE_LIMITS = [
|
|
5
|
+
{han: 26, base: 16000 },
|
|
6
|
+
{han: 13, base: 8000 },
|
|
7
|
+
{han: 11, base: 6000 },
|
|
8
|
+
{han: 8, base: 4000 },
|
|
9
|
+
{han: 6, base: 3000 },
|
|
10
|
+
{han: 5, base: 2000 },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
static calcBaseTensuu(han: number, fu: number): number{
|
|
14
|
+
return Math.min(fu * Math.pow(2, han + 2), 2000);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static calcRonTensuu(base: number, multi: number): number{
|
|
18
|
+
return Math.ceil(base * multi / 100) * 100;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static calcdividedTensuu(ronTensuu: number, divide: number){
|
|
22
|
+
return Math.ceil(Math.floor(ronTensuu / divide) / 100) * 100;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static calcTensuuFromBase(base: number): TensuuResult{
|
|
26
|
+
const ronOya = this.calcRonTensuu(base, 6);
|
|
27
|
+
const ronKo = this.calcRonTensuu(base, 4);
|
|
28
|
+
const tsumoOya = this.calcdividedTensuu(ronOya, 3);
|
|
29
|
+
const tsumoKo = {oya: this.calcdividedTensuu(ronKo, 2), ko: this.calcdividedTensuu(ronKo, 4)};
|
|
30
|
+
|
|
31
|
+
return new TensuuResult(base, ronOya, ronKo, tsumoOya, tsumoKo);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static calcTensuu(han: number, fu: number): TensuuResult{
|
|
35
|
+
for(const limit of this.BASE_LIMITS){
|
|
36
|
+
if(han >= limit.han) return this.calcTensuuFromBase(limit.base);
|
|
37
|
+
}
|
|
38
|
+
return this.calcTensuuFromBase(this.calcBaseTensuu(han, fu));
|
|
39
|
+
}
|
|
40
|
+
}
|