五子棋界面UI代码,基于PyQt5实现
本帖最后由 Stubborn 于 2021-11-30 16:33 编辑如果你想写一个五子棋游戏,又不想写界面代码,那么你可以拿走这篇代码,去实现你的五子棋游戏。
[*]你需要实现电脑的落子算法(博弈树搜索算法),让你的程序更聪明
[*]实现电脑落子算法应该是实现电脑下一步棋子落子在哪里
[*]你应该完善的代码位于PWidget类中的Ai_move函数
[*]PWidget中的record属性,存放的是当前棋盘的额落子数据,建议你应该根据这个数据,初始化你的数据结构或者博弈树。
[*]在使用record前,请确认列表存放的点,和UI界面中的点的相对位置,以便更好的实现你的搜索算法。
[*]保存好图片,请确认图片的相关路径(在PWidget类中的pice属性修改图片路径),避免报错
黑白棋子图片
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '011.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.Do not edit this file unless you know what you are doing.
from PyQt5.QtGui import QPainter, QPen, QColor, QPixmap, QFont
from PyQt5.QtCore import Qt, QPoint, QSize, QRect, QMetaObject, QCoreApplication
from PyQt5.QtWidgets import (
QMessageBox, QComboBox, QLabel, QWidget, QPushButton, QTextEdit, QMenuBar, QStatusBar, QMainWindow,
)
class LaBel(QLabel):
def __init__(self, parent):
super().__init__(parent)
self.setMouseTracking(True)
def enterEvent(self, e):
e.ignore()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1000, 550)
MainWindow.setMinimumSize(QSize(1000, 550))
MainWindow.setMaximumSize(QSize(1000, 550))
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
# TODO 复选框
self.SelectComboBox = QComboBox(self.centralwidget)
self.SelectComboBox.setGeometry(QRect(890, 210, 90, 23))
self.SelectComboBox.setObjectName('SelectComboBox')
self.SelectComboBox.addItems(['执黑先手', '执白后手'])
self.AlgorithmComboBox = QComboBox(self.centralwidget)
self.AlgorithmComboBox.setGeometry(QRect(890, 240, 90, 23))
self.AlgorithmComboBox.setObjectName('AlgorithmComboBox')
self.AlgorithmComboBox.addItems(['算法选择-未装载', 'MaxMin', 'Alpha-Beta', 'MCTS'])
# TODO 按钮
self.AgoButton = QPushButton(self.centralwidget)
self.AgoButton.setGeometry(QRect(890, 90, 90, 23))
self.AgoButton.setObjectName("AgoButton")
self.NewGameButton = QPushButton(self.centralwidget)
self.NewGameButton.setGeometry(QRect(890, 10, 90, 23))
self.NewGameButton.setObjectName("NewGameButton")
self.AfterButton = QPushButton(self.centralwidget)
self.AfterButton.setGeometry(QRect(890, 130, 90, 23))
self.AfterButton.setObjectName("AfterButton")
self.FirstButton = QPushButton(self.centralwidget)
self.FirstButton.setGeometry(QRect(890, 50, 90, 23))
self.FirstButton.setObjectName("FirstButton")
self.EndButton = QPushButton(self.centralwidget)
self.EndButton.setGeometry(QRect(890, 170, 90, 23))
self.EndButton.setObjectName("EndButton")
# TODO 标签横竖轴
font = QFont()
font.setFamily("Arial")
font.setPointSize(12)
font.setBold(True)
font.setWeight(75)
self.y_axis = []
for i in range(1, 16):
lable = QLabel(self.centralwidget)
lable.setGeometry(QRect(330, -4 + i * 30, 50, 12))
lable.setFont(font)
self.y_axis.append((str(i), lable))
self.x_axis = []
for i, a in zip(range(1, 16), 'ABCDEFGHIJKLMNO'):
lable = QLabel(self.centralwidget)
lable.setGeometry(QRect(344 + i * 30, 495, 50, 12))
lable.setFont(font)
self.x_axis.append((a, lable))
# TODO 按钮接口
MainWindow.setCentralWidget(self.centralwidget)
self.menubar =QMenuBar(MainWindow)
self.menubar.setGeometry(QRect(0, 0, 1000, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.NewGameButton.clicked.connect(MainWindow.newgame)# type: ignore
self.FirstButton.clicked.connect(MainWindow.first)# type: ignore
self.AgoButton.clicked.connect(MainWindow.ago)# type: ignore
self.AfterButton.clicked.connect(MainWindow.after)# type: ignore
self.EndButton.clicked.connect(MainWindow.end)# type: ignore
QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.AgoButton.setText(_translate("MainWindow", "前一步"))
self.NewGameButton.setText(_translate("MainWindow", "新游戏"))
self.AfterButton.setText(_translate("MainWindow", "后一步"))
self.FirstButton.setText(_translate("MainWindow", "首步"))
self.EndButton.setText(_translate("MainWindow", "末步"))
for val, obj in self.y_axis:
obj.setText(_translate("MainWindow", val))
for val, obj in self.x_axis:
obj.setText(_translate("MainWindow", val))
class CornerWidget(QWidget):
def __init__(self, parent):
"""跟随鼠标移动的特殊标记"""
super().__init__(parent=parent)
self.setFixedSize(30, 30)
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
pen = QPen(Qt.red, 3, Qt.SolidLine)
qp.setPen(pen)
qp.drawLine(0, 8, 0, 0)
qp.drawLine(0, 0, 8, 0)
qp.drawLine(22, 0, 28, 0)
qp.drawLine(28, 0, 28, 8)
qp.drawLine(28, 22, 28, 28)
qp.drawLine(28, 28, 20, 28)
qp.drawLine(8, 28, 0, 28)
qp.drawLine(0, 28, 0, 22)
class PWidget(QWidget):
def __init__(self, parent=None, *args, **kwargs):
"""
init_ui :: 初始化界面函数
last_pos :: 初始点位,记录位置是否发生变化
PLAYER :: AiW白子,B黑子 ,黑先手
GAME :: 是否开始游戏,如果开始游戏,mousePressEvent可以进行落子触发
black :: 黑棋子
white :: 白棋子
record :: 落子记录
pix_record :: 存放棋子,QLabel对象
tmp :: 临时存放,主要用与进退步数临时数据,QLabel对象
"""
super().__init__(parent, *args, **kwargs)
# TODO 消息框
self.TextEdit = QTextEdit(parent)
self.TextEdit.setGeometry(QRect(0, 0, 321, 501))
self.TextEdit.setObjectName("TextEdit")
self.init_ui()
self.last_pos = (-1, -1)
self.piece = {
"B": QPixmap('img/black.png'),
"W": QPixmap('img/white.png')
}
self.PLAYER = "W"
self.Ai = "B"
self.GAME = False
self.record = []
self.record_tmp = []
self.pix_record = []
self.pix_tmp = []
def init_ui(self):
# TODO 开启鼠标位置的追踪。并在鼠标位置移动时,使用特殊符号标记当前的位置
self.setMouseTracking(True)
self.corner = CornerWidget(self)
self.corner.repaint()
self.corner.hide()
def draw(self, x, y, p):
"""绘制棋子及其数字"""
# TODO 绘制棋子
label = LaBel(self)
label.setVisible(True)
label.setScaledContents(True)
label.setPixmap(self.piece)
label.setGeometry(15 + 30 * x, 15 + 30 * y, 30, 30)
# TODO 数字字体设置
font = QFont()
font.setFamily("Arial")
font.setPointSize(12)
font.setBold(True)
font.setWeight(75)
# TODO 绘制数字
num = LaBel(self)
num.setGeometry(QRect(15 + 30 * x, 15 + 30 * y, 30, 30))
num.setFont(font)
num.setVisible(True)
num.setScaledContents(True)
if p == "W":
num.setStyleSheet("color: black")
else:
num.setStyleSheet("color: white")
num.setAlignment(Qt.AlignCenter)
num.setNum(len(self.record))
num.setGeometry(15 + 30 * x, 15 + 30 * y, 30, 30)
# TODO 存储对象
self.pix_record.append()
def Ai_move(self):
"""AI移动"""
# TODO 这里实现你的AI移动函数, 可以传递 self.record 给你的算法,计算出当前五子棋下一步的着落点
# 我这里个人建议给你,是通过self.record来构建你的数据结构或者博弈树进行搜索最佳着落点,可以不用管前端的任何操作。
# 当然你可以通过自己的想法给界面添加功能
# TODO ===================================
from random import randint
x, y = randint(0, 14), randint(0, 14)
while x + y * 15 in self.record:
x, y = randint(0, 14), randint(0, 14)
# TODO 如果需要想界面输入提示信息
self.TextEdit.append(f"电脑落子在<{x}-{y}>\n")
# TODO=================================
# TODO 如果当前是白棋落子
if len(self.record) % 2:
self.draw(x, y, 'W')
# TODO 如果当前是黑棋落子
else:
self.draw(x, y, 'B')
self.record.append(x + y * 15)
def paintEvent(self, e):
"""绘制棋盘"""
qp = QPainter()
qp.begin(self)
qp.fillRect(self.rect(), QColor("green"))
qp.drawRect(self.rect())
qp.setBackground(QColor("green"))
qp.setPen(QPen(QColor(0, 0, 0), 2, Qt.SolidLine))
# TODO 绘制纵横线
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))
# TODO 绘制棋盘中心的黑点
qp.setBrush(QColor(0, 0, 0))
key_points = [(3, 3), (11, 3), (3, 11), (11, 11), (7, 7)]
for t in key_points:
qp.drawEllipse(QPoint(30 + 30 * t, 30 + 30 * t), 5, 5)
def mouseMoveEvent(self, e):
"""
根据鼠标轨迹,绘制特殊标记
"""
# TODO 获取鼠标坐标, 此工作区域为{350, 0, 480, 480}
mouse_x = e.windowPos().x()
mouse_y = e.windowPos().y()
# TODO 判断鼠标位置,如果在区域内{(350, 0)(830, 0)(350, 460)(830, 460)}
if 365 < mouse_x < 815 and 15 < mouse_y < 445:
game_x, game_y = int((mouse_x - 365) // 30), int((mouse_y - 15) // 30)
else:
game_x = -1
game_y = -1
# TODO 如果发生变化
change = False
if game_x != self.last_pos or game_y != self.last_pos:
change = True
self.last_pos = (game_x, game_y)
# TODO 如果发生变化根据鼠标位置的变化,绘制特殊标记
if change and game_x != -1:
self.setCursor(Qt.PointingHandCursor)
if change and game_x == -1:
self.setCursor(Qt.ArrowCursor)
if change and game_x != -1:
self.corner.move(15 + game_x * 30, 15 + game_y * 30)
self.corner.show()
if change and game_x == -1:
self.corner.hide()
def mousePressEvent(self, e):
"""
根据鼠标的动作,确定是否落子,并确定位置,绘制棋子
"""
# TODO 点击后一步的时候,不是你落子判断
if len(self.record) % 2 and self.PLAYER == 'B':
QMessageBox.about(self, '温馨提示', '当前不是你落子,请点击下一步')
return
elif self.PLAYER == 'W':
QMessageBox.about(self, '温馨提示', '当前不是你落子,请点击下一步')
return
if e.button() == Qt.LeftButton and self.GAME:
# 1. 首先判断按下了哪个格子
mouse_x = e.windowPos().x()
mouse_y = e.windowPos().y()
# TODO 判断鼠标位置,如果在区域内{(350, 0)(830, 0)(350, 460)(830, 460)}
if 365 < mouse_x < 815 and 15 < mouse_y < 445:
game_x, game_y = int((mouse_x - 365) // 30), int((mouse_y - 15) // 30)
else:
return
# TODO 玩家是否可以落子
res = game_x + game_y * 15
if res in self.record:
return
# TODO 玩家落子
self.record_tmp.clear()
self.pix_tmp.clear()
self.draw(game_x, game_y, self.PLAYER)
self.record.append(res)
# TODO 电脑落子
self.Ai_move()
else:
QMessageBox.about(self, '温馨提示', '请点击<新游戏>开始')
class Gomoku(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.debugging = False
self.setupUi(self)
self.setwidget()
def setwidget(self):
self.widget = PWidget(self.centralwidget)
self.widget.setGeometry(QRect(350, 0, 480, 480))
def newgame(self):
"""
开始新的游戏
:return: None
"""
# TODO 游戏开始
self.widget.GAME = True
# TODO 清理数据
self.widget.record.clear()
self.widget.record_tmp.clear()
# TODO 清理图像
self.widget.pix_record += self.widget.pix_tmp
for pix, num in self.widget.pix_record:
pix.setPixmap(QPixmap(""))
num.setPixmap(QPixmap(""))
self.widget.pix_record.clear()
self.widget.pix_tmp.clear()
# TODO 获取选选择
select = self.SelectComboBox.currentText()
self.widget.PLAYER, self.widget.Ai = ["B", "W"] if select == "执黑先手" else ["W", "B"]
if self.widget.Ai == "B":
self.widget.Ai_move()
def first(self):
"""回到首步"""
# TODO 回到首步前提是落子至少超过2个
if len(self.widget.pix_record) >= 2:
# TODO 开启调试模式
self.debugging = True
# TODO 对图像处理
self.widget.pix_tmp = self.widget.pix_record
self.widget.pix_record = , ]
for pix, num in self.widget.pix_tmp:
pix.setVisible(False)
num.setVisible(False)
# # TODO 对落点数据处理
self.widget.record_tmp = self.widget.record
self.widget.record = , ]
return
QMessageBox.about(self, '温馨提示', '当前已经是第一步')
def ago(self):
"""前一步"""
# TODO 如果还可以返回上一步
if len(self.widget.pix_record) > 1:
# # TODO 对图像处理,对落点数据处理
pix, num = self.widget.pix_record.pop()
pix.setVisible(False)
num.setVisible(False)
self.widget.pix_tmp.insert(0, )
self.widget.record_tmp.insert(0, self.widget.record.pop())
# # TODO 第二轮
pix, num = self.widget.pix_record.pop()
pix.setVisible(False)
num.setVisible(False)
self.widget.pix_tmp.insert(0, )
self.widget.record_tmp.insert(0, self.widget.record.pop())
return
QMessageBox.about(self, '温馨提示', '当前已经是第一步')
def after(self):
"""后一步"""
# TODO 如果图像临时数据还有数据,那么从临时数据’回档‘
if self.widget.pix_tmp:
# TODO 对图像处理
pix, num = self.widget.pix_tmp.pop(0)
pix.setVisible(True)
num.setVisible(True)
self.widget.pix_record.append()
# # TODO 对落点数据处理
self.widget.record.append(self.widget.record_tmp.pop(0))
return
# TODO 如果图像临时数据没有数据,那么通过电脑计算下一步
self.widget.Ai_move()
def end(self):
"""回到最后一步"""
# # TODO 前提是临时数据还有数据,否则当前已经是最后一步
if self.widget.pix_tmp:
# TODO 对图像处理,显示图像
while self.widget.pix_tmp:
pix, num = self.widget.pix_tmp.pop(0)
pix.setVisible(True)
num.setVisible(True)
self.widget.pix_record.append()
# # TODO 对落点数据处理
self.widget.record += self.widget.record_tmp
return
QMessageBox.about(self, '温馨提示', '当前已经是最后一步')
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
game = Gomoku()
game.show()
sys.exit(app.exec_())
牛 hrpzcf 发表于 2021-11-30 07:32
牛
多亏大佬指点{:5_109:} 本帖最后由 王力宏本人 于 2021-11-30 21:24 编辑
咋运行不了啊,大佬,是不是不支持Dev啊 王力宏本人 发表于 2021-11-30 21:23
咋运行不了啊,大佬,是不是不支持Dev啊
pyhtnon ver3.8环境的,报错提示是什么的 五子棋ui cy 楼主你好呀,我在做qt三维软件开发,可以交流下吗:我的qq:284 3167 798
页:
[1]