|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 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 :: Ai W白子,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[p])
- 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([label, num])
- 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[0], 30 + 30 * t[1]), 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[0] or game_y != self.last_pos[1]:
- 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[1:]
- self.widget.pix_record = [self.widget.pix_record[0], ]
- for pix, num in self.widget.pix_tmp:
- pix.setVisible(False)
- num.setVisible(False)
- # # TODO 对落点数据处理
- self.widget.record_tmp = self.widget.record[1:]
- self.widget.record = [self.widget.record[0], ]
- 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, [pix, num])
- 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, [pix, num])
- 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([pix, num])
- # # 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([pix, num])
- # # 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_())
复制代码 |
评分
-
查看全部评分
|