[Python][pygame][五子棋]基于Python中pygame库的五子棋小游戏

1、摘要

  通过python的pygame库,实现一款支持人机对战和人人对战的五子棋小游戏。在游戏初始界面会有5秒的规则介绍,五秒后自动进入人机对战模式。玩家可根据游戏中的提示按下Q键切换为初始人人对战对接,按下E键切换为初始人机对战界面。在游戏进行过程中,程序会记录并显示黑白两子的累计获胜局数,提高玩家体验。

完整代码:https://download.csdn.net/download/weixin_45817309/14989460

2、程序框架

2.1 board.py 负责棋盘类的实现

2.1.1 _int_:构造函数,定义棋盘成员

2.1.2 _getBoard(self):返回_board成员

2.1.3 ifDropChess(self, point):判断是否可以落子

2.1.4 countDirection(self, point, value, offsetX, offsetY):判断横竖撇捺四个方向是否达成五子连珠

2.1.5win(self, point):判断是否胜利

2.1.6dropChess(self, chessMan, point):落子

2.2 machine.py 负责人机对战中机器类的实现

2.2.1 _int_:构造函数,定义机器类成员

2.2.2 getRivalDrop(self, point):得到对手落子位置

2.2.3 getPiece(self, point, offsetX, offsetY, TorF):判断所给位置方向上两格内的落子情况

2.2.4 getDirectionScore(self, point, offsetX, offsetY):统计所给方向上的棋子权重值

2.2.5 getPointScore(self, point):统计机器落子优先级

2.2.6 machineDrop(self):机器落子

2.3 五子棋.py 负责串联起整个程序的实现

2.3.1 printText(screen, font, x, y, text, textColor = (255, 255, 255)):文字打印函数:在屏幕的(x,y)处打印文字,文字颜色默认为白色

2.3.2 drawBoard(screen):刻画棋盘

2.3.3 drawChess(screen, Point, pieceColor):绘制棋子

2.3.4 drawChessInformation(screen, pos, color):绘制填充的圆形

2.3.5 getNextRunner(currentRunner):返回下一个执子方

2.3.6 drawInfomation(screen, font, currentRunner, SumOfBlackWin, SumOfWhiteWin):绘制信息栏

2.3.7 getClick(clickPlace):获取鼠标点击位置

2.3.8 main():主函数

3、关键代码实现

3.1 getDirectionScore(self, point, offsetX, offsetY)

3.1.1基本思想

  先设定偏移量offset分别为(1, 0), (0, 1), (1, 1), (1, -1),便于统计横竖撇捺四个方向上的棋子权重值。再根据每个方向上是否有棋子、棋子的种类和个数、棋子之间的空格数分别设定不同的权重值。根据权重值可以实现机器与人对战的效果。

3.1.2具体实现

    # 统计某方向棋子权重值
    def getDirectionScore(self, point, offsetX, offsetY):
        countSelf = 0   # 落子处我方连续子数
        countOpposite = 0  # 落子处对方连续子数
        spaceSelf = None   # 我方连续子中有无空格
        spaceOpposite = None  # 对方连续子中有无空格
        blockSelf = 0    # 我方连续子两端有无阻挡
        blockOpposite = 0   # 对方连续子两端有无阻挡

        # 如果是 1 表示是边上是我方子,2 表示敌方子, 0表示无子
        flagPositive = self.getPiece(point, offsetX, offsetY, True)
        if flagPositive != 0: # 传入的偏移方向上若存在棋子
            for i in range(1, 6): # 循环判断该方向连着几个棋子
                x = point.X + i * offsetX 
                y = point.Y + i * offsetY 
                # 若加上偏移量后仍在棋盘内
                if 0 <= x < self._pointNumber and 0 <= y < self._pointNumber:
                    if flagPositive == 1: # 若该偏移方向两格内有我方棋子
                        if self._board[y][x] == self._my.Value: #若该位置有我方棋子
                            countSelf += 1 # 我方连续棋子数+1
                            if spaceSelf is False: # 若已经出现过空格,且探测到我方棋子
                                spaceSelf = True  # 空格出现在我方连续棋子之间
                        elif self._board[y][x] == self._rival.Value: # 若该位置是敌方棋子
                            blockOpposite += 1 # 敌方棋子受阻挡+1
                            break # 落子后我方安全,跳出循环
                        else: # 若该位置不存在棋子
                            if spaceSelf is None: # 第一次检测到空格时生效
                                spaceSelf = False # 表示存在空格但不在白子之间
                            else:
                                break   # 遇到第二个空格退出循环
                    elif flagPositive == 2: # 若该偏移方向上有敌方棋子
                        if self._board[y][x] == self._my.Value:
                            blockOpposite += 1 # 敌方受阻挡+1
                            break # 我方安全,跳出循环
                        elif self._board[y][x] == self._rival.Value: # 该位置存在敌方棋子
                            countOpposite += 1 # 敌方连续棋子数+1
                            if spaceOpposite is False: # 若第二次出现空格
                                spaceOpposite = True # 对方连续棋子内出现空格事件为
                        else:
                            if spaceOpposite is None: # 若第一次出现空格
                                spaceOpposite = False # 敌方连续棋子内还未出现空格
                            else:
                                break # 若在出现敌方棋子前又出现空格,我方安全,跳出循环
                else: # 偏移后触碰到棋盘边界
                    if flagPositive == 1: # 若为己方棋子
                        blockSelf += 1 # 己方棋子被堵塞量+1
                    elif flagPositive == 2: # 若为敌方棋子
                        blockOpposite += 1 # 敌方棋子被堵塞量+1

        if spaceSelf is False: # 若己方连续棋子内不存在空格
            spaceSelf = None # 重置
        if spaceOpposite is False: # 若对方连续棋子内不存在空格
            spaceOpposite = None # 重置

        # 将设定的偏移量里的X,Y增量取反,重复上述操作
        flagNegative = self.getPiece(point, -offsetX, -offsetY, True)
        if flagNegative != 0:
            for i in range(1, 6):
                x = point.X - i * offsetX
                y = point.Y - i * offsetY
                if 0 <= x < self._pointNumber and 0 <= y < self._pointNumber:
                    if flagNegative == 1:
                        if self._board[y][x] == self._my.Value:
                            countSelf += 1
                            if spaceSelf is False:
                                spaceSelf = True
                        elif self._board[y][x] == self._rival.Value:
                            blockOpposite += 1
                            break
                        else:
                            if spaceSelf is None:
                                spaceSelf = False
                            else:
                                break   # 遇到第二个空格退出
                    elif flagNegative == 2:
                        if self._board[y][x] == self._my.Value:
                            blockOpposite += 1
                            break
                        elif self._board[y][x] == self._rival.Value:
                            countOpposite += 1
                            if spaceOpposite is False:
                                spaceOpposite = True
                        else:
                            if spaceOpposite is None:
                                spaceOpposite = False
                            else:
                                break
                else:
                    if flagNegative == 1:
                        blockSelf += 1
                    elif flagNegative == 2:
                        blockOpposite += 1
        '''
        权重值划分:
        (己方连续四子>敌方连续四子)>(己方连续三子无阻挡>敌方连续三子无阻挡)>(己方连续三子有一个阻挡&&己方连续两子无阻挡
        >敌方连续三子有阻挡&&敌方连续两子无阻挡)>(己方连续两子有阻挡>敌方连续两子有阻挡)
        无空格>有空格,两种情况应在同一数量级(紧跟在括号后)
        优先级量化 8 10 80 100 800 1000 8000 10000 五组(当数值相近的时候会变成人工智障,不知为啥)
        '''
        score = 0 # 初始化权重值,判断落子选择的优先级
        if countSelf == 4: # 若己方连续四子
            score = 10000 # 优先级参考备注
        elif countOpposite == 4: # 若敌方连续四子
            score = 8000 # 优先级参考备注
        elif countSelf == 3: # 若我方连续三子
            if blockSelf == 0: # 若我方连续三子无阻挡
                score = 1000 # 优先级参考备注
            elif blockSelf == 1: # 若我方连续三子中有一个阻挡
                score = 100 # 优先级参考备注
            else:
                score = 0 # 优先级最低
        elif countOpposite == 3: # 若敌方连续三子
            if blockOpposite == 0: # 若敌方连续三子无阻挡
                score = 800 # 优先级参考备注
            elif blockOpposite == 1: # 若敌方连续三子中有一个阻挡
                score = 80 # 优先级参考备注
            else: 
                score = 0 # 优先级最低
        elif countSelf == 2: # 若己方连续两子
            if blockSelf == 0: # 若己方两子间没有阻挡
                score = 100 # 优先级参考备注
            elif blockSelf == 1: # 若两子间有一个阻挡
                score = 80 # 优先级参考备注
            else:
                score = 0 # 优先级最低
        elif countOpposite == 2: # 若敌方连续两子
            if blockOpposite == 0: # 若敌方两子间没有阻挡
                score = 10 # 优先级参考备注
            elif blockOpposite == 1: # 若敌方两子间有一个阻挡
                score = 8 # 优先级参考备注
            else: 
                score = 0 # 优先级为0
        elif countSelf == 1: # 若己方只有单个落子
            score = 10 # 优先级参考备注
        elif countOpposite == 1: # 若对方只有单个落子
            score = 8 # 优先级参考备注
        else:
            score = 0 # 优先级最低

        if spaceSelf or spaceOpposite: # 若己方或对方连续棋子内存在空格
            score /= 2 # 优先级降低

        return score # 返回优先级

3.2 countDirection(self, point, value, offsetX, offsetY)

3.2.1基本思想

  设定循环,判断棋子的横竖撇捺八个方向是否存在五个相同的棋子,若存在,则获胜。

3.2.2具体实现

 #通过横竖撇捺四个方向计算是否五子连珠
    def countDirection(self, point, value, offsetX, offsetY):
        count = 1 # 计算连珠个数
        
        # 判断所下棋子右侧是否五子连珠
        for i in range(1, 5):
            x = point.X + i*offsetX
            y = point.Y + i*offsetY
            if 0 <= x < self._linePoints \
            and 0 <= y < self._linePoints \
            and value == self._board[y][x]:
                count += 1
            else:
                break
            
        # 判断所下棋子左侧是否五子连珠
        for i in range(1, 5):
            x = point.X - i*offsetX
            y = point.Y - i*offsetY
            if 0 <= x < self._linePoints \
                and 0 <= y < self._linePoints \
                and value == self._board[y][x]:
                count += 1
            else:
                break
            
        judgeWin = (count >= 5) # 判断是否达成五子连珠,达成为True
        return judgeWin # 返回判断结果

3.3 getClick(clickPlace)

3.2.1基本思想

  通过pygame库获取鼠标的点击位置,并根据棋盘建立时设置的长、宽、高将其转换为棋盘上的坐标,从而方便进行棋子的绘制。

3.2.2具体实现

# 获取鼠标点击位置,传入参数为pygame库获取的鼠标点击位置
def getClick(clickPlace): 
    placeX = clickPlace[0] - BOARD_START_PLACE # 点击的位置在棋盘中的横坐标
    placeY = clickPlace[1] - BOARD_START_PLACE # 点击的位置在棋盘中的纵坐标
    if placeX < -INSIDE_WIDTH or placeY < -INSIDE_WIDTH: # 若越界
        return None
    x = placeX // POINT_SIZE # 以棋子的大小为单位计算
    y = placeY // POINT_SIZE
    
    # 修正点击位置,当用户点击位置与交点有偏差时自动修正
    if placeX % POINT_SIZE > PIECE_RADIUS_LEFT: 
        x += 1
    if placeY % POINT_SIZE > PIECE_RADIUS_LEFT:
        y += 1
    if x >= POINT_NUMBER or y >= POINT_NUMBER: # 恰好在中间位置时不修正
        return None
    return Point(x, y) # 返回游戏区的坐标

4、不足

  在代码的编写过程中,由于许多基础知识的不熟练,导致了很多基础语法都需要上网去查相关的用法。此外,虽然对人机对战中的机器人进行了很多版的迭代,也参考了网络上前人使用的设置权值的方式进行判断,但是还是能够比较轻松地胜过电脑。对于电脑落子的算法还有很大提升空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值