Exemplo n.º 1
0
    def is_ready(cls, arr: list, left: list = None):

        # block error parameter
        if not arr:
            raise ValueError("arr is empty")
        length = len(arr)
        if length not in cls.concealed_count:
            raise ValueError(f"concealed length error: {length}")

        if left:
            # left tiles set
            keys = left
        else:
            # total mj set
            MjSet.generate_dictionary()
            keys = [key for key in MjSet.dictionary]

        # need 1 tile
        candidates = []
        for key in keys:
            test = arr + [key]
            test.sort()
            if cls.is_good_concealed(test):
                candidates.append(key)

        if candidates:
            return tuple((True, candidates))
        return tuple((False, []))
Exemplo n.º 2
0
    def value_arr(cls,
                  arr: list,
                  left=None,
                  players_count: int = 1,
                  deep: int = 2) -> MjValue:
        if not arr:
            value = MjValue(meld=0,
                            orphan=0,
                            count_down=0,
                            is_ready=False,
                            waiting=[])
            return value

        if not left:
            # total tile types, same chance
            MjSet.generate_dictionary()
            left = [key for key in MjSet.dictionary]
        count_all = len(left)

        # basic info
        melds_count = cls.get_most_melds_length_from_arr(arr)
        orphans_arr = cls.get_orphans(arr)
        orphans_count = len(orphans_arr)
        result = MjValue(meld=melds_count, orphan=orphans_count)

        # is ready test
        ready_info = cls.is_ready(arr)
        is_ready = ready_info[0]
        if is_ready:
            combins = ready_info[1]
            if not combins:
                combins = []
            waiting = []
            fail_chance = 1
            for x in combins:
                chance = cls.chance_of_combin_and_wall(
                    wall=left, combin=[x], players_count=players_count)
                fail_chance *= 1 - chance
                value = 1
                w = ([x], chance, value)
                waiting.append(w)
            total_chance = 1 - fail_chance
            result.is_ready = is_ready
            result.listening = waiting
            result.mahjong_chance = total_chance

        # count down test
        begin = time()
        count_down_result = cls.countdown_of_ready(arr, left=left, deep=deep)
        # ("count_down duration = %0.2f" % (time() - begin))
        # ("count_down_result =", count_down_result)
        count_down = count_down_result[0]
        result.count_down = count_down
        if count_down > max(cls.concealed_count):
            return result
        combins = count_down_result[1]
        waiting = []
        value = 1
        total_chance = 0
        if count_down <= 3 and len(combins) > 0:
            for combin in combins:
                chance = cls.chance_of_combin_and_wall(
                    wall=left, combin=combin, players_count=players_count)
                total_chance += chance
                w = (combin, chance, value)
                waiting.append(w)
        result.waiting = waiting
        result.waiting_chance = total_chance
        return result
Exemplo n.º 3
0
    def countdown_of_ready(cls,
                           concealed: list,
                           left: list = None,
                           deep: int = 2):
        arr = concealed[:]
        arr.sort()
        # block error parameter
        if left is None:
            left = []
        if not arr:
            raise ValueError("arr is empty")
        length = len(arr)
        if length not in cls.concealed_count:
            raise ValueError(f"concealed length error: {length}")
        if deep < 1 or 4 < deep:
            raise ValueError(f"deep {deep}, should be in [1,2,3,4,5]")

        if left:
            # left tiles set
            keys = left
        else:
            # total mj set
            MjSet.generate_dictionary()
            keys = [key for key in MjSet.dictionary]

        # remove the keys that not relative to 'arr' elements
        for key in keys[::-1]:
            if key not in arr \
                    and key + 1 not in arr \
                    and key - 1 not in arr:
                keys.remove(key)

        # need how many tiles?
        candidates = []
        countdown = []

        # best combinations for arr
        combins = MjMath.get_best_meld_combins_from_arr(arr)
        for combin in combins:
            completed = []
            for meld in combin:
                completed = completed + meld
            shorter = MjMath.list_sub(arr, completed)
            shorter_len = len(shorter)
            if shorter_len >= 11:
                return (999, None)
            for drop in range(1, deep + 1):
                if drop >= shorter_len:
                    continue
                for remove in combinations(shorter, drop):
                    remains = MjMath.list_sub(shorter, list(remove))
                    for add in combinations(keys, drop + 1):
                        test = remains + list(add)
                        more = list(add)
                        more.sort()
                        if more in candidates:
                            continue
                        if MjMath.has_orphan(test):
                            continue
                        if len(set(test)) == len(test):
                            continue
                        test.sort()
                        if cls.is_good_concealed(test):
                            candidates.append(more)
                            countdown.append(drop + 1)

        if candidates:
            return tuple((min(countdown), candidates))

        # default
        return tuple((999, None))