您当前的位置: 首页 >  Python
  • 1浏览

    0关注

    265博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Python3+pygame中国象棋 代码完整 非常好 有效果演示

可可爱爱的程序员 发布时间:2021-11-18 18:36:40 ,浏览量:1

这几天看到抖音上有个妹子下象棋超级猛,我的中国象棋也差不到哪去啊,走 做一个。。。。 本中国象棋是用Python3+pygame实现的

一、运行效果

![](

二、代码

下面的代码用到图片素材(images文件夹),下载地​ 址如下:www.itprojects.cn/detail.html… ​

"""
作者:it项目实例网
网址:www.itprojects.cn
"""

import sys

import pygame

# 要显示的窗口的宽、高
WIDTH, HEIGHT = 750, 667


class ClickBox(pygame.sprite.Sprite):
    """
    选中棋子对象
    """
    singleton = None

    def __new__(cls, *args, **kwargs):
        if cls.singleton is None:
            cls.singleton = super().__new__(cls)
        return cls.singleton

    def __init__(self, screen, row, col, team):
        super().__init__()
        self.image = pygame.image.load("images/r_box.png")
        self.rect = self.image.get_rect()
        self.row, self.col = row, col
        self.rect.topleft = (50 + self.col * 57, 50 + self.row * 57)
        self.screen = screen
        self.team = team

    @classmethod
    def show(cls):
        if cls.singleton:
            cls.singleton.screen.blit(cls.singleton.image, cls.singleton.rect)

    @classmethod
    def clean(cls):
        """
        清理上次的对象
        """
        cls.singleton = None


class Dot(pygame.sprite.Sprite):
    """
    可落棋子类
    """
    group = list()

    def __init__(self, screen, position):
        super().__init__()
        self.image = pygame.image.load("images/dot2.png")
        self.rect = self.image.get_rect()
        self.row, self.col = position  # 将元组拆包
        self.rect.topleft = (60 + self.col * 57, 60 + self.row * 57)
        self.group.append(self)
        self.screen = screen

    @classmethod
    def show(cls):
        for dot in cls.group:
            dot.screen.blit(dot.image, dot.rect)

    @classmethod
    def clean_last_postion(cls):
        """
        清除上次落子位置
        """
        cls.group.clear()

    @classmethod
    def click(cls):
        """
        点击棋子
        """
        for dot in cls.group:
            if pygame.mouse.get_pressed()[0] and dot.rect.collidepoint(pygame.mouse.get_pos()):
                print("被点击了「可落子」对象")
                return dot


class Chess(pygame.sprite.Sprite):
    """
    棋子类
    """

    def __init__(self, screen, chess_name, row, col):
        self.screen = screen
        self.image = pygame.image.load("images/" + chess_name + ".png")
        self.rect = self.image.get_rect()
        self.rect.topleft = (50 + col * 57, 50 + row * 57)
        self.team = chess_name[0]  # 队伍(红方 r、黑方b)
        self.name = chess_name[2]  # 名字(炮p、马m等)
        self.row = row
        self.col = col

    def show(self):
        self.screen.blit(self.image, self.rect)

    @staticmethod
    def click(player, chesses):
        """
        点击棋子
        """
        for chess in chesses:
            if pygame.mouse.get_pressed()[0] and chess.rect.collidepoint(pygame.mouse.get_pos()):
                if player == chess.team:
                    print("被点击了")
                    return chess

    def update_postion(self, new_row, new_col):
        """
        更新要显示的图片的坐标
        """
        self.row = new_row
        self.col = new_col
        self.rect.topleft = (50 + new_col * 57, 50 + new_row * 57)


class ChessBoard(object):
    """
    棋盘类
    """

    def __init__(self, screen):
        self.screen = screen
        self.image = pygame.image.load("images/bg.png")
        self.topleft = (50, 50)
        self.__create_default_chess()

    def __create_default_chess(self):
        """
        创建默认棋子
        """
        self.map = [
            ["b_c", "b_m", "b_x", "b_s", "b_j", "b_s", "b_x", "b_m", "b_c"],
            ["", "", "", "", "", "", "", "", ""],
            ["", "b_p", "", "", "", "", "", "b_p", ""],
            ["b_z", "", "b_z", "", "b_z", "", "b_z", "", "b_z"],
            ["", "", "", "", "", "", "", "", ""],
            ["", "", "", "", "", "", "", "", ""],
            ["r_z", "", "r_z", "", "r_z", "", "r_z", "", "r_z"],
            ["", "r_p", "", "", "", "", "", "r_p", ""],
            ["", "", "", "", "", "", "", "", ""],
            ["r_c", "r_m", "r_x", "r_s", "r_j", "r_s", "r_x", "r_m", "r_c"],
        ]
        for row, line in enumerate(self.map):
            for col, chess_name in enumerate(line):
                if chess_name:
                    # 将创建的棋子添加到属性map中
                    self.map[row][col] = Chess(self.screen, chess_name, row, col)
                else:
                    self.map[row][col] = None

    def show(self):
        # 显示棋盘
        self.screen.blit(self.image, self.topleft)
        # 显示棋盘上的所有棋子
        for line_chess in self.map:
            for chess in line_chess:
                if chess:
                    chess.show()

    def get_put_down_postion(self, clicked_chess):
        """
        计算当前棋子可以移动的位置
        """
        # 存储当前棋子可以落子的位置
        all_position = list()
        # 拿到当前棋子的行、列
        row, col = clicked_chess.row, clicked_chess.col
        # 拿到当前棋子的team,即时红方r还是黑方b
        team = clicked_chess.team

        # 计算当前选中棋子的所有可以落子位置
        if clicked_chess.name == "p":  # 炮
            # 一行
            direction_left_chess_num = 0
            direction_right_chess_num = 0
            for i in range(1, 9):
                # 计算当前行中,棋子左边与右边可以落子的位置
                # 左边位置没有越界
                if direction_left_chess_num >= 0 and col - i >= 0:
                    if not self.map[row][col - i] and direction_left_chess_num == 0:
                        # 如果没有棋子,则将当前位置组成一个元组,添加到列表
                        all_position.append((row, col - i))
                    elif self.map[row][col - i]:
                        # 如果当前位置有棋子,那么就判断是否能够吃掉它
                        direction_left_chess_num += 1
                        if direction_left_chess_num == 2 and self.map[row][col - i].team != team:
                            all_position.append((row, col - i))
                            direction_left_chess_num = -1  # 让其不能够在下次for循环时再次判断
                # 右边位置没有越界
                if direction_right_chess_num >= 0 and col + i = 0 and row - i >= 0:
                    if not self.map[row - i][col] and direction_up_chess_num == 0:
                        # 如果没有棋子,则将当前位置组成一个元组,添加到列表
                        all_position.append((row - i, col))
                    elif self.map[row - i][col]:
                        # 如果当前位置有棋子,那么就判断是否能够吃掉它
                        direction_up_chess_num += 1
                        if direction_up_chess_num == 2 and self.map[row - i][col].team != team:
                            all_position.append((row - i, col))
                            direction_up_chess_num = -1

                # 下边位置没有越界
                if direction_down_chess_num >= 0 and row + i = 0:  # 只能向上移动
                    if not self.map[row - 1][col] or self.map[row - 1][col].team != team:
                        all_position.append((row - 1, col))
            else:  # 黑方
                if row + 1 = 0 and col + 1 = 0 and not self.map[row][col - 1]:  # 如果当前棋子没有被蹩马腿,那么再对这个方向的2个位置进行判断
                # 左上2(因为有左上了,暂且称为左上2吧)
                if row - 1 >= 0 and col - 2 >= 0 and (not self.map[row - 1][col - 2] or self.map[row - 1][col - 2].team != team):
                    all_position.append((row - 1, col - 2))
                # 左下2
                if row + 1 = 0 and (not self.map[row + 1][col - 2] or self.map[row + 1][col - 2].team != team):
                    all_position.append((row + 1, col - 2))
            # 右方
            if col + 1 = 0 and col + 2 = row_start and col + 2 = row_start and col + 1  general_row:
            return False
        elif (attack_row - general_row) ** 2 + (attack_col - general_col) ** 2 == 1:
            return True

    def get_general_position(self, general_player):
        """
        找到general_player标记的一方的将的位置
        """
        for row, line in enumerate(self.map):
            for col, chess in enumerate(line):
                if chess and chess.team == general_player and chess.name == "j":
                    return chess.row, chess.col

    def judge_win(self, attack_player):
        """
        判断是否获胜
        """
        # 依次判断是否被攻击方的所有棋子,是否有阻挡攻击的可能
        for line_chesses in self.map:
            for chess in line_chesses:
                if chess and chess.team != attack_player:
                    move_position_list = self.get_put_down_postion(chess)
                    if move_position_list:  # 只要找到一个可以移动的位置,就表示没有失败,还是有机会的
                        return False

        return True


class Game(object):
    """
    游戏类
    """

    def __init__(self, screen):
        self.screen = screen
        self.player = "r"  # 默认走棋的为红方r
        self.player_tips_r_image = pygame.image.load("images/red.png")
        self.player_tips_r_image_topleft = (550, 500)
        self.player_tips_b_image = pygame.image.load("images/black.png")
        self.player_tips_b_image_topleft = (550, 100)
        self.show_attack = False
        self.show_attack_count = 0
        self.show_attack_time = 100
        self.attack_img = pygame.image.load("images/pk.png")
        self.show_win = False
        self.win_img = pygame.image.load("images/win.png")
        self.win_player = None

    def get_player(self):
        """
        获取当前走棋方
        """
        return self.player

    def exchange(self):
        """
        交换走棋方
        """
        self.player = "r" if self.player == "b" else "b"
        return self.get_player()

    def show(self):
        if self.show_win:
            if self.win_player == "b":
                self.screen.blit(self.win_img, (550, 100))
            else:
                self.screen.blit(self.win_img, (550, 450))
            return

        # 通过计时,实现显示一会"将军"之后,就消失
        if self.show_attack:
            self.show_attack_count += 1
            if self.show_attack_count == self.show_attack_time:
                self.show_attack_count = 0
                self.show_attack = False

        if self.player == "r":
            self.screen.blit(self.player_tips_r_image, self.player_tips_r_image_topleft)
            # 显示"将军"效果
            if self.show_attack:
                self.screen.blit(self.attack_img, (230, 400))
        else:
            self.screen.blit(self.player_tips_b_image, self.player_tips_b_image_topleft)
            # 显示"将军"效果
            if self.show_attack:
                self.screen.blit(self.attack_img, (230, 100))

    def set_attack(self):
        """
        标记"将军"效果
        """
        self.show_attack = True

    def set_win(self, win_player):
        """
        设置获胜方
        """
        self.show_win = True
        self.win_player = win_player


def main():
    # 初始化pygame
    pygame.init()
    # 创建用来显示画面的对象(理解为相框)
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    # 游戏背景图片
    background_img = pygame.image.load("images/bg.jpg")
    # 创建游戏对象
    game = Game(screen)
    # 创建一个游戏棋盘对象
    chess_board = ChessBoard(screen)
    # 创建计时器
    clock = pygame.time.Clock()

    # 主循环
    while True:
        # 事件检测(例如点击了键盘、鼠标等)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()  # 退出程序

            # 如果游戏没有获胜方,则游戏继续,否则一直显示"获胜"
            if not game.show_win:
                # 检测是否点击了"可落子"对象
                clicked_dot = Dot.click()
                if clicked_dot:
                    chess_board.move_chess(clicked_dot.row, clicked_dot.col)
                    # 清理「点击对象」、「可落子位置对象」
                    Dot.clean_last_postion()
                    ClickBox.clean()
                    # 判断此棋子走完之后,是否"将军"
                    if chess_board.judge_attack_general(game.get_player()):
                        # 检测对方是否可以挽救棋局,如果能挽救,就显示"将军",否则显示"胜利"
                        if chess_board.judge_win(game.get_player()):
                            game.set_win(game.get_player())
                        else:
                            # 如果攻击到对方,则标记显示"将军"效果
                            game.set_attack()
                    # 落子之后,交换走棋方
                    game.exchange()
                # 检查是否点击了棋子
                clicked_chess = Chess.click(game.get_player(), [chess for line in chess_board.map for chess in line if chess])
                if clicked_chess:
                    # 创建选中棋子对象
                    ClickBox(screen, clicked_chess.row, clicked_chess.col, clicked_chess.team)
                    # 清除之前的所有的可以落子对象
                    Dot.clean_last_postion()
                    # 真的点击了棋子,那么计算当前被点击的棋子可以走的位置
                    all_position = chess_board.get_put_down_postion(clicked_chess)
                    if all_position:
                        # 清空上次可落子对象
                        Dot.clean_last_postion()
                        # 创建可落子对象
                        for position in all_position:
                            Dot(screen, position)

        # 显示游戏背景
        screen.blit(background_img, (0, 0))
        screen.blit(background_img, (0, 270))
        screen.blit(background_img, (0, 540))

        # 显示棋盘以及棋盘上的棋子
        chess_board.show()

        # 显示被点击的棋子
        ClickBox.show()

        # 显示可落子对象
        Dot.show()

        # 显示游戏相关信息
        game.show()

        # 显示screen这个相框的内容(此时在这个相框中的内容像照片、文字等会显示出来)
        pygame.display.update()

        # FPS(每秒钟显示画面的次数)
        clock.tick(60)  # 通过一定的延时,实现1秒钟能够循环60次


if __name__ == '__main__':
    main()
关注
打赏
1656598245
查看更多评论
立即登录/注册

微信扫码登录

0.1087s