Example #1
0
    def _handle_liuju(self, msg):
        self._round_end_info_to_file("    [R{}T{}] NO ONE WINS, NO MORE TILES...".format(
            self.game_table.round_number, len(self.game_table.bot.discard34))
        )
        self.both_log("    NO ONE WINS, NO MORE TILES...\n")
        self.drawer and self.drawer.liuju()

        opp_tiles_dict = {}
        for i in range(1, 4):
            opp_tiles = TenhouParser.parse_opp_tiles(msg, i)
            if opp_tiles and len(opp_tiles) > 0:
                opp_tiles_dict[i] = opp_tiles
        if len(opp_tiles_dict) > 0:
            for k, v in opp_tiles_dict.items():
                tiles34 = [t // 4 for t in v]
                self._round_end_info_to_file("        FT-P{}:{}:{}\n".format(k, Tile.t34_to_g(tiles34), tiles34))
                p_melds = [meld.tiles for meld in self.game_table.get_player(k).meld136]
                p_melds += [v]
                self._log(Tile.t136_to_g(p_melds))
                if self.drawer:
                    self.drawer.update_opp(p_melds, k, True)

        # prd_str = '        WP '
        # for i in range(1, 4):
        #     waitings = self.game_table.get_player(i).waiting_prediction
        #     prd_str += "• P{}:{}:{} ".format(i, waitings, Tile.t34_to_g(waitings))
        # prd_str += '\n'
        # self._round_end_info_to_file(prd_str)

        self.logger_obj.flush_buffer()
        self._wait_for_a_while()
        sleep(2)
        self._send('<NEXTREADY />')
Example #2
0
    def should_call_kan(self, tile136, from_opponent):
        """
        This method has to be implemented. It decides whether the bot should call a Kan(Quad) meld. A Kan meld is set of
        four identical tiles. There are three kinds of Quad sets. (1) MINKAN: When the bot has three identical tiles
        in hand and the opponent discards the fourth tile. (2) KAN: When the bot has all four identical tiles in hand.
        (3) CHAKAN: When the bot has an open Triplet meld and it draws the fourth tile, the bot can update this Triplet
        meld to a Kan meld
        :param tile136: The involved kan tile in 136 form
        :param from_opponent: Whether the tile was from opponent  True/False
        :return: [Kan type], [to be called tile] if should call Kan else False, False
        """
        tile34 = tile136 // 4

        if from_opponent:  # (1) Check Minkan
            should_kan = True  # TODO: should be decided by your own strategy
            if should_kan:
                # the tiles in hand should be removed from the set of hand tiles after having decided
                self_tiles = [t for t in self.tiles136 if t // 4 == tile34]
                for t in self_tiles:
                    self.tiles136.remove(t)
                # developer could access self.thclient.both_log(msg) to display content in logs
                msg = "        [Bot calls minkan]: {}".format(
                    Tile.t34_to_g([tile136 // 4] * 4))
                self.thclient.both_log(msg)
                # return the result
                return Meld.KAN, tile136
        else:  # (2) Check Kan
            ankan_tile = None
            if self.hand34.count(
                    tile34) == 4:  # bot gots the fourth tile at this turn
                ankan_tile = tile34
            else:
                own_tile = [
                    tile for tile in set(self.hand34)
                    if self.hand34.count(tile) == 4
                ]
                if own_tile and len(
                        own_tile
                ) > 0:  # bot had a kan in hand before and did not call kan
                    ankan_tile = own_tile[0]
            if ankan_tile:
                should_call_ankan = True  # TODO: should be decided by your own strategy
                if should_call_ankan:
                    msg = "        🤖[Bot calls ankan]: {}".format(
                        Tile.t34_to_g([ankan_tile] * 4))
                    self.thclient.both_log(msg)
                    return Meld.KAN, self.tile_34_to_136(ankan_tile)
            # (3) Check Chakan
            for meld in self.meld136:
                if meld.tiles[0] // 4 == meld.tiles[1] // 4 == tile34:
                    should_call_chakan = True  # TODO: should be decided by your own strategy
                    if should_call_chakan:
                        msg = "        🤖[Bot calls chakan]: {}".format(
                            Tile.t34_to_g([tile136 // 4] * 4))
                        self.thclient.both_log(msg)
                        return Meld.CHANKAN, tile136

        return False, False
Example #3
0
 def format_hand(self, extra_tile):
     hand = '{} + {}'.format(Tile.t136_to_g(self.tiles136),
                             Tile.t136_to_g([extra_tile]))
     if self.is_open_hand:
         melds = []
         for meld in self.meld136:
             melds.append('{}'.format(meld.tiles_graph))
         hand += ' + [{}]'.format(','.join(melds))
     return hand
Example #4
0
    def _handle_opponent_discard(self, msg):
        opponent_seat = TenhouParser.parse_opponent_seat(msg)
        if opponent_seat == 0:
            # if hasattr(self.game_table.bot, "update_part"):
            #     self.game_table.bot.update_part()
            return -1

        tile = TenhouParser.parse_tile(msg)
        opp_obj = self.game_table.get_player(opponent_seat)

        discard_tag = ['d', 'e', 'f', 'g']  # lower case alphabet means the player discards whatever he had drawn...
        was_direct = discard_tag[opponent_seat] in msg
        opp_obj.add_discard_type(was_direct)

        if hasattr(self.game_table.bot, "handle_opponent_discard"):
            self.game_table.bot.handle_opponent_discard(opponent_seat)

        self.game_table.discard_tile(opponent_seat, tile)
        self.drawer and self.drawer.discard(tile, opponent_seat)
        discard_msg = "    [Player {}] discards: {} + {}".format(opponent_seat, Tile.t34_to_g(opp_obj.discard34[:-1]), Tile.t136_to_g([tile]))
        discard_msg += ", melds: {}".format(Tile.t34_to_g(opp_obj.meld34)) if opp_obj.meld34 else ""
        self.both_log(discard_msg)

        # check meld call for bot
        if 't=' in msg:
            # check kan
            if 't="3"' in msg or 't="7"' in msg:
                if self.game_table.bot.should_call_kan(tile, True)[0] == Meld.KAN:
                    self._send('<N type="2" />')
                    kan_msg = '    [Bot] called an open kan: {}'.format(Tile.t34_to_g([tile // 4] * 4))
                    self.both_log(kan_msg)
                    return -1
            # check chow/pon
            may_call_chi = (msg[1].lower() == 'g')
            meld, tile_to_discard = self.game_table.bot.try_to_call_meld(tile, may_call_chi)
            if meld:
                meld_type = '3' if meld.type == Meld.CHI else '1'
                self_tiles = [t for t in meld.tiles if t != meld.called_tile]
                self._send('<N type="{}" hai0="{}" hai1="{}" />'.format(meld_type, self_tiles[0], self_tiles[1]))
                self.game_table.count_remaining_tiles += 1
                return tile
            else:
                self._wait_for_a_while()
                self._send('<N />')

        self.drawer and self.drawer.set_remain(self.game_table.count_remaining_tiles)

        return -2
Example #5
0
    def try_to_call_meld(self, tile136, might_call_chi):
        """
        This method has to be implemented. It decides whether to call a meld or not.
        :param tile136: the involved opponent's discard in 136 form
        :param might_call_chi: whether is it possible to call CHI
        :return: [Meld object], 0 if should call meld else return False, False
        """
        tile34 = tile136 // 4

        # (1) Check Pon
        if self.hand34.count(tile34) >= 2:
            should_call_pon = True  # TODO: should be decided by your own strategy
            if should_call_pon:
                self_tiles = [
                    t136 for t136 in self.tiles136 if t136 // 4 == tile136 // 4
                ]
                msg = "        🤖[Bot calls pon]: {}".format(
                    Tile.t34_to_g([tile136 // 4] * 3))
                self.thclient.both_log(msg)
                return Meld(Meld.PON, self_tiles[0:2] + [tile136], True,
                            tile136), 0

        # (2) Check Chi
        if might_call_chi and tile34 < 27:
            # There might be multiple possibilities to call Chi
            chi_candidates = []
            if tile34 % 9 > 1 and (tile34 - 2) in self.hand34 and (
                    tile34 - 1) in self.hand34:
                chi_candidates.append([tile34 - 2, tile34 - 1])
            if 8 > tile34 % 9 > 0 and (tile34 - 1) in self.hand34 and (
                    tile34 + 1) in self.hand34:
                chi_candidates.append([tile34 - 1, tile34 + 1])
            if 7 > tile34 % 9 and (tile34 + 1) in self.hand34 and (
                    tile34 + 2) in self.hand34:
                chi_candidates.append([tile34 + 1, tile34 + 2])
            for candidate in chi_candidates:
                should_chi = True  # TODO: should be decided by your own strategy
                if should_chi:
                    opt1, opt2 = self.tile_34_to_136(
                        candidate[0]), self.tile_34_to_136(candidate[1])
                    msg = "        😊[Bot calls chow]: {}".format(
                        Tile.t34_to_g(candidate + [tile34]))
                    self.thclient.both_log(msg)
                    return Meld(Meld.CHI, sorted([opt1, opt2, tile136]), True,
                                tile136), 0

        return False, False
Example #6
0
 def str_hand_tiles(self):
     hand = Tile.t136_to_g(self.tiles136)
     if self.is_open_hand:
         melds = []
         for meld in self.meld136:
             melds.append('{}'.format(meld.tiles_graph))
         hand += ' + [{}]'.format(','.join(melds))
     return hand
Example #7
0
 def _handle_new_bonus_indicator(self, msg):
     tile = TenhouParser.parse_bonus_indicator(msg)
     # save data to objects
     self.game_table.add_bonus_indicator(tile)
     # display in logs
     new_bi_msg = '    New bonus tile indicator revealed: {}'.format(Tile.t136_to_g([tile]))
     self.both_log(new_bi_msg)
     # display in GUI
     self.drawer and self.drawer.add_bonus_indicator(tile)
     self.drawer and self.drawer.set_remain(self.game_table.count_remaining_tiles)
Example #8
0
    def _handle_meld_call(self, msg, meld_tile):
        player_hand = self.game_table.bot.format_hand(meld_tile) if meld_tile else ''
        meld = TenhouParser.parse_meld(msg)

        if meld.by_whom != 0:
            self.game_table.call_meld(meld.by_whom, meld)
            meld_msg = '    [Player {}] called meld: {}'.format(meld.by_whom, meld)
            self.both_log(meld_msg)
            if self.drawer:
                p_melds = [meld.tiles for meld in self.game_table.get_player(meld.by_whom).meld136]
                self.drawer.update_opp(p_melds, meld.by_whom)

        if meld.by_whom == 0:
            self.game_table.bot.call_meld(meld)
            if meld.type != Meld.KAN and meld.type != Meld.CHANKAN:

                discard_tile_136 = self.game_table.bot.to_discard_tile()
                own_hand_msg = '        [Bot] hand tiles: {}'.format(player_hand)
                discard_msg = '        [Bot] discards tile {} after called meld'.format(
                    Tile.t136_to_g([discard_tile_136]))
                self.both_log(own_hand_msg)
                self.both_log(discard_msg)
                self._stream_log('')
                self._send('<D p="{}"/>'.format(discard_tile_136))
                self.game_table.discard_tile(0, discard_tile_136)
                self.game_table.bot.tiles136.remove(discard_tile_136)

                if self.drawer:
                    self.drawer.discard(discard_tile_136, 0)
                    self.drawer.update_self(self.game_table.bot.tiles136,
                                            [meld.tiles for meld in self.game_table.bot.meld136])
            else:
                if self.drawer:
                    self.drawer.update_self(self.game_table.bot.tiles136,
                                            [meld.tiles for meld in self.game_table.bot.meld136])

        if callable(getattr(self.game_table.bot, 'handle_opponent_discard', None)):
            for i in range(1, 4):
                self.game_table.bot.handle_opponent_discard(i)
Example #9
0
 def __str__(self):
     bonus_tile_indicator_repr = Tile.t136_to_g(self.bonus_indicator)
     return 'Round number:{0}, Honba sticks:{1}, Bonus tile indicator:{2}'.format(
         self.round_number, self.honba_sticks, bonus_tile_indicator_repr)
Example #10
0
 def tiles_string(self):
     return Tile.tile136_to_string(self.tiles)
Example #11
0
 def tiles_graph(self):
     return Tile.t136_to_g(self.tiles)
Example #12
0
 def __str__(self):
     return '{}, {}'.format(self.type, Tile.t136_to_g(self.tiles),
                            self.tiles)
Example #13
0
 def form_tiles_string(h, m, mk, ak, ft):
     h_str = Tile.t34_to_g(h)
     m_str = Tile.t34_to_g(m + mk) + " *" + Tile.t34_to_g(ak)
     f_str = Tile.t34_to_g(ft)
     return "Final tiles: {} | {}, Win tile: {}".format(
         h_str, m_str, f_str)
Example #14
0
    def _handle_draw_tile(self, msg):
        drawn_tile_136 = TenhouParser.parse_tile(msg)
        self.drawer and self.drawer.draw(drawn_tile_136)

        if not self.game_table.bot.reach_status:
            # print own hand tiles
            own_hand_str = '    [Bot] draws a tile: {}'.format(self.game_table.bot.format_hand(drawn_tile_136))
            self.game_table.bot.draw_tile(drawn_tile_136)
            self._stream_log('')
            self.both_log(own_hand_str)
            self._wait_for_a_while()

            # check if bot can call reach
            can_call_reach, to_discard_136 = self.game_table.bot.can_call_reach()
            if can_call_reach:
                self._send('<REACH hai="{}"/>'.format(to_discard_136))
                self.game_table.bot.call_reach()
                self._wait_for_a_while()

            # check if bot can call a kan set
            kan_type, kaned_tile136 = self.game_table.bot.should_call_kan(drawn_tile_136, False)
            if kan_type and self.game_table.count_remaining_tiles > 0:
                meld_type = 5 if kan_type == Meld.CHANKAN else 4
                self._send('<N type="{}" hai="{}"/>'.format(meld_type, kaned_tile136))
                return True

            # bot decides what to discard
            discard_tile_136 = self.game_table.bot.to_discard_tile()
            self.game_table.bot.tiles136.remove(discard_tile_136)
            discard_msg = '    [Bot] discards: {} + {}'.format(
                Tile.t34_to_g(self.game_table.bot.discard34),
                Tile.t136_to_g([discard_tile_136])
            )
            self.both_log(discard_msg)
            bot_hand_msg = '    [Bot] hand tiles after discarding: {}'.format(
                self.game_table.bot.str_hand_tiles()
            )
            self.both_log(bot_hand_msg)
        else:
            discard_tile_136 = drawn_tile_136
            own_hand_str = '    Own hand: {}'.format(self.game_table.bot.format_hand(drawn_tile_136))
            self._stream_log('')
            self.both_log(own_hand_str)

            if callable(getattr(self.game_table.bot, 'log_opponents_prediction', None)):
                self.game_table.bot.log_opponents_prediction()
            if callable(getattr(self.game_table.bot, 'show_riichi_waiting', None)):
                self.game_table.bot.show_riichi_waiting()

            discard_msg = '        🤖[Bot(Richii) discards]: {} + {}'.format(
                Tile.t34_to_g(self.game_table.bot.discard34), Tile.t136_to_g([discard_tile_136])
            )
            self.both_log(discard_msg)

        self._send('<D p="{}"/>'.format(int(discard_tile_136)))
        self.game_table.discard_tile(0, discard_tile_136)

        remain_tiles_msg = '    Remaining tiles: {}'.format(self.game_table.count_remaining_tiles)
        self._stream_log('')
        self.both_log(remain_tiles_msg)

        self._flush_buffer()

        self.drawer and self.drawer.set_remain(self.game_table.count_remaining_tiles)
        self._stream_log('')

        self.drawer and self.drawer.discard(discard_tile_136, 0)

        self.game_table.bot.tiles136 = sorted(self.game_table.bot.tiles136)

        self.drawer and self.drawer.update_self(self.game_table.bot.tiles136,
                                                [meld.tiles for meld in self.game_table.bot.meld136])

        return False
Example #15
0
    def _handle_round_end(self, msg=''):
        self.logger_obj.flush_buffer()
        self.logger_obj.add_line('')
        self.logger_obj.add_line("    {}".format(msg))

        # parse message
        who = int(TenhouParser.get_attribute_value(msg, 'who'))
        from_whom = int(TenhouParser.get_attribute_value(msg, 'fromWho'))
        win_tile_136 = int(TenhouParser.get_attribute_value(msg, 'machi'))
        hand_tiles_136 = TenhouParser.parse_initial_hand(msg)
        win_score = TenhouParser.parse_win_score(msg)
        if win_tile_136 in hand_tiles_136:
            hand_tiles_136.remove(win_tile_136)

        # save in logs
        who_str = "{}{}".format(
            who, "(Riichi)" if self.game_table.get_player(who).reach_status else ""
        )
        from_whom_str = "{}{}".format(
            from_whom, "(Riichi)" if self.game_table.get_player(from_whom).reach_status else ""
        )
        res_str = "    [Round-{} Turn-{} Dealer-{}]\n".format(
            self.game_table.round_number, len(self.game_table.bot.discard34), self.game_table.dealer_seat
        )
        res_str += "        [Win]:{}  [By]:{}  [Score]:{}  [Tile]:{} {} \n".format(
            who_str, from_whom_str, win_score, win_tile_136 // 4, Tile.t34_to_g(win_tile_136 // 4)
        )
        hand_tiles_34 = [t // 4 for t in hand_tiles_136] if who != 0 else self.game_table.bot.hand34
        win_tile_34 = win_tile_136 // 4
        meld_tiles_34 = self.game_table.get_player(who).meld34
        res_str += " " * 8 + "[Winning Hand] "
        res_str += "{}+{}+{}".format(
            Tile.t34_to_g(hand_tiles_34), ''.join([Tile.t34_to_g(m) for m in meld_tiles_34]), Tile.t34_to_g(win_tile_34)
        )
        res_str += "  {}+{}+{}\n".format(
            hand_tiles_34, ''.join(["{}".format(m) for m in meld_tiles_34]), win_tile_34
        )
        self._round_end_info_to_file(res_str)

        # display in logs
        if from_whom == who:
            win_msg = "    Player {} wins by own drawn tile {}".format(
                who, Tile.t34_to_g(win_tile_136 // 4)
            )
        else:
            win_msg = "    Player {} wins from player {}'s discard {}".format(
                who, from_whom, Tile.t34_to_g(win_tile_136 // 4)
            )
        self.both_log(win_msg)

        win_melds = self.game_table.get_player(who).meld34
        win_tiles_msg = "    {} {}+ {}, {}p".format(
            Tile.t136_to_g(hand_tiles_136),
            "+{}".format(Tile.t34_to_g(win_melds)) if len(win_melds) > 0 else '',
            Tile.t136_to_g(win_tile_136),
            win_score
        )
        self.both_log(win_tiles_msg)
        self.both_log('')

        # display in GUI
        if self.drawer:
            from_whom != who and self.drawer.lose(from_whom, win_score)
            from_whom != who and self.drawer.yong(who, win_score)
            from_whom == who and self.drawer.zimo(who, win_score)
            if who != 0:
                p_melds = [meld.tiles for meld in self.game_table.get_player(who).meld136]
                p_melds += [hand_tiles_136]
                p_melds += [[win_tile_136]]
                self.drawer.update_opp(p_melds, who, True)
            else:
                self.drawer.draw(win_tile_136)

        self._flush_buffer()
        self._wait_for_a_while()
        sleep(2)
        self._send('<NEXTREADY />')