def hand_calculator(tiles, win_tile, config=HandConfig()): calculator = HandCalculator() tiles = TilesConverter.one_line_string_to_136_array(str(tiles), has_aka_dora=True) win_tile = TilesConverter.one_line_string_to_136_array( str(win_tile), has_aka_dora=True)[0] return calculator.estimate_hand_value(tiles, win_tile, config=config)
def test_one_line_string_to_136_array(self): initial_string = '789m456p555s11222z' tiles = TilesConverter.one_line_string_to_136_array(initial_string) self.assertEqual(len(tiles), 14) new_string = TilesConverter.to_one_line_string(tiles) self.assertEqual(initial_string, new_string)
def getTile(s: str) -> dict: #translate s into supported format if len(s) == 0: return {} tiles = TilesConverter.one_line_string_to_136_array(s, True) test = {} # check tiles length added = [] if len(tiles) % 3 == 0: #3n, add a random tile while True: tmp = randint(0, 135) if tmp not in tiles: tiles.append(tmp) added.append(tmp) break if len(tiles) % 3 == 1: #3n+1, add a random tile while True: tmp = randint(0, 135) if tmp not in tiles: tiles.append(tmp) added.append(tmp) break if len(added) > 0: test[-1] = added # now is a normal form tiles.sort() avaliable = [] for i in range(0, 136): if i not in tiles: avaliable.append(i) calculator = Shanten() baseShanten = calculator.calculate_shanten( TilesConverter.to_34_array(tiles)) if baseShanten == -1: return {-2: '已和牌'} #14*122 try tmp = copy.deepcopy(tiles) for t in tiles: tmp.remove(t) one_try = [] for i in avaliable: tmp.append(i) res = calculator.calculate_shanten( TilesConverter.to_34_array(sorted(tmp))) if res < baseShanten: one_try.append(i) tmp.remove(i) t = 4 * (t // 4) if len(one_try) > 0 and t not in test.keys(): test[t] = copy.deepcopy(one_try) tmp.append(t) return test
def win(): req = flask.request.get_json() calculator = HandCalculator() try: tiles = TilesConverter.one_line_string_to_136_array(req["hands"], has_aka_dora=True) except KeyError: return flask.jsonify({"error": "hands required"}), 400 except ValueError: return flask.jsonify({"error": "invalid hands"}), 400 try: win_tile = TilesConverter.one_line_string_to_136_array( req["win_tile"], has_aka_dora=True)[0] except KeyError: return flask.jsonify({"error": "win_tile required"}), 400 except ValueError: return flask.jsonify({"error": "invalid win_tile"}), 400 melds = [] try: melds = req["melds"] except KeyError: pass meld_tiles = [] for meld in melds: try: m = meld["meld"] except KeyError: return flask.jsonify({"error": "meld required"}), 400 try: if m == "chi": meld_tiles.append( Meld( Meld.CHI, TilesConverter.one_line_string_to_136_array( meld["tiles"]))) elif m == "pon": meld_tiles.append( Meld( Meld.PON, TilesConverter.one_line_string_to_136_array( meld["tiles"]))) elif m == "minkan": meld_tiles.append( Meld( Meld.KAN, TilesConverter.one_line_string_to_136_array( meld["tiles"]), True)) elif m == "ankan": meld_tiles.append( Meld( Meld.KAN, TilesConverter.one_line_string_to_136_array( meld["tiles"]), False)) elif m == "kakan": meld_tiles.append( Meld( Meld.CHANKAN, TilesConverter.one_line_string_to_136_array( meld["tiles"]))) else: return flask.jsonify({"error": "invalid meld"}), 400 except KeyError: return flask.jsonify({"error": "meld tiles required"}), 400 except ValueError: return flask.jsonify({"error": "invalid meld tiles"}), 400 try: tsumo = req["tsumo"] except KeyError: tsumo = False try: riichi = req["riichi"] except KeyError: riichi = False try: ippatsu = req["ippatsu"] except KeyError: ippatsu = False try: rinshan = req["rinshan"] except KeyError: rinshan = False try: chankan = req["chankan"] except KeyError: chankan = False try: haitei = req["haitei"] except KeyError: haitei = False try: houtei = req["houtei"] except KeyError: houtei = False try: double_riichi = req["double_riichi"] except KeyError: double_riichi = False try: tenhou = req["tenhou"] except KeyError: tenhou = False try: chiihou = req["chiihou"] except KeyError: chiihou = False try: wind_player = req["wind_player"] if wind_player == "east": wind_player = EAST elif wind_player == "south": wind_player = SOUTH elif wind_player == "west": wind_player = WEST elif wind_player == "north": wind_player = NORTH else: return flask.jsonify({"error": "invalid wind_player"}), 400 except KeyError: return flask.jsonify({"error": "wind_player required"}), 400 try: wind_round = req["wind_round"] if wind_round == "east": wind_round = EAST elif wind_round == "south": wind_round = SOUTH elif wind_round == "west": wind_round = WEST elif wind_round == "north": wind_round = NORTH else: return flask.jsonify({"error": "invalid wind_round"}), 400 except KeyError: return flask.jsonify({"error": "wind_round required"}), 400 try: dora_indicators = TilesConverter.one_line_string_to_136_array( req["dora_indicators"], has_aka_dora=True) except KeyError: return flask.jsonify({"error": "dora_indicators required"}), 400 except ValueError: return flask.jsonify({"error": "invalid dora_indicators"}), 400 option = OptionalRules(has_open_tanyao=True, has_aka_dora=True, has_double_yakuman=False) config = HandConfig(is_tsumo=tsumo, is_riichi=riichi, is_ippatsu=ippatsu, is_rinshan=rinshan, is_chankan=chankan, is_haitei=haitei, is_houtei=houtei, is_daburu_riichi=double_riichi, is_tenhou=tenhou, is_chiihou=chiihou, player_wind=wind_player, round_wind=wind_round, options=option) result = calculator.estimate_hand_value(tiles, win_tile, dora_indicators=dora_indicators, config=config) if result.error: return flask.jsonify({"error": result.error}), 400 yaku = map(lambda y: y.japanese, result.yaku) return flask.jsonify({ "cost": result.cost, "han": result.han, "fu": result.fu, "yaku": list(yaku), "error": None })
def tsumo(self, draw, game): if draw in winning_tiles(str(self.hand)): calculator = HandCalculator() dora = str(game.dora) if self.in_riichi: dora += str(game.ura_dora) dora_indicators = TilesConverter.one_line_string_to_136_array( dora, has_aka_dora=True) haitei = False tenhou = False chiihou = False if game.wall.remaining == 0: haitei = True if game.tenhou and game.wall.remaining > 65: if self.seat == EAST: tenhou = True else: chiihou = True config = HandConfig(options = OptionalRules(has_open_tanyao = True, has_aka_dora = True),\ is_tsumo = True,\ is_riichi = self.in_riichi,\ is_ippatsu = self.ippatsu,\ is_rinshan = self.rinshan,\ is_haitei = haitei,\ is_daburu_riichi = self.double_riichi,\ is_tenhou = tenhou,\ is_chiihou = chiihou,\ player_wind = self.seat,\ round_wind = game.round_wind\ ) melds = [] meld_string = '' for meld in self.melds.melds: opened = True if len(meld) == 4: meld_type = mjMeld.KAN opened = meld.opened elif meld.tiles.count(meld.tiles[0]) == 3: meld_type = mjMeld.PON else: meld_type = mjMeld.CHI #not sure if the meld_type is necessary, but since it's not a hassle i'll leave it in new_meld = mjMeld(meld_type = meld_type,\ tiles = TilesConverter.one_line_string_to_136_array(str(meld), has_aka_dora = True),\ opened = opened) melds.append(new_meld) #really dumb workaround because the hand is supposed to be exactly 14 tiles #and adding the kan as a string directly to the rest of the hand #causes the hand to exceed 14 tiles #this would subtract by 4 if len(meld) referred to the length of the #string associated with the meld, but len(meld) actually refers #to the number of tiles in the meld (aka it ignores the suit) #so the length of '222p' is 3 and the length of '1111m' is 4 meld_string += str(meld)[len(meld) - 3:] hand = TilesConverter.one_line_string_to_136_array( str(self.hand) + str(draw) + str(meld_string), has_aka_dora=True) draw = TilesConverter.one_line_string_to_136_array( str(draw), has_aka_dora=True)[0] result = calculator.estimate_hand_value(hand, draw, melds = melds, \ dora_indicators = dora_indicators, config = config) if result.yaku and result.cost and not result.error: return result else: return False else: return False