Esempio n. 1
0
def main():
    import datetime
    
    manzu = [1,0,0,1,0,0,0,0,0]
    pinzu = [0,0,0,0,0,2,2,0,0]
    souzu = [0,0,0,2,0,0,2,0,0]
    ji = [0,3,0,0,0,0,0]
    """
    manzu = [1,0,0,0,0,0,0,0,1]
    pinzu = [1,0,0,0,0,0,0,0,1]
    souzu = [1,0,3,0,0,0,0,0,1]
    ji = [1,1,1,1,0,0,1]
    """
    tehai = manzu + pinzu + souzu + ji
    
    tehai = [
        0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 1, 1, 3, 0, 0, 0,
        0, 0, 0, 3, 2, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0
    ]
    
    
    depth = 3
    furo_num = (14-sum(tehai))//3

    from mjaigym.board.function.furo import Furo
    w_pai = Pai.from_str("W")
    pai_3m =Pai.from_str("3m")
    furos = [
        Furo({"type":"pon", "actor":0, "target":2, "taken":pai_3m, "consumed":[pai_3m,pai_3m]}),
        # Furo({"type":"pon", "actor":0, "target":2, "taken":w_pai, "consumed":[w_pai,w_pai]}),
        # Furo({"type":"pon", "actor":0, "target":2, "taken":pai_3m, "consumed":[pai_3m,pai_3m]}),
    ]
    assert 13 <= sum(tehai) + 3 * len(furos) <= 14

    dfs = Dfs()
    shanten_normal, shanten_kokushi, shanten_chitoitsu = shanten.get_shanten_all(tehai, len(furos))
    start = datetime.datetime.now()
    for i in range(40):
        
        tehai = get_random_tehai()
        # if 0 <= shanten_normal < depth-1:
        result = dfs.dfs_with_score_normal(tehai, furos, depth, oya=True, shanten_normal=shanten_normal)
        # result = dfs.dfs_with_score_chitoitsu(tehai, furos, depth, doras=Pai.from_list(["1m","3m"]), shanten_chitoitsu=shanten_chitoitsu)
        # result = dfs.dfs_with_score_kokushi(tehai, furos, depth, oya=True, shanten_kokushi=shanten_kokushi)
    
    # result = dfs.dfs(tehai, furo_num, depth)
    end = datetime.datetime.now()
    
    print(tehai)
    for r in result:
        print(r)
        pass
    print(len(result))
    print(end - start)
Esempio n. 2
0
def compare():
    import datetime

    depth = 3

    dfs = Dfs()
    print("start dfs a")
    start = datetime.datetime.now()

    manzu = [0,0,0,0,0,0,0,0,0]
    pinzu = [0,0,0,0,0,0,0,0,0]
    souzu = [0,0,0,0,0,0,0,0,0]
    ji = [1,0,0,0,0,3,1]
    tehai_a = manzu + pinzu + souzu + ji
    
    depth = 3
    furo_num = (14-sum(tehai_a))//3

    from mjaigym.board.function.furo import Furo
    w_pai = Pai.from_str("W")
    pai_3m =Pai.from_str("3m")
    furos = [
        Furo({"type":"pon", "actor":0, "target":2, "taken":w_pai, "consumed":[w_pai,w_pai]}),
        Furo({"type":"pon", "actor":0, "target":2, "taken":w_pai, "consumed":[w_pai,w_pai]}),
        Furo({"type":"pon", "actor":0, "target":2, "taken":pai_3m, "consumed":[pai_3m,pai_3m]}),
    ]
    assert 13 <= sum(tehai_a) + 3 * len(furos) <= 14

    shanten_normal, shanten_kokushi, shanten_chitoitsu = shanten.get_shanten_all(tehai_a, len(furos))
    result_a = dfs.dfs_with_score_normal(tehai_a, furos, depth, shanten_normal=shanten_normal)

    # a_changes = set()
    # for ra in result_a:
    #     a_changes.add(tuple(ra[0]))
    print("start dfs b")
    dfs = Dfs()
    manzu = [0,0,0,0,0,0,0,0,0]
    pinzu = [0,0,0,0,0,0,0,0,0]
    souzu = [0,0,0,0,0,0,0,0,0]
    ji = [1,0,0,0,0,1,3]
    tehai_b = manzu + pinzu + souzu + ji
    shanten_normal, shanten_kokushi, shanten_chitoitsu = shanten.get_shanten_all(tehai_b, len(furos))
    result_b = dfs.dfs_with_score_normal(tehai_b, furos, depth, shanten_normal=shanten_normal)

    result_a = sorted(result_a, key=lambda x:x[0])
    result_b = sorted(result_b, key=lambda x:x[0])

    print(len(result_a))
    print(len(result_b))

    for i in range(max(len(result_a),len(result_b))):
        if i >= len(result_b):
            print(result_a[i][0])
        else:
            print(result_a[i][0], result_b[i][0])
Esempio n. 3
0
    def can_kakan(self, pai:str=None):
        if self.reach_state == "accepted":
            return False, []

        if pai is None:
            pons = {}
            for f in self.furos:
                if f.type == 'pon':
                    pons[f.pais[0].id] = [p.str for p in f.pais]

            can_kakan_ids = [p.id for p in self.tehais if p.id in pons]
            if len(can_kakan_ids) == 0:
                return False, []
            
            candidates = []
            for can_kakan_id in can_kakan_ids:
                candidate = [p.str for p in self.tehais if p.id == can_kakan_id]
                # assert len(candidate) == 1
                candidates.append([candidate[0], pons[can_kakan_id]])
            
            return True, candidates
        else:
            target_pai = Pai.from_str(pai)
            in_tehai = [p for p in self.tehais if p.is_same_symbol(target_pai)]
            in_pon = [[p.str for p in f.pais] for f in self.furos if f.type == 'pon' and f.pais[0].is_same_symbol(target_pai)]
            return len(in_tehai)==1 & len(in_pon)==1, [in_tehai[0], in_pon]
Esempio n. 4
0
    def can_hora(self):
        previous_action = self.board.previous_action
        if not previous_action:
            return False

        if previous_action['type'] == MjMove.tsumo.value and self.id == previous_action['actor']:
            hora_type = 'tsumo'
            pais = self.tehais
            shanten = self.shanten
        elif previous_action['type'] in [MjMove.dahai.value, MjMove.kakan.value] and self.id != previous_action['actor']:
            hora_type = 'ron'
            pais = self.tehais + [Pai.from_str(previous_action['pai'])]
            shanten = self.calc_added_shanten(previous_action['pai'])
        else:
            return False

        if shanten != -1:
            return False

        action = {
            'type':MjMove.hora.value,
            'pai':previous_action['pai'],
            'actor':self.id,
            'target':previous_action['actor'],    
        }
        hora = self.board.get_hora(action, **{'previous_action':previous_action})
        
        return hora.valid and (hora_type == 'tsumo' or self.furiten == False)
Esempio n. 5
0
    def get_chi_dahai(self, chi_action):
        dahai_dic = {}
        chi_pai = Pai.from_str(chi_action['pai'])
        chi_consumed = Pai.from_list(chi_action['consumed'])

        upper_kuikae_exists = False
        lower_kuikae_exists = False
        if chi_consumed[0].number + 1 == chi_consumed[1].number:
            if chi_pai.number + 1 == chi_consumed[0].number and chi_pai.number != 7:
                upper_kuikae_exists = True
            elif chi_pai.number - 2 == chi_consumed[0].number and chi_pai.number != 3:
                lower_kuikae_exists = True
            
        for tehai in self.tehais:
            if tehai.str in dahai_dic:
                continue
            if chi_pai.is_same_symbol(tehai):
                continue
            if upper_kuikae_exists and \
                tehai.type == chi_pai.type and \
                tehai.number - 3 == chi_pai.number:
                    continue
            if lower_kuikae_exists and \
                tehai.type == chi_pai.type and \
                tehai.number + 3 == chi_pai.number:
                    continue
            
            dahai_dic[tehai.str] = ''
        return [d for d in dahai_dic.keys()]
Esempio n. 6
0
    def can_pon(self, pai:str):
        if self.reach_state == "accepted":
            return False, []

        target_pai = Pai.from_str(pai)
        candidates_pai = [t for t in self.tehais if target_pai.is_same_symbol(t)]
        
        if len(candidates_pai) < 2:
            return False, []
        
        red_consumed = ([c for c in candidates_pai if c.is_red])
        not_red_consumed = ([c for c in candidates_pai if c.is_red == False])
        if len(red_consumed) > 0:
            if  len(candidates_pai) == 2:
                return True, [
                    [red_consumed[0].str,     not_red_consumed[0].str],
                    ]
            else:
                return True, [
                    [red_consumed[0].str,     not_red_consumed[0].str],
                    [not_red_consumed[0].str, not_red_consumed[0].str],
                    ]
        else:
            return True, [
                [not_red_consumed[0].str, not_red_consumed[1].str]
                ]
Esempio n. 7
0
    def update_oya(self, renchan, ryukyoku_reason):
        if renchan == False:
            self.oya = (self.oya + 1) % self.PLAYER_NUM
            if self.oya == self.chicha:
                self.bakaze = Pai.from_str(self.bakaze).succ.str

        if renchan or ryukyoku_reason:
            self.honba += 1
        else:
            self.honba = 0

        if self.game_type == 'tonpu':
            self.last = self.decide_last(Pai.from_str('E'), renchan,
                                         ryukyoku_reason)
        elif self.game_type == 'tonnan':
            self.last = self.decide_last(Pai.from_str('S'), renchan,
                                         ryukyoku_reason)
Esempio n. 8
0
    def calc_added_shanten(self, pai:str):
        pai = Pai.from_str(pai)
        dahaied_tehai = self.tehais.copy()
        dahaied_tehai.append(pai)

        tehai = [0] * 34
        for t in dahaied_tehai:
            tehai[t.id] += 1
        return self.shanten_analysis.calc_shanten(tehai, len(self.furos))
Esempio n. 9
0
 def can_daiminkan(self, pai:str):
     if self.reach_state == "accepted":
         return False, []
     
     target_pai = Pai.from_str(pai)
     candidates = [t.str for t in self.tehais if target_pai.is_same_symbol(t)]
     if len(candidates) < 3:
         return False, []
     else:
         return True, candidates
Esempio n. 10
0
    def get_hora(self, action, **params):

        if action['type'] != MjMove.hora.value:
            raise Exception('shoud not happen')

        hora_type = 'tsumo' if action['actor'] == action['target'] else 'ron'

        if hora_type == 'tsumo':
            tehais = self.players[action['actor']].tehais[:-1]  # remove tsumo
        else:
            tehais = self.players[action['actor']].tehais

        doras = [Pai.from_str(p).succ for p in self.dora_markers]
        if 'uradora_markers' in params:
            uradoras = [
                Pai.from_str(p).succ for p in params['uradora_markers']
            ]
        else:
            uradoras = []

        hora_player = self.players[action['actor']]
        # return Hora(
        return HoraRs(
            tehais=tehais,
            furos=hora_player.furos,
            taken=Pai.from_str(action['pai']),
            hora_type=hora_type,
            oya=self.oya == action['actor'],
            bakaze=self.bakaze,
            jikaze=self.players[action['actor']].jikaze,
            doras=doras,
            uradoras=uradoras,
            reach=hora_player.reach_state == 'accepted',
            double_reach=hora_player.double_reach,
            ippatsu=hora_player.ippatsu_chance,
            rinshan=hora_player.rinshan,
            haitei=(self.yama.get_rest_num() == 0)
            and (hora_player.rinshan == False),
            first_turn=self.first_turn,
            chankan=('previous_action' in params)
            and (params['previous_action']['type'] == MjMove.kakan.value),
        )
Esempio n. 11
0
    def calc_dahaied_shanten(self, pai:str):
        pai = Pai.from_str(pai)
        if pai not in self.tehais:
            raise Exception(f'tehais not condains pai:{pai}')
        dahaied_tehai = self.tehais.copy()
        dahaied_tehai.remove(pai)

        tehai = [0] * 34
        for t in dahaied_tehai:
            tehai[t.id] += 1
        return self.shanten_analysis.calc_shanten(tehai, len(self.furos))
Esempio n. 12
0
 def calc(cls, result:np.array, board_state:BoardState, player_id:int, oracle_enable_flag:bool=False):
     dora_markers = board_state.dora_markers
     nums = {}
     for dora_maker in dora_markers:
         pai = Pai.from_str(dora_maker)
         dora_pai = pai.succ
         if dora_pai.id not in nums:
             nums[dora_pai.id] = 0
         nums[dora_pai.id] += 1
     
     for pai_id, n in nums.items():
         if n > 0:
             result[0:n, pai_id] = 1
Esempio n. 13
0
    def calc(cls,
             result: np.array,
             board_state: BoardState,
             player_id: int,
             oracle_enable_flag: bool = False):
        if board_state.previous_action["type"] != MjMove.dahai.value:
            return
        if board_state.previous_action["actor"] != player_id:
            return

        last_dahai_pai = Pai.from_str(board_state.previous_action['pai'])
        result[0, last_dahai_pai.id] = 1

        if last_dahai_pai.is_red:
            result[1, :] = 1
    def calc(cls, result:np.array, board_state:BoardState, player_id:int, candidate_furo:Dict, oracle_enable_flag:bool=False):
        player_tehai = board_state.tehais[player_id]
        

        tehais = [0] * 34
        for pai in player_tehai:
            tehais[pai.id] += 1
        
        furo_num = len(board_state.furos[player_id])
        current_shanten = cls.shanten_analysis.calc_shanten(tehais, furo_num)

        # ignore kan
        if candidate_furo["type"] not in [MjMove.ankan.value, MjMove.daiminkan.value, MjMove.kakan.value]:
            add_pai = Pai.from_str(candidate_furo["pai"])
            tehais[add_pai.id] += 1
            added_shanten = cls.shanten_analysis.calc_shanten(tehais, furo_num)
            if current_shanten > added_shanten:
                result[0,:,0] = 1
        
        offset = 1
        target_channel = max(0,min(6,current_shanten)) + offset
        result[target_channel, :, 0] = 1
Esempio n. 15
0
    def can_ankan(self, pai:str=None):
        if pai is None:
            pai_count = {}
            for t in self.tehais:
                if t.id not in pai_count:
                    pai_count[t.id] = 0
                
                pai_count[t.id] += 1

            can_ankan_ids = [p for p in pai_count if pai_count[p] == 4]
            if len(can_ankan_ids) == 0:
                return False, []
            
            candidates = []
            for can_ankan_id in can_ankan_ids:
                candidate = [p.str for p in self.tehais if p.id == can_ankan_id]
                candidates.append(candidate)
            
            return True, candidates

        else:
            in_tehai = [p for p in self.tehais if p.is_same_symbol(Pai.from_str(pai))]
            return len(in_tehai) == 4, in_tehai
Esempio n. 16
0
    def tsumo(self, pai: str):
        self._consumed_num += 1
        if pai == UNKNOWN_PAI_STR:
            return

        self.rest.remove(Pai.from_str(pai))
Esempio n. 17
0
    def restnum_update(self, action):
        if action is None or "type" not in action:
            return

        if action["type"] == MjMove.start_kyoku.value:
            # initialize
            self.rest_pai_view = [[4] * 34 for _ in range(4)]

            dora_marker_id = Pai.str_to_id(action["dora_marker"])
            for i in range(4):
                self.rest_pai_view[i][dora_marker_id] -= 1
                for pai in action["tehais"][i]:
                    pai_id = Pai.str_to_id(pai)
                    self.rest_pai_view[i][pai_id] -= 1

        elif action["type"] == MjMove.tsumo.value:
            pai_id = Pai.from_str(action["pai"]).id
            actor = action["actor"]
            self.rest_pai_view[actor][pai_id] -= 1

        elif action["type"] == MjMove.dahai.value:
            pai_id = Pai.from_str(action["pai"]).id
            actor = action["actor"]
            for i in range(4):
                if actor != i:
                    self.rest_pai_view[i][pai_id] -= 1

        elif action["type"] == MjMove.dora.value:
            pai_id = Pai.from_str(action["dora_marker"]).id
            for i in range(4):
                self.rest_pai_view[i][pai_id] -= 1

        elif action["type"] == MjMove.chi.value:
            actor = action["actor"]
            for i in range(4):
                if actor != i:
                    for consume in action["consumed"]:
                        self.rest_pai_view[i][Pai.str_to_id(consume)] -= 1

        elif action["type"] == MjMove.pon.value:
            actor = action["actor"]
            for i in range(4):
                if actor != i:
                    for consume in action["consumed"]:
                        self.rest_pai_view[i][Pai.str_to_id(consume)] -= 1

        elif action["type"] == MjMove.daiminkan.value:
            actor = action["actor"]
            for i in range(4):
                if actor != i:
                    for consume in action["consumed"]:
                        self.rest_pai_view[i][Pai.str_to_id(consume)] -= 1

        elif action["type"] == MjMove.ankan.value:
            actor = action["actor"]
            for i in range(4):
                if actor != i:
                    for consume in action["consumed"]:
                        self.rest_pai_view[i][Pai.str_to_id(consume)] -= 1

        elif action["type"] == MjMove.kakan.value:
            actor = action["actor"]
            pai_id = Pai.str_to_id(action["pai"])
            for i in range(4):
                if actor != i:
                    self.rest_pai_view[i][pai_id] -= 1
Esempio n. 18
0
    def possible_actions(self):
        # calclate valid move
        if self.previous_action is None or \
            self.previous_action['type'] in [
                MjMove.start_game.value,
                MjMove.start_kyoku.value,

                MjMove.reach_accepted.value,
                MjMove.ryukyoku.value,

                MjMove.end_kyoku.value,
                MjMove.end_game.value,
            ]:
            # pattern of all player returns none
            return self.get_all_none_response()

        elif self.previous_action['type'] == MjMove.tsumo.value:
            # tsumo next actions

            tsumo_actor = self.previous_action['actor']
            tehais = self.players[tsumo_actor].tehais
            actions = []

            if self.players[tsumo_actor].reach or self.players[
                    tsumo_actor].double_reach:
                # add dahai
                action = {
                    'type': MjMove.dahai.value,
                    'actor': tsumo_actor,
                    'pai': self.previous_action['pai'],
                    'tsumogiri': True,
                }
                actions.append(action)
            else:
                dahais_dic = {}
                for i, tehai in enumerate(tehais[:-1]):
                    # remove duplicate
                    if tehai.str in dahais_dic:
                        continue
                    dahais_dic[tehai.str] = ''
                    action = {
                        'type': MjMove.dahai.value,
                        'actor': tsumo_actor,
                        'pai': tehai.str,
                        'tsumogiri': False,
                    }
                    actions.append(action)
                # add tsumogiri
                action = {
                    'type': MjMove.dahai.value,
                    'actor': tsumo_actor,
                    'pai': tehais[-1].str,  # tsumo pai is in last index.
                    'tsumogiri': True,
                }
                actions.append(action)

            # add reach
            if self.players[tsumo_actor].reach_state == 'none' and \
                self.players[tsumo_actor].menzen and \
                self.players[tsumo_actor].shanten <= 0 and \
                self.yama.get_rest_num() >= 1:
                action = {
                    'type': MjMove.reach.value,
                    'actor': tsumo_actor,
                }
                actions.append(action)
            # add ankan
            can_ankan, consumeds = self.players[tsumo_actor].can_ankan()
            yama_can_kan = self.yama.get_rest_num() > 0
            if can_ankan and yama_can_kan:
                for consumed in consumeds:
                    action = {
                        'type': MjMove.ankan.value,
                        'actor': tsumo_actor,
                        'consumed': consumed,
                    }
                    actions.append(action)

            # add kakan
            can_kakan, pai_consumeds = self.players[tsumo_actor].can_kakan()
            if can_kakan and yama_can_kan:
                for pai_consumed in pai_consumeds:
                    action = {
                        'type': MjMove.kakan.value,
                        'actor': tsumo_actor,
                        'pai': pai_consumed[0],
                        'consumed': pai_consumed[1],
                    }
                    actions.append(action)

            # add hora
            can_hora = self.players[tsumo_actor].can_hora()
            if can_hora:
                previous_actor = self.previous_action['actor']
                previous_pai = self.previous_action['pai']
                action = {
                    'type': MjMove.hora.value,
                    'actor': tsumo_actor,
                    'target': previous_actor,
                    'pai': previous_pai,
                }
                actions.append(action)

            response = self.get_all_none_response()
            response[tsumo_actor] = actions  # not use none
            return response

        elif self.previous_action['type'] == MjMove.reach.value:
            tsumo_actor = self.previous_action['actor']
            tehais = self.players[tsumo_actor].tehais
            actions = []

            dahais_dic = {}
            for i, tehai in enumerate(tehais[:-1]):
                # remove duplicate
                if tehai.str in dahais_dic:
                    continue
                dahais_dic[tehai.str] = ''
                action = {
                    'type': MjMove.dahai.value,
                    'actor': tsumo_actor,
                    'pai': tehai.str,
                    'tsumogiri': False,
                }
                actions.append(action)
            # add tsumogiri
            action = {
                'type': MjMove.dahai.value,
                'actor': tsumo_actor,
                'pai': tehais[-1].str,  # tsumo pai is in last index.
                'tsumogiri': True,
            }
            actions.append(action)

            tenpai_actions = []
            for a in actions:
                if self.players[tsumo_actor].calc_dahaied_shanten(
                        a['pai']) <= 0:
                    tenpai_actions.append(a)
            # assert len(tenpai_actions) > 0

            response = self.get_all_none_response()
            response[tsumo_actor] = tenpai_actions  # not use none
            return response

        elif self.previous_action['type'] == MjMove.dahai.value:
            # check ryukyoku
            if self.yama.get_rest_num() == 0:
                # none
                return self.get_all_none_response()

            # dahai next actions

            previous_actor = self.previous_action['actor']
            previous_pai = self.previous_action['pai']
            response = self.get_all_none_response()
            for i in range(self.PLAYER_NUM):
                if i == previous_actor:
                    continue

                # add hora
                if self.players[i].can_hora():
                    action = {
                        'type': MjMove.hora.value,
                        'actor': i,
                        'target': previous_actor,
                        'pai': previous_pai
                    }
                    response[i].append(action)

                # add daiminkan
                can_daiminkan, consumed = self.players[i].can_daiminkan(
                    previous_pai)
                if can_daiminkan:
                    action = {
                        'type': MjMove.daiminkan.value,
                        'actor': i,
                        'target': previous_actor,
                        'pai': previous_pai,
                        'consumed': consumed
                    }
                    response[i].append(action)

                # add pon
                # NOTE: consumed could be 2 pattern, with red, without red.
                can_pon, consumed_list = self.players[i].can_pon(previous_pai)
                if can_pon:
                    for consumed in consumed_list:
                        action = {
                            'type': MjMove.pon.value,
                            'actor': i,
                            'target': previous_actor,
                            'pai': previous_pai,
                            'consumed': consumed
                        }
                        response[i].append(action)

                # add chi
                # check distance
                if self.distance(i, previous_actor) == 1:
                    can_chi, consumed_list = self.players[i].can_chi(
                        previous_pai)
                    if can_chi:
                        for consumed in consumed_list:
                            action = {
                                'type': MjMove.chi.value,
                                'actor': i,
                                'target': previous_actor,
                                'pai': previous_pai,
                                'consumed': consumed
                            }
                            response[i].append(action)
            return response

        elif self.previous_action['type'] == MjMove.pon.value:
            previous_actor = self.previous_action['actor']
            previous_pai = Pai.from_str(self.previous_action['pai'])
            actions = []
            tehais = self.players[previous_actor].tehais
            dahais_dic = {}
            for i, tehai in enumerate(tehais):
                # ignore kuikae
                if previous_pai.is_same_symbol(tehai):
                    continue
                # ignore already considerd symbol
                if tehai.str in dahais_dic:
                    continue

                dahais_dic[tehai.str] = 0
                action = {
                    'type': MjMove.dahai.value,
                    'actor': previous_actor,
                    'pai': tehai.str,
                    'tsumogiri': False,
                }
                actions.append(action)

            response = self.get_all_none_response()
            response[previous_actor] = actions  # not use none
            return response

        elif self.previous_action['type'] == MjMove.chi.value:
            previous_actor = self.previous_action['actor']
            dahais = self.players[previous_actor].get_chi_dahai(
                self.previous_action)
            actions = []
            for d in dahais:
                action = {
                    'type': MjMove.dahai.value,
                    'actor': previous_actor,
                    'pai': d,
                    'tsumogiri': False,
                }
                actions.append(action)

            response = self.get_all_none_response()
            response[previous_actor] = actions  # not use none
            return response

        elif self.previous_action['type'] == MjMove.daiminkan.value:
            response = self.get_all_none_response()
            return response

        elif self.previous_action['type'] == MjMove.dora.value:
            if self.previous_without_dora_action['type'] == MjMove.ankan.value:
                response = self.get_all_none_response()

            elif self.previous_without_dora_action[
                    'type'] == MjMove.dahai.value:
                response = self.get_all_none_response()

            elif self.previous_without_dora_action[
                    'type'] == MjMove.kakan.value:
                response = self.get_all_none_response()

            elif self.previous_without_dora_action[
                    'type'] == MjMove.daiminkan.value:  # 大明槓即乗り
                response = self.get_all_none_response()

            elif self.previous_without_dora_action[
                    'type'] == MjMove.tsumo.value:  # 大明槓後めくり時の打牌
                tsumo_actor = self.previous_two_action['actor']
                tehais = self.players[tsumo_actor].tehais
                actions = []
                dahais_dic = {}
                for i, tehai in enumerate(tehais[:-1]):
                    # remove duplicate
                    if tehai.str in dahais_dic:
                        continue
                    dahais_dic[tehai.str] = ''
                    action = {
                        'type': MjMove.dahai.value,
                        'actor': tsumo_actor,
                        'pai': tehai.str,
                        'tsumogiri': False,
                    }
                    actions.append(action)
                # add tsumogiri
                action = {
                    'type': MjMove.dahai.value,
                    'actor': tsumo_actor,
                    'pai': tehais[-1].str,  # tsumo pai is in last index.
                    'tsumogiri': True,
                }
                actions.append(action)
                response = self.get_all_none_response()
                response[tsumo_actor] = actions  # not use none
                return response
            else:
                raise Exception('invalid path')
            return response

        elif self.previous_action['type'] == MjMove.ankan.value:
            response = self.get_all_none_response()
            return response

        elif self.previous_action['type'] == MjMove.kakan.value:
            response = self.get_all_none_response()
            # chankan
            previous_actor = self.previous_action['actor']
            previous_pai = self.previous_action['pai']
            for i in range(self.PLAYER_NUM):
                if i == previous_actor:
                    continue

                # add hora
                if self.players[i].can_hora():
                    action = {
                        'type': MjMove.hora.value,
                        'actor': i,
                        'target': previous_actor,
                        'pai': previous_pai
                    }
                    response[i].append(action)

            return response

        elif self.previous_action['type'] == MjMove.hora.value:
            response = self.get_all_none_response()
            return response

        else:
            raise Exception('invalid path')
Esempio n. 19
0
        normal, chitoitsu, kokushi = self.shanten_analysis.calc_all_shanten(
            self.tehai, self.furo_num)
        if chitoitsu < 1 and chitoitsu < normal and chitoitsu < kokushi:
            return chitoitsu
        if kokushi < 3 and kokushi < normal and kokushi < chitoitsu:
            return kokushi
        else:
            return normal


if __name__ == "__main__":
    pais = [
        '1m', '2m', '3m', '4m', '8m', '2p', '3p', '8p', '8p', '9p', '1s', '4s',
        '5s', '7p'
    ]
    pais = [Pai.from_str(p) for p in pais]
    node = Node(pais)
    print(pais)
    print(node.calc_ukeire_num())

    start = datetime.datetime.now()
    print(start)

    loopnum = 1000
    for i in range(loopnum):
        node.calc_ukeire_num()

    end = datetime.datetime.now()
    print(datetime.datetime.now())
    print(loopnum, end - start)
Esempio n. 20
0
    def update_state(self, action):
        if self.board.previous_action is not None and \
                self.board.previous_action['type'] in [MjMove.dahai.value, MjMove.kakan.value] and \
                'actor' in self.board.previous_action and \
                self.board.previous_action['actor'] != self.id and \
                action['type'] != MjMove.hora.value:
            
            self.extra_anpais.append(Pai.from_str(self.board.previous_action['pai']))





        action_type = action['type']

        if action_type == MjMove.start_game.value:
            if not self.id and 'id' in action:
                self.id = action['id']
                self.name = f"player{self.id}"
            if 'names' in action:
                self.name = action['names'][self.id]
            self.score = 25000
            self.tehais = []
            self.furos = []
            self.ho = []
            self.sutehais = []
            self.extra_anpais = []
            self.reach_state = None
            self.reach_ho_index = None
            self.reach_sutehais_index = None
            self.double_reach = False
            self.ippatsu_chance = False
            self.pao_for_id = None
            self.rinshan = False

        elif action_type == MjMove.start_kyoku.value:
            self.tehais = sorted(Pai.from_list(action['tehais'][self.id]))
            
            self.furos = []
            self.ho = []
            self.sutehais = []
            self.extra_anpais = []
            self.reach_state = "none"
            self.reach_ho_index  = None
            self.reach_sutehais_index = None
            self.double_reach = False
            self.ippatsu_chance = False
            self.pao_for_id = None
            self.rinshan = False

        elif action_type in [
            MjMove.chi.value,
            MjMove.pon.value, 
            MjMove.daiminkan.value, 
            MjMove.ankan.value
            ]:
            self.ippatsu_chance = False
        elif action_type == MjMove.tsumo.value:
            if self.board.previous_action['type'] == MjMove.kakan.value:
                self.ippatsu_chance = False


        if 'actor' in action and action['actor'] == self.id:
            if action_type == MjMove.tsumo.value:
                pai = Pai.from_str(action['pai'])
                self.tehais = sorted(self.tehais)
                self.tehais.append(pai)
            elif action_type == MjMove.dahai.value:
                pai = Pai.from_str(action['pai'])
                self.delete_tehai(pai)
                self.tehais = sorted(self.tehais)
                self.ho.append(pai)
                self.sutehais.append(pai)
                self.ippatsu_chance = False
                self.rinshan = False
                if self.reach == False:
                    self.extra_anpais.clear()
            elif action_type in [
                MjMove.chi.value, 
                MjMove.pon.value, 
                MjMove.daiminkan.value, 
                MjMove.ankan.value
                ]:

                consumed_pais = Pai.from_list(action['consumed'])
                for c in consumed_pais:
                    self.delete_tehai(c)

                furo = {
                        'type':action['type'],
                        'consumed':consumed_pais,   
                    }
                if action_type != MjMove.ankan.value:
                    pai = Pai.from_str(action['pai'])
                    furo['taken'] = pai

                if action_type == MjMove.chi.value:                    
                    furo['pai_id'] = min(
                        Pai.str_to_id(action['pai']),
                        min(
                            Pai.str_to_id(action['consumed'][0]), 
                            Pai.str_to_id(action['consumed'][1]))
                    )
                else:
                    furo['pai_id'] = Pai.str_to_id(action['consumed'][0])

                if action_type == MjMove.ankan.value:
                    furo['target'] = self.id
                else:
                    furo['target'] = action['target']

                self.furos.append(Furo(furo))
                if action_type in [MjMove.daiminkan.value, MjMove.ankan.value]:
                    self.rinshan = True

                # pao
                if action_type in [MjMove.daiminkan.value, MjMove.pon.value]:
                    pai = Pai.from_str(action['pai'])
                    if pai.is_sangenpai():
                        if self.is_daisangen_pao():
                            self.pao_for_id = action['target']
                    elif pai.is_wind():
                        if self.is_daisushi_pao():
                            self.pao_for_id = action['target']
            
            elif action_type == MjMove.kakan.value:
                pai = Pai.from_str(action['pai'])
                self.delete_tehai(pai)
                pon_index = -1
                for i,f in enumerate(self.furos):
                    if f.type == MjMove.pon.value and pai.is_same_symbol(f.taken):
                        pon_index = i
                if pon_index == -1:
                    raise Exception('not have same symbole pon')
                self.furos[pon_index] = Furo({
                    'type':MjMove.kakan.value,
                    'taken':self.furos[pon_index].taken,
                    'consumed':self.furos[pon_index].consumed + [pai],
                    'target':self.furos[pon_index].target,
                    'pai_id':self.furos[pon_index].pai_id,
                })
                self.rinshan = True
            
            elif action_type == MjMove.reach.value:
                self.reach_state = 'declared'
                self.double_reach = self.board.first_turn
            
            elif action_type == MjMove.reach_accepted.value:
                self.reach_state = 'accepted'
                self.reach_ho_index = len(self.ho)-1
                self.reach_sutehais_index = len(self.sutehais)-1
                self.ippatsu_chance = True
                
        
        if 'target' in action and action['target'] == self.id:
            pai = Pai.from_str(action['pai'])
            if action_type in [
                MjMove.pon.value,
                MjMove.chi.value, 
                MjMove.daiminkan.value
                ]:
                taken = self.ho.pop()
                # assert taken == pai
        
        if 'scores' in action:
            self.score = action['scores'][self.id]
Esempio n. 21
0
    def think_on_other_dahai(self, board_state: BoardState,
                             candidates: List[Dict]):

        my_tehais = board_state.tehais[self.id]

        # if there is only one candidate, return that.
        if len(candidates) == 1:
            return candidates[-1]

        # if can hora, always do hora
        hora_candidates = [
            c for c in candidates if c['type'] == MjMove.hora.value
        ]
        if len(hora_candidates) == 1:
            return hora_candidates[0]

        # if there is other player reach, don't furo.
        if any(board_state.reach):
            none_candidates = [
                c for c in candidates if c['type'] == MjMove.none.value
            ]
            if len(none_candidates) == 0:
                raise Exception("not intended path, none candidate not found.")
            else:
                return none_candidates[0]

        # this path assumes there is no other player reach

        # shanten reduceable yakuhai pon is executed.
        furo_types = [
            MjMove.daiminkan.value, MjMove.pon.value, MjMove.chi.value
        ]
        furo_candidates = [c for c in candidates if c['type'] in furo_types]

        for candidate in furo_candidates:
            if candidate["type"] == MjMove.pon.value:
                candidate_pai = Pai.from_str(candidate["pai"])
                is_dragon = candidate_pai.is_sangenpai()
                is_jikaze = candidate_pai.str == board_state.jikaze
                is_bakaze = candidate_pai.str == board_state.bakaze

                # if is_yakuhai, 20% do pon
                if is_dragon or is_jikaze or is_bakaze:
                    before_node = Node(board_state.tehais[self.id])
                    executed_node = Node(board_state.tehais[self.id],
                                         candidate_pai)
                    if before_node.shanten > executed_node.shanten and\
                            random.random() < 0.2:
                        return candidate

            # if already furo, and shanten reduceable, 60% furo.
            if len(board_state.furos[self.id]) > 0:
                candidate_pai = Pai.from_str(candidate["pai"])
                before_node = Node(board_state.tehais[self.id])
                executed_node = Node(board_state.tehais[self.id],
                                     candidate_pai)
                if before_node.shanten > executed_node.shanten and\
                        random.random() < 0.6:
                    return candidate

        none_candidates = [
            c for c in candidates if c['type'] == MjMove.none.value
        ]
        if len(none_candidates) == 0:
            raise Exception("not intended path, none candidate not found.")
        else:
            return none_candidates[0]
Esempio n. 22
0
    def can_chi(self, pai:str):
        if self.reach_state == "accepted":
            return False, []
        

        target_pai = Pai.from_str(pai)
        if not target_pai.is_number():
            return False, []

        
        can_chi = False
        candidates = []
        
        tehais_single = {}
        for t in self.tehais:
            if t.str not in tehais_single:
                tehais_single[t.str] = t

        single_tehais = tehais_single.values()

        minus2_candidates_pai = [t.str for t in single_tehais if t.number == target_pai.number-2  and t.type == target_pai.type]
        minus1_candidates_pai = [t.str for t in single_tehais if t.number == target_pai.number-1  and t.type == target_pai.type]
        plus1_candidates_pai = [t.str for t in single_tehais if t.number == target_pai.number+1  and t.type == target_pai.type]
        plus2_candidates_pai = [t.str for t in single_tehais if t.number == target_pai.number+2  and t.type == target_pai.type]
        
        cannot_dahai_number = []
        # right chi [1,2] [3]
        if len(minus2_candidates_pai) > 0 and len(minus1_candidates_pai) > 0:
            cannot_dahai_number.append(target_pai.number-3)
            cannot_dahai_number.append(target_pai.number)
            for m2, m1 in itertools.product(minus2_candidates_pai, minus1_candidates_pai):
                candidates.append([m2, m1])
                
            
        # center chi [1,3] [2]
        if len(minus1_candidates_pai) > 0 and len(plus1_candidates_pai) > 0:
            cannot_dahai_number.append(target_pai.number)
            for m1, p1 in itertools.product(minus1_candidates_pai, plus1_candidates_pai):
                candidates.append([m1, p1])
        # left chi [2,3] [1]        
        if len(plus1_candidates_pai) > 0 and len(plus2_candidates_pai) > 0:
            cannot_dahai_number.append(target_pai.number)
            cannot_dahai_number.append(target_pai.number+3)
            for p1, p2 in itertools.product(plus1_candidates_pai, plus2_candidates_pai):
                candidates.append([p1, p2])


        # kuikae check
        ignore_candidates = []
        if all([t for t in self.tehais if t.type == target_pai.type]):
            for candidate in candidates:
                temp_tehai = copy.copy(self.tehais)
                temp_tehai.remove(Pai.from_str(candidate[0]))
                temp_tehai.remove(Pai.from_str(candidate[1]))
                if len([t for t in temp_tehai if t.number not in cannot_dahai_number]) == 0:
                    ignore_candidates.append(candidate)
        
        for ignore_candidate in ignore_candidates:
            candidates.remove(ignore_candidate)

        return len(candidates) > 0, candidates
Esempio n. 23
0
            if line["type"] == "reach":
                reachs[line["actor"]] = True
                if first_turns[line["actor"]]:
                    double_reachs[line["actor"]] = True

            # furo update
            if line["type"] in ["chi", "pon", "daiminkan"]:
                all_furos[line["actor"]].append({
                    "type": line["type"],
                    "taken": line["pai"],
                    "consumed": line["consumed"],
                })
            elif line["type"] == "kakan":
                for f in all_furos[line["actor"]]:
                    if f["type"] == "pon":
                        pon_taken = Pai.from_str(f["taken"])
                        kakan_pai = Pai.from_str(line["pai"])
                        if pon_taken.is_same_symbol(kakan_pai):
                            f["type"] = "kakan"
                            f["consumed"].append(f["taken"])
                            f["taken"] = line["pai"]
            elif line["type"] == "ankan":
                all_furos[line["actor"]].append({
                    "type": line["type"],
                    "consumed": line["consumed"],
                })

            # check hora
            if line["type"] == "hora":
                tehai = line["hora_tehais"]
                taken = line["pai"]