结对项目-数独程序扩展

Runnable on x64 Only

sudoku17.txt 须放置在可执行文件同目录中,可移步以下链接进行下载

Core-Github项目地址

GUI-Github项目地址

Github说明中有详细运行规定,请运行前仔细查看。感谢留意。

时间预估

PSP2.1 Personal Software Process Stages 预估时间(分钟) 实际耗时(分钟)
Planning 计划 60
· Estimate · 估计这个任务需要多少时间 60
Development 开发 1590
· Analysis · 需求分析 (包括学习新技术) 60
· Design Spec · 生成设计文档 0
· Design Review · 设计复审 (和同事审核设计文档) 60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30
· Design · 具体设计 180
· Coding · 具体编码 900
· Code Review · 代码复审 300
· Test · 测试(自我测试,修改代码,提交修改) 60
Reporting 报告 250
· Test Report · 测试报告 180
· Size Measurement · 计算工作量 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60
合计 1670

接口设计

  • Information Hiding:编译生成动态链接库之后,外部只可通过dll调用上述接口,具体接口的实现方式被封装起来;
  • Interface Design: 考虑到跨语言、跨平台性,接口函数的返回值均为基本数据类型,避免了数据转换错误;接口函数名有意义,直接表达该接口意义;
  • Loose Coupling:根据 Wikipedia-Loose Coupling

An example of tight coupling occurs when a dependent class contains a pointer directly to a concrete class which provides the required behavior. The dependency cannot be substituted, or its "signature" changed, without requiring a change to the dependent class. Loose coupling occurs when the dependent class contains a pointer only to an interface, which can then be implemented by one or many concrete classes.

本程序耦合度低,除solve函数之外,其余接口对调用者传入参数无其它行为要求,即传入任何符合数据类型定义的参数皆可,降低了耦合度;而solve接口第一个参数则有一定的行为要求:如挖空处须置0等。

计算模块接口的设计与实现过程

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图

我们的计算模块就这一丢丢,不允许雷同也得雷同了。。

计算模块接口部分的性能改进

以下是运行-r 55~55 -u -n 10000时的性能分析图

这里可以看到,最耗时的函数是getFeasible,这一点和之前的作业相同。。。暂时没有什么办法去优化了。

Design & Code by Contract

  • Design by Contract(DbC) 优点在于通过如下信息规定了软件各个函数、类的规格;
  • Acceptable and unacceptable input values or types, and their meanings
  • Return values or types, and their meanings
  • Error and exception condition values or types that can occur, and their meanings
  • Side effects
  • Preconditions
  • Postconditions
  • Invariants
  • (more rarely) Performance guarantees, e.g. for time or space used

因此Dbc能够通过对上述条件的检查判断程序运行或模块的正确性,并且能够促进代码的重用。实现DbC的方式可通过Assertion来进行,然而因为一个程序不可能一个bug都没有,Assertion对程序的正确性则显得要求得过于严苛。

  • Code Contract 是微软开发的一款代码契约检查器,可检查程序执行过程中是否符合规定好的代码七月。优点在于:将契约式编程规范化;可用在所有.NEt框架实现的软件或程序中;Code Contract大大节省了时间,其对变量旧值的访问非常简单,因此可以轻易地检查前置、后置条件及对象不变式;契约也能被文档生成器提取为文档信息。 缺点:会造成额外时间开销。

计算模块部分单元测试

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个方格由TextBlockTextBox组成,局中不可修改方格由TextBlock显示,不可修改;局中需要用户填数的方格由TextBox组成,可以修改,但仅可填写一个字符(用户可以填入任何字符,填入非数字字符程序不会提示,任何情况下该方格不会被判为正确)。之所以使用TextBoxTextBlock进行不同情况的显示,是因为这两个组件的样式在父组件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后,提示板中绿底数字即为可填数字,此时提示板显示的提示信息对应的须填格左上角按钮变为红色以提示用户。利用提示板左上方的Clear Button可清除提示信息。

    • 提交按钮:利用Button实现,并绑定相应函数进行事件响应提交操作。若提交的数独终局答案正确,则弹出子窗口(Window组件实现)提示用户正确、难度、用时并输入玩家名称,若用户不输入则默认为“MyName”;若不正确,则弹出提示不正确,玩家继续修改数独。



    • 难度选择:难度选择板块由RadioButton组件实现,提供单选Easy, Medium, Hard的下一局难度选择。利用Checked属性与响应函数进行绑定,默认情况(初始)为Medium难度。

    • 换题按钮:利用Button按钮实现,获取难度并生成下一个数独局并铺至9x9数独板上。

界面模块与计算模块的对接

界面模块由C#实现,核心计算模块由Cpp实现。C#通过调用Cpp生成的Core.dll来进行交互对接。

Core.dll定义见接口设计节,UI界面共使用三个接口:

void generateM(int number, int mode, int result[][81]);用以生成number个mode难度的数独,存储在result中。

bool check(int board[81]);用以检查board数独是否正确。

int getFeasible(int board[81], int x, int y);获得x行y列的格子处可填的数字。

C#调用方法:

[DllImport(@"Core.dll", EntryPoint = "check")]
extern static bool check(int[] board); [DllImport(@"Core.dll", EntryPoint = "generateM")]
extern static void generateM(int number, int mode, ref int board); [DllImport(@"Core.dll", EntryPoint = "getFeasible")]
extern static int getFeasible(int[] board, int x, int y);

可以看到generateM函数第三个参数在Cpp中定义时为二维整型数组,而在C#调用时却使用ref int来进行转换。如下:

int[,] tmp = new int[1,81];
generateM(1, diff, ref tmp[0,0]);

其中tmp是一个C#中对应的二维整型数组结构,其与int[][]结构最大的区别在于每个元素的维度必须相同,刚好符合第三个参数int result[][81]的定义。

其它调用:

public bool CheckValidation(int[] board)
{
return check(board);
}

UI响应:

结对编程总结

  • 结对编程优点:两个人共同解决一个问题,两个人的能力在不同方面得以施展,能够提高开发效率;能考虑到平时考虑不到的问题,这与团队项目还是有一些不同的。领航员与驾驶员的角色使两人之间除了各司其职,更是互补与促进;一人coding另一人观察,除了能及时发现逻辑错误之外,对code style也能起到好的约束。好的习惯互相学习,坏的习惯就尽量改掉,两人平起平坐,能互相学到不少。

  • 结对编程缺点:需要的时间会比两个人各做一个模块再合起来长一些;如果结对编程的双发有矛盾难以化解,难以停下来倾听并讨论对方的意见,那么效率更会大打折扣。

  • 本次结对编程优点:队友实现速度快,代码经验丰富,为咱的Core.dll做出了巨大的贡献;任劳任怨,愉快沟通;言简意赅,做事认真专心专业。

  • 本次结对编程不足:小有拖延症....

实际花费时间

PSP2.1 Personal Software Process Stages 预估时间(分钟) 实际耗时(分钟)
Planning 计划 60 90
· Estimate · 估计这个任务需要多少时间 60 90
Development 开发 1590 1460
· Analysis · 需求分析 (包括学习新技术) 60 180
· Design Spec · 生成设计文档 0 0
· Design Review · 设计复审 (和同事审核设计文档) 60 120
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 180 180
· Coding · 具体编码 900 500
· Code Review · 代码复审 300 300
· Test · 测试(自我测试,修改代码,提交修改) 60 150
Reporting 报告 250 220
· Test Report · 测试报告 180 150
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 60 60
合计 1900 1770

[BUAA_SE_2017]结对项目-数独程序扩展的更多相关文章

  1. [buaa-SE-2017]结对项目-数独程序扩展

    结对项目-数独程序扩展 step1~step3:github:SE-Sudoku-Pair-master step4:github:SE-Sudoku-Pair-dev-combine step5:g ...

  2. [2017BUAA软工]结对项目-数独程序扩展

    零.github地址 GitHub地址:https://github.com/Liu-SD/SudoCmd (这个地址是命令行模式数独的仓库,包含了用作测试的BIN.DLL核心计算模块地址是:http ...

  3. Week3结对项目-数独游戏

    题目要求 1)在文章开头给出Github项目地址.(1') 2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间.(0.5') 3)看教科书和其它资料中关于Info ...

  4. [2017BUAA软工]结对项目:数独扩展

    结对项目:数独扩展 1. Github项目地址 https://github.com/Slontia/Sudoku2 2. PSP估计表格 3. 关于Information Hiding, Inter ...

  5. [2017BUAA软工助教]结对项目小结

    2017BUAA结对项目小结 一.作业链接 http://www.cnblogs.com/jiel/p/7604111.html 二.评分细则 1.注意事项 按时间完成并提交--正常评分 晚交一周以内 ...

  6. [2017BUAA软工]结对项目

    软工结对项目 一. Github项目地址 https://github.com/crvz6182/sudoku_partner 二. PSP表格 Psp personal software progr ...

  7. 结对项目作业报告——四则运算web项目

    成员:顾思宇2016011993 程羚2016012050   1.仓库地址:https://git.coding.net/DandelionClaw/WEB_Calculator.git 注: 本项 ...

  8. 结对项目:四则运算web

    1)Coding.Net项目地址 https://git.coding.net/DandelionClaw/WEB_Calculator.git 注:本项目为web端,并且需要连接SQL Server ...

  9. 复利计算--结对项目<04-11-2016> 1.0.0 lastest 阶段性完工~

    结对项目:Web复利计算 搭档博客地址:25江志彬  http://www.cnblogs.com/qazwsxedcrfv/ 个人摘要: (2016-04-09-12:00)补充:之前传送门没做好, ...

随机推荐

  1. C3P0与DBUtil配合实现DAO层的开发

    写在前面:菜鸟拙见,望请纠正 一:为什么需要连接池 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,需要数据库连 ...

  2. nodeJs搭建简单服务器

    实现简单的例子:hello word 把第一个文件命名为server.js 在server.js里写入 var http  = require("http"); http.crea ...

  3. java核心技术-多线程之线程基础

    说起线程,无法免俗首先要弄清楚的三个概念就是:进程.线程.协程.OK,那什么是进程,什么是线程,哪协程又是啥东西.进程:进程可以简单的理解为运行在操作系统中的程序,程序时静态代码,进程是动态运行着的代 ...

  4. 远程连接服务器端Jupyter Notebook

    1. 安装 输入命令: sudo apt-get install sshfs 2. 服务器端开启Jupyter Notebook Ubuntu服务器端安装过程参考:www.cnblogs.com/la ...

  5. IOCP详解

    http://blog.csdn.net/piggyxp/article/details/6922277 ps: 原作者很厉害了, 把一个iocp模型讲解的这么形象,不过在实践过程中发现一些细节说得有 ...

  6. Hadoop(18)-MapReduce框架原理-WritableComparable排序和GroupingComparator分组

    1.排序概述 2.排序分类 3.WritableComparable案例 这个文件,是大数据-Hadoop生态(12)-Hadoop序列化和源码追踪的输出文件,可以看到,文件根据key,也就是手机号进 ...

  7. Android开发——布局性能优化的一些技巧(二)

    , 0, drawable.getMinimumWidth(),dra.getMinimumHeight()); tv.setCompoundDrawables(null, null, drawabl ...

  8. 【转载】D3D中的Texture应用示例

    原文:D3D中的texture应用示例 本文列举了Direct3D中各种纹理应用实现:黑暗贴图,发光贴图,漫反射映射贴图,细节纹理,纹理混合,有较详尽的注解.其中黑暗贴图,发光贴图,细节纹理都是采用多 ...

  9. 【转载】MFC的Main函数跑哪去了

    原文:http://blog.csdn.net/weiwenhp/article/details/8455471 习惯的思维 用习惯了C的人要看一个程序时首先会想到找到那个main函数在哪,然后再顺着 ...

  10. Redis学习之路(一)之缓存知识体系

    转自:https://www.unixhot.com/page/cache 缓存分层 缓存分级 内容 内容简介/主要技术关键词 用户层 DNS 浏览器DNS缓存 Firefox默认60秒,HTML5的 ...