Exemplo n.º 1
0
    def decide_discard_by_is_ready(self):
        arr = Rule.convert_tiles_to_arr(self.concealed)
        arr_sorted = arr[:]
        arr_sorted.sort()
        candidates = dict()
        for x in arr_sorted:
            test = arr_sorted[:]
            test.remove(x)
            result = MjMath.is_ready(test)
            if result[0]:  # is_ready!
                if x not in candidates:
                    candidates[x] = set()
                    candidates[x] = set(result[1])
                else:
                    continue

        if not candidates:
            return None

        arr_len = [len(candidates[x]) for x in candidates]
        max_len = max(arr_len)

        best_choices = []
        for x in candidates:
            if len(candidates[x]) == max_len:
                best_choices.append(x)

        one = choice(best_choices)
        tile = Rule.convert_key_to_tile(one)
        return tile
Exemplo n.º 2
0
def test_hand():
    mj_set = MjSet()
    mj_set.shuffle()
    # ai = PlayerAiPro("bot02")
    ai = PlayerAiAdv("bot02")
    for _ in range(13):
        ai.draw(mj_set)
    ai.sort_concealed()
    print("concealed: " + Rule.convert_tiles_to_str(ai.concealed))
    mj_set.shuffle()
    for _ in range(100):
        tile = ai.draw(mj_set)
        print(f"draw: {tile}")
        if Rule.is_mahjong(ai.concealed):
            print("Mahjong!!!")
            break
        tile = ai.decide_discard()
        print(f"discard: {tile}")
        ai.discard(tile)
        ai.sort_concealed()

    print("concealed: " + Rule.convert_tiles_to_str(ai.concealed))
    print("discarded: " + Rule.convert_tiles_to_str(ai.discarded))
    print(f"strategies: {ai.strategies}")
    print(f"strategies_time: {ai.strategies_time}")
    print("draw count: " + str(ai.draw_count))
Exemplo n.º 3
0
 def decide_discard_by_random_orphan(self):
     arr = Rule.convert_tiles_to_arr(self.concealed)
     orphans_arr = MjMath.get_orphans(arr)
     if not orphans_arr:
         return None
     one = choice(orphans_arr)
     tile = Rule.convert_key_to_tile(one)
     return tile
Exemplo n.º 4
0
    def try_exposed_chow(self, tile: Tile, owner) -> bool:
        arr = Rule.convert_tiles_to_arr(self.concealed)
        outer = tile.key

        # 胡吃一气
        combins = MjMath.get_chow_combins_from_arr(arr=arr, outer=outer)
        if not combins:
            return False
        combin = choice(combins)
        inners = Rule.convert_arr_to_tiles(combin)
        self.exposed_chow(inners=inners, tile=tile, owner=owner)
        return True
Exemplo n.º 5
0
 def __init__(self,
              expose_type: str,
              inners: list,
              outer=None,
              outer_owner=None):
     self.expose_type = expose_type
     self._inners = inners
     self._outer = outer
     self._outer_owner = outer_owner
     self._all: list = self._inners[:]
     if outer:
         self._all.append(outer)
         Rule.sort(self._all)
Exemplo n.º 6
0
def test_decide_discard_by_left_meld_and_eye():
    mj_set = MjSet()
    ai = PlayerAiAdv("bot02")
    for _ in range(3):
        ai.draw(mj_set)
    mj_set.shuffle()
    for _ in range(11):
        ai.draw(mj_set)
    ai.sort_concealed()
    print("ai.concealed_str =", ai.concealed_str)
    result = ai.decide_discard_by_left_meld_and_eye()
    for x in result:
        print(x,
              Rule.convert_tiles_to_str(Rule.convert_arr_to_tiles(result[x])))
Exemplo n.º 7
0
    def decide_discard_by_value(self,
                                wall=None,
                                players_count: int = 4,
                                deep: int = 2):
        left = Rule.convert_tiles_to_arr(wall)
        arr = Rule.convert_tiles_to_arr(self.concealed)
        arr.sort()
        if deep == 1:
            # (arr)
            pass
        keys = list(set(arr))
        candidates = dict()
        self.sort_concealed()
        # ("self.concealed_str =", self.concealed_str)
        # ("self.concealed =", Rule.convert_tiles_to_arr(self.concealed))
        for key in keys:
            test = arr[:]
            test.remove(key)
            mj_value = MjMath.value_arr(test,
                                        left=left,
                                        players_count=players_count,
                                        deep=deep)
            value = MjMath.convert_mj_value_to_int(mj_value)
            # (value)
            if key not in candidates:
                candidates[key] = value
            else:
                raise LookupError(f"duplicate key: {key}")

        max_chance = 0
        # find max_chance
        for key in candidates:
            if candidates[key] == 0:
                # don't care about candidate who have no chance
                continue
            if max_chance < candidates[key]:
                max_chance = candidates[key]
        if not max_chance:
            return None

        # find candidate who have the min chance
        temp = []
        for key in candidates:
            if candidates[key] == max_chance:
                temp.append(key)
        if not temp:
            return None

        tiles = Rule.convert_arr_to_tiles(temp)
        return tiles
Exemplo n.º 8
0
 def try_mahjong(self, tile=None) -> bool:
     test = self.concealed[:]
     if tile:
         test.append(tile)
     if Rule.is_mahjong(test):
         return True
     return False
Exemplo n.º 9
0
    def try_exposed_chow(self, tile: Tile, owner) -> bool:
        arr = Rule.convert_tiles_to_arr(self.concealed)
        outer = tile.key

        combins = MjMath.get_chow_combins_from_arr(arr=arr, outer=outer)
        if not combins:
            return False

        # convert combins to index arrays
        choices = []
        for combin in combins:
            choice = []
            for x in combin:
                index = arr.index(x)
                choice.append(index)
            choices.append(choice)

        allowed_cmd = ['chow', 'cancel']
        cmd = self.waiting_4_cmd(allowed_cmd=allowed_cmd, choices=choices)
        if cmd == 'cancel':
            return False
        elif cmd == 'chow':
            arr = choices[self.current_index]
            inners = []
            for index in arr:
                inners.append(self._concealed[index])
            super().exposed_chow(inners=inners, tile=tile, owner=owner)
            return True
        else:
            raise ValueError("exposed chow error cmd:", cmd)
Exemplo n.º 10
0
 def decide_discard_by_loneliest_orphan(self,
                                        keep_one_orphan: bool = False):
     arr = Rule.convert_tiles_to_arr(self.concealed)
     orphans_arr = MjMath.get_orphans(arr)
     if keep_one_orphan:
         if len(orphans_arr) <= 1:
             return None
     else:
         if len(orphans_arr) <= 0:
             return None
     loneliest_arr = MjMath.get_loneliest_from_arr(orphans_arr)
     if not loneliest_arr:
         return None
     one = choice(loneliest_arr)
     tile = Rule.convert_key_to_tile(one)
     return tile
Exemplo n.º 11
0
    def test_get_melds_from_arr(self):
        arr = [1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9]  # 九宝莲灯
        combins = MjMath.get_best_meld_combins_from_arr(arr)
        should_be = '''[[], [1, 1, 1], [3, 4, 5], [6, 7, 8], [9, 9, 9]]
[[], [1, 1, 1], [2, 3, 4], [5, 6, 7], [9, 9, 9]]
[[], [1, 1, 1], [2, 3, 4], [6, 7, 8], [9, 9, 9]]'''
        test = "\n".join([f'{x}' for x in combins])
        self.assertEqual(test, should_be)

        arr = [50, 50, 50, 60, 60, 60, 70, 70, 70, 80, 80]  # 小四喜
        combins = MjMath.get_best_meld_combins_from_arr(arr)
        should_be = """[[], [50, 50, 50], [60, 60, 60], [70, 70, 70]]"""
        test = "\n".join([f'{x}' for x in combins])
        self.assertEqual(test, should_be)

        # 随机牌组
        mj_set = MjSet()
        mj_set.shuffle()
        tiles = []
        for _ in range(13):
            tiles.append(mj_set.draw())
        arr = Rule.convert_tiles_to_arr(tiles)
        melds = MjMath.get_best_meld_combins_from_arr(arr)
        test = "\n".join([f'{x}' for x in melds])
        self.assertTrue(test.index('[[]') == 0)
Exemplo n.º 12
0
 def decide_discard_by_not_in_meld(self):
     arr = Rule.convert_tiles_to_arr(self.concealed)
     not_123 = MjMath.get_not_in_123(arr)
     not_pair = MjMath.get_not_in_pair(arr)
     if (not not_123) and (not not_pair):
         return None
     loneliest = not_123 & not_pair
     key = 0
     if loneliest:
         key = choice(list(loneliest))
     if not_pair:
         key = choice(list(not_pair))
     if not_123:
         key = choice(list(not_123))
     if key:
         return Rule.convert_key_to_tile(key)
     return None
Exemplo n.º 13
0
    def decide_discard_by_remove_melds(self):
        arr = Rule.convert_tiles_to_arr(self.concealed)
        combins = MjMath.get_best_meld_combins_from_arr(arr)
        if not combins:
            return None
        remain_combins = []
        for combin in combins:
            arr_completed = []
            for meld in combin:
                arr_completed += meld
            arr_remain = MjMath.list_sub(arr, arr_completed)
            arr_remain.sort()
            remain_combins.append(arr_remain)

        loneliest_arrs = []
        for combin in remain_combins:
            loneliest_arr = MjMath.get_loneliest_from_arr(combin)
            loneliest_arrs.append(loneliest_arr)

        # select orphans from loneliest_arrs
        orphans = []
        for arr in loneliest_arrs:
            orphans += arr
        test = list(set(orphans))
        test.sort()
        orphans = MjMath.get_orphans(test)
        if orphans:
            one = choice(orphans)
            tile = Rule.convert_key_to_tile(one)
            return tile

        # select one from loneliest_arrs by distance
        loneliest_candidates = MjMath.get_loneliest_from_arr(test)
        if not loneliest_candidates:
            raise LookupError(
                f"loneliest_candidates of {test} is empty! I can't believe that!"
            )
        one = choice(loneliest_candidates)
        tile = Rule.convert_key_to_tile(one)
        return tile
Exemplo n.º 14
0
    def decide_discard_by_left_meld_and_eye(self):
        arr = Rule.convert_tiles_to_arr(self.concealed)
        arr.sort()
        keys = list(set(arr))
        candidates = dict()
        for key in keys:
            test = arr[:]
            test.remove(key)
            result = MjMath.count_of_melds_and_eys(test)
            count = result[0]
            if result[1]:  # has eye
                count += 1
            if count not in candidates:
                candidates[count] = []
            candidates[count].append(key)

        if not candidates:
            return None
        if len(candidates) == 1:
            return None

        len_arr = [key for key in candidates]
        max_len = max(len_arr)
        return Rule.convert_arr_to_tiles(candidates[max_len])
Exemplo n.º 15
0
 def draw_from_back(self, mj_set: MjSet):
     tile = mj_set.draw_from_back()
     if not tile:
         print("mj_set.draw_from_back() None")
         return None
     while Rule.is_flower(tile) and mj_set.tiles:
         # (self, 'get a flower from back:', tile)
         self.flowers.append(tile)
         self._draw_count += 1
         tile = mj_set.draw_from_back()
         if not tile:
             print("mj_set.draw_from_back() is_flower None")
             return None
     self.add(tile)
     return tile
Exemplo n.º 16
0
    def draw(self, mj_set: MjSet):
        tile = mj_set.draw()
        if not tile:
            print("mj_set.draw() None")
            return None
        if not Rule.is_flower(tile):
            self.add(tile)
            self._draw_count += 1
            return tile

        print(self, 'get a flower:', tile)
        self.flowers.append(tile)
        tile = self.draw_from_back(mj_set)
        print("draw_from_back:", tile)
        return tile
Exemplo n.º 17
0
    def try_mahjong(self, tile=None) -> bool:
        test = self.concealed[:]
        if tile:
            test.append(tile)
        if Rule.is_mahjong(test):
            choices = []
            allowed_cmd = ['hu', 'cancel']
            cmd = self.waiting_4_cmd(allowed_cmd=allowed_cmd, choices=choices)
            if cmd == 'cancel':
                return False
            elif cmd == 'hu':
                return True
            else:
                raise ValueError("exposed pong error cmd:", cmd)

        return False
Exemplo n.º 18
0
    def decide_discard(self) -> Tile:
        # discard the orphan tile
        start = time()
        tile = self.decide_discard_by_loneliest_orphan()
        duration = time() - start
        self.record_strategies_time('decide_discard_by_loneliest_orphan',
                                    duration)
        if tile:
            self.record_strategies('decide_discard_by_loneliest_orphan')
            return tile

        # check for ready chance
        test = self._concealed[:-1]
        is_ready = Rule.is_ready(test)

        if not is_ready:
            start = time()
            tile = self.decide_discard_by_remove_melds()
            duration = time() - start
            self.record_strategies_time('decide_discard_by_remove_melds',
                                        duration)
            if tile:
                self.record_strategies('decide_discard_by_remove_melds')
                return tile

        if is_ready:
            start = time()
            tile = self.decide_discard_by_is_ready()
            duration = time() - start
            self.record_strategies_time('decide_discard_by_is_ready', duration)
            if tile:
                self.record_strategies('decide_discard_by_is_ready')
                return tile

        # finally, decide by random
        start = time()
        tile = self.decide_discard_random()
        duration = time() - start
        self.record_strategies_time('decide_discard_random', duration)
        self.record_strategies('decide_discard_random')
        return tile
Exemplo n.º 19
0
    def test_ai_mahjong(self):
        mj_set = MjSet()
        ai = PlayerAi("西施")
        mj_set.shuffle()
        for _ in range(13):
            ai.draw(mj_set)
        ai.sort_concealed()

        mj_set.shuffle()
        mahjong = False
        for _ in range(100):
            tile = ai.draw(mj_set)
            if Rule.is_mahjong(ai.concealed):
                mahjong = True
                break
            tile = ai.decide_discard()
            ai.discard(tile)
            ai.sort_concealed()

        # self.assertTrue(mahjong)
        self.assertGreater(len(ai.concealed), 0)
Exemplo n.º 20
0
    def play(self):
        current = self._dealer  # the current player
        player = current
        before = None  # the previous player
        current_discard = None  # discard tile by previous player

        for player in self._players:
            print(f'{player.position}: {player}')
        print("=" * 40)

        have_winner = False
        while not have_winner and not self.out_of_tiles:

            if current_discard:
                wind = current.position
                player = current
                for index in range(3):
                    # test for hu by other
                    if player.try_mahjong(current_discard):
                        player.concealed.append(current_discard)
                        print(f"winner is {player}, by {before}")
                        self._winner = player
                        self.firer = before
                        self._state_machine.mahjong()
                        have_winner = True
                        self.refresh_screen(state='mahjong')
                        break
                    wind = Suit.get_next_wind(wind)
                    player = self.positions[wind]

            if have_winner:
                break

            # interrupted by exposed kong / pong / chow
            interrupted = False
            if current_discard:

                # Melding another for current, +1, +2 players
                player = None

                # try kong ( must have tiles ):
                if self.mj_set.tiles:
                    wind = current.position
                    player = current
                    for index in range(3):
                        try:
                            if player.try_exposed_kong(tile=current_discard,
                                                       owner=before,
                                                       mj_set=self._mj_set):
                                self.refresh_screen('drawing')
                                # self._sound_kong.play()
                                interrupted = True
                                break
                        except OutOfTilesError as e:
                            self.withdraw()
                        except HaveWinnerError as e:
                            self._winner = player
                            self.mahjong_on_kong = True
                            self.firer = player
                            self._state_machine.mahjong()
                            have_winner = True
                            self.refresh_screen(state='mahjong')
                            break
                        wind = Suit.get_next_wind(wind)
                        player = self.positions[wind]
                    if self._winner:
                        break

                # try pong:
                if not interrupted:
                    wind = current.position
                    player = current
                    for index in range(3):
                        if player.try_exposed_pong(tile=current_discard,
                                                   owner=before):
                            self.refresh_screen()
                            self._sound_pong.play()
                            interrupted = True
                            break
                        wind = Suit.get_next_wind(wind)
                        player = self.positions[wind]

                # try chow:
                if not interrupted:
                    # wind = current.position
                    player = current
                    if player.try_exposed_chow(current_discard, before):
                        self.refresh_screen()
                        self._sound_chow.play()
                        interrupted = True

                if not interrupted:
                    before.put_on_desk(current_discard)
                before.discarding = None
            # end if current_discard:

            if interrupted:
                current = player
            else:
                # test for out of tiles
                if not self.mj_set.tiles:
                    self.withdraw()
                    break

                # draw
                new_tile = current.draw(self._mj_set)
                if not new_tile:
                    self.withdraw()
                    break
                self.refresh_screen(state='drawing')

                # test for hu by self
                if current.try_mahjong(tile=None):
                    print(f"winner is {current}, by self!")
                    self._winner = current
                    self.firer = current
                    self._state_machine.mahjong()
                    self.refresh_screen(state='mahjong')
                    break

                # self kong
                try:
                    if current.try_conceal_kong(self.mj_set):
                        pass
                except OutOfTilesError as e:
                    self.withdraw()
                except HaveWinnerError as e:
                    self._winner = current
                    self.mahjong_on_kong = True
                    self.firer = current
                    self._state_machine.mahjong()
                    have_winner = True
                    self.refresh_screen(state='mahjong')
                    break
                if self.winner:
                    break

                # test for exposed kong from exposed pong
                result_of_try = False
                try:
                    result_of_try = current.try_exposed_kong_from_exposed_pong(
                        mj_set=self.mj_set)
                except OutOfTilesError as e:
                    self.withdraw()
                except HaveWinnerError as e:
                    self._winner = current
                    self.mahjong_on_kong = True
                    self.firer = current
                    self._state_machine.mahjong()
                    have_winner = True
                    self.refresh_screen(state='mahjong')
                    break

                if self._winner:
                    break

                if result_of_try:
                    # try rob kong by others
                    self.refresh_screen()

                    # others try mahjong
                    player = None
                    wind = current.position
                    for index in range(3):
                        wind = Suit.get_next_wind(wind)
                        player = self.positions[wind]
                        if player.try_mahjong(tile=new_tile):
                            print(f"winner is {player}, by rob {current}!")
                            player.concealed.append(new_tile)
                            self._winner = player
                            self.firer = current
                            self._state_machine.mahjong()
                            self.refresh_screen(state='mahjong')
                            self.robbing_a_kong = True
                            break
                    if self._winner:
                        break

            if isinstance(current, PlayerAiAdv):
                wall = self._mj_set.tiles
                tile = current.decide_discard(players_count=len(self.players),
                                              wall=wall)
            else:
                tile = current.decide_discard()
            current.discard(tile)
            print(current, 'discard tile:', tile)
            current_discard = tile
            current.sort_concealed()

            # (f'{current} discard {tile}')
            # (Rule.convert_tiles_to_str(current.concealed))

            # next player
            next = self._get_next_player(current)
            before = current
            current = next

            self.refresh_screen()
            self.sound_discard()
        # end while

        self.refresh_screen(state='scoring')
        print("tiles left :", len(self.mj_set.tiles))
        for player in self.players:
            if player == self.winner:
                print(f"winner {player}: ",
                      Rule.convert_tiles_to_str(player.concealed))
            else:
                print(f"{player}: ",
                      Rule.convert_tiles_to_str(player.concealed))

        left = Rule.convert_tiles_to_arr(self.mj_set.tiles)
        left.sort()
Exemplo n.º 21
0
    def decide_discard(self, players_count: int = 4, wall: list = []) -> Tile:
        start = time()
        if len(self._concealed) <= 5:
            tiles = self.decide_discard_by_value(wall=None,
                                                 players_count=players_count,
                                                 deep=2)
            duration = time() - start
            self.record_strategies_time('decide_discard_by_value_2_last',
                                        duration)
            self.record_strategies('decide_discard_by_value_2_last')
            if tiles:
                return choice(tiles)

        # discard the orphan tile
        # check for ready chance
        # discard the orphan tile
        start = time()
        test = self._concealed[:-1]
        is_ready = Rule.is_ready(test)
        if is_ready:
            tile = self.decide_discard_by_loneliest_orphan(
                keep_one_orphan=True)
        else:
            tile = self.decide_discard_by_loneliest_orphan(
                keep_one_orphan=False)
        duration = time() - start
        self.record_strategies('decide_discard_by_loneliest_orphan')
        self.record_strategies_time('decide_discard_by_loneliest_orphan',
                                    duration)
        if tile:
            return tile

        start = time()
        tiles = self.decide_discard_by_value(wall=wall,
                                             players_count=players_count,
                                             deep=1)
        duration = time() - start
        self.record_strategies_time('decide_discard_by_value_1', duration)
        self.record_strategies('decide_discard_by_value_1')
        if tiles:
            return choice(tiles)

        start = time()
        tiles = self.decide_discard_by_left_meld_and_eye()
        duration = time() - start
        self.record_strategies_time('decide_discard_by_left_meld_and_eye',
                                    duration)
        self.record_strategies('decide_discard_by_left_meld_and_eye')
        if tiles:
            return choice(tiles)

        start = time()
        arr = Rule.convert_tiles_to_arr(self.concealed)
        combins = MjMath.get_best_meld_combins_from_arr(arr)
        if combins and len(combins) > 0 and len(combins[0]) > 3:
            tiles = self.decide_discard_by_value(wall=None,
                                                 players_count=players_count,
                                                 deep=2)
            duration = time() - start
            self.record_strategies_time('decide_discard_by_value_2', duration)
            self.record_strategies('decide_discard_by_value_2')
            if tiles:
                return choice(tiles)

        # finally, decide by random
        start = time()
        tile = self.decide_discard_random()
        duration = time() - start
        self.record_strategies_time('decide_discard_random', duration)
        self.record_strategies('decide_discard_random')
        return tile
Exemplo n.º 22
0
 def sort_concealed(self):
     arr = Rule.convert_tiles_to_arr(self._concealed)
     arr.sort()
     self._concealed = Rule.convert_arr_to_tiles(arr)
Exemplo n.º 23
0
    def play(self):

        current = self._dealer  # the current player
        player = current
        before = None  # the previous player
        current_discard = None  # discard tile by previous player

        for player in self._players:
            print(f'{player.position}: {player}')
        print("=" * 40)

        have_winner = False
        while not have_winner:

            if current_discard:
                wind = current.position
                player = current
                for index in range(3):
                    # test for hu by other
                    test = player.concealed[:]
                    test.append(current_discard)
                    if Rule.is_mahjong(test):
                        player.concealed.append(current_discard)
                        print(f"winner is {player}, by {before}")
                        self._winner = player
                        self._state_machine.mahjong()
                        have_winner = True
                        break
                    wind = Suit.get_next_wind(wind)
                    player = self.positions[wind]

            if have_winner:
                break

            # interrupted by exposed kong / pong / chow
            interrupted = False
            if current_discard:

                # Melding another for current, +1, +2 players
                player = None

                # try kong ( must have tiles ):
                if self.mj_set.tiles:
                    wind = current.position
                    player = current
                    for index in range(3):
                        if player.try_exposed_kong(tile=current_discard,
                                                   owner=before,
                                                   mj_set=self._mj_set):
                            interrupted = True
                            break
                        wind = Suit.get_next_wind(wind)
                        player = self.positions[wind]

                # try pong:
                if not interrupted:
                    wind = current.position
                    player = current
                    for index in range(3):
                        if player.try_exposed_pong(tile=current_discard,
                                                   owner=before):
                            interrupted = True
                            break
                        wind = Suit.get_next_wind(wind)
                        player = self.positions[wind]

                # try chow:
                if not interrupted:
                    wind = current.position
                    player = current
                    if player.try_exposed_chow(current_discard, before):
                        interrupted = True

                if not interrupted:
                    before.put_on_desk(current_discard)
            # end if current_discard:

            if interrupted:
                current = player
            else:
                # test for out of tiles
                if not self.mj_set.tiles:
                    print("out of tiles!")
                    self._winner = None
                    self._state_machine.withdraw()
                    break

                # draw
                current.draw(self._mj_set)
                # test for hu by self
                if Rule.is_mahjong(current.concealed):
                    print(f"winner is {current}, by self!")
                    self._winner = current
                    self._state_machine.mahjong()
                    break

                # self kong
                current.try_conceal_kong(self.mj_set)

            if isinstance(current, PlayerAiAdv):
                wall = self._mj_set.tiles
                tile = current.decide_discard(players_count=len(self.players),
                                              wall=wall)
            else:
                tile = current.decide_discard()
            print(current, 'discard:', tile)
            current.discard(tile)
            current.sort_concealed()
            current_discard = tile

            # (f'{current} discard {tile}')
            # (Rule.convert_tiles_to_str(current.concealed))

            # next player
            next = self._get_next_player(current)
            before = current
            current = next
        # end while

        print("tiles left :", len(self.mj_set.tiles))
        for player in self.players:
            if player == self.winner:
                print(f"winner {player}: ",
                      Rule.convert_tiles_to_str(player.concealed))
            else:
                print(f"{player}: ",
                      Rule.convert_tiles_to_str(player.concealed))

        left = Rule.convert_tiles_to_arr(self.mj_set.tiles)
        left.sort()
        print(self.players[0].strategies)
        print(self.players[0].strategies_time)