结对项目-数独程序扩展
项目地址
Core-Github项目地址
GUI-Github项目地址
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 10 | |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 30 | |
· Design Spec | · 生成设计文档 | 30 | |
· Design Review | · 设计复审 (和同事审核设计文档) | 0 | |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 0 | |
· Design | · 具体设计 | 30 | |
· Coding | · 具体编码 | 240 | |
· Code Review | · 代码复审 | 0 | |
· Test | · 测试(自我测试,修改代码,提交修改) | 240 | |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | |
· Size Measurement | · 计算工作量 | 30 | |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | |
合计 | 690 |
开发过程
看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的。
我们的接口设计非常简单,除了作业要求中的几个接口外(generate, solve),就只有check接口(用于验证终局是否正确)和getFeasible接口(用于获得某个方格的可行解)。
因为我们的整个结构是使用C++实现Core.dll来负责计算,C#实现GUI,C#调用C++编写的dll限制很大,只能使用基础的类型,所以我们在Core.dll中设计的接口就只有几个函数,没有定义类。接口隐藏了内部实现的所有信息,外部只能调用几个接口函数,这样与其他的模块做到了松耦合。
计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。
Core中的所有接口如下:
//对应-m -n参数
void generateM(int number, int mode, int result[][81]);
//对应-r -u -n参数
void generateR(int number, int lower, int upper, bool unique, int result[][81]);
//对应-c 参数
void generate(int number,int result[][81]);
//对应-s 参数
bool solve(int puzzle[], int solution[]);
//检查终局正确性
bool check(int board[81]);
//获取格子可行解
int getFeasible(int board[81], int x, int y)
各个接口通过SudokuSolver和SudokuBoard两个类来实现。SudokuBoard作为数独棋盘的表示,包括一些获取数独特性的方法。SudokuSolver的核心是回溯搜索+剪枝的求解方法。
生成数独终局
随机在数独棋盘上摆放一些合法的数字,使用求解算法求解,每当得到一个合法的数独终盘,就记录下来,然后继续回溯,直到生成的个数满足要求。这样生成的数独可以满足一定的初局要求。
生成特定难度
我们定义数独的难度是求解算法的回溯次数。三个难度等级分别对应三个不同的回溯次数范围。生成的时候,首先随机生成一个数独终局,然后随机挖空一定量的格子,求解数独获得回溯次数,如果满足当前难度规定的次数范围,就记录下来。不断生成直到满足数量要求。
但由于高难度的数独较难生成,当要求数量较多时,无法快速完成。这里我们提前存储了一些高难度数独,需要时直接读取文件输出。
生成给定挖空范围+唯一解
生成给定挖空数量范围的要求上节已经完成。
生成唯一解的思路是这样的,还是先生成一个数独终局,然后对所有格子进行尝试,每次挖空一个格子,使用求解算法判断是否有唯一解,如果有,那么继续挖,否则就回填这个格子,这时我们认为这个格子是对唯一解有贡献的。当尝试完所有格子以后,如果满足数量要求,则返回,否则重新从头开始。
画出UML图显示计算模块部分各个实体之间的关系(画一个图即可)。
计算模块接口部分的性能改进。记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展示你程序中消耗最大的函数。
以下是运行-r 55~55 -u -n 10000时的性能分析图
这里可以看到,最耗时的函数是getFeasible,这一点和之前的作业相同。。。暂时没有什么办法去优化了。
看Design by Contract, Code Contract的内容:
http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的。
契约精确定义了函数的行为,同时规定了函数的条件和结果。
对于函数的实现者,明确了在哪些情况下应当有哪些行为,如果不满足条件,功能无法完成,自己不用背锅。
对于函数的调用者,可以对于函数的行为有准确的预期,避免了调用出错。
计算模块部分单元测试展示。
void testArgM()
{
printf("test generate(10000, 1, result)\n");
SudokuSolver::generate(10000, 1, re);
for (int i = 0; i < 10000; i++)
{
SudokuSolver::solve(re[i], sol);
if(!SudokuSolver::check(SudokuBoard(sol)))
printf("fail\n");
}
printf("test generate(1000, 2, result)\n");
SudokuSolver::generate(1000, 2, re);
for (int i = 0; i < 1000; i++)
{
SudokuSolver::solve(re[i], sol);
if(!SudokuSolver::check(SudokuBoard(sol)))
printf("fail\n");
}
printf("test generate(100, 3, result)\n");
SudokuSolver::generate(1000, 3, re);
for (int i = 0; i < 1000; i++)
{
SudokuSolver::solve(re[i], sol);
if(!SudokuSolver::check(SudokuBoard(sol)))
printf("fail\n");
}
}
void testArgR()
{
printf("test generate(10000,20,55,false,result)\n");
SudokuSolver::generate(10000, 20, 55, false, re);
for (int i = 0; i < 10000; i++)
{
SudokuSolver::solve(re[i], sol);
if(!SudokuSolver::check(SudokuBoard(sol)))
printf("fail\n");
}
printf("test generate(10000,20,55,true,result)\n");
SudokuSolver::generate(10000, 20, 55, true, re);
for (int i = 0; i < 10000; i++)
{
if (!SudokuSolver::isU(SudokuBoard(re[i])))
printf("fail\n");
SudokuSolver::solve(re[i], sol);
if(!SudokuSolver::check(SudokuBoard(sol)))
printf("fail\n");
}
}
check判断终局是否合法,isU判断是否有唯一解。
使用两种生成方式的各种参数组合,生成解,若要求唯一则使用isU判断,调用solve求解,最后用check判断。
计算模块部分异常处理说明。
计算模块接口的异常有以下几种:
-
generate函数number错误
number应在[1,10000]之间。
测试用例:0,-1,+100,100000
-
solve函数传入数独不合法
数独每个数字应在1~9之间
测试用例:int input[81] = {10};
还有其他的异常在输入处理的时候抛出。
界面模块详细设计过程
界面模块由布局从左至右,利用C# WPF实现:9x9的数独板、其它信息或选项操作板及排行版信息板。
-
9x9数独板:用于显示当前数独局,81个方格由
TextBlock
和TextBox
组成,局中不可修改方格由TextBlock
显示,不可修改;局中需要用户填数的方格由TextBox
组成,可以修改,但仅可填写一个字符(用户可以填入任何字符,填入非数字字符程序不会提示,任何情况下该方格不会被判为正确)。之所以使用TextBox
和TextBlock
进行不同情况的显示,是因为这两个组件的样式在父组件Grid
中直接定义了,切换数独局时只须设置其显示值和可见性即可,还严格保证了用户不得修改其他数字;每一个用户可填处左上角均绑定了一个蓝色Button
,点击即可查看当前情况下(全局)该空格能填写的所有数字。每一个格子都须点击后由键盘进行输入。 -
排行榜信息:用于显示用户利用本程序玩数独的记录。排行榜利用
TabControl
组件进行三个难度榜单的切换显示,每一个Tab
下由ListView
组件绑定GridView
来显示表头(Rank Name Time)和记录(按排名由小到大,排名按时间由短及长)。排行榜信息来自于与可执行文件同目录下的Rank.txt
,文件中排行榜存储方式固定。若文件不存在,则程序初始化时会使用默认的排行榜单,并在可执行文件目录下新建Rank.txt
文件;若文件存在但内容不符合程序内部规定的存储方式,程序也会使用默认的排行榜单,并将默认榜单覆盖Rank.txt
文件。
-
信息查看及选项操作板:
-
计时器:用于记录用户解数独的耗时。计时开始:从程序展示一个新数独局开始计时;计时暂停:用户提交该盘数独;计时继续:用户提交该盘数独但不正确,则用户继续做本题,计时器继续;计时停止:用户提交该盘数独且合法。计时器实现:利用
DispatcherTimer
启动另一个线程来进行计时,设置count
整形变量记录经过的step
次数,来还算时间。本程序中step
设置为1000ms。
-
难易度显示板:利用
TextBlock
组件实现,显示当前难度:Easy, Medium, Hard。 -
提示板:利用3x3的
TextBlock
组件实现,当用户点击9x9数独板中须填数格左上角的蓝色Button
后,提示板中绿底数字即为可填数字,此时提示板显示的提示信息对应的须填格左上角按钮变为红色以提示用户。利用提示板左上方的ClearButton
可清除提示信息。
-
提交按钮:利用
Button
实现,并绑定相应函数进行事件响应提交操作。若提交的数独终局答案正确,则弹出子窗口(Window
组件实现)提示用户正确、难度、用时并输入玩家名称,若用户不输入则默认为“MyName”;若不正确,则弹出提示不正确,玩家继续修改数独。
-
难度选择:难度选择板块由
RadioButton
组件实现,提供单选Easy, Medium, Hard的下一局难度选择。利用Checked
属性与响应函数进行绑定,默认情况(初始)为Medium难度。 -
换题按钮:利用
Button
按钮实现,获取难度并生成下一个数独局并铺至9x9数独板上。
-
界面模块与计算模块的对接
界面模块由C#实现,核心计算模块由Cpp实现。C#通过调用Cpp生成的`Core.
描述结对的过程,提供非摆拍的两人在讨论的结对照片。
看教科书和其它参考书,网站中关于结对编程的章节,
例如:http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html>
说明结对编程的优点和缺点。
结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
结对编程的优点
- 两个人一起思考问题,解决问题的能力更强
- 两个人之间的知识经验可以互相传授
- 两个人一起工作可以减少偷懒
结对编程的缺点
- 如果两个人意见不合,会比较难受
- 如果两人段位相差较多,会降低效率
关于本次结对伙伴
- 优点:交流起来很愉快,能够push我,比较有耐心
- 缺点:代码注释少
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 30 | 60 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 0 | 0 |
· Design | · 具体设计 | 30 | 60 |
· Coding | · 具体编码 | 240 | 360 |
· Code Review | · 代码复审 | 100 | 120 |
· Test | · 测试(自我测试,修改代码,提交修改) | 240 | 360 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 720 | 1140 |