Esempio n. 1
0
    def has_any_valid_cell(self) -> bool:
        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                if self.is_valid_cell(row_index, col_index, self.current_step_cell()):
                    return True

        return False
Esempio n. 2
0
    def has_any_valid_cell_for(self, cell_color) -> bool:
        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                if self.is_valid_cell(row_index, col_index, cell_color):
                    return True

        return False
Esempio n. 3
0
    def get_valid_cells(self, current_step_cell) -> List[Tuple[int, int]]:
        valid_cells = []
        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                if self.is_valid_cell(row_index, col_index, current_step_cell):
                    valid_cells.append((row_index, col_index))

        return valid_cells
Esempio n. 4
0
    def is_game_over(self) -> bool:
        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                if cell == cell_empty:
                    return False

        return True
Esempio n. 5
0
    def score(self, cell_color) -> int:
        score = 0

        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                if cell == cell_color:
                    score += 1

        return score
Esempio n. 6
0
    def weight_sum(self, ai_step_cell) -> int:
        weights = 0

        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                if self.board[row_index][col_index] not in [cell_blue, cell_red]:
                    continue

                weights += weight_map[row_index - 1][col_index - 1] * self.board[row_index][col_index]

        return ai_step_cell * weights
Esempio n. 7
0
    def is_game_over(self) -> bool:
        if not self.has_any_valid_cell_for(self.step_cell) and \
                not self.has_any_valid_cell_for(self.other_step_cell(self.step_cell)):
            # 游戏已经结束
            return True

        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                if cell == cell_empty:
                    return False

        return True
Esempio n. 8
0
    def init_invalid_cells_randomly(self):
        # 随机选择五个位置不可下棋
        possiable_invalid_cells = list(filter(lambda v: not (
                (v[0] == 4 and v[1] == 4) or
                (v[0] == 5 and v[1] == 5) or
                (v[0] == 4 and v[1] == 5) or
                (v[0] == 5 and v[1] == 4)
        ), [(row, col) for col in range_from_one(board_size) for row in range_from_one(board_size)]))
        for row, col in random.sample(possiable_invalid_cells, k=invalid_cell_count):
            self.board[row][col] = cell_invalid
            self.invalid_cell_count = self.invalid_cell_count + 1

        self.paint()
Esempio n. 9
0
    def get_current_winner_info(self) -> Tuple[int, int, int]:
        # 数子
        counter = Counter()

        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]

                counter[cell] += 1

        blue, red = counter[cell_blue], counter[cell_red]
        if blue > red:
            winner = cell_blue
        else:
            winner = cell_red

        return (blue, red, winner)
Esempio n. 10
0
    def show_game_result(self):
        # 数子
        counter = Counter()

        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]

                counter[cell] += 1

        blue, red = counter[cell_blue], counter[cell_red]
        if blue > red:
            winner = self.cell_name(cell_blue)
        else:
            winner = self.cell_name(cell_red)

        logger.info(f"{self.cell_name(cell_blue)}={blue}")
        logger.info(f"{self.cell_name(cell_red)}={red}")
        logger.info(color("bold_yellow") + f"胜方为{winner}")
Esempio n. 11
0
                             uploader.history_version_prefix),
                        ])]

    logger.info(color("bold_green") + f"具体上传列表如下:")
    for upload_folder, upload_list in upload_info_list:
        logger.info(color("bold_cyan") + f"\t{upload_folder.name}:")
        for local_filepath, history_file_prefix in upload_list:
            logger.info(f"\t\t{local_filepath}")

        logger.info('\n')

    for upload_folder, upload_list in upload_info_list:
        for local_filepath, history_file_prefix in reversed(upload_list):
            # 逆序遍历,确保同一个网盘目录中,列在前面的最后才上传,从而在网盘显示时显示在最前方
            total_try_count = 1
            for try_index in range_from_one(total_try_count):
                upload_ok = uploader.upload_to_lanzouyun(
                    local_filepath,
                    upload_folder,
                    history_file_prefix=history_file_prefix)
                if upload_ok:
                    break

                logger.warning(
                    f"第{try_index}/{total_try_count}次尝试上传{local_filepath}失败,等待一会后重试"
                )
                if try_index < total_try_count:
                    count_down("上传到网盘", 5 * try_index)

else:
    logger.error("蓝奏云登录失败")
Esempio n. 12
0
def release():
    # ---------------准备工作
    prompt = f"如需直接使用默认版本号:{now_version} 请直接按回车\n或手动输入版本号后按回车:"
    version = input(prompt) or now_version

    version_reg = r"\d+\.\d+\.\d+"

    if re.match(version_reg, version) is None:
        logger.info(f"版本号格式有误,正确的格式类似:1.0.0 ,而不是 {version}")
        pause_and_exit(-1)

    # 最大化窗口
    change_console_window_mode_async(disable_min_console=True)

    version = "v" + version

    run_start_time = datetime.now()
    show_head_line(f"开始发布版本 {version}", color("bold_yellow"))

    set_title_cmd = f"title 发布 {version}"
    os.system(set_title_cmd)

    # 先声明一些需要用到的目录的地址
    dir_src = os.path.realpath(".")
    dir_all_release = os.path.realpath(os.path.join("releases"))
    release_dir_name = f"DNF蚊子腿小助手_{version}_by风之凌殇"
    release_7z_name = f"{release_dir_name}.7z"
    dir_github_action_artifact = "_github_action_artifact"

    # ---------------构建
    # 调用构建脚本
    os.chdir(dir_src)
    build()

    # ---------------清除一些历史数据
    make_sure_dir_exists(dir_all_release)
    os.chdir(dir_all_release)
    clear_github_artifact(dir_all_release, dir_github_action_artifact)

    # ---------------打包
    os.chdir(dir_src)
    package(dir_src, dir_all_release, release_dir_name, release_7z_name, dir_github_action_artifact)

    # ---------------构建增量补丁
    create_patch_for_latest_n_version = 3

    # ---------------构建增量包
    os.chdir(dir_all_release)
    show_head_line(f"开始构建增量包,最多包含过去{create_patch_for_latest_n_version}个版本到最新版本的补丁", color("bold_yellow"))
    create_patch(dir_src, dir_all_release, create_patch_for_latest_n_version, dir_github_action_artifact)

    # ---------------获取补丁地址(分开方便调试)
    os.chdir(dir_all_release)
    patch_file_name = create_patch(
        dir_src,
        dir_all_release,
        create_patch_for_latest_n_version,
        dir_github_action_artifact,
        get_final_patch_path_only=True,
    )

    # ---------------标记新版本
    show_head_line("提交版本和版本变更说明,并同步到docs目录,用于生成github pages", color("bold_yellow"))
    os.chdir(dir_src)
    commit_new_version()

    # ---------------上传到蓝奏云
    show_head_line("开始上传到蓝奏云", color("bold_yellow"))
    os.chdir(dir_src)
    with open("upload_cookie.json") as fp:
        cookie = json.load(fp)
    os.chdir(dir_all_release)
    uploader = Uploader()
    uploader.login(cookie)
    if uploader.login_ok:
        logger.info("蓝奏云登录成功,开始上传压缩包")

        def path_in_src(filepath_relative_to_src: str) -> str:
            return os.path.realpath(os.path.join(dir_src, filepath_relative_to_src))

        realpath = os.path.realpath

        upload_info_list = [
            (
                uploader.folder_djc_helper,
                [
                    (realpath(release_7z_name), uploader.history_version_prefix),
                    (path_in_src("utils/auto_updater.exe"), ""),
                    (path_in_src("使用教程/使用文档.docx"), ""),
                    (path_in_src("使用教程/视频教程.txt"), ""),
                    (path_in_src("付费指引/付费指引.docx"), ""),
                    (path_in_src("utils/不要下载增量更新文件_这个是给自动更新工具使用的.txt"), ""),
                    (realpath(patch_file_name), uploader.history_patches_prefix),
                ],
            ),
            (
                uploader.folder_dnf_calc,
                [
                    (realpath(release_7z_name), uploader.history_version_prefix),
                ],
            ),
        ]

        logger.info(color("bold_green") + "具体上传列表如下:")
        for upload_folder, upload_list in upload_info_list:
            logger.info(color("bold_cyan") + f"\t{upload_folder.name}:")
            for local_filepath, _history_file_prefix in upload_list:
                logger.info(f"\t\t{local_filepath}")

            logger.info("\n")

        for upload_folder, upload_list in upload_info_list:
            for local_filepath, history_file_prefix in reversed(upload_list):
                # 逆序遍历,确保同一个网盘目录中,列在前面的最后才上传,从而在网盘显示时显示在最前方
                total_try_count = 1
                for try_index in range_from_one(total_try_count):
                    upload_ok = uploader.upload_to_lanzouyun(
                        local_filepath, upload_folder, history_file_prefix=history_file_prefix
                    )
                    if upload_ok:
                        break

                    logger.warning(f"第{try_index}/{total_try_count}次尝试上传{local_filepath}失败,等待一会后重试")
                    if try_index < total_try_count:
                        count_down("上传到网盘", 5 * try_index)

    else:
        logger.error("蓝奏云登录失败")

    # ---------------推送版本到github
    # 打包完成后git添加标签
    os.chdir(dir_src)
    show_head_line("开始推送到github", color("bold_yellow"))
    push_github(version)

    # ---------------结束
    logger.info("+" * 40)
    logger.info(color("bold_yellow") + f"{version} 发布完成,共用时{datetime.now() - run_start_time},请检查上传至蓝奏云流程是否OK")
    logger.info("+" * 40)

    os.system("PAUSE")
Esempio n. 13
0
def create_patch(dir_src,
                 dir_all_release,
                 create_patch_for_latest_n_version,
                 dir_github_action_artifact,
                 get_final_patch_path_only=False) -> str:
    latest_version = now_version
    path_bz = os.path.join(dir_src, "utils/bandizip_portable", "bz.exe")

    old_cwd = os.getcwd()
    os.chdir(dir_all_release)
    if not get_final_patch_path_only:
        logger.info(f"工作目录已调整为{os.getcwd()},最新版本为v{latest_version}")

    uploader = Uploader()

    if not get_final_patch_path_only:
        logger.info(
            f"尝试从网盘查找在{latest_version}版本之前最近{create_patch_for_latest_n_version}个版本的信息"
        )
    old_version_infos = []  # type: List[HistoryVersionFileInfo]

    # 获取当前网盘的最新版本,若比当前发布版本低,也加入
    netdisk_latest_version_fileinfo = uploader.find_latest_version()
    netdisk_latest_version = uploader.parse_version_from_djc_helper_file_name(
        netdisk_latest_version_fileinfo.name)
    if version_less(netdisk_latest_version, latest_version):
        old_version_infos.append(
            HistoryVersionFileInfo(netdisk_latest_version_fileinfo,
                                   netdisk_latest_version))

    # 从历史版本网盘中查找旧版本
    for page in range_from_one(100):
        folder_info = uploader.get_folder_info_by_url(
            uploader.folder_history_files.url, get_this_page=page)
        for file in folder_info.files:
            filename = file.name  # type: str

            if not filename.startswith(uploader.history_version_prefix):
                # 跳过非历史版本的文件
                continue

            file_version = uploader.parse_version_from_djc_helper_file_name(
                filename)
            info = HistoryVersionFileInfo(file, file_version)

            if not version_less(file_version, latest_version):
                continue

            if info in old_version_infos:
                # 已经加入过(可能重复)
                continue

            old_version_infos.append(info)

        if len(old_version_infos) >= create_patch_for_latest_n_version + 2:
            # 已经找到超过前n+2个版本,因为网盘返回的必定是按上传顺序排列的,不过为了保险起见,多考虑一些
            break

    if create_patch_for_latest_n_version > len(old_version_infos):
        create_patch_for_latest_n_version = len(old_version_infos)

    old_version_infos = sorted(
        old_version_infos)[-create_patch_for_latest_n_version:]

    # 确认最终文件名
    patch_oldest_version = old_version_infos[0].version
    patch_newest_version = old_version_infos[-1].version
    patches_dir = f"DNF蚊子腿小助手_增量更新文件_v{patch_oldest_version}_to_v{patch_newest_version}"
    temp_dir = "patches_temp"

    patch_7z_file = f"{patches_dir}.7z"
    if get_final_patch_path_only:
        return patch_7z_file

    logger.info(f"需要制作补丁包的版本为{old_version_infos}")

    # 确保版本都在本地
    logger.info(f"确保以上版本均已下载并解压到本地~")
    for info in old_version_infos:
        local_folder_path = os.path.join(dir_all_release,
                                         f"DNF蚊子腿小助手_v{info.version}_by风之凌殇")
        local_7z_path = local_folder_path + ".7z"

        if os.path.isdir(local_folder_path):
            # 本地已存在对应版本,跳过
            continue

        logger.info(f"本地发布目录不存在 {local_folder_path}")
        if not os.path.isfile(local_7z_path):
            logger.info(f"本地不存在{info.fileinfo.name}的7z文件,将从网盘下载")
            uploader.download_file(info.fileinfo, dir_all_release)

        logger.info(f"尝试解压 {info.fileinfo.name} 到 {local_folder_path}")
        decompress_dir_with_bandizip(local_7z_path, dir_src)

    # --------------------------- 实际只做补丁包 ---------------------------
    logger.info(color("bold_yellow") + f"将为【{old_version_infos}】版本制作补丁包")

    shutil.rmtree(patches_dir, ignore_errors=True)
    os.mkdir(patches_dir)
    shutil.rmtree(temp_dir, ignore_errors=True)
    os.mkdir(temp_dir)

    def temp_path(dir_name):
        return os.path.realpath(os.path.join(temp_dir, dir_name))

    def preprocess_before_patch(temp_version_path):
        for filename in ["config.toml", "utils/auto_updater.exe"]:
            filepath = os.path.join(temp_version_path, filename)
            if os.path.isfile(filepath):
                os.remove(filepath)

    # 为旧版本创建patch文件
    target_version_dir = f"DNF蚊子腿小助手_v{latest_version}_by风之凌殇"
    logger.info(f"目标版本目录为{target_version_dir}")
    shutil.copytree(target_version_dir, temp_path(target_version_dir))
    preprocess_before_patch(temp_path(target_version_dir))

    for idx, version_info in enumerate(old_version_infos):
        version = version_info.version
        patch_file = f"{patches_dir}/{version}.patch"

        logger.info("-" * 80)
        logger.info(
            color("bold_yellow") +
            f"[{idx + 1}/{len(old_version_infos)}] 创建从v{version}升级到v{latest_version}的补丁{patch_file}"
        )

        version_dir = f"DNF蚊子腿小助手_v{version}_by风之凌殇"

        shutil.copytree(version_dir, temp_path(version_dir))
        preprocess_before_patch(temp_path(version_dir))

        subprocess.call([
            os.path.realpath(os.path.join(dir_src, "utils/hdiffz.exe")),
            f"-p-{multiprocessing.cpu_count()}",  # 设置系统最大cpu数
            os.path.realpath(os.path.join(temp_dir, version_dir)),
            os.path.realpath(os.path.join(temp_dir, target_version_dir)),
            patch_file,
        ])

        filesize = os.path.getsize(patch_file)
        logger.info(f"创建补丁{patch_file}结束,最终大小为{human_readable_size(filesize)}")

    # 移除临时目录
    shutil.rmtree(temp_dir, ignore_errors=True)

    # 压缩打包
    compress_dir_with_bandizip(patches_dir, patch_7z_file, dir_src)

    # 额外备份一份最新的供github action 使用
    shutil.copyfile(
        patch_7z_file,
        os.path.join(dir_github_action_artifact, 'djc_helper_patches.7z'))

    os.chdir(old_cwd)

    return patch_7z_file
Esempio n. 14
0
    def stable_score(self, current_step_cell) -> int:
        # 一些辅助函数
        def add(cell_position, direction):
            return tuple(v + delta
                         for v, delta in zip(cell_position, direction))

        def reverse(direction):
            return tuple(-delta for delta in direction)

        def continuously_nonempty_cell_count(first_cell_position, direction,
                                             max_count) -> int:
            not_empty = 0
            current_position = first_cell_position
            for i in range(7):
                cell = get_cell(current_position)
                if cell == cell_empty:
                    break

                not_empty += 1

                current_position = add(current_position, direction)

            return not_empty

        def get_cell(cell_position) -> int:
            row, col = cell_position
            return self.board[row][col]

        # 返回各自所属的 左上到右下的对角线(1-15),左下到右上的对角线(1-15)
        def get_diagonal(row, col) -> Tuple[int, int]:
            upper_diagonal = col - row + 8
            lower_diagonal = col + row - 1

            return (upper_diagonal, lower_diagonal)

        # 角、边、其他(八个方向都无空位) 之差
        # note: 与参考文献不同,自己和对方取差值,而不是相加,因为对方越多稳定子,对自己不利
        corner, edge, other = 0, 0, 0

        # 计算角
        corner_cell_positions = [
            (1, 1),
            (1, 8),
            (8, 1),
            (8, 8),
        ]
        for row, col in corner_cell_positions:
            cell = self.board[row][col]
            if cell in [cell_blue, cell_red]:
                corner += current_step_cell * cell

        # 计算边
        edge_cell_positions = [
            ((0, 1), [(1, col) for col in range(2, 7 + 1)]),  # 上
            ((0, 1), [(8, col) for col in range(2, 7 + 1)]),  # 下
            ((1, 0), [(row, 1) for row in range(2, 7 + 1)]),  # 左
            ((1, 0), [(row, 8) for row in range(2, 7 + 1)]),  # 右
        ]

        for direction, cell_positions in edge_cell_positions:
            # 计算两个边界格子
            lower = add(cell_positions[0], reverse(direction))
            upper = add(cell_positions[-1], direction)

            # 计算lower->upper方向格子连续非空的数目
            lu = continuously_nonempty_cell_count(lower, direction, 7)

            # 计算upper->lower方向格子连续非空的数目
            ul = continuously_nonempty_cell_count(upper, reverse(direction), 7)

            # 计算本边上与边界间连续无空格的位置数目
            for idx, _position in enumerate(cell_positions):
                cell = self.board[_position[0]][_position[1]]
                if cell not in [cell_blue, cell_red]:
                    continue

                index = 2 + idx
                if (index <= lu and get_cell(lower) != cell_empty) or \
                        (index >= board_size - ul + 1 and get_cell(upper) != cell_empty):
                    edge += current_step_cell * cell

        # 计算其他位置(八个方向都无空位)

        # 预计算
        # 非空行
        not_empty_rows = set(row for row in range_from_one(8))
        # 非空列
        not_empty_cols = set(col for col in range_from_one(8))
        # 非空的左上到右下方向的对角线
        not_empty_upper_diagonal = set(dia for dia in range_from_one(15))
        # 非空的左下到右上方向的对角线
        not_empty_lower_diagonal = set(dia for dia in range_from_one(15))

        for row in range_from_one(board_size):
            for col in range_from_one(board_size):
                cell = self.board[row][col]
                if cell != cell_empty:
                    continue

                # 标记所在行列和两个对角线为非空
                upper_diagonal, lower_diagonal = get_diagonal(row, col)

                not_empty_rows.discard(row)
                not_empty_cols.discard(col)
                not_empty_upper_diagonal.discard(upper_diagonal)
                not_empty_lower_diagonal.discard(lower_diagonal)

        # 实际计算出非边角位置的八方向都无空格的格子
        for row in range(2, 7 + 1):
            for col in range(2, 7 + 1):
                cell = self.board[row][col]
                if cell not in [cell_blue, cell_red]:
                    continue

                upper_diagonal, lower_diagonal = get_diagonal(row, col)

                if row in not_empty_rows and \
                        col in not_empty_cols and \
                        upper_diagonal in not_empty_upper_diagonal and \
                        lower_diagonal in not_empty_lower_diagonal:
                    other += cell * current_step_cell

        return corner + edge + other
Esempio n. 15
0
    def init_ui(self):
        width = 800
        height = 580

        self.setFixedSize(width, height)

        # 设置棋盘背景
        oBackGroundImage = QImage("reversi_images/board.png")
        sBackGroundImage = oBackGroundImage.scaled(QSize(
            width, height))  # resize Image to widgets size
        palette = QPalette()
        palette.setBrush(QPalette.Window, QBrush(sBackGroundImage))
        self.setPalette(palette)

        # 初始化棋盘元素
        self.label_count_down = QLabel('', self)
        self.label_count_down.setStyleSheet(
            f"color: orange; font-size: 30px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_count_down.setGeometry(350, 0, 500, 60)

        self.label_turn = QLabel('蓝方回合', self)
        self.label_turn.setStyleSheet(
            f"color: blue; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_turn.setGeometry(320, 60, 500, 40)

        self.label_blue_name = QLabel('蓝方-AI托管', self)
        self.label_blue_name.setStyleSheet(
            f"color: gray; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_blue_name.setGeometry(150, 40, 180, 20)

        self.label_blue_score = QLabel('2', self)
        self.label_blue_score.setStyleSheet(
            f"color: yellow; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_blue_score.setGeometry(180, 60, 120, 30)

        self.label_red_name = QLabel('大师南瓜球', self)
        self.label_red_name.setStyleSheet(
            f"color: gray; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_red_name.setGeometry(520, 40, 180, 20)

        self.label_red_score = QLabel('2', self)
        self.label_red_score.setStyleSheet(
            f"color: yellow; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_red_score.setGeometry(570, 60, 120, 30)

        self.btn_manunal_bye = QPushButton('手动轮空', self)
        self.btn_manunal_bye.setStyleSheet(
            f"color: #cf8160; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei; background-color: #89090a"
        )
        self.btn_manunal_bye.setGeometry(685, 460, 80, 30)
        self.btn_manunal_bye.clicked.connect(self.manunal_bye)

        self.btn_restart = QPushButton('重新开始', self)
        self.btn_restart.setStyleSheet(
            f"color: #cf8160; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei; background-color: #89090a"
        )
        self.btn_restart.setGeometry(685, 505, 80, 30)
        self.btn_restart.clicked.connect(self.restart)

        # 180 120
        # 445 -> 480 (row 1 -> 8 top )
        mid_top_x, mid_top_y = 400, 120
        self.btn_list_board = []

        self.qicon_blue = QIcon(QPixmap("reversi_images/blue.png"))
        self.qicon_red = QIcon(QPixmap("reversi_images/red.png"))
        self.qicon_empty = QIcon()
        self.qicon_next_step = QIcon(QPixmap("reversi_images/next_step.png"))
        self.qicon_invalid = QIcon(QPixmap("reversi_images/invalid.png"))
        self.qicon_current_blue = QIcon(
            QPixmap("reversi_images/current_blue.png"))
        self.qicon_current_red = QIcon(
            QPixmap("reversi_images/current_red.png"))

        for row_index in range_from_one(board_size):
            label_row = []

            row_width = 445 + int((480 - 445) / 7 * (row_index - 1))

            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                x, y = mid_top_x - row_width // 2 + row_width // 8 * (
                    col_index - 1), mid_top_y + 47 * (row_index - 1)

                btn = QPushButton(self)
                btn.setIconSize(QSize(60, 50))
                btn.setGeometry(x, y, row_width // 8, 50)

                btn.setStyleSheet(
                    "QPushButton { background-color: transparent; border: 0px }"
                )

                def cb(ri, ci):
                    def _cb():
                        logger.debug(f"clicked row={ri}, col={ci}")

                        # 初始化无效格子
                        if self.invalid_cell_count < invalid_cell_count:
                            if self.board[ri][ci] != cell_empty:
                                logger.info("该格子不为空,不能设置为无效格子")
                                return

                            self.board[ri][ci] = cell_invalid
                            self.invalid_cell_count = self.invalid_cell_count + 1
                            logger.info(f"设置第{self.invalid_cell_count}个无效位置")
                            self.paint()

                            if self.invalid_cell_count == invalid_cell_count:
                                # 记录点击次数,到达五个按钮时进入正式游戏模式(尝试ai点击)并隐藏提示按钮
                                self.ai_try_put_cell()
                            return

                        if self.current_step_cell(
                        ) in self.ai_cells and not self.ai_moving:
                            logger.info("当前回合由机器人托管,将无视该点击")
                            return
                        self.ai_moving = False

                        # 判断是否可行
                        if self.is_game_over():
                            self.game_over()
                            return

                        if not self.has_any_valid_cell():
                            logger.info("本轮无任何可行落子,将轮空")
                            self.next_turn()
                            self.loop_index += 1
                            if not self.has_any_valid_cell():
                                logger.info("双方均不可再落子,游戏结束")
                                self.game_over()
                                return

                        # 记录下当前方
                        current_step_cell = self.current_step_cell()

                        # 落子
                        is_valid = self.put_cell(ri, ci) is not None
                        if is_valid:
                            self.loop_index += 1

                        # 计算落子后当前方局面分
                        current_score = self.evaluate(current_step_cell)
                        if current_score >= 0:
                            cr = "bold_red"
                        else:
                            cr = "bold_green"
                        logger.info(
                            color(cr) +
                            f"落子后当前{self.cell_name_without_color(current_step_cell)}局面分为{current_score}"
                        )

                        # 重绘界面
                        self.paint()

                        # 若轮到机器人
                        self.ai_try_put_cell()

                    return _cb

                btn.clicked.connect(cb(row_index, col_index))

                label_row.append(btn)

            self.btn_list_board.append(label_row)

        self.paint()

        self.show()
Esempio n. 16
0
    def paint(self, show_cui_detail=False, game_overd=False):
        logger.info('-' * 20)
        blue_score = self.with_color(f"蓝方:{self.score(cell_blue)}", "blue")
        red_score = self.with_color(f"红方:{self.score(cell_red)}", "red")
        logger.info(f"{blue_score}\t{red_score}")

        if show_cui_detail:
            logger.info(' '.join(
                ['  ', *[str(col_idx + 1) for col_idx in range(board_size)]]))
            for row_index in range_from_one(board_size):

                state = [f'{chr(ord("a") + row_index - 1)} ']

                for col_index in range_from_one(board_size):
                    cell = self.board[row_index][col_index]
                    if cell in [cell_blue, cell_red]:
                        if cell == cell_blue:
                            val, show_color = 'B', 'blue'
                        else:
                            val, show_color = 'R', 'red'

                        if row_index == self.last_step[
                                0] and col_index == self.last_step[1]:
                            show_color = "bold_purple"

                        state.append(self.with_color(val, show_color))
                    elif cell == cell_empty:
                        if self.is_valid_cell(row_index, col_index,
                                              self.current_step_cell()):
                            current_color = 'blue'
                            if self.current_step_cell() == cell_red:
                                current_color = 'red'
                            state.append(self.with_color('*', current_color))
                        else:
                            state.append(' ')
                    elif cell == cell_invalid:
                        state.append(self.with_color('X', 'bold_white'))

                state.append('')
                logger.info('|'.join(state))

            if not self.has_any_valid_cell():
                logger.info("本轮无任何可行落子,将轮空")

        # gui
        # 绘制格子
        for row_index in range_from_one(board_size):
            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                btn = self.btn_list_board[row_index - 1][col_index - 1]

                ico = None
                if cell in [cell_blue, cell_red]:
                    if row_index == self.last_step[
                            0] and col_index == self.last_step[1]:
                        if cell == cell_blue:
                            ico = self.qicon_current_blue
                        else:
                            ico = self.qicon_current_red
                    else:
                        if cell == cell_blue:
                            ico = self.qicon_blue
                        else:
                            ico = self.qicon_red
                elif cell == cell_empty:
                    if self.is_valid_cell(row_index, col_index,
                                          self.current_step_cell()):
                        ico = self.qicon_next_step
                    else:
                        ico = self.qicon_empty
                else:
                    # cell_invalid
                    ico = self.qicon_invalid

                btn.setIcon(ico)

        # 绘制其他界面元素
        if self.invalid_cell_count < invalid_cell_count:
            self.label_turn.setText(
                f"请继续点击{invalid_cell_count - self.invalid_cell_count}个格子,设置为无效格子"
            )
            self.label_turn.setStyleSheet(
                f"color: cyan; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
            )
        else:
            turn_name = ""
            if self.current_step_cell() == cell_blue:
                turn_name = "蓝方回合"
                self.label_turn.setStyleSheet(
                    f"color: blue; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
                )
            else:
                turn_name = "红方回合"
                self.label_turn.setStyleSheet(
                    f"color: red; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
                )

            if self.current_step_cell() in self.ai_cells:
                turn_name += "-AI托管"

            self.label_turn.setText(f"{self.loop_index}-{turn_name}")

            if not self.has_any_valid_cell():
                logger.info("本轮无任何可行落子,将轮空")
                if len(self.ai_cells) < 2:
                    self.notify(
                        self.cell_name(self.current_step_cell(),
                                       with_color=False) + '轮空,请点击任意位置结束本轮')

        blue_evaluted_score = self.evaluate(cell_blue,
                                            ignore_game_over=game_overd)
        red_evaluted_score = -blue_evaluted_score
        self.label_blue_score.setText(
            f"{self.score(cell_blue)}({blue_evaluted_score})")
        self.label_red_score.setText(
            f"{self.score(cell_red)}({red_evaluted_score})")

        self.update()