前幾天突然想寫一個數獨遊戲。本來以爲沒什麼難度。結果實現起來還是花了一點功夫。要做數據遊戲,並不是隨意地在九宮格里放一些數字讓玩家來填寫。一是你得保證你放的數字符合數獨的規則。二是你得保證你的數獨是有解的。
所以在開始一局數獨遊戲之前,你要生成一個完整的數獨,然後從中扣掉一些數字。讓用戶來填寫。
數獨規則:
數獨(すうどく,Sūdoku),是源自18世紀瑞士發明,流傳到美國,再由日本發揚光大的一種數學遊戲。是一種運用紙、筆進行演算的邏輯遊戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩餘空格的數字,並滿足每一行、每一列、每一個粗線宮內的數字均含1-9,不重複。 數獨盤面是個九宮,每一宮又分爲九個小格。在這八十一格中給出一定的已知數字和解題條件,利用邏輯和推理,在其他的空格上填入1-9的數字。使1-9每個數字在每一行、每一列和每一宮中都只出現一次,所以又稱“九宮格”。
本文介紹生成數獨的算法。
先來一個九宮格的圖,方便看着它思考。
一共九九八十一個格子,算法主要思路。爲每一個格子填上一個符合數獨條件的值。
數組以左上角爲圓點,橫爲x軸,豎爲y軸 在數組中對應的下標爲:
11 21 31 41 51 61 71 81 91
12 22 32 42 52 62 72 82 92
13 23 33 43 53 63 73 83 93
14 24 34 44 54 64 74 84 94
15 25 35 45 55 65 75 85 95
16 26 36 46 56 66 76 86 96
17 27 37 47 57 67 77 87 97
18 28 38 48 58 68 78 88 98
19 29 39 49 59 69 79 89 99
隨機生成一個值填入數組。
隨機生成下一個值。判斷不在數組中其所在橫、豎、三宮格對應的下標裏。獲取橫、豎、三宮的的下標使用過的值,算其可用的數字。隨機取可用數字中的一個。如果沒有可用數字,本次循環失敗,回到開始重新算。
循環生成下一個值
……
直到81個下標位置都有符合條件的值。數獨數組就生成完畢了!
然後我做了一點優化:
其實對於最開始的一些值,可以不用判斷那麼多,只需要把1-9亂序,依次放入九個格子中去。 我的做法是先在對角線上的三宮亂序依次放入1-9。就可以只用很少的計算量得出來27個值。因爲對角線上填的值,不會互相影響。沒有橫豎的影響。
生成了這27個值之後,再使用上面的算法來計算,計算量少了不少,計算效率和成功率就大了很多了。
隨機測試了幾次,看我console出來的結果:
數獨生成完畢,耗時:0.972秒!
數獨生成完畢,耗時:0.081秒!
數獨生成完畢,耗時:1.305秒!
數獨生成完畢,耗時:0.102秒!
數獨生成完畢,耗時:0.065秒!
數獨生成完畢,耗時:0.813秒!
……
還是不算慢吧。
核心代碼,從完整的裏面扣出來的:
createSdArr:function(){
//生成數獨數組。
var that = this;
try{
this.sdArr = [];
this.setThird(2,2);
this.setThird(5,5);
this.setThird(8,8);
var allNum = [1,2,3,4,5,6,7,8,9];
outerfor:
for(var i=1;i<=9;i++){
innerfor:
for(var j=1;j<=9;j++){
if(this.sdArr[parseInt(i+''+j)]){
continue innerfor;
}
var XArr = this.getXArr(j,this.sdArr);
var YArr = this.getYArr(i,this.sdArr);
var thArr = this.getThArr(i,j,this.sdArr);
var arr = getConnect(getConnect(XArr,YArr),thArr);
var ableArr = arrMinus(allNum,arr);
if(ableArr.length == 0){
this.createSdArr();
return;
break outerfor;
}
var item;
//如果生成的重複了就重新生成。
do{
item = ableArr[getRandom(ableArr.length)-1];
}while(($.inArray(item, arr)>-1));
this.sdArr[parseInt(i+''+j)] = item;
}
}
this.backupSdArr = this.sdArr.slice();
}catch(e){
//如果因爲超出瀏覽器的棧限制出錯,就重新運行。
that.createSdArr();
}
},
getXArr:function(j,sdArr){
//獲取所在行的值。
var arr = [];
for(var a =1;a<=9;a++){
if(this.sdArr[parseInt(a+""+j)]){
arr.push(sdArr[parseInt(a+""+j)])
}
}
return arr;
},
getYArr:function(i,sdArr){
//獲取所在列的值。
var arr = [];
for(var a =1;a<=9;a++){
if(sdArr[parseInt(i+''+a)]){
arr.push(sdArr[parseInt(i+''+a)])
}
}
return arr;
},
getThArr:function(i,j,sdArr){
//獲取所在三宮格的值。
var arr = [];
var cenNum = this.getTh(i,j);
var thIndexArr = [cenNum-11,cenNum-1,cenNum+9,cenNum-10,cenNum,cenNum+10,cenNum-9,cenNum+1,cenNum+11];
for(var a =0;a<9;a++){
if(sdArr[thIndexArr[a]]){
arr.push(sdArr[thIndexArr[a]]);
}
}
return arr;
},
getTh:function(i,j){
//獲取所在三宮格的中間位座標。
var cenArr = [22,52,82,25,55,85,28,58,88];
var index = (Math.ceil(j/3)-1) * 3 +Math.ceil(i/3) -1;
var cenNum = cenArr[index];
return cenNum;
},
setThird:function(i,j){
//爲對角線上的三個三宮格隨機生成。
var numArr = [1,2,3,4,5,6,7,8,9];
var sortedNumArr= numArr.sort(function(){return Math.random()-0.5>0?-1:1});
var cenNum = parseInt(i+''+j);
var thIndexArr = [cenNum-11,cenNum-1,cenNum+9,cenNum-10,cenNum,cenNum+10,cenNum-9,cenNum+1,cenNum+11];
for(var a=0;a<9;a++){
this.sdArr[thIndexArr[a]] = sortedNumArr[a];
}
}
生成的完整數獨是這樣:
這篇就介紹這麼多。歡迎留言交流。
也可以自己去試玩下這個數獨:
http://runningls.com/demos/2016/soduku/
看完整代碼可以到我的github。
https://github.com/liusaint/games/tree/master/soduku
最近剛開始弄的,五月份要去上海找工作了,要show code了啊。
生成數獨數組之後,後續工作是從中扣除一定數量的值,然後讓用戶填寫。
然後檢測用戶輸入是不是符合數獨規則。(並不是直接和我們生成的數組對比,考慮到可能有不同的解法。)
另外解數獨跟生成數獨的方法也是差不多的。
轉載請註明出處:http://blog.csdn.net/liusaint1992/article/details/51147149 因爲文章可能會修改。