示例#1
0
    def test_second_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(sou="111123666789", honors="11")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ["111s", "123s", "666s", "789s", "11z"])
    def test_second_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(sou='111123666789', honors='11')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ['111s', '123s', '666s', '789s', '11z'])
示例#3
0
    def test_simple_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man="234567",
                                            sou="23455",
                                            honors="777")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ["234m", "567m", "234s", "55s", "777z"])
示例#4
0
    def test_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man="11122233388899")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 2)
        self.assertEqual(self._string(result[0]),
                         ["111m", "222m", "333m", "888m", "99m"])
        self.assertEqual(self._string(result[1]),
                         ["123m", "123m", "123m", "888m", "99m"])
    def test_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man='11122233388899')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 2)
        self.assertEqual(self._string(result[0]),
                         ['111m', '222m', '333m', '888m', '99m'])
        self.assertEqual(self._string(result[1]),
                         ['123m', '123m', '123m', '888m', '99m'])
示例#6
0
    def test_second_simple_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man='123', pin='123', sou='123', honors='11222')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(
            self._string(result[0]),
            ['123m', '123p', '123s', '11z', '222z']
        )
示例#7
0
    def test_simple_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man='234567', sou='23455', honors='777')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(
            self._string(result[0]),
            ['234m', '567m', '234s', '55s', '777z']
        )
示例#8
0
    def test_hand_with_pairs_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man="23444",
                                            pin="344556",
                                            sou="333")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ["234m", "44m", "345p", "456p", "333s"])
示例#9
0
    def test_hand_with_pairs_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man='23444', pin='344556', sou='333')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(
            self._string(result[0]),
            ['234m', '44m', '345p', '456p', '333s']
        )
示例#10
0
    def test_second_simple_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man="123",
                                            pin="123",
                                            sou="123",
                                            honors="11222")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ["123m", "123p", "123s", "11z", "222z"])
示例#11
0
    def __init__(self, player):
        super(ImplementationAI, self).__init__(player)

        self.agari = Agari()
        self.shanten_calculator = Shanten()
        self.defence = DefenceHandler(player)
        self.riichi = Riichi(player)
        self.hand_divider = HandDivider()
        self.finished_hand = HandCalculator()
        self.hand_builder = HandBuilder(player, self)

        self.erase_state()
示例#12
0
    def test_third_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(pin="234777888999", honors="22")
        melds = [
            self._make_meld(Meld.CHI, pin="789"),
            self._make_meld(Meld.CHI, pin="234"),
        ]
        result = hand.divide_hand(tiles_34, melds)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ["234p", "789p", "789p", "789p", "22z"])
示例#13
0
    def test_chitoitsu_like_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man="112233",
                                            pin="99",
                                            sou="445566")
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 2)
        self.assertEqual(self._string(result[0]),
                         ["11m", "22m", "33m", "99p", "44s", "55s", "66s"])
        self.assertEqual(self._string(result[1]),
                         ["123m", "123m", "99p", "456s", "456s"])
    def test_chitoitsu_like_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(man='112233',
                                            pin='99',
                                            sou='445566')
        result = hand.divide_hand(tiles_34)
        self.assertEqual(len(result), 2)
        self.assertEqual(self._string(result[0]),
                         ['11m', '22m', '33m', '99p', '44s', '55s', '66s'])
        self.assertEqual(self._string(result[1]),
                         ['123m', '123m', '99p', '456s', '456s'])
    def test_third_one_suit_hand_dividing(self):
        hand = HandDivider()

        tiles_34 = self._string_to_34_array(pin='234777888999', honors='22')
        melds = [
            self._make_meld(Meld.CHI, pin='789'),
            self._make_meld(Meld.CHI, pin='234'),
        ]
        result = hand.divide_hand(tiles_34, melds)
        self.assertEqual(len(result), 1)
        self.assertEqual(self._string(result[0]),
                         ['234p', '789p', '789p', '789p', '22z'])
示例#16
0
    def __init__(self, player):
        super(ImplementationAI, self).__init__(player)

        self.agari = Agari()
        self.shanten = Shanten()
        self.defence = DefenceHandler(player)
        self.hand_divider = HandDivider()
        self.finished_hand = HandCalculator()
        self.previous_shanten = 7
        self.current_strategy = None
        self.waiting = []
        self.in_defence = False
        self.last_discard_option = None
    def __init__(self, player):
        self.player = player
        self.table = player.table

        self.chi = Chi(player)
        self.pon = Pon(player)
        self.kan = Kan(player)
        self.riichi = Riichi(player)
        self.discard = Discard(player)
        self.grp = GlobalRewardPredictor()
        self.hand_builder = HandBuilder(player, self)
        self.shanten_calculator = Shanten()
        self.hand_cache_shanten = {}
        self.placement = player.config.PLACEMENT_HANDLER_CLASS(player)
        self.finished_hand = HandCalculator()
        self.hand_divider = HandDivider()

        self.erase_state()
示例#18
0
    def __init__(self, player):
        self.player = player
        self.table = player.table

        self.kan = Kan(player)
        self.agari = Agari()
        self.shanten_calculator = Shanten()
        self.defence = TileDangerHandler(player)
        self.riichi = Riichi(player)
        self.hand_divider = HandDivider()
        self.finished_hand = HandCalculator()
        self.hand_builder = HandBuilder(player, self)
        self.placement = player.config.PLACEMENT_HANDLER_CLASS(player)

        self.suji = Suji(player)
        self.kabe = Kabe(player)

        self.erase_state()
示例#19
0
 def _hand(self, tiles, hand_index=0):
     hand_divider = HandDivider()
     return hand_divider.divide_hand(tiles)[hand_index]
示例#20
0
 def __init__(self, player):
     super(ImplementationAI, self).__init__(player)
     self.shanten = Shanten()
     # self.agari = Agari()
     self.hand_divider = HandDivider()
示例#21
0
 def __init__(self):
     self.divider = HandDivider()
示例#22
0
    def estimate_hand_value(self,
                            tiles,
                            win_tile,
                            melds=None,
                            dora_indicators=None,
                            config=None):
        """
        :param tiles: array with 14 tiles in 136-tile format
        :param win_tile: 136 format tile that caused win (ron or tsumo)
        :param melds: array with Meld objects
        :param dora_indicators: array of tiles in 136-tile format
        :param config: HandConfig object
        :return: HandResponse object
        """

        if not melds:
            melds = []

        if not dora_indicators:
            dora_indicators = []

        self.config = config or HandConfig()

        agari = Agari()
        hand_yaku = []
        scores_calculator = ScoresCalculator()
        tiles_34 = TilesConverter.to_34_array(tiles)
        divider = HandDivider()
        fu_calculator = FuCalculator()

        opened_melds = [x.tiles_34 for x in melds if x.opened]
        all_melds = [x.tiles_34 for x in melds]
        is_open_hand = len(opened_melds) > 0

        # special situation
        if self.config.is_nagashi_mangan:
            hand_yaku.append(self.config.yaku.nagashi_mangan)
            fu = 30
            han = self.config.yaku.nagashi_mangan.han_closed
            cost = scores_calculator.calculate_scores(han, fu, self.config,
                                                      False)
            return HandResponse(cost, han, fu, hand_yaku)

        if win_tile not in tiles:
            return HandResponse(error="Win tile not in the hand")

        if self.config.is_riichi and is_open_hand:
            return HandResponse(
                error="Riichi can't be declared with open hand")

        if self.config.is_ippatsu and is_open_hand:
            return HandResponse(
                error="Ippatsu can't be declared with open hand")

        if self.config.is_ippatsu and not self.config.is_riichi and not self.config.is_daburu_riichi:
            return HandResponse(
                error="Ippatsu can't be declared without riichi")

        if not agari.is_agari(tiles_34, all_melds):
            return HandResponse(error='Hand is not winning')

        if not self.config.options.has_double_yakuman:
            self.config.yaku.daburu_kokushi.han_closed = 13
            self.config.yaku.suuankou_tanki.han_closed = 13
            self.config.yaku.daburu_chuuren_poutou.han_closed = 13
            self.config.yaku.daisuushi.han_closed = 13
            self.config.yaku.daisuushi.han_open = 13

        hand_options = divider.divide_hand(tiles_34, melds)

        calculated_hands = []
        for hand in hand_options:
            is_chiitoitsu = self.config.yaku.chiitoitsu.is_condition_met(hand)
            valued_tiles = [
                HAKU, HATSU, CHUN, self.config.player_wind,
                self.config.round_wind
            ]

            win_groups = self._find_win_groups(win_tile, hand, opened_melds)
            for win_group in win_groups:
                cost = None
                error = None
                hand_yaku = []
                han = 0

                fu_details, fu = fu_calculator.calculate_fu(
                    hand, win_tile, win_group, self.config, valued_tiles,
                    melds)

                is_pinfu = len(
                    fu_details) == 1 and not is_chiitoitsu and not is_open_hand

                pon_sets = [x for x in hand if is_pon(x)]
                chi_sets = [x for x in hand if is_chi(x)]

                if self.config.is_tsumo:
                    if not is_open_hand:
                        hand_yaku.append(self.config.yaku.tsumo)

                if is_pinfu:
                    hand_yaku.append(self.config.yaku.pinfu)

                # let's skip hand that looks like chitoitsu, but it contains open sets
                if is_chiitoitsu and is_open_hand:
                    continue

                if is_chiitoitsu:
                    hand_yaku.append(self.config.yaku.chiitoitsu)

                is_daisharin = self.config.yaku.daisharin.is_condition_met(
                    hand, self.config.options.has_daisharin_other_suits)
                if self.config.options.has_daisharin and is_daisharin:
                    self.config.yaku.daisharin.rename(hand)
                    hand_yaku.append(self.config.yaku.daisharin)

                is_tanyao = self.config.yaku.tanyao.is_condition_met(hand)
                if is_open_hand and not self.config.options.has_open_tanyao:
                    is_tanyao = False

                if is_tanyao:
                    hand_yaku.append(self.config.yaku.tanyao)

                if self.config.is_riichi and not self.config.is_daburu_riichi:
                    hand_yaku.append(self.config.yaku.riichi)

                if self.config.is_daburu_riichi:
                    hand_yaku.append(self.config.yaku.daburu_riichi)

                if self.config.is_ippatsu:
                    hand_yaku.append(self.config.yaku.ippatsu)

                if self.config.is_rinshan:
                    hand_yaku.append(self.config.yaku.rinshan)

                if self.config.is_chankan:
                    hand_yaku.append(self.config.yaku.chankan)

                if self.config.is_haitei:
                    hand_yaku.append(self.config.yaku.haitei)

                if self.config.is_houtei:
                    hand_yaku.append(self.config.yaku.houtei)

                if self.config.is_renhou:
                    if self.config.options.renhou_as_yakuman:
                        hand_yaku.append(self.config.yaku.renhou_yakuman)
                    else:
                        hand_yaku.append(self.config.yaku.renhou)

                if self.config.is_tenhou:
                    hand_yaku.append(self.config.yaku.tenhou)

                if self.config.is_chiihou:
                    hand_yaku.append(self.config.yaku.chiihou)

                if self.config.yaku.honitsu.is_condition_met(hand):
                    hand_yaku.append(self.config.yaku.honitsu)

                if self.config.yaku.chinitsu.is_condition_met(hand):
                    hand_yaku.append(self.config.yaku.chinitsu)

                if self.config.yaku.tsuisou.is_condition_met(hand):
                    hand_yaku.append(self.config.yaku.tsuisou)

                if self.config.yaku.honroto.is_condition_met(hand):
                    hand_yaku.append(self.config.yaku.honroto)

                if self.config.yaku.chinroto.is_condition_met(hand):
                    hand_yaku.append(self.config.yaku.chinroto)

                # small optimization, try to detect yaku with chi required sets only if we have chi sets in hand
                if len(chi_sets):
                    if self.config.yaku.chanta.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.chanta)

                    if self.config.yaku.junchan.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.junchan)

                    if self.config.yaku.ittsu.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.ittsu)

                    if not is_open_hand:
                        if self.config.yaku.ryanpeiko.is_condition_met(hand):
                            hand_yaku.append(self.config.yaku.ryanpeiko)
                        elif self.config.yaku.iipeiko.is_condition_met(hand):
                            hand_yaku.append(self.config.yaku.iipeiko)

                    if self.config.yaku.sanshoku.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.sanshoku)

                # small optimization, try to detect yaku with pon required sets only if we have pon sets in hand
                if len(pon_sets):
                    if self.config.yaku.toitoi.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.toitoi)

                    if self.config.yaku.sanankou.is_condition_met(
                            hand, win_tile, melds, self.config.is_tsumo):
                        hand_yaku.append(self.config.yaku.sanankou)

                    if self.config.yaku.sanshoku_douko.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.sanshoku_douko)

                    if self.config.yaku.shosangen.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.shosangen)

                    if self.config.yaku.haku.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.haku)

                    if self.config.yaku.hatsu.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.hatsu)

                    if self.config.yaku.chun.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.chun)

                    if self.config.yaku.east.is_condition_met(
                            hand, self.config.player_wind,
                            self.config.round_wind):
                        if self.config.player_wind == EAST:
                            hand_yaku.append(self.config.yaku.yakuhai_place)

                        if self.config.round_wind == EAST:
                            hand_yaku.append(self.config.yaku.yakuhai_round)

                    if self.config.yaku.south.is_condition_met(
                            hand, self.config.player_wind,
                            self.config.round_wind):
                        if self.config.player_wind == SOUTH:
                            hand_yaku.append(self.config.yaku.yakuhai_place)

                        if self.config.round_wind == SOUTH:
                            hand_yaku.append(self.config.yaku.yakuhai_round)

                    if self.config.yaku.west.is_condition_met(
                            hand, self.config.player_wind,
                            self.config.round_wind):
                        if self.config.player_wind == WEST:
                            hand_yaku.append(self.config.yaku.yakuhai_place)

                        if self.config.round_wind == WEST:
                            hand_yaku.append(self.config.yaku.yakuhai_round)

                    if self.config.yaku.north.is_condition_met(
                            hand, self.config.player_wind,
                            self.config.round_wind):
                        if self.config.player_wind == NORTH:
                            hand_yaku.append(self.config.yaku.yakuhai_place)

                        if self.config.round_wind == NORTH:
                            hand_yaku.append(self.config.yaku.yakuhai_round)

                    if self.config.yaku.daisangen.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.daisangen)

                    if self.config.yaku.shosuushi.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.shosuushi)

                    if self.config.yaku.daisuushi.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.daisuushi)

                    if self.config.yaku.ryuisou.is_condition_met(hand):
                        hand_yaku.append(self.config.yaku.ryuisou)

                    # closed kan can't be used in chuuren_poutou
                    if not len(
                            melds
                    ) and self.config.yaku.chuuren_poutou.is_condition_met(
                            hand):
                        if tiles_34[win_tile // 4] == 2 or tiles_34[win_tile //
                                                                    4] == 4:
                            hand_yaku.append(
                                self.config.yaku.daburu_chuuren_poutou)
                        else:
                            hand_yaku.append(self.config.yaku.chuuren_poutou)

                    if not is_open_hand and self.config.yaku.suuankou.is_condition_met(
                            hand, win_tile, self.config.is_tsumo):
                        if tiles_34[win_tile // 4] == 2:
                            hand_yaku.append(self.config.yaku.suuankou_tanki)
                        else:
                            hand_yaku.append(self.config.yaku.suuankou)

                    if self.config.yaku.sankantsu.is_condition_met(
                            hand, melds):
                        hand_yaku.append(self.config.yaku.sankantsu)

                    if self.config.yaku.suukantsu.is_condition_met(
                            hand, melds):
                        hand_yaku.append(self.config.yaku.suukantsu)

                # yakuman is not connected with other yaku
                yakuman_list = [x for x in hand_yaku if x.is_yakuman]
                if yakuman_list:
                    hand_yaku = yakuman_list

                # calculate han
                for item in hand_yaku:
                    if is_open_hand and item.han_open:
                        han += item.han_open
                    else:
                        han += item.han_closed

                if han == 0:
                    error = 'There are no yaku in the hand'
                    cost = None

                # we don't need to add dora to yakuman
                if not yakuman_list:
                    tiles_for_dora = tiles[:]

                    # we had to search for dora in kan fourth tiles as well
                    for meld in melds:
                        if meld.type == Meld.KAN or meld.type == Meld.CHANKAN:
                            tiles_for_dora.append(meld.tiles[3])

                    count_of_dora = 0
                    count_of_aka_dora = 0

                    for tile in tiles_for_dora:
                        count_of_dora += plus_dora(tile, dora_indicators)

                    for tile in tiles_for_dora:
                        if is_aka_dora(tile, self.config.options.has_aka_dora):
                            count_of_aka_dora += 1

                    if count_of_dora:
                        self.config.yaku.dora.han_open = count_of_dora
                        self.config.yaku.dora.han_closed = count_of_dora
                        hand_yaku.append(self.config.yaku.dora)
                        han += count_of_dora

                    if count_of_aka_dora:
                        self.config.yaku.aka_dora.han_open = count_of_aka_dora
                        self.config.yaku.aka_dora.han_closed = count_of_aka_dora
                        hand_yaku.append(self.config.yaku.aka_dora)
                        han += count_of_aka_dora

                if not error:
                    cost = scores_calculator.calculate_scores(
                        han, fu, self.config,
                        len(yakuman_list) > 0)

                calculated_hand = {
                    'cost': cost,
                    'error': error,
                    'hand_yaku': hand_yaku,
                    'han': han,
                    'fu': fu,
                    'fu_details': fu_details
                }

                calculated_hands.append(calculated_hand)

        # exception hand
        if not is_open_hand and self.config.yaku.kokushi.is_condition_met(
                None, tiles_34):
            if tiles_34[win_tile // 4] == 2:
                hand_yaku.append(self.config.yaku.daburu_kokushi)
            else:
                hand_yaku.append(self.config.yaku.kokushi)

            if self.config.is_renhou and self.config.options.renhou_as_yakuman:
                hand_yaku.append(self.config.yaku.renhou_yakuman)

            if self.config.is_tenhou:
                hand_yaku.append(self.config.yaku.tenhou)

            if self.config.is_chiihou:
                hand_yaku.append(self.config.yaku.chiihou)

            # calculate han
            han = 0
            for item in hand_yaku:
                if is_open_hand and item.han_open:
                    han += item.han_open
                else:
                    han += item.han_closed

            fu = 0
            cost = scores_calculator.calculate_scores(han, fu, self.config,
                                                      len(hand_yaku) > 0)
            calculated_hands.append({
                'cost': cost,
                'error': None,
                'hand_yaku': hand_yaku,
                'han': han,
                'fu': fu,
                'fu_details': []
            })

        # let's use cost for most expensive hand
        calculated_hands = sorted(calculated_hands,
                                  key=lambda x: (x['han'], x['fu']),
                                  reverse=True)
        calculated_hand = calculated_hands[0]

        cost = calculated_hand['cost']
        error = calculated_hand['error']
        hand_yaku = calculated_hand['hand_yaku']
        han = calculated_hand['han']
        fu = calculated_hand['fu']
        fu_details = calculated_hand['fu_details']

        return HandResponse(cost, han, fu, hand_yaku, error, fu_details)