數獨游戲-如何用代碼實現
最近開始喜歡起來玩數獨,在手機上找來幾個數獨小游戲玩着玩着突然想起我是個程序員.........
那我何不自己寫一個數獨軟件,網上查了一下數獨有6,670,903,752,021,072,936,960(約有6.67×10的21次方)種組合 。我xxxx.......算了算了不想那么多了,先自己嘗試做一個簡單的
下面的是我設計思路
首先給沒有玩過的同學普及一下數獨:數獨(shù dú)是源自18世紀瑞士的一種數學游戲。是一種運用紙、筆進行演算的邏輯游戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩余空格的數字,並滿足每一行、每一列、每一個粗線宮 (3*3)內的數字均含1-9,不重復
那么第一件事考慮如何如何生成一個9*9並且每一行,每一列,每一宮都不重復的二維數組?
突然我靈機一動,如果..我手寫個二維數組怎么樣.
int[][] arrray1 = new int[9][];
arrray1[0] = new int[9] { 5, 6, 4, 8, 9, 7, 2, 3, 1 };
arrray1[1] = new int[9] { 9, 7, 8, 3, 1, 2, 6, 4, 5 };
arrray1[2] = new int[9] { 3, 1, 2, 6, 4, 5, 9, 7, 8 };
arrray1[3] = new int[9] { 6, 4, 5, 9, 7, 8, 3, 1, 2 };
arrray1[4] = new int[9] { 7, 8, 9, 1, 2, 3, 4, 5, 6 };
arrray1[5] = new int[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
arrray1[6] = new int[9] { 4, 5, 6, 7, 8, 9, 1, 2, 3 };
arrray1[7] = new int[9] { 8, 9, 7, 2, 3, 1, 5, 6, 4 };
arrray1[8] = new int[9] { 2, 3, 1, 5, 6, 4, 8, 9, 7 };
嗯~ o( ̄▽ ̄)o感覺也不是不可以,如果每行扣去4個的話 C94 有126種 那一共應該有1134種組合
上面的數組是固定的如果在寫幾組數組呢?好像是可以有更多的組合但是成本太高,並且要找到每一宮每一列都不重復的組合也需要花費些時間而且也失去了樂趣,
換一個角度想如果不能變當前這個二維數組,可不可以通過其他方式來改變這個數組,可以使用一個一位數組同樣是1-9的數字,通過判斷二維數組中和一維數組相等的數據並取下一個位置的值,目的就是讓一維數組把二維數組中的值循環變一下,因為一維數組也是1-9的不重復數字因此不會影響到行和列及宮的組合,大家可以自行驗證一下.
private static int[][] creatSudokuArray(int[][] seedArray, List<int> randomList)
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
for (int k = 0; k < 9; k++)
{
if (seedArray[i][j] == randomList[k])
{
seedArray[i][j] = randomList[(k + 1) % 9];
break;
}
}
}
}
return seedArray;
}
這樣生成的數獨也就有9!=362880 在通過扣掉若干個格子那最終的結果
OK 數獨生成了那下面具體就是需要去畫一個9*9的矩陣並且把生成的數獨填充進去然后扣掉部分
我這里使用的是winform實現的具體填充代碼就不說了下面會給貼出github的源碼地址大家可以去下載,主要還是說思路.
扣數據部分也是用隨機數來操作的單純的去掉幾個感覺太死板了,附代碼
/// <summary>
/// 生成數獨選擇難度
/// </summary>
/// <param name="a"></param>
private void printArray(int[][] a)
{
int length = 4;
switch (this.comboBox1.Text)
{
case "簡單":
length = 4;
break;
case "中等":
length = 5;
break;
case "困難":
length = 6;
break;
}
Random random = new Random();
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
int randomNum = random.Next(9);
if (randomNum > length)
{
tbArray[i, j].Text = a[i][j].ToString();
}
else
{
tbArray[i, j].ReadOnly = false;
}
}
}
}
最后就是要去驗證自己填進去的數字是否滿足數獨的要求
那么我們就需要每一行,每一列,每一宮的檢查,行和列很簡單只要驗證當前行或者列是否有重復數據或者去重之后的長度是否小於9即可,相信大家可以通過各種語言來實現了把,下面貼出我的代碼
/// <summary>
/// 校驗行
/// </summary>
public bool rowCheck()
{
for (int y = 0; y < 9; y++)
{
List<int> lines = new List<int>();
for (int x = 0; x < 9; x++)
{
string value = tbArray[x, y].Text;
if (!String.IsNullOrEmpty(value))
{
lines.Add(Convert.ToInt32(value));
}
else
{
MessageBox.Show("請確認是否填寫完畢");
return false;
}
}
if (lines.Distinct().Count() < 9)
{
MessageBox.Show("驗證失敗,請檢查完重新提交");
return false;
}
}
return true;
}
/// <summary>
/// 校驗列
/// </summary>
public bool cellCheck()
{
for (int y = 0; y < 9; y++)
{
List<int> lines = new List<int>();
for (int x = 0; x < 9; x++)
{
string value = tbArray[y, x].Text;
if (!string.IsNullOrEmpty(value))
{
lines.Add(Convert.ToInt32(value));
}
else
{
MessageBox.Show("請確認是否填寫完畢");
return false;
}
}
if (lines.Distinct().Count() < 9)
{
MessageBox.Show("驗證失敗,請檢查完重新提交");
return false;
}
}
return true;
}
那么每一宮怎么檢驗呢?其實和行列的方法相識我們可以吧每一宮的數據計算出來放到一個數組中最后相同的方式去判斷即可
/// <summary>
/// 校驗九宮格每一宮是否有重復
/// </summary>
public bool palaceCheck()
{
int[,] newArrary = block();
for (int y = 0; y < 9; y++)
{
List<int> lines = new List<int>();
for (int x = 0; x < 9; x++)
{
lines.Add(newArrary[x, y]);
}
if (lines.Distinct().Count() < 9)
{
MessageBox.Show("驗證失敗,請檢查完重新提交");
return false;
}
}
return true;
}
//將每塊的數字保存至一個二維數組
public int[,] block()
{
int[,] b = new int[9, 9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
{
//將數獨從左至右從上至下分為9塊,求該單元格屬於第幾塊,將該塊數字保存至b第幾行
int rowOfB = i / 3 * 3 + j / 3;
//每塊有9個數字,求該數字屬於第幾個,保存至b第幾列
int columnOfB = i % 3 * 3 + j % 3;
b[rowOfB, columnOfB] = Convert.ToInt32(tbArray[i, j].Text);
}
return b;
}
整個的實現思路就完成了,下面的就是具體優化了,下面是我的最終效果
源碼:https://github.com/boPrivateSpace/Sudu
小伙伴們有更好的方式可以分享出來大家一起討論