Python3结合Qt5开发简易五子棋游戏完整教程

码头散步
码头散步
发布时间:2026-10-16| 4人浏览过
原创

本文详细介绍如何使用Python3和PyQt5库开发一个简易的五子棋游戏。文章从核心逻辑入手,讲解如何利用二维列表构建15x15棋盘,并通过遍历检查水平、垂直及对角线方向实现五子连珠判定。随后,重点解析Qt5的绘制机制,包括重写paintEvent方法绘制网格与棋子,以及通过mousePressEvent处理鼠标点击坐标转换。最后提供完整的GUI交互代码,涵盖开始、重开及胜负提示功能,适合Python初学者参考学习。

五子棋核心逻辑与棋盘构建

开发五子棋游戏时,首要任务是确立核心逻辑,即如何实现落子机制以及如何准确判定“五子连珠”,而非优先处理画面渲染。因此,第一步是构建棋盘数据结构。

标准五子棋使用15x15的棋盘,我们可以采用二维列表来模拟这一结构。在数据表示上,定义0代表空位,1代表白子,2代表黑子,以此区分不同状态。

在初始化棋盘时,需特别注意Python列表的复制机制,避免直接使用乘法运算符*进行复制,因为这会导致引用错误,应使用列表推导式或循环正确创建独立的二维数组。

self.chess_board = [[0 for i in range(15)] for i in range(15)]

落子操作相对简单,只需根据坐标更新对应索引的值即可。接下来的关键挑战在于如何高效且准确地判断是否形成五子连珠。

每次落子后,系统需立即检测胜负。对于新放置的棋子,其可能形成的连珠方向主要包含四种情况:

1. 水平方向 2. 右下斜方向 3. 垂直方向 4. 右上斜方向

判断逻辑以刚落子的坐标为起点,向前(或向左/上)遍历最多4个相邻棋子,统计连续同色棋子的数量,遇到异色或边界即停止;随后从起点向后(或向右/下)遍历剩余最多4个棋子,同样统计连续数量。若前后统计的总数达到5个,则判定获胜。需注意边界检查,确保遍历不超出棋盘范围。

五子连珠判定算法实现

以水平方向的判断为例,具体实现代码如下:

def judge_1(self,x:int,y:int) -> bool:
        count = 1
        if self.chess_board[x][y] != 0:
            for i in range(1,5):
                if y - i >= 0:
                    if self.chess_board[x][y] == self.chess_board[x][y-i]:
                        print(x,y-i)
                        count += 1
                    else:
                        break
                else:
                    break
            for i in range(1,5):
                if y + i <=14:
                    if self.chess_board[x][y] == self.chess_board[x][y+i]:
                        print(x,y+i)
                        count += 1
                    else:
                        break
                else:
                    break
        if count == 5:
            return True
        return False

采用相同的逻辑框架完成其余三个方向的判断函数,至此五子棋的核心算法逻辑已构建完毕。剩余工作是将这些逻辑与PyQt5图形界面结合,完成游戏的可视化呈现。

PyQt5图形界面与绘制实现

我们需要创建一个继承自QWidget的类来构建主窗口,并定义若干属性用于存储游戏状态数据,如当前玩家、棋子坐标及棋盘状态等。

#棋子的坐标
self.x = -1
self.y = -1
#区分玩家
#开始标签
self.flag = False
#储存已经下好的白子
self.white_chess = []
#储存已经下好的黑子
self.black_chess = []

在Qt5中进行图形绘制,需重写paintEvent方法,该方法由系统自动调用。在方法内部创建QPainter对象,通过begin()和end()方法包裹绘制指令,即可实现画面的渲染。

具体绘制中,使用drawLine方法绘制棋盘网格线,利用drawEllipse绘制棋子图形。通过setPen设置线条颜色与宽度,使用setBrush设置棋子的填充颜色,以区分黑白棋子。

以下是绘制棋盘与棋子的完整代码实现(注:此部分代码参考了现有资源,为作者初次接触Qt绘制功能的实践):

注意:在Qt GUI坐标系中,x轴通常竖直向下延伸,y轴水平向右延伸。因此,在绘制棋子时,需将逻辑坐标的x与y值进行交换处理,以匹配视觉显示。

#绘制棋盘与棋子
    def paintEvent(self, e) -> None:
        qp = QPainter()
        qp.begin(self)
        qp.fillRect(self.rect(), QColor("light blue"))
        qp.drawRect(self.rect())
        qp.setBackground(QColor("yellow"))
        qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))
        for i in range(15):
            qp.drawLine(QPoint(30, 30 + 30 * i), QPoint(450, 30 + 30 * i))
        for i in range(15):
            qp.drawLine(QPoint(30 + 30 * i, 30), QPoint(30 + 30 * i, 450))
        qp.setBrush(QColor(0, 0, 0))
        key_points = [(3, 3), (11, 3), (3, 11), (11, 11), (7, 7)]
        if len(self.black_chess) != 0:
            for t in self.black_chess:
                #画黑子
                qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
        for t in key_points:
            #棋盘的5个定点
            qp.drawEllipse(QPoint(30 + 30 * t[0], 30 + 30 * t[1]), 3, 3)
        qp.setBrush(QColor(255,255,255))
        if len(self.white_chess) != 0:
            for t in self.white_chess:
                #画白子
                qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
        qp.end()

鼠标事件处理与坐标转换

另一个关键问题是获取用户点击的棋子坐标。通过重写鼠标按下事件mousePressEvent,可以捕获鼠标位置。由于用户点击往往存在误差,不能精确落在交叉点上,因此需要计算点击位置距离最近网格点的坐标,以此确定实际落子点。

def mousePressEvent(self, e) -> None:
        if e.buttons() == QtCore.Qt.LeftButton:
            if e.x() > 15 and e.x() < 465 and e.y() > 15 and e.y() < 465:
                x = e.x()/30 - e.x()//30
                y = e.y()/30 - e.y()//30
                self.y = (e.x()-30)//30 if x < 0.5 else (e.x()-30)//30 + 1
                self.x = (e.y()-30)//30 if y < 0.5 else (e.y()-30)//30 + 1
                if self.flag:
                    print(self.x,self.y)
                    if self.player % 2 == 1:
                        if goBang.put_white_chess(self.x,self.y):
                            self.player += 1 
                            print('黑子行动')
                        else:
                            print('白子行动')
                        if goBang.judge(self.x,self.y):
                            msg_box = QMessageBox(QMessageBox.Information, '提示', '白子获胜!')
                            msg_box.exec_()
                    else:
                        if goBang.put_black_chess(self.x,self.y):
                            self.player += 1
                            print('白子行动')
                        else:
                            print('黑子行动')
                        if goBang.judge(self.x,self.y):
                            msg_box = QMessageBox(QMessageBox.Information, '提示', '黑子获胜!')
                            msg_box.exec_()

游戏重置与状态管理

游戏结束时,需提供重置功能。这包括将所有存储数据的变量(如玩家状态、棋子列表、棋盘数组)重新初始化为默认值,并调用更新方法重绘棋盘,以便开始新游戏。

#清除棋盘,重开游戏
    def clear(self) -> None:
        self.x = -1
        self.y = -1
        self.player = 0
        self.flag = False
        self.white_chess = []
        self.black_chess = []
        self.chess_board = [[0 for i in range(15)] for i in range(15)]
        self.update()

至此,五子棋游戏的主要功能模块已基本完成。

以下是整个项目的完整源代码:

from PyQt5 import *
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
 
class GoBang(QWidget):
    #初始化棋盘
    def __init__(self):
        super().__init__()
        self.setWindowTitle('五子棋Hi~ o(* ̄▽ ̄*)ブ')
        self.x = -1
        self.y = -1
        #区分玩家
        self.player = 0
        #开始标签
        self.flag = False
        #储存已经下好的白子
        self.white_chess = []
        #储存已经下好的黑子
        self.black_chess = []
        self.setFixedSize(800,600)
        self.chess_board = [[0 for i in range(15)] for i in range(15)]
        btn1 = QPushButton('开始',self)
        btn1.setGeometry(500,100,50,30)
        btn1.clicked.connect(self.setFlag)
        btn2 = QPushButton('重开',self)
        btn2.setGeometry(550,100,50,30)
        btn2.clicked.connect(self.clear)
        self.show()
    
    #绘制棋盘与棋子
    def paintEvent(self, e) -> None:
        qp = QPainter()
        qp.begin(self)
        qp.fillRect(self.rect(), QColor("light blue"))
        qp.drawRect(self.rect())
        qp.setBackground(QColor("yellow"))
        qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))
        for i in range(15):
            qp.drawLine(QPoint(30, 30 + 30 * i), QPoint(450, 30 + 30 * i))
        for i in range(15):
            qp.drawLine(QPoint(30 + 30 * i, 30), QPoint(30 + 30 * i, 450))
        qp.setBrush(QColor(0, 0, 0))
        key_points = [(3, 3), (11, 3), (3, 11), (11, 11), (7, 7)]
        if len(self.black_chess) != 0:
            for t in self.black_chess:
                #画黑子
                qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
        for t in key_points:
            #棋盘的5个定点
            qp.drawEllipse(QPoint(30 + 30 * t[0], 30 + 30 * t[1]), 3, 3)
        qp.setBrush(QColor(255,255,255))
        if len(self.white_chess) != 0:
            for t in self.white_chess:
                #画白子
                qp.drawEllipse(QPoint(30 + 30 * t[1], 30 + 30 * t[0]), 6, 6)
        qp.end()
 
    #更改标签,开始游戏
    def setFlag(self) -> None:
        self.flag = True
 
    def mousePressEvent(self, e) -> None:
        if e.buttons() == QtCore.Qt.LeftButton:
            if e.x() > 15 and e.x() < 465 and e.y() > 15 and e.y() < 465:
                x = e.x()/30 - e.x()//30
                y = e.y()/30 - e.y()//30
                self.y = (e.x()-30)//30 if x < 0.5 else (e.x()-30)//30 + 1
                self.x = (e.y()-30)//30 if y < 0.5 else (e.y()-30)//30 + 1
                if self.flag:
                    print(self.x,self.y)
                    if self.player % 2 == 1:
                        if goBang.put_white_chess(self.x,self.y):
                            self.player += 1 
                            print('黑子行动')
                        else:
                            print('白子行动')
                        if goBang.judge(self.x,self.y):
                            msg_box = QMessageBox(QMessageBox.Information, '提示', '白子获胜!')
                            msg_box.exec_()
                    else:
                        if goBang.put_black_chess(self.x,self.y):
                            self.player += 1
                            print('白子行动')
                        else:
                            print('黑子行动')
                        if goBang.judge(self.x,self.y):
                            msg_box = QMessageBox(QMessageBox.Information, '提示', '黑子获胜!')
                            msg_box.exec_()
 
            
    #下白子
    def put_white_chess(self,x:int,y:int) -> bool:
        if self.chess_board[x][y] != 0:
            msg_box = QMessageBox(QMessageBox.Information, '提示', '这个位置已经有棋子了!')
            msg_box.exec_()
            return False
        else:
            self.chess_board[x][y] = 1
            self.white_chess.append((x,y))
            self.update()
            return True
 
    #下黑子
    def put_black_chess(self,x:int,y:int) -> bool:
        if self.chess_board[x][y] != 0:
            msg_box = QMessageBox(QMessageBox.Information, '提示', '这个位置已经有棋子了!')
            msg_box.exec_()
            return False
        else:
            self.chess_board[x][y] = 2
            self.black_chess.append((x,y))
            self.update()
            return True
 
    #清除棋盘,重开游戏
    def clear(self) -> None:
        self.x = -1
        self.y = -1
        self.player = 0
        self.flag = False
        self.white_chess = []
        self.black_chess = []
        self.chess_board = [[0 for i in range(15)] for i in range(15)]
        self.update()
 
    #判断是否已经五子连珠
    def judge(self,x:int,y:int) -> bool:
        if self.judge_1(x,y) or self.judge_2(x,y) or self.judge_3(x,y) or self.judge_4(x,y):
            return True
        return False
 
    #判断横线
    def judge_1(self,x:int,y:int) -> bool:
        count = 1
        if self.chess_board[x][y] != 0:
            for i in range(1,5):
                if y - i >= 0:
                    if self.chess_board[x][y] == self.chess_board[x][y-i]:
                        print(x,y-i)
                        count += 1
                    else:
                        break
                else:
                    break
            for i in range(1,5):
                if y + i <=14:
                    if self.chess_board[x][y] == self.chess_board[x][y+i]:
                        print(x,y+i)
                        count += 1
                    else:
                        break
                else:
                    break
        if count == 5:
            return True
        return False
 
    #判断右下线
    def judge_2(self,x:int,y:int) -> bool:
        count = 1
        if self.chess_board[x][y] != 0:
            for i in range(1,5):
                if x-i >= 0 and y - i >= 0:
                    if self.chess_board[x][y] == self.chess_board[x-i][y-i]:
                        print(x-i,y-i)
                        count += 1
                    else:
                        break
                else:
                    break
            for i in range(1,5):
                if x + i <= 14 and y + i <= 14:
                    if self.chess_board[x][y] == self.chess_board[x+i][y+i]:
                        print(x+i,y+i)
                        count += 1
                    else:
                        break
                else:
                    break
        if count == 5:
            return True
        return False
 
    #判断竖线
    def judge_3(self,x:int,y:int) -> bool:
        count = 1
        if self.chess_board[x][y] != 0:
            for i in range(1,5):
                if x - i >= 0:
                    if self.chess_board[x][y] == self.chess_board[x-i][y]:
                        print(x-i,y)
                        count += 1
                    else:
                        break
                else:
                    break
            for i in range(1,5):
                if x + i <= 14:
                    if self.chess_board[x][y] == self.chess_board[x+i][y]:
                        print(x+i,y)
                        count += 1
                    else:
                        break
                else:
                    break
        if count == 5:
            return True
        return False
 
    #判断右上线
    def judge_4(self,x:int,y:int) -> bool:
        count = 1
        if self.chess_board[x][y] != 0:
            for i in range(1,5):
                if x - i >= 0 and y + i <= 14:
                    if self.chess_board[x][y] == self.chess_board[x-i][y+i]:
                        print(x-i,y+i)
                        count += 1
                    else:
                        break
                else:
                    break
            for i in range(1,5):
                if x + i <= 14 and y - i >= 0:
                    if self.chess_board[x][y] == self.chess_board[x+i][y-i]:
                        print(x+i,y-i)
                        count += 1
                    else:
                        break
                else:
                    break
        if count == 5:
            return True
        return False
 
#程序入口
if __name__ == '__main__':  
    app = QApplication(sys.argv)  
    goBang = GoBang()
    sys.exit(app.exec_())

以上就是本文的全部内容,希望能为读者的学习提供帮助,也欢迎大家继续支持本站获取更多技术资源。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。