工程文件下载,点击这里:工程文件下载地址
我挺喜欢的一个女同学喜欢做数独题目,作为忠实的备胎,当然是要为她提供一个能在她冥思苦想未果后寻求答案的小软件啦。
说干就干,不能做的太low,所以得有个界面。
我的小软件可以由用户调整数独大小,这样就不用担心如何适配更复杂和简单的数独了。
刚开始做的一版本没有考虑大宫格内的小宫格也不能出现重复数字,例如:下面这个图的左上角九个宫格。
只是考虑了行列不能出现一样的数据。当然了,我的愚蠢被女生骂了一顿。所以赶紧进行了改进,添加了一个函数即可。
求解数独的方法还是比较直接的——不断尝试,不断递归。
好了,话不多说,直接上程序:
using DevExpress.XtraEditors;
using System;
using System.Windows.Forms;
namespace Sudoku
{
public partial class Form1 : DevExpress.XtraBars.Ribbon.RibbonForm
{
Form form1;
int SudokuSize;
int PartSize;//将数独分开若干小份,用于划分小区域
int[,] SudokuBuf;
public Form1()
{
form1 = this;
InitializeComponent();
}
private void fun(int[,] sourcebuf )
{
sourcebuf[0, 0] = 1;
}
private void Form1_Load(object sender, EventArgs e)
{
SudokuBuf = new int[2, 2];
fun(SudokuBuf);
barEditSudokuSize_EditValueChanged(sender,e);
}
/// <summary>
/// 改变数独框体大小
/// </summary>
private void Change_sudoku_size()
{
int height = 40;
int width = 40;
this.dataGridView1.Rows.Clear();
this.dataGridView1.Columns.Clear();
//生成列
for (int i = 0; i < SudokuSize; i++)
{
string columnName = i.ToString();
this.dataGridView1.Columns.Add(columnName, "");
}
this.dataGridView1.RowTemplate.Height = height;//行例
this.dataGridView1.Rows.Add(SudokuSize);
for (int i = 0; i < SudokuSize; i++)
{
this.dataGridView1.Columns[i].Width = width;//调整列宽
}
this.dataGridView1.Width = (this.dataGridView1.Columns[1].Width) * SudokuSize;
this.dataGridView1.Height = (this.dataGridView1.RowTemplate.Height) * SudokuSize + 3;
form1.Height = ((this.dataGridView1.Height + 180) < 248) ? 248 : (this.dataGridView1.Height + 180);
form1.Width = ((this.dataGridView1.Width + 150) < 394) ? 394 : (this.dataGridView1.Width + 150);
}
/// <summary>
/// 改变大小事件发生
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void barEditSudokuSize_EditValueChanged(object sender, EventArgs e)
{
SudokuSize = Convert.ToUInt16(barEditSudokuSize.EditValue);
double temp = Math.Sqrt(SudokuSize);
int.TryParse(temp.ToString(), out PartSize);
if(PartSize == 0)//不能开平方
{
if (SudokuSize % 2 == 0)//能整除2
{
PartSize = SudokuSize / 2;
}
else {}//否则就是一整块,不进行划分
}
Change_sudoku_size();
}
/// <summary>
/// 生成数独
/// </summary>
private void Make_sudoku()
{
SudokuBuf = new int[SudokuSize,SudokuSize];
for(int i = 0; i < SudokuSize; i++)
{
for (int j = 0; j < SudokuSize; j++)
{
SudokuBuf[i, j] = Convert.ToInt16(dataGridView1.Rows[i].Cells[j].Value);
if(SudokuBuf[i, j] != 0)
{
dataGridView1.Rows[i].Cells[j].Style.BackColor = System.Drawing.Color.LightPink;
}
else
{
dataGridView1.Rows[i].Cells[j].Style.BackColor = System.Drawing.Color.LightBlue;
}
}
}
}
/// <summary>
/// 生成数独按钮事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnMakeSudoku_Click(object sender, EventArgs e)
{
Make_sudoku();
}
/// <summary>
/// 解数独点击
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSolveSudoku_Click(object sender, EventArgs e)
{
Solve_sudoku();
}
/// <summary>
/// 清除宫格
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnClear_Click(object sender, EventArgs e)
{
Change_sudoku_size();
}
/// <summary>
/// 解数独
/// </summary>
private void Solve_sudoku()
{
Make_sudoku();
//开始求解数独
Filled_try(SudokuBuf, 0,0);
for (int i = 0; i < SudokuSize; i++)
{
for (int j = 0; j < SudokuSize; j++)
{
dataGridView1.Rows[i].Cells[j].Value = SudokuBuf[i, j];
}
}
for (int i = 0; i < SudokuSize; i++)
{
for(int j = 0; j < SudokuSize; j++)
{
if(Convert.ToInt16(dataGridView1.Rows[i].Cells[j].Value) == 0)
{
XtraMessageBox.Show("数独错误");
return;
}
}
}
}
/// <summary>
/// 递归判断,该函数只作用一个元素
/// </summary>
/// <param name="SourceBuf"></param>
/// <param name="RowNum"></param>
/// <param name="ColumnNum"></param>
/// <returns></returns>
private bool Filled_try(int[,] SourceBuf,int RowNum,int ColumnNum)
{
int TempValue;//临时填充值
int WrongCnt = 0;//错误的填充值计数
int [] WrongNumBuf = new int[SudokuSize];//盛放错误数据的buf
//参数判断,是否越界
if (ColumnNum == SudokuSize)
{
RowNum++;
ColumnNum = 0;
if (RowNum == SudokuSize)
{
return true; //到达了数独末尾
}
}
while(SourceBuf[RowNum, ColumnNum] != 0)//当前有固定值,找到下一个无固定值的
{
ColumnNum++;
if (ColumnNum == SudokuSize)
{
RowNum++;
ColumnNum = 0;
if (RowNum == SudokuSize)
{
return true; //到达了数独末尾
}
}
}//循环结束后证明无固定值
while(true)
{
//获取一个可能正确的数值
TempValue = Get_a_number(SourceBuf, RowNum,ColumnNum,WrongNumBuf);
if (TempValue == -1)//如果不能产生出一个数值,证明之前的推算是错误的,重来
{
return false;
}
SourceBuf[RowNum, ColumnNum] = TempValue;
//开始下一轮递归,如果正确,TempBuf值赋值给SourceBuf,否则不动
if (Filled_try(SourceBuf, RowNum,ColumnNum + 1) == true)
{
return true;
}
else//下一个元素无法找到正确答案
{
SourceBuf[RowNum,ColumnNum] = 0;//重新赋值
WrongNumBuf[WrongCnt++] = TempValue;
}
}
}
/// <summary>
/// 获取一个此行不存在的数值,且无论该行还是列都不冲突
/// </summary>
/// <param name="SourceBuf"></param>
/// <param name="RowNum"></param>
/// <param name="ColumnNum"></param>
/// <param name="WrongNumBuf"></param>
/// <returns></returns>
private int Get_a_number(int [,] SourceBuf,int RowNum,int ColumnNum,int[] WrongNumBuf)
{
int a;
//循环产生数据1 -> n
for(int i = 1; i <= SudokuSize; i++)
{
//此数据是否已经尝试过
for(int j = 0; j < SudokuSize;j++)
{
if(i == WrongNumBuf[j])//尝试过,是错误数据
{
goto bb;
}
}
//判断行检测和列检测都能通过
if (Check_row(SourceBuf,RowNum,i) && Check_column(SourceBuf,ColumnNum,i))
{
if (Check_group(SourceBuf, RowNum, ColumnNum, i))
{
return i;
}
}
bb:
;
}
//没有数据能够产生
return -1;
}
/// <summary>
/// 判断此行是否可行
/// </summary>
/// <param name="tempbuf"></param>
/// <param name="RowNum"></param>
/// <param name="num"></param>
/// <returns></returns>
private bool Check_row(int[,] tempbuf,int RowNum,int num)
{
for(int i = 0; i < SudokuSize; i++)
{
if(tempbuf[RowNum,i] == num)
{
return false;//有相等的,验证不通过
}
}
//都没有和这个相等的,验证通过
return true;
}
/// <summary>
/// 判断此列是否可行
/// </summary>
/// <param name="tempbuf"></param>
/// <param name="ColumnNum"></param>
/// <param name="num"></param>
/// <returns></returns>
private bool Check_column(int[,] tempbuf ,int ColumnNum,int num)
{
for(int i = 0; i < SudokuSize; i++)
{
if(tempbuf[i,ColumnNum] == num)
{
return false;//有相等的,验证不通过
}
}
//都没有和这个相等的,验证通过
return true;
}
/// <summary>
/// 判断小组内是否出现过
/// </summary>
/// <param name="tempbuf"></param>
/// <param name="RowNum"></param>
/// <param name="ColumnNum"></param>
/// <param name="num"></param>
/// <returns></returns>
private bool Check_group(int[,] tempbuf, int RowNum, int ColumnNum, int num)
{
if (PartSize == 0)
{
return true;
}
//int m = RowNum % (SudokuSize / PartSize);
int m = RowNum % PartSize;
int k = ColumnNum % PartSize;
for(int i = 0 + (RowNum - m); i < PartSize + (RowNum - m); i++)
{
for (int j = 0 + (ColumnNum - k); j < PartSize + (ColumnNum - k); j++)
{
if (num == tempbuf[i, j])
{
return false;
}
}
}
return true;
}
}
}
下面是程序运行界面:
1. 填入数据。这个数独是
芬兰数学家因卡拉花费3个月做出的迄今为止最难的数独,我们就用它测试吧
2. 点击生成数独
3.点击 解出数独,即可解出答案
工程文件下载,点击这里:工程文件下载地址
好了,女生就这样追到手了。
————来自一个业余时间研究把妹技术的嵌入式工程狗