结对编程作业
队友的博客链接:李秋杰
项目地址:组队作业
具体分工
| 原型设计 | AI实现 | 游戏界面代码 | 游戏操作代码 | 游戏测试 | |
|---|---|---|---|---|---|
| 林必涵 | √ | √ | √ | √ | √ |
| 李秋杰 | √ | √ |
1.原型设计
这次作业设计主要采用了QT的界面设计工具

实际效果

组件:
start按钮:获取题目并初始化gridlayout布局
gridlayout布局控件:存放题目的图片,提供游戏的操作控制功能和游戏逻辑(胜利条件)
help按钮:请求提示,给出题目的解
Tips标签:显示题目的解
9个Label放在gridlayout控件内,显示题目图片的九宫格
设计说明:

大致的思路:
先将本地的图片(所有可能的图片)分为九宫格的样式,并编上1-9的序号,然后将从网络上获取的题目均等的切为九宫格,然后和本地的图片库进行匹配
最后将题目中的图片也编上序号,放入九宫格内。
结对过程,讨论问题时的照片

遇到的困难及解决方法
1.UI设计问题
一开始打算用tkinter,但是之后用发现QTDesigner可以直接转为python于是就果断转pyqt了,但是pyqt也用的不熟练,消息的传递研究了很久,按键响应也
研究了很久
2.控件的研究
选择什么样的控件才能更好的展示和进行游戏控制
3.设计完成后发现要加一个新的窗口,放不下所有的控件
重新设计了所有的控件布局,并且换了一个更大的窗口
收获:以后一定要先设计再写代码
4.图片的路径问题
很多文件一开始保存的的都是直接放在当前路径下,导致后期将各种模块组合再一起时,导包经常出问题,代码的管理也不是很规范
2.AI设计及原型设计实现
网络接口的使用
网络接口使用python的requests库来获取题目信息
UML类图

算法关建
算法使用的是A*算法,经典的8数码问题,无解前返回-1,有解返回操作序列的字符串
import collections
import itertools
import heapq
class Solution(object):
def __init__(self,blocks,zero_column,zero_row):
super().__init__()
self.board = []
self.zero_column = 0
self.zero_row =0
self.zero_column = zero_column
self.zero_row = zero_row
t = 0
for i in blocks:
self.board.append([])
for j in i:
tmp = j.number
self.board[t].append(tmp)
t += 1
self.zero_num = self.board[zero_row][zero_column]
def slidingPuzzle(self):
board = self.board
R, C = len(board), len(board[0])
#board[self.zero_row][self.zero_column] = 0
start = tuple(itertools.chain(*board))
#target = tuple([*range(1, R * C)] + [0])
target = tuple([*range(1, R * C + 1)])
target_wrong = tuple([*range(0, R*C-3)] + [R*C-3, R*C-1, R*C-2])
operations = ""
pq = [(0, 0, start, start.index(self.zero_num),operations)]
cost = {start: 0}
expected = {(C*r+c+1) % (R*C+1) : (r, c)
for r in range(R) for c in range(C)}
def heuristic(board):
ans = 0
for r in range(R):
for c in range(C):
val = board[C*r + c]
#if val == self.zero_num: continue
er, ec = expected[val]
ans += abs(r - er) + abs(c - ec)
return ans
while pq:
#f = estimated distance (priority)
#g = actual distance travelled (depth)
f, g, board, zero, ops = heapq.heappop(pq)
if board == target: return ops
if board == target_wrong: return "-1"
if f > cost[board]: continue
for delta in (-1, 1, -C, C):
tops = ops
if delta == -1:
tops += "a"
elif delta == 1:
tops += "d"
elif delta == -C:
tops += "w"
elif delta == C:
tops += "s"
nei = zero + delta
if abs(zero // C - nei // C) + abs(zero % C - nei % C) != 1:
continue
if 0 <= nei < R*C:
board2 = list(board)
board2[zero], board2[nei] = board2[nei], board2[zero]
board2t = tuple(board2)
ncost = g + 1 + heuristic(board2t)
if ncost < cost.get(board2t, float('inf')):
cost[board2t] = ncost
heapq.heappush(pq, (ncost, g+1, board2t, nei, tops))
return "-1"
流程图

代码性能分析

单元测试代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'C:\Users\lbh\Desktop\SoftwareProject\CooperateWork\UI\Huarong.ui'
#
# Created by: PyQt5 UI code generator 5.15.1
#
# 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.
#这里仅为测试UI用,没什么其他用处
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(966, 665)
self.Gamestart = QtWidgets.QPushButton(Dialog)
self.Gamestart.setGeometry(QtCore.QRect(160, 430, 111, 61))
self.Gamestart.setObjectName("Gamestart")
self.gridLayoutWidget = QtWidgets.QWidget(Dialog)
self.gridLayoutWidget.setGeometry(QtCore.QRect(160, 60, 341, 321))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setObjectName("gridLayout")
self.pciture1 = QtWidgets.QLabel(self.gridLayoutWidget)
self.pciture1.setText("")
self.pciture1.setScaledContents(True)
self.pciture1.setObjectName("pciture1")
self.gridLayout.addWidget(self.pciture1, 0, 0, 1, 1)
self.picture2 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture2.setText("")
self.picture2.setScaledContents(True)
self.picture2.setObjectName("picture2")
self.gridLayout.addWidget(self.picture2, 0, 1, 1, 1)
self.picture8 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture8.setText("")
self.picture8.setScaledContents(True)
self.picture8.setObjectName("picture8")
self.gridLayout.addWidget(self.picture8, 2, 1, 1, 1)
self.picture5 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture5.setText("")
self.picture5.setScaledContents(True)
self.picture5.setObjectName("picture5")
self.gridLayout.addWidget(self.picture5, 1, 1, 1, 1)
self.picture4 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture4.setText("")
self.picture4.setScaledContents(True)
self.picture4.setObjectName("picture4")
self.gridLayout.addWidget(self.picture4, 1, 0, 1, 1)
self.picture7 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture7.setText("")
self.picture7.setScaledContents(True)
self.picture7.setObjectName("picture7")
self.gridLayout.addWidget(self.picture7, 2, 0, 1, 1)
self.picture10 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture10.setText("")
self.picture10.setScaledContents(True)
self.picture10.setObjectName("picture10")
self.gridLayout.addWidget(self.picture10, 2, 2, 1, 1)
self.picture6 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture6.setText("")
self.picture6.setScaledContents(True)
self.picture6.setObjectName("picture6")
self.gridLayout.addWidget(self.picture6, 1, 2, 1, 1)
self.picture3 = QtWidgets.QLabel(self.gridLayoutWidget)
self.picture3.setText("")
self.picture3.setScaledContents(True)
self.picture3.setObjectName("picture3")
self.gridLayout.addWidget(self.picture3, 0, 2, 1, 1)
self.tips = QtWidgets.QLabel(Dialog)
self.tips.setGeometry(QtCore.QRect(610, 90, 130, 130))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tips.sizePolicy().hasHeightForWidth())
self.tips.setSizePolicy(sizePolicy)
self.tips.setMaximumSize(QtCore.QSize(400, 400))
self.tips.setBaseSize(QtCore.QSize(400, 400))
self.tips.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.tips.setObjectName("tips")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.Gamestart.setText(_translate("Dialog", "start"))
self.tips.setText(_translate("Dialog", "提示:"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv) # 创建一个QApplication,也就是你要开发的软件app
MainWindow = QtWidgets.QMainWindow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
ui = Ui_Dialog() # ui是你创建的ui类的实例化对象
ui.setupUi(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
MainWindow.show() # 执行QMainWindow的show()方法,显示这个QMainWindow
sys.exit(app.exec_())
github代码签入



表格与学习进度条
| PSP2.1 | Personal Software Process Stages |
预估耗时 (分钟) |
实际耗时 (分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 10 | 10 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术) | 800 | 1000 |
| · Design Spec | · 生成设计文档 | 200 | 260 |
| · Design Review | · 设计复审 | 30 | 55 |
| · Coding Standard | · 代码规范 (为目前的开发 制定合适的规范) |
20 | 40 |
| · Design | · 具体设计 | 120 | 90 |
| · Coding | · 具体编码 | 160 | 200 |
| · Code Review | · 代码复审 | 60 | 60 |
| · Test | · 测试(自我测试,修改 代码,提交修改) |
30 | 50 |
| Reporting | 报告 | ||
| · Test Repor | · 测试报告 | 70 | 70 |
| · Size Measurement | · 计算工作量 | 10 | 20 |
| · Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改 进计划 |
30 | 30 |
| · 合计 | 1530 | 1875 |
队友评价
值得学习的地方:心态好
需要改进的地方:需要更勤奋点

浙公网安备 33010602011771号