JavaScript九宮格數獨生成算法

前幾天突然想寫一個數獨遊戲。本來以爲沒什麼難度。結果實現起來還是花了一點功夫。要做數據遊戲,並不是隨意地在九宮格里放一些數字讓玩家來填寫。一是你得保證你放的數字符合數獨的規則。二是你得保證你的數獨是有解的。

所以在開始一局數獨遊戲之前,你要生成一個完整的數獨,然後從中扣掉一些數字。讓用戶來填寫。


數獨規則:

數獨(すうどく,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 因爲文章可能會修改。

發佈了72 篇原創文章 · 獲贊 109 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章