Python3结合Qt5开发简易五子棋游戏完整教程
本文详细介绍如何使用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_())以上就是本文的全部内容,希望能为读者的学习提供帮助,也欢迎大家继续支持本站获取更多技术资源。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









