使用C#编写一个求解数独的小软件

工程文件下载,点击这里:工程文件下载地址

我挺喜欢的一个女同学喜欢做数独题目,作为忠实的备胎,当然是要为她提供一个能在她冥思苦想未果后寻求答案的小软件啦。
说干就干,不能做的太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.点击 解出数独,即可解出答案

工程文件下载,点击这里:工程文件下载地址

好了,女生就这样追到手了。

————来自一个业余时间研究把妹技术的嵌入式工程狗


  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值