123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- """ 立直麻将和牌点数计算
- 使用 https://github.com/MahjongRepository/mahjong"""
- from mahjong.hand_calculating.hand import HandCalculator
- from mahjong.hand_calculating.hand_response import HandResponse
- from mahjong.hand_calculating.hand_config import HandConfig, OptionalRules
- from mahjong.tile import TilesConverter
- from mahjong.constants import (EAST, WEST, SOUTH, NORTH, HAKU, HATSU, CHUN, FIVE_RED_MAN, FIVE_RED_PIN, FIVE_RED_SOU)
- from mahjong.hand_calculating.yaku import Yaku
- from mahjong.hand_calculating.yaku_list import (
- AkaDora,
- Chankan,
- Chantai,
- Chiitoitsu,
- Chinitsu,
- Chun,
- DaburuOpenRiichi,
- DaburuRiichi,
- Dora,
- Haitei,
- Haku,
- Hatsu,
- Honitsu,
- Honroto,
- Houtei,
- Iipeiko,
- Ippatsu,
- Ittsu,
- Junchan,
- NagashiMangan,
- OpenRiichi,
- Pinfu,
- Renhou,
- Riichi,
- Rinshan,
- Ryanpeikou,
- Sanankou,
- SanKantsu,
- Sanshoku,
- SanshokuDoukou,
- Shosangen,
- Tanyao,
- Toitoi,
- Tsumo,
- YakuhaiEast,
- YakuhaiNorth,
- YakuhaiOfPlace,
- YakuhaiOfRound,
- YakuhaiSouth,
- YakuhaiWest,
- )
- from mahjong.hand_calculating.yaku_list.yakuman import (
- Chiihou,
- Chinroutou,
- ChuurenPoutou,
- DaburuChuurenPoutou,
- DaburuKokushiMusou,
- Daichisei,
- Daisangen,
- Daisharin,
- DaiSuushii,
- KokushiMusou,
- Paarenchan,
- RenhouYakuman,
- Ryuuiisou,
- Sashikomi,
- Shousuushii,
- Suuankou,
- SuuankouTanki,
- Suukantsu,
- Tenhou,
- Tsuuiisou,
- )
- from tools.toolbase import *
- YAKU_CN_NAME = {
- AkaDora: "红宝牌",
- Tsumo: "自摸",
- Chankan: "抢杠",
- Chantai: "混全带幺九",
- Chiitoitsu: "七对子",
- Chinitsu: "清一色",
- Chun: "中",
- DaburuOpenRiichi: "两立直",
- DaburuRiichi: "双立直",
- Dora: "宝牌",
- Haitei: "海底捞月",
- Haku: "白",
- Hatsu: "发",
- Honitsu: "混一色",
- Honroto: "混老头",
- Houtei: "河底捞鱼",
- Iipeiko: "一杯口",
- Ippatsu: "一发",
- Ittsu: "一气通贯",
- Junchan: "纯全带幺九",
- NagashiMangan: "流局满贯",
- OpenRiichi: "明牌立直",
- Pinfu: "平和",
- Renhou: "人和",
- Riichi: "立直",
- Rinshan: "岭上开花",
- Ryanpeikou: "两杯口",
- Sanankou: "三暗刻",
- SanKantsu: "三杠子",
- Sanshoku: "三色同顺",
- SanshokuDoukou: "三色同刻",
- Shosangen: "小三元",
- Tanyao: "断幺九",
- Toitoi: "对对和",
- YakuhaiEast: "东",
- YakuhaiNorth: "北",
- YakuhaiOfPlace: "自风",
- YakuhaiOfRound: "场风",
- YakuhaiSouth: "南",
- YakuhaiWest: "西",
- # 役满以上
- Chiihou: "地和",
- Chinroutou: "清老头",
- ChuurenPoutou: "九莲宝灯",
- DaburuChuurenPoutou: "纯正九莲宝灯",
- DaburuKokushiMusou: "国士无双十三面",
- Daichisei: "大七星",
- Daisangen: "大三元",
- Daisharin: "大车轮",
- DaiSuushii: "大四喜",
- KokushiMusou: "国士无双",
- Paarenchan: "八连庄",
- RenhouYakuman: "人和役满",
- Ryuuiisou: "绿一色",
- Sashikomi: "放铳",
- Shousuushii: "小四喜",
- Suuankou: "四暗刻",
- SuuankouTanki: "四暗刻单骑",
- Suukantsu: "四杠子",
- Tenhou: "天和",
- Tsuuiisou: "字一色",
- }
- class Tool_mahjong_agari(ToolBase):
- """ 麻将和牌计算 """
- @property
- def name(self) -> str:
- return "mahjong_agari"
- @property
- def desc(self) -> str:
- return "计算日本麻将和牌点数, 番数, 符数, 以及役种"
- @property
- def function_json(self) -> dict:
- function_definition = {
- "name": "mahjong_agari",
- "description": """计算日本麻将(立直麻将)规则下, 和牌的点数, 番数, 符数, 以及役种。
- 输入牌型时, 用m代表万, p代表筒, s代表条, z代表字牌。赤宝牌用0表示, 比如0m表示赤五万。
- 对于场风和自风, 用EAST表示东, SOUTH表示南, WEST表示西, NORTH表示北。
- 如果某个参数用户没有输入, 则使用默认值""",
- "parameters": {
- "type": "object",
- "properties": {
- "hand_tiles": {
- "type": "string",
- "description": "和牌时的手牌, 包含 win_tile, 例如: 123456m123p123s11z. 手牌需要包含所有副露的牌。如果手牌小于14张,请将win_tile加入到手牌中"
- },
- "win_tile": {
- "type": "string",
- "description": "和牌时自摸或者荣和的那张牌。例如: 1p。如果用户没有提供, 则假设手牌输入的最后一张牌为 win_tile"
- },
- "dora_indicators":{
- "type": "string",
- "description": "宝牌指示牌列表, 例: 1m3z。可以为空"
- },
- "round_wind":{
- "type": "string",
- "description": "场风. 默认为东风 EAST",
- "enum": ["EAST", "SOUTH", "WEST", "NORTH"]
- },
- "player_wind":{
- "type": "string",
- "description": "自风. 默认为东风 EAST",
- "enum": ["EAST", "SOUTH", "WEST", "NORTH"]
- },
- "is_trumo":{
- "type": "boolean",
- "description": "是否自摸, 如果自摸和牌为True, 荣和他人和牌则为False. 默认为自摸 True"
- },
- "is_riichi":{
- "type": "boolean",
- "description": "是否已经立直, 如果立直则为True。默认为不立直,为False"
- }
- },
- "required": ["hand_tiles","win_tile"]
- }
- }
- return function_definition
- def process_toolcall(self, arguments:str, callback_msg:MSG_CALLBACK) -> str:
- """ 计算日麻和牌打点,役种等
- 使用方法: 输入手牌,自摸或荣和的牌,以及其他可选信息:是否立直,场风,自风,宝牌。计算和牌役种和点数
- """
- args = json.loads(arguments)
- try:
- # 计算手牌必须包含和牌牌
- hand_tiles = TilesConverter.one_line_string_to_136_array(args["hand_tiles"],True)
- win_tile = TilesConverter.one_line_string_to_136_array(args["win_tile"],True)[0]
- dora_indi = args.get("dora_indicators", None)
- if not dora_indi:
- dora_indi = None
- else:
- dora_indi = TilesConverter.one_line_string_to_136_array(dora_indi, True)
- round_wind = args.get("round_wind", "EAST")
- round_wind = str_to_wind(round_wind)
- player_wind = args.get("player_wind", "EAST")
- player_wind = str_to_wind(player_wind)
- is_trumo = args.get("is_trumo", True)
- is_riichi = args.get("is_riichi", False)
- calculator = HandCalculator()
- note = f"正在计算和牌点数:{args['hand_tiles']}"
- callback_msg(ChatMsg(ContentType.text, note))
- # log_msg = f"计算和牌点数, hand={args['hand_tiles']}, win_tile={args['win_tile']}, dora_indicators={args.get('dora_indicators', None)}"
- # common.logger().info(log_msg)
- config = HandConfig(is_tsumo=is_trumo, is_riichi=is_riichi,
- round_wind=round_wind, player_wind=player_wind,
- options=OptionalRules(has_aka_dora=True, has_open_tanyao=True))
- agari:HandResponse = calculator.estimate_hand_value(hand_tiles, win_tile,
- dora_indicators=dora_indi, config=config)
- if agari.error is not None:
- return agari.error
- print_str = f"和牌{agari.cost['total']}点"
- print_str += f"({agari.han}番{agari.fu}符)"
- yaku_str = ','.join([yaku_cn_name(y) for y in agari.yaku])
- print_str += f"\n役种:[{yaku_str}]"
- print_str += f"\n计算条件: 场风={wind_to_str(round_wind)}, 自风={wind_to_str(player_wind)}"
- if is_trumo:
- print_str += ", 自摸"
- else:
- print_str += ", 荣和"
- return str(print_str)
- except Exception as e:
- return f"计算和牌失败: {str(e)}"
- def yaku_cn_name(yaku:Yaku) -> str:
- """返回役的中文"""
- yaku_type = type(yaku)
- if yaku_type not in YAKU_CN_NAME:
- return "未知/不存在"
- ret_str = YAKU_CN_NAME[yaku_type]
- # 如果是dora, 显示数字个数
- if yaku_type in (AkaDora, Dora):
- ret_str += f"{yaku.han_closed}"
- return ret_str
- def wind_to_str(wind):
- """ 返回风字中文 """
- if wind == EAST:
- return "东"
- elif wind == SOUTH:
- return "南"
- elif wind == WEST:
- return "西"
- elif wind == NORTH:
- return "北"
- else:
- return "错误"
- def str_to_wind(wind_str:str):
- if wind_str == "EAST":
- return EAST
- elif wind_str == "SOUTH":
- return SOUTH
- elif wind_str == "WEST":
- return WEST
- elif wind_str == "NORTH":
- return NORTH
- else:
- return None
|