Exemple #1
0
def main():
    arguements = docopt(
        data_io.set_up_doc(__doc__),
        version='0.1'
    )

    record = Record(debug=arguements['--debug'])
    if arguements['<data>']:
        # translation
        from_lang, to_lang, data = _extract(arguements)

        # translate data
        translator = Translator(from_lang, to_lang, data,
                                debug=arguements['--debug'])
        # result is a dictionary contains decoded infomation of the
        # trnaslation.
        result = translator.translate()
        translator.display_result(result)
        # add record
        record.add(from_lang, to_lang,
                   data, result)

    elif arguements['--record']:
        # display record
        record.display()
    else:
        raise Exception('No Implemented Yet.')
Exemple #2
0
def monitor():
    """
    The core method of the cronjob. Monitors each configured repository for their current branch.
    """
    repositories = Configuration(file_name="repositories").read()
    if len(repositories) == 0:
        print("There are no repositories configured to be monitored.")
        return

    record = Record(file_name="records")
    for repository in repositories:
        branch = retrieve_branch(repository)
        timestamp = datetime.datetime.now().replace(microsecond=0).isoformat()
        record.add(repository, branch, timestamp)
def simulate(nw0, nw1, init='fixed'):
    board = rule.init_board() if init == 'fixed' else rule.random_init_board()
    player = 1
    records = Record()
    while True:
        nw = nw0 if player == 1 else nw1
        try:
            bd = board.copy()
            from_, action, vp, p = nw.policy(board, player)
            # print('>', from_, action)
            assert board[from_] == player
            to_ = tuple(np.add(from_, rule.actions_move[action]))
            command, eat = rule.move(board, from_, to_)
            reward = len(eat)
            records.add(bd, from_, action, reward, vp, win=command == rule.WIN)
        except NoActionException:
            return Record(), 0
        except Exception as e:
            logging.info('board is:')
            logging.info(board)
            logging.info('player is: %s', player)
            valid = rule.valid_action(board, player)
            logging.info('predict is:')
            print(nw.p)
            logging.info('sum is: %s', nw.p.sum())
            logging.info('valid action is:')
            logging.info(nw.valid)
            logging.info('p * valid is:')
            logging.info(nw.vp)
            logging.info('from:%s, action:%s', from_, action)
            logging.info('prob is: %s', valid[from_][action])
            records.save('records/train/1st_')
            raise e
        # if eat:
        #     print(player, from_, to_, eat, N)
        if command == rule.WIN:
            logging.info('%s WIN, step use: %s', str(player), records.length())
            return records, player
        if records.length() > 10000:
            logging.info('走子数过多: %s', records.length())
            return Record(), 0
        player = -player
        board = rule.flip_board(board)
Exemple #4
0
class Tags_recom(object):
    def __init__(self):
        self.char_data = Character()
        self.char_data.extract_all_char()
        self.all_tags = {
            '狙击',
            '术师',
            '特种',
            '重装',
            '辅助',
            '先锋',
            '医疗',
            '近卫',
            '减速',
            '输出',
            '生存',
            '群攻',
            '爆发',
            '召唤',
            '快速复活',
            '费用回复',
            '新手',
            '治疗',
            '防护',
            '位移',
            '削弱',
            '控场',
            '支援',
            '支援机械',
            "机械",
            '近战位',
            '远程位',
            '近战',
            '远程',
            '资深干员',
            '高级资深干员',
            '高级资深',
            '资深',
            '高资',
            #'女', '男',
            #'女性', '男性',
            '狙击干员',
            '术师干员',
            '特种干员',
            '重装干员',
            '辅助干员',
            '先锋干员',
            '医疗干员',
            '近卫干员',
            #'女性干员', '男性干员',
            # flags
            '全部'
        }

        self.ocr_tool = Ocr_tool()
        self.record = Record(path_prefix + "record_tags.txt", writecnt=50)

    def recom_tags(self, tags, flags={}):
        tags = self.strip_tags(tags)

        itertag = self.iter_all_combine(tags)
        if itertag is None: return []
        cob_lis = list(itertag)
        cob_lis.remove([])
        cob_lis = [(tags_lis, list(self.char_data.filter(tags_lis, flags)))
                   for tags_lis in cob_lis]
        cob_lis = [x for x in cob_lis if x[1] != []]
        # print(cob_lis)
        # char_data[name]

        # print("")
        # for x in cob_lis:
        # print(x)

        # remove same result
        for i in range(0, len(cob_lis)):
            for j in range(0, len(cob_lis)):
                if i == j: continue
                if set(cob_lis[i][1]) == set(cob_lis[j][1]):
                    if set(cob_lis[i][0]).issubset(set(cob_lis[j][0])):
                        cob_lis[j] = (cob_lis[j][0], [])
        cob_lis = [x for x in cob_lis if x[1] != []]
        # print("")
        # for x in cob_lis:
        # print(x)

        # special remove
        if ('show_all' not in flags or flags['show_all'] == False):
            for i in range(len(cob_lis)):
                if self.is_special_rm(cob_lis[i]):
                    cob_lis[i] = (cob_lis[i][0], [])
            cob_lis = [x for x in cob_lis if x[1] != []]
            # print("")
            # for x in cob_lis:
            # print(x)

        # sort
        cob_lis.sort(key=self.avg_rank, reverse=True)
        for tags_lis, lis in cob_lis:
            lis.sort(key=lambda x: self.char_data.char_data[x]["rank"],
                     reverse=True)
        # print("")
        # for x in cob_lis:
        # print(x)

        # for x in cob_lis:
        # print(self.avg_rank(x))

        # # build reverse index
        # char_dic=dict()
        # for i in range(len(cob_lis)):
        # for name in cob_lis[i][1]:
        # if name not in char_dic:
        # char_dic[name]=[i]
        # else:
        # char_dic[name].append(i)
        # # print("")
        # # print(char_dic)

        # # remove duplicate
        # min_size_id=dict()
        # for name, lis in char_dic.items():
        # if len(lis)>1:
        # min_size_id[name]=lis[0]
        # for id in lis:
        # if len(cob_lis[id][1])<len(cob_lis[min_size_id[name]][1]):
        # min_size_id[name]=id

        # for name, lis in char_dic.items():
        # if len(lis)>1:
        # for id in lis:
        # if id!=min_size_id[name]:
        # cob_lis[id][1].remove(name)
        # cob_lis=[x for x in cob_lis if x[1]!=[]]
        # # print("")
        # # for x in cob_lis:
        # # print(x)

        #merge less rank 3
        if ('show_all' not in flags or flags['show_all'] == False):
            tag_cnt = 0
            max_num_until_del = 15
            for tags_lis, lis in cob_lis:
                cnt = 0
                sp_lis = []
                while len(lis) > 0 and self.char_data.char_data[
                        lis[-1]]["rank"] <= "3":
                    res = lis.pop()
                    if res in ["Castle-3", "Lancet-2", "THRM-EX"]:
                        sp_lis.append(res)
                    else:
                        cnt += 1

                if len(sp_lis) > 0:
                    lis.extend(sp_lis)
                if cnt > 0 and len(lis) > 0:
                    lis.append("...{0}".format(cnt))
                    # delete all contain <=3
                    if tag_cnt + len(lis) > max_num_until_del:
                        lis.clear()
                        max_num_until_del = -1
                tag_cnt += len(lis)
            cob_lis = [x for x in cob_lis if x[1] != []]

        return cob_lis
        # print("")
        # for x in cob_lis:
        # print(x)

    def is_special_rm(self, cob_i):
        if set(cob_i[0]) == set(["女"]):
            return True
        # if set(cob_i[0])==set(["男"]):
        # return True
        return False

    def avg_rank(self, cob_i):
        rank_map = {1: 0.5, 2: 1, 3: 10, 4: 2, 5: 0.5, 6: 3}
        rank_list = list(
            map(lambda x: int(self.char_data.char_data[x]["rank"]), cob_i[1]))
        sum_score = 0
        sum_cnt = 0
        for i in range(1, 7):
            sum_score += rank_list.count(i) * rank_map[i] * i
            sum_cnt += rank_list.count(i) * rank_map[i]
        if sum_cnt == 0: return 0
        else: return sum_score / sum_cnt

    def strip_tags(self, tags):
        restags = []
        for tag in tags:
            if tag in ["高级资深干员", "高资", "高级资深"]:
                restags.append("高级资深干员")
            elif tag in ["资深干员", "资深"]:
                restags.append("资深干员")
            elif tag in ["近战", "远程"]:
                restags.append(tag + "位")
            elif tag in ["机械"]:
                restags.append(tag + "支援机械")
            # elif tag in ["男性","女性"]:
            #     tag=tag.replace("性","")
            #     restags.append(tag)
            # elif "性干员" in tag:
            #     tag=tag.replace("性干员","")
            #     restags.append(tag)
            elif "干员" in tag:
                tag = tag.replace("干员", "")
                restags.append(tag)
            else:
                restags.append(tag)
        return restags

    def iter_all_combine(self, tags):
        if len(tags) == 0:
            yield []
            return
        tag = tags[0]
        new_tags = tags[:]
        new_tags.remove(tag)
        for x in self.iter_all_combine(new_tags):
            yield [tag] + x
        for x in self.iter_all_combine(new_tags):
            yield x

    def check_legal_tags(self, tags):
        if not tags: return False
        for tag in tags:
            if tag not in self.all_tags:
                return False
        return True

    def split_tags(self, t_tag):
        rest_tag = t_tag[:]
        flag = True
        tag_lis = []
        for tag in self.all_tags:
            if rest_tag == tag:
                return [tag]
        for tag in self.all_tags:
            if tag in rest_tag:
                res = self.split_tags(rest_tag.replace(tag, ""))
                if res:
                    return [tag] + res
        return []

    def filter_legal_tags(self, tags):
        if not tags: return []
        new_tags = []
        for tag in tags:
            split_res = self.split_tags(tag)
            new_tags.append(tag)
            if len(split_res) > 1: new_tags.extend(split_res)
        tags = new_tags
        # print(new_tags)
        res = []
        for tag in tags:
            if tag in self.all_tags:
                res.append(tag)
        return res

    def split_flags(self, tags):
        if not tags: return [], {}
        tags = list(set(tags))
        flags = {}
        if '全部' in tags:
            flags['show_all'] = True
            tags.remove('全部')
        else:
            flags['show_all'] = False

        return tags, flags

    def record_tags(self, tags):
        for tag in tags:
            self.record.add(tag)

    def recom(self, tags=None, images=None):
        if not tags:
            if images:
                tags = self.get_tags_from_image(images)
                if not tags:
                    print("MYDEBUG image checkfail {0}".format(images[0]))
                    return None
            else:
                return None

        tags, flags = self.split_flags(tags)
        # print(tags, flags)

        if not self.check_legal_tags(tags):
            print("MYDEBUG no legal tags")
            return None

        self.record_tags(tags)
        cob_lis = self.recom_tags(tags, flags)
        if not cob_lis:
            return "没有或者太多"
        line_lis = []
        for tags_lis, lis in cob_lis:
            new_lis = []
            for x in lis:
                if x in self.char_data.char_data:
                    new_lis.append(x + "★" +
                                   self.char_data.char_data[x]["rank"])
                else:
                    new_lis.append("★1~3" + x)
            lef = '【' + '+'.join(tags_lis) + "】:\n"
            rig = ', '.join(new_lis)
            line_lis.append(lef + rig)
        res = "\n\n".join(line_lis)
        return res

    def get_tags_from_image(self, images):
        tags = self.ocr_tool.get_tags_from_url(images[0])
        tags = self.filter_legal_tags(tags)
        tags = list(set(tags))
        print("ocr res=", tags)
        if len(tags) >= 2 and len(tags) <= 8:
            return tags
        else:
            return []
Exemple #5
0
class Character(object):
    def __init__(self):
        self.char_data = dict()
        self.enemy_data = dict()
        self.head_data = []
        self.head_key_map = {
            "职业": "job",
            "星级": "rank",
            "性别": "sex",
            "阵营": "affiliation",
            "标签": "tags",
            "获取途径": "obtain_method"
        }
        self.fuzzname = Fuzzname()
        self.record = Record(path_prefix + "record_peo.txt")

    def extract_all_char(self, text_file=None, enemy_file=None):
        if text_file is None: text_file = path_prefix + "chardata.json"
        if enemy_file is None: enemy_file = path_prefix + "enemylist.json"

        if not os.path.exists(text_file) or not os.path.exists(enemy_file):
            self.fetch_data()

        # deal char
        with open(text_file, encoding='UTF-8') as fp:
            self.char_data = json.load(fp)

        # deal enemy
        with open(enemy_file, encoding='UTF-8') as fp:
            self.enemy_data = json.load(fp)

        # fuzzy name+pinyin -> name
        self.fuzzname.fit(
            list(self.char_data.keys()) + list(self.enemy_data.keys()))

    def filter(self, tags, flags={}):
        tags = tags[:]
        ranks = self.gen_ranks(tags)
        for name, dic in self.char_data.items():
            if set(tags).issubset(set(dic["tags"])): pass
            else: continue
            if dic["rank"] in ranks or ('show_all' in flags
                                        and flags['show_all'] == True):
                pass
            else:
                continue
            if "公开招募" in dic["obtain_method"] or ('show_all' in flags and
                                                  flags['show_all'] == True):
                pass
            else:
                continue

            yield name

    def gen_ranks(self, tags):
        ranks = ["1", "2", "3", "4", "5", "6"]
        for i in range(1, 7):
            if ">={0}".format(i) in tags:
                ranks = [x for x in ranks if x >= str(i)]
                tags.remove(">={0}".format(i))
            if "<={0}".format(i) in tags:
                ranks = [x for x in ranks if x <= str(i)]
                tags.remove("<={0}".format(i))
        if "高级资深干员" not in tags:
            ranks.remove("6")
        if "资深干员" in tags:
            ranks = ["5"]
        if "高级资深干员" in tags:
            ranks = ["6"]
        if "资深干员" in tags and "高级资深干员" in tags:
            ranks = ["5", "6"]

        return ranks

    def get_peo_info(self, name=None):
        if not name: return None
        res = "None"
        if name in self.char_data:
            res = self.format_friend_info(name)
            self.record.add("friend/" + name)
        elif name in self.enemy_data:
            res = self.format_enemy_info(name)
            self.record.add("enemy/" + name)
        else:
            res = self.fuzzname.predict(name)
            res = "你可能想查 {0}".format(res)

        return res

    def format_friend_info(self, name):
        res = []
        for tp, cont in self.char_data[name]['all'].items():
            if tp:
                if tp == "干员代号": tp = "姓名"
                res.append("{0}: {1}".format(tp, cont))
        url = self.char_data[name]["link"]
        res.append(url)
        res_text = "\n".join(res)

        # r=requests.get(self.char_data[name]["head_pic"],timeout=30)
        # buffer=r.content
        bytes_image = get_bytes_image_from_url(
            self.char_data[name]["head_pic"])

        ret_msg = [
            Image.fromBytes(bytes_image),
            # Image.fromRemote(self.char_data[name]["head_pic"]),
            # Image.fromFileSystem("./plugins/image.jpg"),
            Plain(text="\n"),
            Plain(text=res_text)
        ]

        return ret_msg

    def format_enemy_info(self, name):
        res = [name]
        url = self.enemy_data[name]["link"]
        res.append(url)
        res_text = "\n".join(res)

        # r=requests.get(self.enemy_data[name]["head_pic"],timeout=30)
        # buffer=r.content
        bytes_image = get_bytes_image_from_url(
            self.enemy_data[name]["head_pic"])

        ret_msg = [
            Image.fromBytes(bytes_image),
            Plain(text="\n"),
            Plain(text=res_text)
        ]

        return ret_msg

    def fetch_data(self):
        # self.fetch_character_from_wikijoyme()
        self.fetch_character_from_akmooncell()

        self.fetch_enemy_from_akmooncell()

    def fetch_character_from_wikijoyme(self, filename="chardata.json"):
        r = requests.get("http://wiki.joyme.com/arknights/干员数据表")
        if r.status_code != 200:
            raise IOError("Cannot fetch char from wikijoyme")
        tree = html.fromstring(r.text)

        # table head data
        tb_head = tree.xpath("//table[@id='CardSelectTr']//th/text()")
        tb_head = [x.strip() for x in tb_head]

        # deal with character
        char_res_lis = tree.xpath("//tr[@data-param1]")

        char_data = dict()
        for char_tr in char_res_lis:
            name = char_tr.xpath("./td[2]/a[1]/text()")[0]
            char_data[name] = dict()
            char_data[name]["job"] = char_tr.xpath("./@data-param1")[0]
            char_data[name]["rank"] = char_tr.xpath("./@data-param2")[0].split(
                ",")[0]
            char_data[name]["sex"] = char_tr.xpath("./@data-param3")[0]
            char_data[name]["affiliation"] = char_tr.xpath("./@data-param4")[0]
            tag_string=char_tr.xpath("./@data-param5")[0]+", " \
                        +char_data[name]["sex"]+", " \
                        +char_data[name]["job"]+", " \
                        +("资深干员" if char_data[name]["rank"]=="5" else "")+", " \
                        +("高级资深干员" if char_data[name]["rank"]=="6" else "")+", "
            taglist = [x.strip() for x in tag_string.split(",")]
            taglist = [x for x in taglist if x != ""]
            char_data[name]["tags"] = taglist
            #如果俩小车是支援机械的话加个tag
            if name in ["Castle-3", "Lancet-2"]:
                char_data[name]["tags"].append("支援机械")
            char_data[name]["obtain_method"] = list(
                map(lambda x: x.strip(),
                    char_tr.xpath("./@data-param6")[0].split(",")))

            #deal head and data
            td_lis = char_tr.xpath(".//td")
            text_lis = [
                "".join([xx.strip() for xx in x.xpath(".//text()")])
                for x in td_lis
            ]
            all_lis = [x.strip() for x in text_lis]
            all_dict = dict(zip(tb_head, all_lis))
            char_data[name]["all"] = all_dict

            # link
            char_link_root = "http://wiki.joyme.com/arknights/"
            url = char_link_root + urllib.parse.quote(name)
            char_data[name]["link"] = url

            char_data[name]["type"] = "friend"

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(char_data, fp)

        return char_data

    def fetch_character_from_akmooncell(self, filename="chardata.json"):
        def fetch_data_with_json_format():
            r = requests.get(
                "http://prts.wiki/load.php?debug=false&lang=zh-cn&modules=ext.gadget.charFilter&skin=vector&version=0vmy0ui"
            )
            if r.status_code != 200:
                raise IOError("Cannot fetch char from akmooncell")
            rtext = r.text.replace("\n", "")
            rtext = re.sub(r"<.*?>", "", rtext)
            result = re.search(r"(?<=datalist=)(.*?)(?=;console)",
                               rtext).group(1)
            content = eval(result)
            # print(content)

            char_data = dict()
            for char_tr in content:
                name = char_tr["cn"]
                # if name=="杜宾":
                #     print(char_tr)
                char_data[name] = dict()
                char_data[name]["job"] = char_tr["class"]
                char_data[name]["rank"] = str(int(char_tr["rarity"]) + 1)
                char_data[name]["sex"] = char_tr["sex"]
                char_data[name]["affiliation"] = char_tr["camp"]
                char_data[name]["tags"]=char_tr["tag"]\
                            +[char_data[name]["job"]]\
                            +[char_tr["position"]]\
                            +(["资深干员"] if char_data[name]["rank"]=="5" else [])\
                            +(["高级资深干员"] if char_data[name]["rank"]=="6" else [])
                char_data[name]["obtain_method"] = char_tr["approach"]

                #deal head and data
                char_data[name]["all"] = {
                    "姓名": char_tr["cn"],
                    "出身": char_tr["camp"],
                    "种族": ','.join(char_tr["race"]),
                    "初始生命": char_tr["oriHp"],
                    "初始攻击": char_tr["oriAtk"],
                    "初始防御": char_tr["oriDef"],
                    "初始法术抗性": char_tr["oriRes"],
                    "初始再部署时间": char_tr["oriDt"],
                    "初始部署费用": char_tr["oriDc"],
                    "初始阻挡数": char_tr["oriBlock"],
                    "初始攻击间隔": char_tr["oriCd"],
                    "标签": ','.join(char_tr["tag"]),
                    "特性": char_tr["feature"],
                }

                # link
                char_link_root = "http://prts.wiki/w/"

                url = char_link_root + urllib.parse.quote(name)
                char_data[name]["link"] = url

                char_data[name]["type"] = "friend"
            return char_data

        def fetch_data_with_source():
            r = requests.get("http://prts.wiki/w/干员一览")
            if r.status_code != 200:
                raise IOError("Cannot fetch char from akmooncell")
            rtext = r.text
            rtext = rtext.replace("\n", "")
            rtext = re.sub(
                r"&lt;span style=&quot;display:none.*?&gt;(.*?)&lt;/span&gt;",
                r"(\1)", rtext)
            rtext = re.sub(r"&lt;br/&gt;", "\n", rtext)
            rtext = re.sub(r"&lt;span.*?&gt;", "", rtext)
            rtext = re.sub(r"&lt;/span&gt;", "", rtext)

            tree = html.fromstring(rtext)
            char_res_lis = tree.xpath("//div[@class='smwdata']")

            char_data = dict()
            for char_a in char_res_lis:
                name = char_a.xpath("./@data-cn")[0]
                char_data[name] = dict()
                char_data[name]["job"] = char_a.xpath("./@data-class")[0]
                char_data[name]["rank"] = str(
                    int(char_a.xpath("./@data-rarity")[0]) + 1)
                char_data[name]["sex"] = char_a.xpath("./@data-sex")[0]
                char_data[name]["affiliation"] = char_a.xpath(
                    "./@data-camp")[0]
                char_data[name]["tags"]=char_a.xpath("./@data-tag")[0].split(" ")\
                            +[char_data[name]["job"]]\
                            +[char_a.xpath("./@data-position")[0]]\
                            +(["资深干员"] if char_data[name]["rank"]=="5" else [])\
                            +(["高级资深干员"] if char_data[name]["rank"]=="6" else [])
                char_data[name]["obtain_method"] = char_a.xpath(
                    "./@data-approach")[0].split(", ")
                char_data[name]["head_pic"] = char_a.xpath("./@data-icon")[0]

                #deal head and data
                char_data[name]["all"] = {
                    "姓名": name,
                    "出身": char_a.xpath("./@data-camp")[0],
                    "种族": char_a.xpath("./@data-race")[0],
                    "初始生命": char_a.xpath("./@data-ori-hp")[0],
                    "初始攻击": char_a.xpath("./@data-ori-atk")[0],
                    "初始防御": char_a.xpath("./@data-ori-def")[0],
                    "初始法术抗性": char_a.xpath("./@data-ori-res")[0],
                    "初始再部署时间": char_a.xpath("./@data-ori-dt")[0],
                    "初始部署费用": char_a.xpath("./@data-ori-dc")[0],
                    "初始阻挡数": char_a.xpath("./@data-ori-block")[0],
                    "初始攻击间隔": char_a.xpath("./@data-ori-cd")[0],
                    "标签": char_a.xpath("./@data-tag")[0],
                    "特性": char_a.xpath("./@data-feature")[0],
                }

                # link
                char_link_root = "http://prts.wiki/w/"
                url = char_link_root + urllib.parse.quote(name)
                char_data[name]["link"] = url

                char_data[name]["type"] = "friend"
                # if name=="杜宾":
                #     print(char_data[name])
            return char_data

        char_data = fetch_data_with_source()

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(char_data, fp)

        return char_data

    def fetch_enemy_from_akmooncell(self, filename="enemylist.json"):
        import threading, time

        def try_enemy_head_pic_path(name, enemy_data):
            def modify_data(url):
                enemy_data_rlock.acquire()
                enemy_data[name]["head_pic"] = url
                enemy_data_rlock.release()

            print("%s is trying %s" % (threading.current_thread().name, name))
            try:
                print(name)
                session_requests = requests.session()
                for i in range(256):
                    h = hex(i)[2:]
                    if len(h) == 1: h = '0' + h
                    url = "http://prts.wiki/images/{0}/{1}/头像_敌人_{2}.png".format(
                        h[0], h, name)
                    r = session_requests.get(url, allow_redirects=False)
                    if r.status_code == 200:
                        print('SUCCESS %s get %s' %
                              (threading.current_thread().name, url))
                        modify_data(url)
                        return

                print(name, "didnt find right url, return default")
                return modify_data(
                    "http://prts.wiki/images/3/3e/头像_敌人_源石虫.png")

            except Exception as e:
                print(e)
                print('FAIL %s error %s' %
                      (threading.current_thread().name, name))

                return modify_data(
                    "http://prts.wiki/images/3/3e/头像_敌人_源石虫.png")

        print("begin fetch enemy")

        # get enemy data
        r = requests.get("http://prts.wiki/index.php?title=敌人一览/数据&action=raw")
        if r.status_code != 200:
            raise IOError("Cannot fetch enemy from akmooncell")
        enemy_data_raw = r.json()
        enemy_data = dict()
        enemy_link_root = "http://prts.wiki/w/"
        for enemy_a in enemy_data_raw:
            name = enemy_a["name"]
            # print("===="+name)
            enemy_data[name] = dict()
            link = enemy_link_root + urllib.parse.quote(enemy_a["enemyLink"])

            enemy_data[name]["link"] = link
            enemy_data[name]["type"] = "enemy"
            if name in self.enemy_data and "head_pic" in self.enemy_data[
                    name] and (name == "源石虫"
                               or self.enemy_data[name]["head_pic"] !=
                               self.enemy_data["源石虫"]["head_pic"]):
                enemy_data[name]["head_pic"] = self.enemy_data[name][
                    "head_pic"]
            else:
                enemy_data[name]["head_pic"] = ""

        enemy_name_lis = list(enemy_data.keys())
        threads = []
        enemy_data_rlock = threading.RLock()
        for name in enemy_name_lis:
            if not enemy_data[name]["head_pic"]:
                t = threading.Thread(target=try_enemy_head_pic_path,
                                     args=(name, enemy_data))
                threads.append(t)
                while sum(map(lambda x: 1
                              if x.is_alive() else 0, threads)) >= 50:
                    time.sleep(1)
                t.start()
        for t in threads:
            if t.is_alive(): t.join()

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(enemy_data, fp)

        print("end fetch enemy")
        return enemy_data

    """
    def fetch_enemy_from_akmooncell(self, filename="enemylist.json"):
        # get enemy data
        r=requests.get("http://prts.wiki/w/敌人一览")
        if r.status_code!=200: raise IOError("Cannot fetch enemy from akmooncell")
        tree=html.fromstring(r.text)

        enemy_res_lis=tree.xpath("//div[@class='smwdata']")

        enemy_data=dict()
        enemy_link_root="http://prts.wiki/w/"
        for enemy_a in enemy_res_lis:
            name=enemy_a.xpath("./@data-name")[0]
            # print("===="+name)
            enemy_data[name]=dict()
            link=enemy_link_root+urllib.parse.quote(name)

            enemy_data[name]["link"]=link
            enemy_data[name]["type"]="enemy"
            enemy_data[name]["head_pic"]=enemy_a.xpath("./@data-file")[0]
        
        with open(path_prefix+filename,"w",encoding='utf-8') as fp:
            json.dump(enemy_data, fp)

        return enemy_data
    """

    def fetch_enemy_from_wikijoyme(self, filename="enemylist.json"):
        # get enemy data
        r = requests.get("http://wiki.joyme.com/arknights/敌方图鉴")
        if r.status_code != 200:
            raise IOError("Cannot fetch enemy from wikijoyme")
        tree = html.fromstring(r.text)

        enemy_res_lis = tree.xpath("//tr[@data-param1]")

        enemy_data = dict()
        enemy_link_root = "http://prts.wiki/w/"
        for enemy_a in enemy_res_lis:
            name = enemy_a.xpath("./td[2]/a[1]/text()")[0]
            print("====" + name)
            enemy_data[name] = dict()
            link = enemy_link_root + urllib.parse.quote(name)

            enemy_data[name]["link"] = link
            enemy_data[name]["type"] = "enemy"

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(enemy_data, fp)

        return enemy_data
class ChessBoard:
    def __init__(self):
        window = tk.Tk()
        window.title('五子棋')
        window.geometry('600x720')

        w = 100  # 棋格宽度
        r = 35  # 棋子半径
        row = 5
        col = 5
        canvas = tk.Canvas(window, bg='blue', width=w * 6, height=w * 6)
        canvas.pack()

        # 棋盘
        for i in range(1, 6):
            canvas.create_line(w, i * w, 5 * w, i * w, width=2)
            canvas.create_line(i * w, w, i * w, 5 * w, width=2)

        # 棋子
        self._board = [[None for _ in range(col)] for _ in range(row)]

        # Q值
        self._qtext = [[[
            canvas.create_text(*(np.array([w * (j + 1), w * (i + 1)]) +
                                 np.array(a)[::-1] * 25),
                               text='',
                               fill='green') for a in rule.actions_move
        ] for j in range(5)] for i in range(5)]

        # 棋手的名字
        self.name_white = canvas.create_text(40,
                                             30,
                                             text=WHITE,
                                             fill='#EEE',
                                             font=font.Font(size=20))
        self.name_black = canvas.create_text(40,
                                             w * 6 - 30,
                                             text=BLACK,
                                             fill='#111',
                                             font=font.Font(size=20))

        # 指示当前棋手的信号灯
        sig_r = 5
        self.sig_white = canvas.create_oval(82 - sig_r,
                                            30 - sig_r,
                                            82 + sig_r,
                                            30 + sig_r,
                                            fill='#66FF66',
                                            outline='#66FF66',
                                            state=tk.HIDDEN)
        self.sig_black = canvas.create_oval(82 - sig_r,
                                            w * 6 - (30 - sig_r),
                                            82 + sig_r,
                                            w * 6 - (30 + sig_r),
                                            fill='#66FF66',
                                            outline='#66FF66',
                                            state=tk.HIDDEN)

        # 显示胜利者
        self.winner_white = canvas.create_text(125,
                                               30,
                                               text='WINNER',
                                               fill='red',
                                               font=font.Font(size=20),
                                               state=tk.HIDDEN)
        self.winner_black = canvas.create_text(125,
                                               w * 6 - 30,
                                               text='WINNER',
                                               fill='red',
                                               font=font.Font(size=20),
                                               state=tk.HIDDEN)

        # 计时钟
        self.clock_white = canvas.create_text(w * 6 - 40,
                                              30,
                                              text='00:00',
                                              fill='#EEE',
                                              font=font.Font(family='Times',
                                                             size=20))
        self.clock_black = canvas.create_text(w * 6 - 40,
                                              w * 6 - 30,
                                              text='00:00',
                                              fill='#111',
                                              font=font.Font(family='Times',
                                                             size=20))

        # ----------------- 第一排按扭 -----------------------------------------
        # 对手选择
        tk.Label(window, text='对手:').place(x=10, y=620)
        player_var = tk.StringVar()
        player_classes = [
            ('White(HummanPlayer)', HummanPlayer,
             ('White', WHITE_VALUE, self.sig_white, self.winner_white,
              self.clock_white), {}),
            # ('Paul(PolicyPlayer)', PolicyNetworkPlayer, ['Paul', WHITE_VALUE, self.sig_white, self.winner_white, self.clock_white], {'play_func':self._play, 'model_file':'model/policy_network/convolution_0130w.model'}),
            ('Quin(DQNPlayer)', DQNPlayer, [
                'Quin', WHITE_VALUE, self.sig_white, self.winner_white,
                self.clock_white
            ], {
                'play_func':
                self._play,
                'weights_file':
                'model/qlearning_network/DQN_fixed_sigmoid_00029w.weights'
            }),
            ('Vance(ValuePlayer)', ValuePlayer, [
                'Vance', WHITE_VALUE, self.sig_white, self.winner_white,
                self.clock_white
            ], {
                'play_func':
                self._play,
                'weights_file':
                'model/value_network/value_network_sigmoid_00074w.weights'
            }),
            ('Toms(MCTSPlayer)', MCTSPlayer, [
                'Toms', WHITE_VALUE, self.sig_white, self.winner_white,
                self.clock_white
            ], {
                'play_func':
                self._play,
                'policy_model':
                '',
                'value_model':
                'model/qlearning_network/DQN_fixed_sigmoid_00029w.weights'
            }),
        ]
        self.player_map = {n: (c, p, kp) for n, c, p, kp in player_classes}
        players = [n for n, *_ in player_classes]
        player_choosen = ttk.Combobox(window,
                                      width=16,
                                      textvariable=player_var,
                                      values=players,
                                      state='readonly')
        player_choosen.current(0)
        player_choosen.place(x=50, y=617)
        self.player_var = player_var
        self.player_choosen = player_choosen

        # 开局选择
        tk.Label(window, text='开局:').place(x=225, y=620)
        board_var = tk.IntVar(0)
        boards = list(range(len(init_boards)))
        board_choosen = ttk.Combobox(window,
                                     width=2,
                                     textvariable=board_var,
                                     values=boards,
                                     state='readonly')
        board_choosen.current(0)
        board_choosen.place(x=267, y=617)
        self.board_var = board_var
        self.board_choosen = board_choosen
        board_choosen.bind('<<ComboboxSelected>>', self.board_selected)

        # 先手
        first_player = tk.IntVar(value=WHITE_VALUE)
        tk.Radiobutton(window,
                       text='黑先',
                       variable=first_player,
                       value=BLACK_VALUE).place(x=315, y=620)
        tk.Radiobutton(window,
                       text='白先',
                       variable=first_player,
                       value=WHITE_VALUE).place(x=370, y=620)
        self.first_player = first_player

        # 开始按扭
        start_btn_text = tk.StringVar(window, value='start')
        tk.Button(window,
                  textvariable=start_btn_text,
                  command=self.start,
                  width=5).place(x=425, y=617)

        # 隐藏路径按扭
        hide_q_str = tk.StringVar(value='hideQ')
        tk.Button(window,
                  textvariable=hide_q_str,
                  command=self.hide_q,
                  width=5).place(x=483, y=617)
        self.hide_q_str = hide_q_str
        self.print_q_flag = True

        # 帮助按扭
        tk.Button(window, text='help', command=self.help, width=4).place(x=548,
                                                                         y=617)

        # ---------------- 第二排按扭 ------------------------------------------
        # 选择棋谱
        tk.Label(window, text='棋谱:').place(x=10, y=658)
        record_path = tk.StringVar()
        record_entry = tk.Entry(window, textvariable=record_path, width=18)
        record_entry.place(x=45, y=655)
        tk.Button(text='select', command=self.select_record).place(x=222,
                                                                   y=655)

        # replay按扭
        tk.Button(window, text='replay', command=self.replay).place(x=290,
                                                                    y=655)

        # 速度调节按扭
        tk.Button(window,
                  text='faster',
                  command=lambda: self.change_period(0.5)).place(x=360, y=655)
        tk.Button(window, text='slower',
                  command=lambda: self.change_period(2)).place(x=430, y=655)

        # 暂停按扭
        pause_text = tk.StringVar(value='pause')
        tk.Button(window, textvariable=pause_text, command=self.pause,
                  width=6).place(x=510, y=655)

        # ---------------- 第三排按扭 ------------------------------------------
        # 消息显示
        lb = tk.Label(window, width=40)
        lb.place(x=85, y=690)

        self.w = w
        self.r = r
        self.row = row
        self.col = col
        self.period = 1
        self.window = window
        self.canvas = canvas
        self.pause_text = pause_text
        self.text = lb
        self.black_player = None
        self.white_player = None
        self.players = [None, None, None]
        self.current_player = None
        self.current_stone = None
        self.timer = None
        self.play_timer = None
        self.replay_timer = None
        self.winner = None
        self.started = False
        self.ended = False
        self.action_select_signal = None
        self.start_btn_text = start_btn_text
        self.record_path = record_path
        self.record_entry = record_entry
        self.event = threading.Event()
        self.record = Record()  # 记录棋谱: board:from_:to_:del_num
        self.stones = []
        init_board = init_boards[self.board_var.get()]
        self.init_stone(init_board)

    def start(self):
        if self.start_btn_text.get() == 'stop':
            if messagebox.askokcancel(title='请确认', message='确定停止当前棋局?'):
                self.stop()
            return
        init_board = np.array(init_boards[self.board_var.get()])
        self.init_stone(init_board)
        first_player = self.first_player.get()
        player_name = self.player_var.get()
        player_class, p, kp = self.player_map[player_name]
        white_player = player_class(*p,
                                    init_board=init_board,
                                    first_player=first_player,
                                    **kp)
        black_player = HummanPlayer('Jhon',
                                    BLACK_VALUE,
                                    self.sig_black,
                                    self.winner_black,
                                    self.clock_black,
                                    init_board=init_board,
                                    first_player=first_player)
        self.players[WHITE_VALUE] = white_player
        self.players[BLACK_VALUE] = black_player
        for stone in self.stones:
            stone.player = white_player if stone.value == WHITE_VALUE else black_player
        self.black_player = black_player
        self.white_player = white_player
        self.canvas.itemconfigure(self.name_white, text=white_player.name)
        self.canvas.itemconfigure(self.name_black, text=black_player.name)
        self.current_player = self.players[first_player]
        self.show_signal()
        self.begin_timer()
        self.canvas.bind('<Button-1>', self.onclick)
        self.start_btn_text.set('stop')
        self.white_player.start(init_board=init_board,
                                first_player=first_player)
        self.black_player.start(init_board=init_board,
                                first_player=first_player)
        self.started = True
        self.play()

    def board_selected(self, _):
        self.board_choosen.selection_clear()
        if not self.started:
            init_board = init_boards[self.board_var.get()]
            self.init_stone(init_board)

    def play(self):
        logger.info('%s play...', self.current_player)
        # if self.current_player.is_humman():
        # 对手预测走棋
        # bd = self.board()
        # pl = self.current_player.stone_val
        # op = self.opponent().predict_opponent(bd)
        # if op is not None:
        #     valid = rule.valid_action(bd, pl)
        #     self.show_qtext(op, valid, hide=False)
        # return
        board = self.board()
        self.current_player.play(board)

    def _play(self, player, from_, to_, p, opp_q=None):
        logger.info('from:%s, to_:%s', from_, to_)
        logger.debug('p:\n%s', p)
        board = self.board()
        valid_action = rule.valid_action(board, player)
        logger.debug('valid_action:\n%s', valid_action)
        self.show_qtext(p, valid_action)
        self.show_select(from_, to_)
        stone = self.stone(from_)

        def play_later():
            result = self.move_to(stone, to_)
            if opp_q is not None:
                opp_valid = rule.valid_action(self.board(), -player)
                self.show_qtext(opp_q, opp_valid, hide=False)
            if result == rule.ACCQUIRE:
                # 对手走棋
                self.switch_player_and_play()
            elif result == rule.WIN:
                logger.info('GAME OVER, WINNER IS %s', stone.player.name)
                self.game_over(stone.player)

        self.play_timer = self.window.after(int(self.period * 1000),
                                            play_later)

    def init_stone(self, board=None):
        self.clear()
        if board is None:
            board = init_boards[0]
        board = np.array(board)
        n = 1
        for i, j in np.argwhere(board == WHITE_VALUE):
            self.create_stone(i, j, WHITE_VALUE, text=str(n))
            n += 1
        n = 1
        for i, j in np.argwhere(board == BLACK_VALUE):
            self.create_stone(i, j, BLACK_VALUE, text=str(n))
            n += 1

    def onmotion(self, event):
        # self.show_message('position is (%d,%d)' % (event.x, event.y))
        self.move_to_pos(self.current_stone, event.x, event.y)

    def onclick(self, event):
        x, y = event.x, event.y
        if not (self.w - self.r < x < self.w * 5 + self.r
                and self.w - self.r < y < self.w * 5 + self.r):
            # self.show_message('click at (%d,%d)' % (event.x, event.y))
            return
        if not self.current_player.is_humman():
            return
        posx = x / self.w - 0.5
        posy = y / self.w - 0.5
        # 整数部分
        posx_i = int(posx)
        posy_i = int(posy)
        # 小数部分
        posx_d = posx - posx_i
        posy_d = posy - posy_i

        # self.show_message('click at (%d,%d) position is %d,%d' % (event.x, event.y, posx_i, posy_i))

        if 0.25 < posx_d < 0.75 and 0.25 < posy_d < 0.75:
            loc = self.pos_to_loc(x, y)
            if self.current_stone is None:
                stone = self.stone(loc)
                if stone and stone.player == self.current_player:
                    self.move_to_pos(stone, x, y)
                    self.begin_moving(stone)
            else:
                self.end_moving(loc)

    def begin_moving(self, stone):
        self.current_stone = stone
        self.canvas.bind('<Motion>', self.onmotion)

    def end_moving(self, loc):
        stone = self.current_stone
        board = self.board()
        from_ = stone.loc
        result = self.move_to(stone, loc)
        if result == rule.INVALID_MOVE:
            return False
        self.canvas.unbind('<Motion>')
        self.current_stone = None

        if result == rule.NOT_MOVE:
            self.move_to_loc(stone, loc)

        if result == rule.ACCQUIRE:
            # 将走的子告知对手
            self.opponent().opponent_play(board, from_, loc)
            self.switch_player_and_play()
        elif result == rule.WIN:
            logger.info('GAME OVER, WINNER IS %s', stone.player.name)
            # self.opponent().stop()
            self.game_over(stone.player)

    def move_to(self, stone, to_loc):
        """
        把棋子移动到to_loc处,同时判断是否吃子
        :param stone:
        :param to_loc:
        :return: True:终止移动,False:继续移动
        """
        old_board = self.board()
        from_ = stone.loc
        result, del_stone_loc = rule.move(self.board(), stone.loc, to_loc)
        if result == rule.ACCQUIRE or result == rule.WIN:
            self.move_to_loc(stone, to_loc)
            for loc in del_stone_loc:
                self.del_stone(self.stone(loc))
            logger.info('from %s to %s, result:%s, del:%s', from_, to_loc,
                        result, del_stone_loc)
            action = rule.actions_move.index(tuple(np.subtract(to_loc, from_)))
            logger.debug('action is: %s', action)
            self.record.add(old_board,
                            from_,
                            action,
                            len(del_stone_loc),
                            None,
                            win=(result == rule.WIN))
        return result

    def replay(self):
        recordpath = self.record_path.get()
        if not recordpath:
            self.show_message(message='请点击"open"按扭选择棋谱位置')
            return
        self.stop()
        self.event.set()
        record = Record()
        record.read(recordpath)
        init_bd = record[0][0]
        self.init_stone(init_bd)
        record_iter = iter(record)
        length = len(record)
        record.n = 1

        def play_next_step():
            self.event.wait()
            try:
                board, from_, action, reward = next(record_iter)
                player = board[from_]
                to_ = tuple(np.add(from_, rule.actions_move[action]))
                assert (board == self.board()).all(), str(board) + '\n' + str(
                    self.board())
                result = self.move_to(self.stone(from_), to_)
                if result == rule.WIN:
                    winner_text = self.winner_black if player == 1 else self.winner_white
                    self.canvas.itemconfigure(winner_text, state=tk.NORMAL)
                self.show_message(
                    str(length) + ':' + str(record.n) + ',reward: ' +
                    str(reward))
                record.n += 1
                self.replay_timer = threading.Timer(self.period,
                                                    play_next_step)
                self.replay_timer.start()
            except StopIteration:
                return

        self.replay_timer = threading.Timer(self.period, play_next_step)
        self.replay_timer.start()

    def select_record(self):
        f = filedialog.askopenfilename()
        if f:
            self.record_path.set(f)
            self.record_entry.index(len(f) - 10)

    def pause(self):
        if self.pause_text.get() == 'pause':
            self.event.clear()
            self.pause_text.set('resume')
        else:
            self.event.set()
            self.pause_text.set('pause')

    def del_stone(self, stone):
        self.canvas.delete(stone.oval)
        self.canvas.delete(stone.text)
        self.stone(stone.loc, None)

    def switch_player(self):
        self.hide_signal()
        self.cancel_timer()
        self.current_player = self.white_player if self.current_player is self.black_player else self.black_player
        self.show_signal()
        self.begin_timer()

    def switch_player_and_play(self):
        self.switch_player()
        self.play()

    def move_to_loc(self, stone, loc):
        self.stone(stone.loc, None)
        self.stone(loc, stone)
        i, j = loc
        self.move_to_pos(stone, (j + 1) * self.w, (i + 1) * self.w)

    def move_to_pos(self, stone, x, y):
        self.canvas.coords(stone.oval, x - self.r, y - self.r, x + self.r,
                           y + self.r)
        self.canvas.coords(stone.text, x, y)

    def opponent(self, player=None):
        if player is None:
            player = self.current_player
        return self.players[-player.stone_val]

    def stone(self, loc, value=Stone.NONE):
        i, j = loc
        if value != Stone.NONE:
            self._board[i][j] = value
            if value is not None:
                value.loc = loc
        return self._board[i][j]

    def create_stone(self, i, j, value, text=''):
        w, r = self.w, self.r
        color = '#EEE' if value == WHITE_VALUE else '#111'
        text_color = '#111' if value == WHITE_VALUE else '#EEE'
        s = Stone((i, j),
                  self.create_oval(w * (j + 1),
                                   w * (i + 1),
                                   r,
                                   fill=color,
                                   outline=color),
                  value=value,
                  text=self.canvas.create_text(w * (j + 1),
                                               w * (i + 1),
                                               text=text,
                                               fill=text_color,
                                               font=font.Font(size=24,
                                                              weight='bold')))
        self.stone((i, j), value=s)
        self.stones.append(s)

    def pos_to_loc(self, x, y):
        i = int(y / self.w - 0.5)
        j = int(x / self.w - 0.5)
        if -1 < i < 5 and -1 < j < 5:
            return i, j

    def clear_stone(self):
        for s in self.stones:
            self.del_stone(s)
        self.stones.clear()

    def stop(self):
        if self.timer:
            self.cancel_timer()
        if self.play_timer:
            self.window.after_cancel(self.play_timer)
        if self.replay_timer:
            self.replay_timer.cancel()
        self.start_btn_text.set('start')
        self.pause_text.set('pause')
        if self.black_player:
            self.black_player.stop()
            self.black_player = None
        if self.white_player:
            self.white_player.stop()
            self.white_player = None
        self.started = False
        self.ended = True

    def clear(self):
        self.clear_stone()
        self.clear_timer()
        self.hide_signal()
        self.hide_winner()
        self.hide_qtext()
        self.hide_select()
        self.show_message('')
        self.current_player = None
        self.current_stone = None
        self.timer = None
        self.winner = None
        self.record.clear()
        self.ended = False

    def game_over(self, winner):
        # self.lb.config(text='winner is: ' + str(winner))
        self.winner = winner
        self.started = False
        self.ended = True
        self.canvas.unbind('<Button-1>')
        self.cancel_timer()
        self.hide_signal()
        self.show_winner()
        self.start_btn_text.set('start')
        failer = self.opponent(winner)
        self.record_path.set(
            self.record.save('records/app/' + winner.name + '_' + failer.name +
                             '_'))
        self.record.clear()
        self.white_player.stop()
        self.black_player.stop()

    def board(self):
        return np.array([[0 if s is None else s.value for s in row]
                         for row in self._board])

    def begin_timer(self):
        self.canvas.itemconfigure(self.current_player.clock, text='00:00')
        self.current_player.begin_time = int(time.time())

        def timer(player):
            use_time = int(time.time()) - player.begin_time
            player.total_time += use_time
            self.canvas.itemconfigure(player.clock,
                                      text='%02d:%02d' %
                                      (use_time // 60, use_time % 60))
            self.timer = self.canvas.after(1000, timer, player)

        self.timer = self.canvas.after(1000, timer, self.current_player)

    def cancel_timer(self):
        self.canvas.after_cancel(self.timer)

    def clear_timer(self):
        self.canvas.itemconfigure(self.clock_white, text='00:00')
        self.canvas.itemconfigure(self.clock_black, text='00:00')

    def change_period(self, scale):
        self.period *= scale
        logger.debug(self.period)

    def show_select(self, from_, to_):
        """
        显示选择的动作
        """
        if not self.print_q_flag:
            return
        self.hide_select()
        a = np.subtract(to_, from_)
        logger.debug('select action is: %s', tuple(a))
        i, j = from_
        x, y = np.array([self.w * (j + 1), self.w *
                         (i + 1)]) + np.array(a)[::-1] * 25
        self.action_select_signal = self.create_oval(x,
                                                     y,
                                                     r=13,
                                                     outline='#FFB90F',
                                                     width=2)

    def hide_select(self):
        """
        隐藏选择的动作
        """
        self.canvas.delete(self.action_select_signal)

    def create_oval(self, x, y, r, **config):
        return self.canvas.create_oval(x - r, y - r, x + r, y + r, **config)

    def show_qtext(self, qtable, valid_action, hide=True):
        """
        显示动作的Q值
        """
        if not self.print_q_flag:
            return
        if hide:
            self.hide_qtext(valid_action)
        maxq = np.max(qtable)
        avgq = qtable.sum() / valid_action.sum()
        idx = np.argwhere(valid_action == 1)
        # logger.info(idx)
        for i, j, k in idx:
            q = qtable[i, j, k]
            qtext = self.qtext(i, j, k)
            stone = self.stone((i, j))
            self.canvas.itemconfigure(qtext,
                                      text=str(round(q, 2)).replace('0.', '.'),
                                      fill='red' if q == maxq else
                                      ('#BF3EFF' if q > avgq else 'green'),
                                      state=tk.NORMAL)
            self.canvas.tag_raise(qtext, stone.oval)

    def hide_qtext(self, valid_action=None):
        """
        隐藏动作的Q值
        """
        if valid_action is None:
            valid_action = np.zeros((5, 5, 4))
        idx = np.argwhere(valid_action == 0)
        for i, j, k in idx:
            self.canvas.itemconfigure(self.qtext(i, j, k),
                                      text='',
                                      state=tk.HIDDEN)

    def hide_q(self):
        if self.hide_q_str.get() == 'hideQ':
            self.hide_qtext()
            self.hide_select()
            self.print_q_flag = False
            self.hide_q_str.set('showQ')
        elif self.hide_q_str.get() == 'showQ':
            self.print_q_flag = True
            self.hide_q_str.set('hideQ')

    def qtext(self, i, j, k):
        return self._qtext[i][j][k]

    def show_message(self, message):
        self.text.config(text=message)

    def show_signal(self):
        self.canvas.itemconfigure(self.current_player.signal, state=tk.NORMAL)

    def hide_signal(self):
        self.canvas.itemconfigure(self.sig_white, state=tk.HIDDEN)
        self.canvas.itemconfigure(self.sig_black, state=tk.HIDDEN)

    def show_winner(self):
        self.canvas.itemconfigure(self.winner.winner_text, state=tk.NORMAL)

    def hide_winner(self):
        self.canvas.itemconfigure(self.winner_white, state=tk.HIDDEN)
        self.canvas.itemconfigure(self.winner_black, state=tk.HIDDEN)

    def help(self):
        window_help = tk.Toplevel(self.window)
        window_help.geometry('400x620')
        tk.Label(window_help,
                 text='1.规则',
                 font=font.Font(size=16, weight='bold')).grid(row=0,
                                                              sticky='w',
                                                              padx=5)
        tk.Label(window_help,
                 text='1)走棋:一次只能上下左右移动一步',
                 font=font.Font(size=14)).grid(row=1, sticky='w', padx=5)
        tk.Label(window_help, text='2)吃子:',
                 font=font.Font(size=14)).grid(row=2, sticky='w', padx=5)
        canvas = tk.Canvas(window_help, width=200, height=100, bg='blue')
        w = 50  # 棋格宽度
        r = 20  # 棋子半径
        # 棋盘
        canvas.create_line(0, 50, 200, 50, width=2)
        canvas.create_line(5, 0, 5, 100, width=2)
        for i in range(1, 5):
            canvas.create_line(i * w, 0, i * w, 100, width=2)
        canvas.create_oval(50 - r,
                           50 - r,
                           50 + r,
                           50 + r,
                           fill='#111',
                           outline='#111')
        canvas.create_oval(100 - r,
                           50 - r,
                           100 + r,
                           50 + r,
                           fill='#111',
                           outline='#111')
        canvas.create_oval(150 - r,
                           50 - r,
                           150 + r,
                           50 + r,
                           fill='#EEE',
                           outline='#EEE')
        d = 2**0.5 * r // 2
        canvas.create_line(150 - d,
                           50 - d,
                           150 + d,
                           50 + d,
                           fill='red',
                           width=2)
        canvas.create_line(150 - d,
                           50 + d,
                           150 + d,
                           50 - d,
                           fill='red',
                           width=2)
        canvas.grid(row=3, sticky='w', padx=20)
        text = '  如上所示:\n' \
               '  a.任意一个黑子走至当前位置后\n' \
               '  b.形成在一条直线上有两个黑子对一个白子(白子在边上)\n' \
               '  c.且这三个棋子是连续的\n' \
               '  d.且该直线上只有这三个棋子时\n' \
               '  白子被吃掉(直线横坚均可)。\n' \
               '  注意abcd四个条件缺一不可,白子走到当前位置不会被吃。'
        tk.Label(window_help,
                 text=text,
                 anchor='w',
                 justify='left',
                 font=font.Font(size=14)).grid(row=5, sticky='w', padx=5)
        tk.Label(window_help,
                 text='3)赢棋:对方棋子少于两个或无路可走时赢棋',
                 font=font.Font(size=14)).grid(row=6, sticky='w', padx=5)

        tk.Label(window_help,
                 text='2.使用',
                 font=font.Font(size=16, weight='bold')).grid(row=7,
                                                              sticky='w',
                                                              padx=5)
        tk.Label(window_help,
                 text='1)安装:建议安装anaconda,keras',
                 font=font.Font(size=14)).grid(row=8, sticky='w', padx=5)
        tk.Label(window_help,
                 text='2)开始:选择对手,先手后点击start按扭开始',
                 font=font.Font(size=14)).grid(row=9, sticky='w', padx=5)
        tk.Label(window_help,
                 text='3)走棋:点击己方棋子,移动到目标位置后单击即可落子',
                 font=font.Font(size=14)).grid(row=10, sticky='w', padx=5)
        tk.Label(window_help,
                 text='4)结束:点击stop按扭可结束棋局',
                 font=font.Font(size=14)).grid(row=11, sticky='w', padx=5)
        tk.Label(window_help, text='5)回放:点击replay按扭可以回放棋局,\n'
                                   '  点击select按扭可选择棋谱,\n'
                                   '  点击faster,slower按扭可调节回放速度,\n'
                                   '  点击pause/resume按扭可暂停/继续回放',
                 anchor='w', justify='left', font=font.Font(size=14)) \
          .grid(row=12, sticky='w', padx=5)
        tk.Label(window_help, text='6)可以根据需要选择不同的开局局面,\n'
                                   '  局面可在init_boards.py中添加。',
                 anchor='w', justify='left', font=font.Font(size=14)) \
          .grid(row=13, sticky='w', padx=5)
        tk.Label(window_help, text='7)点击hideQ/showQ按扭可隐藏/显示Q值', anchor='w', justify='left', font=font.Font(size=14)) \
          .grid(row=14, sticky='w', padx=5)

    @staticmethod
    def launch():
        tk.mainloop()
class Material(object):
    def __init__(self):
        self.material_data = dict()
        self.columns_name = []
        self.name_lis = []
        self.fuzzname = Fuzzname()

        self.load_data(o_path + "res-0810.csv")
        self.record = Record(o_path + "record_material.txt")

    def load_data(self, filename):
        self.material_data = dict()
        self.name_lis = []
        with open(filename, encoding='UTF-8') as fp:
            csv_reader = csv.reader(fp)
            self.columns_name = next(csv_reader)[:5]
            for line in csv_reader:
                name = line[0]
                self.material_data[name] = dict(
                    list(zip(self.columns_name[0:5], line[0:5])))
                self.name_lis.append(name)

        self.fuzzname.fit(self.material_data.keys())

    def format(self, name):
        # lines=[]
        # lines.append(["\t"]+self.columns_name[1:4])
        # lines.append([name]+[self.material_data[name][colname] for colname in self.columns_name[1:4]])
        # lines.append([self.columns_name[4]+":"+self.material_data[name][self.columns_name[4]]])
        # res="\n".join(["\t".join(line) for line in lines])
        reslis = []
        reslis.append(name + "  " + self.material_data[name]["材料等级"] + "色")
        for colname in self.columns_name[2:5]:
            if self.material_data[name][colname]:
                reslis.append("{0}: {1}".format(
                    colname, self.material_data[name][colname]))
        res = "\n".join(reslis)
        return res

    def recom(self, name):
        if not name: return None
        if name not in self.material_data:
            name = self.fuzzname.predict(name)
        res = self.format(name)
        self.record.add(name)

        return res

    def export_table_md(self):
        self.fetch_wiki()
        item_lis = ["img_div"] + self.columns_name[:5]
        with open(o_path + "materials.md", "w", encoding='UTF-8') as fp:
            fp.write("# 刷材料推荐地点\n\n")
            fp.write("数据来源——丸丸\n\n")
            fp.write("<table>\n")
            fp.write("<tr>\n")
            for colname in [""] + self.columns_name[:5]:
                fp.write("<td>")
                fp.write(colname)
                fp.write("</td>\n")
            fp.write("</tr>\n")

            for name in self.name_lis:
                fp.write("<tr>\n")

                for item_name in item_lis:
                    fp.write("<td>")
                    fp.write(self.material_data[name][item_name])
                    fp.write("</td>\n")

                fp.write("</tr>\n")
            fp.write("</table>\n")

    def fetch_wiki(self):
        url_prefix = "http://wiki.joyme.com"
        r = requests.get(url_prefix + "/arknights/材料")
        tree = html.fromstring(r.text)

        mati_lis = tree.xpath("//span[@class='itemhover']/div[1]")
        for mati in mati_lis:
            name = mati.xpath("./a/@title")[0]
            if name and name in self.material_data:
                sub_link = mati.xpath("./a/@href")[0]
                self.material_data[name]["link"] = url_prefix + sub_link
                self.material_data[name]["img"] = mati.xpath("./a/img/@src")[0]
                self.material_data[name]["img_div"] = unescape(
                    html.tostring(mati).decode('utf-8')).replace(
                        sub_link, self.material_data[name]["link"])
class Character(object):
    def __init__(self):
        self.char_data = dict()
        self.enemy_data = dict()
        self.head_data = []
        self.head_key_map = {
            "职业": "job",
            "星级": "rank",
            "性别": "sex",
            "阵营": "affiliation",
            "标签": "tags",
            "获取途径": "obtain_method"
        }
        self.fuzzname = Fuzzname()
        self.record = Record(path_prefix + "record_peo.txt")

    def extract_all_char(self, text_file=None, enemy_file=None):
        if text_file is None: text_file = path_prefix + "chardata.json"
        if enemy_file is None: enemy_file = path_prefix + "enemylist.json"

        if not os.path.exists(text_file) or not os.path.exists(enemy_file):
            self.fetch_data()

        # deal char
        with open(text_file, encoding='UTF-8') as fp:
            self.char_data = json.load(fp)

        # deal enemy
        with open(enemy_file, encoding='UTF-8') as fp:
            self.enemy_data = json.load(fp)

        # fuzzy name+pinyin -> name
        self.fuzzname.fit(
            list(self.char_data.keys()) + list(self.enemy_data.keys()))

    def filter(self, tags, flags={}):
        tags = tags[:]
        ranks = self.gen_ranks(tags)
        for name, dic in self.char_data.items():
            if set(tags).issubset(set(dic["tags"])): pass
            else: continue
            if dic["rank"] in ranks or ('show_all' in flags
                                        and flags['show_all'] == True):
                pass
            else:
                continue
            if "公开招募" in dic["obtain_method"] or ('show_all' in flags and
                                                  flags['show_all'] == True):
                pass
            else:
                continue

            yield name

    def gen_ranks(self, tags):
        ranks = ["1", "2", "3", "4", "5", "6"]
        for i in range(1, 7):
            if ">={0}".format(i) in tags:
                ranks = [x for x in ranks if x >= str(i)]
                tags.remove(">={0}".format(i))
            if "<={0}".format(i) in tags:
                ranks = [x for x in ranks if x <= str(i)]
                tags.remove("<={0}".format(i))
        if "高级资深干员" not in tags:
            ranks.remove("6")
        if "资深干员" in tags:
            ranks = ["5"]
        if "高级资深干员" in tags:
            ranks = ["6"]
        return ranks

    def get_peo_info(self, name=None):
        if not name: return None
        res = "None"
        if name in self.char_data:
            res = self.format_friend_info(name)
            self.record.add("friend/" + name)
        elif name in self.enemy_data:
            res = self.format_enemy_info(name)
            self.record.add("enemy/" + name)
        else:
            res = self.fuzzname.predict(name)
            res = "你可能想查 {0}".format(res)

        return res

    def format_friend_info(self, name):
        res = []
        for tp, cont in self.char_data[name]['all'].items():
            if tp:
                if tp == "干员代号": tp = "姓名"
                res.append("{0}: {1}".format(tp, cont))
        url = self.char_data[name]["link"]
        res.append(url)

        return "\n".join(res)

    def format_enemy_info(self, name):
        res = [name]
        url = self.enemy_data[name]["link"]
        res.append(url)

        return "\n".join(res)

    def fetch_data(self):
        self.fetch_character_from_wikijoyme()

        try:
            self.fetch_enemy_from_akmooncell()
        except Exception as e:
            print(e)
            self.fetch_enemy_from_wikijoyme()

    def fetch_character_from_wikijoyme(self, filename="chardata.json"):
        r = requests.get("http://wiki.joyme.com/arknights/干员数据表")
        if r.status_code != 200:
            raise IOError("Cannot fetch char from wikijoyme")
        tree = html.fromstring(r.text)

        # table head data
        tb_head = tree.xpath("//table[@id='CardSelectTr']//th/text()")
        tb_head = [x.strip() for x in tb_head]

        # deal with character
        char_res_lis = tree.xpath("//tr[@data-param1]")

        char_data = dict()
        for char_tr in char_res_lis:
            name = char_tr.xpath("./td[2]/a[1]/text()")[0]
            char_data[name] = dict()
            char_data[name]["job"] = char_tr.xpath("./@data-param1")[0]
            char_data[name]["rank"] = char_tr.xpath("./@data-param2")[0].split(
                ",")[0]
            char_data[name]["sex"] = char_tr.xpath("./@data-param3")[0]
            char_data[name]["affiliation"] = char_tr.xpath("./@data-param4")[0]
            tag_string=char_tr.xpath("./@data-param5")[0]+", " \
                        +char_data[name]["sex"]+", " \
                        +char_data[name]["job"]+", " \
                        +("资深干员" if char_data[name]["rank"]=="5" else "")+", " \
                        +("高级资深干员" if char_data[name]["rank"]=="6" else "")+", "
            taglist = [x.strip() for x in tag_string.split(",")]
            taglist = [x for x in taglist if x != ""]
            char_data[name]["tags"] = taglist
            char_data[name]["obtain_method"] = list(
                map(lambda x: x.strip(),
                    char_tr.xpath("./@data-param6")[0].split(",")))

            #deal head and data
            td_lis = char_tr.xpath(".//td")
            text_lis = [
                "".join([xx.strip() for xx in x.xpath(".//text()")])
                for x in td_lis
            ]
            all_lis = [x.strip() for x in text_lis]
            all_dict = dict(zip(tb_head, all_lis))
            char_data[name]["all"] = all_dict

            # link
            char_link_root = "http://wiki.joyme.com/arknights/"
            url = char_link_root + urllib.parse.quote(name)
            char_data[name]["link"] = url

            char_data[name]["type"] = "friend"

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(char_data, fp)

        return char_data

    def fetch_enemy_from_akmooncell(self, filename="enemylist.json"):
        # get enemy data
        r = requests.get("http://ak.mooncell.wiki/w/敌人一览")
        if r.status_code != 200:
            raise IOError("Cannot fetch enemy from akmooncell")
        tree = html.fromstring(r.text)

        enemy_res_lis = tree.xpath("//div[@class='smwdata']")

        enemy_data = dict()
        enemy_link_root = "http://ak.mooncell.wiki/w/"
        for enemy_a in enemy_res_lis:
            name = enemy_a.xpath("./@data-name")[0]
            # print("===="+name)
            enemy_data[name] = dict()
            link = enemy_link_root + urllib.parse.quote(name)

            enemy_data[name]["link"] = link
            enemy_data[name]["type"] = "enemy"

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(enemy_data, fp)

        return enemy_data

    def fetch_enemy_from_wikijoyme(self, filename="enemylist.json"):
        # get enemy data
        r = requests.get("http://wiki.joyme.com/arknights/敌方图鉴")
        if r.status_code != 200:
            raise IOError("Cannot fetch enemy from wikijoyme")
        tree = html.fromstring(r.text)

        enemy_res_lis = tree.xpath("//tr[@data-param1]")

        enemy_data = dict()
        enemy_link_root = "http://ak.mooncell.wiki/w/"
        for enemy_a in enemy_res_lis:
            name = enemy_a.xpath("./td[2]/a[1]/text()")[0]
            print("====" + name)
            enemy_data[name] = dict()
            link = enemy_link_root + urllib.parse.quote(name)

            enemy_data[name]["link"] = link
            enemy_data[name]["type"] = "enemy"

        with open(path_prefix + filename, "w", encoding='utf-8') as fp:
            json.dump(enemy_data, fp)

        return enemy_data