我的GitHub地址:https://github.com/gmj0301/gmj
请查看前面的博客!
思路简述
功能简述
首先,这个项目我决定有开始游戏、提示、检查这三个主要的大功能。
(1)开始游戏
点击 “开始游戏” 按键后,出现三个按键 “简单”,“中等”,“难” 三个按键(简单:题目中有 30 - 40 个空;中等:题目中有 40 - 50 个空;难:题目中有 50 - 60 个空)。点击选择难度后,出现数独题目,可以开始填入数字。此时可以点击 “提示” 和 “检查” 按键进行相关功能。再次点击 “开始游戏” ,出现新的数独题目。
(2) 提示
当填入的数字有错误时,出现窗口告知用户 “有出错的数字,请修改!”,该数字所在位置背景变红,窗口关闭后,背景颜色恢复。当填入的数字没有错误时,在第一个空白处自动填入数字,并保证正确。
(3) 检查
点击 “检查” 按键后,如果有空没有填,则出现窗口告知用户 “有出错的数字,请修改!”;如果出现错误,则出现窗口告知用户 “有出错的数字,请修改!”;如果全部正确,则出现窗口告知用户 “有出错的数字,请修改!”
该项目的流程图是:
设计思路
因为在前面已经实现了生成数独终局与解数独的功能,所以我决定在前面的基础上更改一些,设计思路不变。大致思路如下:
生成数独题目是在生成的数独终局的基础上,随机生成挖空的数目,再随机选择位置挖空。
数独终局和挖空后的题目都保存下来,数独终局作为答案,可以方便设计提示和检查功能。
难点
整个项目会使用 VS 的 Windows 窗体应用以及 C# 语言。难点在于我前面的项目使用的语言是 C 语言,没有使用 C++,没有使用类。而现在我需要再前面已有的代码进行修改,创建类。而且我对于 C# 并不清楚,使用起来需要学习。
实现过程
首先在 VS 中建立了一个 WPF 工程。
处理框架
主要的函数设计如下:
public partial class Form1 : Form
{
TextBox[ , ] textbox = new TextBox[9, 9];
Sudoku sudoku = new Sudoku();
bool temp = true;
int flag = 0;
public Form1(); //构造函数
void Initial(); //初始化文本框
public void write(); //写入文本框
private void Txt_KeyPress(object sender, KeyPressEventArgs e); //设置键入
private bool Check_fill(); //检查是否每个空都填了
private bool Check_correct(); //检查是否每个空都填对了
private void button_start_Click(object sender, EventArgs e); //点击 “开始游戏” 按键
private void button_tishi_Click(object sender, EventArgs e); //点击 “提示” 按键
private void button_check_Click(object sender, EventArgs e); //点击 “检查” 按键
public void click_dif(); //点击难易程度按键
//点击“简单”,“中等”,“难” 按键
private void button_easy_Click(object sender, EventArgs e);
private void button_middle_Click(object sender, EventArgs e);
private void button_hard_Click(object sender, EventArgs e);
}
先搭了基础界面,由功能可知,设计三个按键 “开始游戏”,“提示”,“检查”:
五个按键的设置
(1)“简单”,“中等”,“难” 三个按键一开始是不可见的,“提示”,“检查” 按键不可用。
public Form1()
{
InitializeComponent();
button_tishi.Enabled = false; //禁止使用“下一轮游戏”按键
button_check.Enabled = false; //禁止使用“检查答案”按键
//“简单”“中等”“难”三个按键不可见
button_easy.Visible = false;
button_middle.Visible = false;
button_hard.Visible = false;
}
(2)点击 “开始游戏” 就可以出现 “简单”,“中等”,“难” 三个按键, “开始游戏” ,“提示”,“检查” 按键不可见。如果是第二次点击,之前出现的文本框需要重新设置不可见。代码同理(1)中。
(3)点击 “简单”,“中等”,“难” 三个按键出现文本框(里面有数独题目),“简单”,“中等”,“难” 三个按键不可见, “开始游戏” ,“提示”,“检查” 按键可见,文本框可见(如果第一次开始,需要初始化文本框)。
public void click_dif()
{
//“开始游戏”“提示”“检查”三个按键可见
button_tishi.Visible = true;
button_start.Visible = true;
button_check.Visible = true;
//“提示”“检查”三个按键可以使用
button_tishi.Enabled = true;
button_check.Enabled = true;
//“简单”“中等”“难”三个按键不可见
button_easy.Visible = false;
button_hard.Visible = false;
button_middle.Visible = false;
if (flag == 1 || flag == 2 || flag == 3) //判断到底点的“简单”“中等”“难”三个按键中的哪个
{ //方便生成数独题目使用
if (temp == false) //如果是不是第一次点“开始游戏”,此时文本框不可见
{
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
textbox[i, j].Visible = true; //重新设置使其可见
}
if (temp == true) //如果是第一次点“开始游戏”
{
Initial(); //初始化文本框
temp = false; //修改判断标识符
}
sudoku.create_quzi(flag); //用flag把题目的难以程度传递,生成符合要求的数独题目
write(); //把数独题目写进文本框
}
}
初始化文本框的代码如下:
private void Initial()
{
int x = 80, y = 8, w = 40, d = 45;
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++)
{
TextBox txt = new TextBox
{
//输入长度,上,左,宽,高,是否可见,标签
MaxLength = 1, Top = y + i * d, Left = x + j * d,
Width = w, Height = w, Visible = true, Tag = i * 9 + j
};
txt.Font = new Font("宋体", 25);
txt.Text = "";
txt.KeyPress += Txt_KeyPress; //建立按键事件
textbox[i, j] = txt;
this.Controls.Add(txt);
}
}
(4)点击 “提示” 按键,可以提示错误;没有错误填入数字;如果都填完,显示正确。通过通过与作为答案的数独终局比较得到。设计参考上面展示的流程图。
(5)点击 “检查” 按键,还有空,提示;有错误,提示;全部正确,提示。通过与作为答案的数独终局比较得到。设计参考上面展示的流程图。
处理数独
大致的设置如下:
其中生成数独终局可以查看前面的博客,这里不再重复(生成数独终局)。
生成数独题目,使用 create_sudo(int flag) 函数,flag是标志,标志题目难易程度的要求。根据要求,每个数独3×3格子中至少要挖掉两个,并且总共会生成[30,60]个空,所以我们要先随机出一个总空白数,然后先让每个3×3小矩阵中挖掉两个空(置0),然后再在整个数独矩阵上随机挖空。
具体流程如下:
代码如下:
public void create_quzi(int flag) //随机生成题目
{
create_sudo();
Random rdm = new Random(); //一个随机数
int blank;
if (flag == 1) blank = rdm.Next(30, 40); //简单,30 - 40 个空
else if (flag == 2) blank = rdm.Next(40, 50); //中等,40 - 50 个空
else blank = rdm.Next(50, 60); //难,50 - 60 个空
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
answer[i, j] = sudo[i, j]; //先赋值
for (int i = 0; i < 9 && blank > 0; i++) //9个九宫格
{
for (int j = 0; j < 2 && blank > 0; j++) //每个九宫格选两个空
{
int position = rdm.Next(9); //随机生成一个数,来选九宫格里的位置
int x = i / 3 * 3 + position / 3;
int y = i % 3 * 3 + position % 3;
if (answer[x, y] != 0) //这个位置没被挖空
{
answer[x, y] = 0;
blank--;
}
else //这个位置已经是空了
{
j--;
continue;
}
}
}
while (blank > 0) //当还有空时
{
int block = rdm.Next(9); //选择哪个九宫格
int position = rdm.Next(9); //九宫格里的哪个位置
int x = block / 3 * 3 + position / 3;
int y = block % 3 * 3 + position % 3;
if (answer[x, y] != 0) //没有被挖空
{
answer[x, y] = 0;
blank--;
}
else continue;
}
}
结果显示
开始界面
点击“开始游戏”
点击“简单”
点击“提示”
点击“检查”
总结
虽然大体思路很清晰,但是还是出现了很多细节上问题。整个项目做下来,尽管有前面 C 语言代码的基础,依然花费了很长时间,但是整个项目下来,我收获了很多,也学到了很多。当然,这个功能并不是很完善,而且可能会有错误,比如我没有判断生成的数独题目是不是有唯一解,以后有机会会把它继续完善的。