基于python pyqt的围棋游戏制作


本文使用python中的pygame库设计制作了一个围棋游戏。
参考项目资料: https://github.com/HapHac/weiqi

1、初始化棋盘

# 绘制棋盘
    def draw_chessboard(self):
        # 画棋盘,填充颜色
        pygame.draw.rect(self.screen, (204, 85, 17), [0 * self.size, 0 * self.size, 400 * self.size, 400 * self.size])
        pygame.draw.rect(self.screen, (51, 102, 153),
                         [400 * self.size, 0 * self.size, 200 * self.size, 400 * self.size])
        # 画外框
        outer_frame_color = (60, 20, 0)
        pygame.draw.rect(self.screen, outer_frame_color,
                         [20 * self.size, 20 * self.size, 360 * self.size, 360 * self.size],
                         5)
        # 棋盘上的九个定位点,以中点为模型,移动位置,以作出其余八个点
        for m in [-1, 0, 1]:
            for n in [-1, 0, 1]:
                rect = pygame.Rect([200 * self.size - self.size * 2,
                                    200 * self.size - self.size * 2,
                                    self.size * 4, self.size * 4])
                pygame.draw.ellipse(self.screen, (0, 0, 0), rect, width=0)
                rect = rect.move(m * self.interval * (2 if self.mode_num == 9 else (3 if self.mode_num == 13 else 6)),
                                 n * self.interval * (2 if self.mode_num == 9 else (3 if self.mode_num == 13 else 6)))
                pygame.draw.ellipse(self.screen, (0, 0, 0), rect, width=0)
        # 画中间的线条
        for i in range(1, self.mode_num - 1):
            pygame.draw.line(self.screen, (0, 0, 0), (20 * self.size, 20 * self.size + i * self.interval),
                             (380 * self.size, 20 * self.size + i * self.interval), width=2)
            pygame.draw.line(self.screen, (0, 0, 0), (20 * self.size + i * self.interval, 20 * self.size),
                             (20 * self.size + i * self.interval, 380 * self.size), width=2)

        # 放置右侧初始图片
        if self.stop:
            self.screen.blit(self.photoW, (480 * self.size, 65 * self.size))
            self.screen.blit(self.photoB, (480 * self.size - 45, 65 * self.size))
        elif self.present == 1:
            self.screen.blit(self.photoB, (480 * self.size - 45, 65 * self.size))
        else:
            self.screen.blit(self.photoW, (480 * self.size, 65 * self.size))
        # # 几个功能按钮
        button_color = (255, 255, 255)

        self.start_button = Button('开始游戏', button_color, 'msyh.ttc', 23)
        self.start_button.draw(self.screen, 480 * self.size, 200 * self.size)
        self.pass_me_button = Button('弃一子', button_color, 'msyh.ttc', 23)
        self.pass_me_button.draw(self.screen, 480 * self.size, 225 * self.size)
        self.regert_button = Button('悔棋', button_color, 'msyh.ttc', 23)
        self.regert_button.draw(self.screen, 480 * self.size, 250 * self.size)
        self.restsrt_button = Button('重新开始', button_color, 'msyh.ttc', 23)
        self.restsrt_button.draw(self.screen, 480 * self.size, 275 * self.size)
        self.thirteen_way_button = Button('十三路棋', button_color, 'msyh.ttc', 23)
        self.thirteen_way_button.draw(self.screen, 480 * self.size, 300 * self.size)
        self.nineteenth_way_button = Button('十九路棋', button_color, 'msyh.ttc', 23)
        self.nineteenth_way_button.draw(self.screen, 480 * self.size, 325 * self.size)
        self.exit_button = Button('退出游戏', button_color, 'msyh.ttc', 23)
        self.exit_button.draw(self.screen, 480 * self.size, 350 * self.size)

在上述代码中,首先绘制棋盘的框架,使用pygame中的pygame.draw.rect,然后通过pygame.draw.line绘制线条,形成完整的棋盘。除此之外,在棋盘的一旁,添加了一个太极图用以表示当前的棋手,以及一些按钮用于控制游戏的进程(按钮使用自定义类完成)。

class Text:
    def __init__(self, text: str, text_color, font_type: str, font_size: int):
        """
        text: 文本内容,注意是字符串形式
        text_color: 字体颜色
        font_type: 字体文件
        font_size: 字体大小
        """
        self.text = text
        self.text_color = text_color
        self.font_type = font_type
        self.font_size = font_size

        font = pygame.font.Font(os.path.join('font', (self.font_type)), self.font_size)
        self.text_image = font.render(self.text, True, self.text_color).convert_alpha()

        self.text_width = self.text_image.get_width()
        self.text_height = self.text_image.get_height()

    def draw(self, surface: pygame.Surface, center_x, center_y):
        """
        surface: 文本放置的表面
        center_x, center_y: 文本放置在表面的<中心坐标>
        """
        upperleft_x = center_x - self.text_width / 2
        upperleft_y = center_y - self.text_height / 2
        surface.blit(self.text_image, (upperleft_x, upperleft_y))


class Button(Text):
    def __init__(self, text: str, text_color, font_type: str, font_size: int):
        super().__init__(text, text_color, font_type, font_size)
        self.rect = self.text_image.get_rect()
        self.enable = True

    def draw(self, surface: pygame.Surface, center_x, center_y):
        super().draw(surface, center_x, center_y)
        self.rect.center = center_x, center_y

    def handle_event(self, command):
        self.hovered = self.rect.collidepoint(pygame.mouse.get_pos())
        if self.hovered:
            if self.enable:
                command()
            else:
                pass

2、初始化棋子

    # 绘制棋子
    def draw_chess(self):
        # 遍历棋盘二维数组,绘制出棋盘中所有已下子
        for i in range(self.mode_num):
            for j in range(self.mode_num):
                pos = i * self.interval + 20 * self.size - 30 * self.p, j * self.interval + 20 * self.size - 30 * self.p
                if self.positions[i][j] == 0:
                    continue
                elif self.positions[i][j] == 1:
                    self.screen.blit(self.photoBD, pos)
                elif self.positions[i][j] == -1:
                    self.screen.blit(self.photoWD, pos)

定义一个数组:self.positions用以存储棋盘中棋子的状态。

3、开始游戏

点击开始游戏按钮,正式开始游戏,删除白色太极图,表示黑棋先行。

    # 开始游戏函数,点击“开始游戏”时调用
    def start(self):
        # 删除右侧太极图
        self.screen.blit(self.bg_rect, (400 * self.size, 65 * self.size))
        # 利用右侧图案提示开始时谁先落子
        if self.present == 1:
            self.screen.blit(self.photoB, (480 * self.size - 45, 65 * self.size))
        else:
            self.screen.blit(self.photoW, (480 * self.size, 65 * self.size))
        # 开始标志,解除stop
        self.stop = None

4、落子设置

首先定义一个函数,用以获取当前鼠标的位置。

    # 获取鼠标当前位置
    def get_mouse_current_position(self, x, y):
        self.mouse_x = x
        self.mouse_y = y

然后设置棋子跟随鼠标移动。

    # 棋子跟随鼠标移动
    def chess_follow(self):
        if 20 * self.size < self.mouse_x < 380 * self.size and 20 * self.size < self.mouse_y < 380 * self.size:
            pos = self.mouse_x - 30 * self.p, self.mouse_y - 35 * self.p
            if self.present == 1:
                self.screen.blit(self.photoBD, pos)
            else:
                self.screen.blit(self.photoWD, pos)

最后定义一个函数,在鼠标左键点击时,落下棋子。

 # 落子,并驱动玩家的轮流下棋行为
    def getDown(self):
        # 拷贝三份棋盘“快照”,悔棋和判断“打劫”时需要作参考
        self.last_3_positions = copy.deepcopy(self.last_2_positions)
        self.last_2_positions = copy.deepcopy(self.last_1_positions)
        self.last_1_positions = copy.deepcopy(self.positions)
        dx = (self.mouse_x - 20 * self.size) % self.interval
        dy = (self.mouse_y - 20 * self.size) % self.interval
        if not self.stop:
            row = int((self.mouse_x - 20 * self.size) / self.interval) + round(dx / self.interval)
            col = int((self.mouse_y - 20 * self.size) / self.interval) + round(dy / self.interval)
            if self.positions[row][col] == 0:
                self.positions[row][col] = self.present
                deadlist = self.get_deadlist()
                self.kill(deadlist)
                # 自杀判定
                # 对方无“气”棋子全部提走后,对己方棋子进行有无“气”的判断,若己方仍存在无“气”棋子,则判定为自杀行为,自杀标志置1(因只需检测到一个无“气”子即说明是自杀,故无需继续检测,跳出循环)
                for i in range(9):
                    for j in range(9):
                        if self.positions[i][j] == self.present:
                            if self.if_dead(i, j) == True:
                                win32api.MessageBox(0, "禁止自杀", "警告", win32con.MB_ICONWARNING)
                                self.positions[i][j] = 0
                                self.present = - self.present
            else:
                # 警告信息框
                win32api.MessageBox(0, "此处以存在棋子", "警告", win32con.MB_ICONWARNING)

5、吃子判断

在每次落子之后,遍历整个棋盘,并返回死棋列表位置

    # 落子后,依次判断四周是否有棋子被杀死,并返回死棋位置列表
    def get_deadlist(self):
        deadlist = []
        for i in range(self.mode_num):
            for j in range(self.mode_num):
                if self.positions[i][j] == 0:
                    continue
                elif self.positions[i][j] == -self.present and self.if_dead(i, j) == True:
                    # continue
                    self.visit_positions = [[0 for k in range(self.mode_num + 2)] for k in range(self.mode_num + 2)]
                    deadlist.append([i, j])
        # print(deadlist)
        return deadlist

判断棋子是否有气函数if_dead

    # 判断棋子是否无气(死亡),有气则返回False,无气则返回无气棋子的列表
    def if_dead(self, x, y):
        # 为避免重复搜索,走过的位置记为1
        self.visit_positions[x][y] = 1
        directions = [[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]]
        # 左,右,上,下四个方向
        for dx, dy in directions:
            # 左边是墙 或 右边是墙 或 上边是墙 或 下边是墙,即死路,则跳过此方向
            if dx < 0 or dx > 8 or dy < 0 or dy > 8:
                continue
            # 若此方向没有搜索过,则开始搜索
            elif self.visit_positions[dx][dy] == 0:
                # 此方向没有棋子,可看做迷宫的出口,于是该棋子有“气”,停止搜索
                if self.positions[dx][dy] == 0:
                    return False
                # 此方向是对方棋子,是死路,跳过此方向
                elif self.positions[dx][dy] == - self.positions[x][y]:
                    continue
                # 此方向是己方棋子,即通路,继续递归执行DFS
                elif self.positions[dx][dy] == self.positions[x][y]:
                    self.if_dead(dx, dy)
        # 以上条件都不满足,即所有路径都为死路,该棋子无“气”,停止搜索
        return True

杀死死棋

    # 杀死位置列表killList中的棋子,即删除图片,位置值置0
    def kill(self, killList):
        if len(killList) > 0:
            for i, j in killList:
                self.positions[i][j] = 0

6 悔棋和弃一子

    # 放弃一子
    def passme(self):
        self.present = -self.present

    # 悔棋函数
    def regret(self):
        self.positions = self.last_2_positions

7 重新开始、推出与切换棋盘

# 重新开始游戏
    def reload(self):
        self.present = 1
        self.draw_chessboard()
        # 定义棋盘阵列,无子:0,黑棋:1,白棋:-1
        self.positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.visit_positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.draw_chess()

    # 退出游戏
    def exit(self):
        pygame.quit()
        sys.exit()

    # 更改游戏棋盘
    def newgame1(self):
        self.mode_num = 13
        # 棋盘每格的边长
        self.interval = 360 * self.size / (self.mode_num - 1)
        # 相对九路棋盘的矫正比例
        self.p = 1 if self.mode_num == 9 else (2 / 3 if self.mode_num == 13 else 4 / 9)
        # 定义棋盘阵列,无子:0,黑棋:1,白棋:-1
        self.positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.visit_positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.photoBD = pygame.image.load("./Pictures/" + "BD" + "-" + str(self.mode_num) + ".png")  # .convert()
        self.photoWD = pygame.image.load("./Pictures/" + "WD" + "-" + str(self.mode_num) + ".png")  # .convert()

    # 更改游戏棋盘
    def newgame2(self):
        self.mode_num = 19
        # 棋盘每格的边长
        self.interval = 360 * self.size / (self.mode_num - 1)
        # 相对九路棋盘的矫正比例
        self.p = 1 if self.mode_num == 9 else (2 / 3 if self.mode_num == 13 else 4 / 9)
        # 定义棋盘阵列,无子:0,黑棋:1,白棋:-1
        self.positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.visit_positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.photoBD = pygame.image.load("./Pictures/" + "BD" + "-" + str(self.mode_num) + ".png")  # .convert()
        self.photoWD = pygame.image.load("./Pictures/" + "WD" + "-" + str(self.mode_num) + ".png")  # .convert()

8 显示

    def show(self):
        while True:
            pygame.display.flip()
            self.clock = pygame.time.Clock()
            # 绘制棋盘
            self.draw_chessboard()
            # 绘制棋子
            self.draw_chess()
            if not self.stop:
                # 棋子跟随鼠标移动
                self.chess_follow()
            # 监听所有事件
            for event in pygame.event.get():
                # 点击x则关闭窗口
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                # 点击窗口里面类容则完成相应指令
                elif event.type == MOUSEMOTION:
                    x, y = event.pos
                    self.get_mouse_current_position(x, y)
                elif event.type == MOUSEBUTTONDOWN:
                    # 鼠标点击按钮函数
                    self.start_button.handle_event(self.start)
                    self.pass_me_button.handle_event(self.passme)
                    self.regert_button.handle_event(self.regret)
                    self.restsrt_button.handle_event(self.reload)
                    self.thirteen_way_button.handle_event(self.newgame1)
                    self.nineteenth_way_button.handle_event(self.newgame2)
                    self.exit_button.handle_event(self.exit)
                    # 如果鼠标左键在棋盘内按下
                    if 20 * self.size < self.mouse_x < 380 * self.size and 20 * self.size < self.mouse_y < 380 * self.size:
                        self.getDown()
                        self.present = - self.present

9 初始化

class weiqi():
    def __init__(self):
        # 模式,九路棋:9,十三路棋:13,十九路棋:19
        self.mode_num = 9
        # 窗口尺寸设置,默认:1.8
        self.size = 1.8
        # 棋盘每格的边长
        self.interval = 360 * self.size / (self.mode_num - 1)
        # 相对九路棋盘的矫正比例
        self.p = 1 if self.mode_num == 9 else (2 / 3 if self.mode_num == 13 else 4 / 9)
        # 定义棋盘阵列,无子:0,黑棋:1,白棋:-1
        self.positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        self.visit_positions = [[0 for i in range(self.mode_num + 2)] for i in range(self.mode_num + 2)]
        # 当前轮到的玩家,黑:1,白:-1,执黑先行
        self.present = 1
        # 初始停止运行,点击“开始游戏”运行游戏
        self.stop = True
        # 悔棋次数,次数大于0才可悔棋,初始置0(初始不能悔棋),悔棋后置0,下棋或弃手时恢复为1,以禁止连续悔棋
        self.regretchance = 0
        self.mouse_x = -100
        self.mouse_y = -100
        self.last_1_positions = copy.deepcopy(self.positions)
        self.last_2_positions = copy.deepcopy(self.positions)
        self.last_3_positions = copy.deepcopy(self.positions)

        pygame.init()
        # 创建一个窗口
        self.screen = pygame.display.set_mode([600 * self.size, 400 * self.size])
        # 设置窗口标题
        pygame.display.set_caption("围棋")

        self.photoW = pygame.image.load("./Pictures/W.png")  # .convert()
        self.photoB = pygame.image.load("./Pictures/B.png")  # .convert()
        self.photoBD = pygame.image.load("./Pictures/" + "BD" + "-" + str(self.mode_num) + ".png")  # .convert()
        self.photoWD = pygame.image.load("./Pictures/" + "WD" + "-" + str(self.mode_num) + ".png")  # .convert()
        self.bg_rect = pygame.Surface((200 * self.size, 100 * self.size), flags=pygame.HWSURFACE)
        self.bg_rect.fill((51, 102, 153))

完整代码
https://download.csdn.net/download/m0_55818687/87698112

猜你喜欢

转载自blog.csdn.net/m0_55818687/article/details/130198528