From 0cba7cbc6cf225245e35aba143a0c5a9754c5a7f Mon Sep 17 00:00:00 2001 From: shinkuan Date: Mon, 14 Aug 2023 09:25:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=97=E6=A7=93=E3=81=AB=E5=AF=BE=E3=81=99?= =?UTF-8?q?=E3=82=8B=E5=9B=BD=E5=A3=AB=E7=84=A1=E5=8F=8C=E3=81=AE=E6=A7=8D?= =?UTF-8?q?=E6=A7=93=E3=82=92=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=81=99?= =?UTF-8?q?=E3=82=8B=EF=BC=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _kanachan.py | 17 +++++++++++++-- hand_calculator.py | 53 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/_kanachan.py b/_kanachan.py index ee45e85..bb9f769 100644 --- a/_kanachan.py +++ b/_kanachan.py @@ -607,6 +607,14 @@ ) +_TILE34TILE37 = ( + 1, 2, 3, 4, 5, 6, 7, 8, 9, + 11,12,13,14,15,16,17,18,19, + 21,22,23,24,25,26,27,28,29, + 30,31,32,33,34,35,36 +) + + class GameState: def __init__( self, *, my_name: str, room: int, game_style: int, my_grade: int, @@ -1170,8 +1178,13 @@ def on_angang( self.__progression.append(1881 + actor * 34 + angang) if seat != actor: - # TODO: 暗槓に対する国士無双の槍槓をチェックする. - return None + # 暗槓に対する国士無双の槍槓をチェックする. + player_wind = (seat + 4 - self.__index) % 4 + can_ron_kokushi = self.__hand_calculator.check_kokushi( + self.__chang, player_wind, self.__my_hand, self.__my_fulu_list, + _TILE34TILE37[angang], rong=False) + if not can_ron_kokushi: + return None if self.__zimo_pai is None: raise RuntimeError('TODO: (A suitable error message)') diff --git a/hand_calculator.py b/hand_calculator.py index 44106ee..b857fe7 100644 --- a/hand_calculator.py +++ b/hand_calculator.py @@ -4,6 +4,7 @@ from mahjong.meld import Meld from mahjong.hand_calculating.hand_config import (OptionalRules, HandConfig,) from mahjong.hand_calculating.hand import HandCalculator as Impl +from mahjong.hand_calculating.yaku_list import yakuman _FULU2MELD = { @@ -307,7 +308,7 @@ def has_yihan( melds = [] # `tiles` には副露牌も含めなければならない. - # ただし,槓は3枚としてカウントする. + # ただし,槓は4枚としてカウントする. for fulu in fulu_list: if 148 <= fulu and fulu <= 181: meld = _FULU2MELD[fulu] @@ -397,3 +398,53 @@ def has_yihan( raise e return response.han >= 1 + + def check_kokushi( + self, chang: int, player_wind: int, hand: List[int], + fulu_list: List[int], hupai: int, rong: bool): + options = OptionalRules(has_open_tanyao=True, has_aka_dora=True) + config = HandConfig( + is_tsumo=not rong, player_wind=27 + player_wind, + round_wind=27 + chang, options=options) + + hand_calculator = Impl() + + if len(fulu_list) > 0 or len(hand) != 13: + return False + + tiles = [] + for tile in hand: + flag = False + first, last = _TILE_OFFSET_RANGE[tile] + for t in range(first, last): + if t not in tiles: + tiles.append(t) + flag = True + break + if not flag: + raise RuntimeError('TODO: (A suitable error message)') + flag = False + first, last = _TILE_OFFSET_RANGE[hupai] + for t in range(first, last): + if t not in tiles: + tiles.append(t) + flag = True + break + if not flag: + raise RuntimeError('TODO: (A suitable error message)') + tiles.sort() + hupai = _TILE_OFFSET_RANGE[hupai][0] + + try: + response = hand_calculator.estimate_hand_value( + tiles=tiles, win_tile=hupai, config=config) + if response.error is not None: + if response.error == 'no_yaku': + return False + raise RuntimeError(response.error) + except Exception as e: + import sys + print(f'tiles = {tiles}', file=sys.stderr) + print(f'hupai = {hupai}', file=sys.stderr) + raise e + return any([isinstance(yaku, yakuman.DaburuKokushiMusou) or isinstance(yaku, yakuman.KokushiMusou) for yaku in response.yaku]) \ No newline at end of file