数独游戏 js
规则:玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字;保证每一行,每一列,每个宫的数字刚好含1-9,并且不重复。
一、步骤:
生成格子 —— 生成9×9满足规则的数字 —— 置空一定个数的格子,让玩家选填 —— 检查每次选填结果是否有误 —— 待全部空格被填完,检查结果;
1、生成格子
为方便获取格子的值,给每个格子一个对应的key。
我们给左上点第一个格子的key值为11,那右下角的格子key值则为99,如图:
下面是代码生成的格子:
2、生成数独数组
因为要保证每一行,每一列,每一宫,均含1-9且不重复,如果从第一个值 开始随机给值,有点无从下手,并且很慢。
考虑到斜对角的三个宫的数字,是没有任何关系的,所以先随机出来,这样比较方便。
然后从第一行还没有赋值的格子开始,获取格子所在行,所在列,所在宫已经有值的数字,排除这些数字后,从可用的数字中随机取一个。直到所以的格子都有值。
3、置空一定个数的格子,让玩家选填
4、每次输入后,检查数字是否符合规则,而不是把玩家输入的数字和自己之前生成数字做对比,因为要考虑到多种解法。
蓝色是无误,标红是已存在
二、附源码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> * { margin: 0; padding: 0; box-sizing: border-box; } ul, ol { list-style: none; } .shudu { width: 226px; height: 226px; border-top: 1px solid #000; border-left: 1px solid #000; margin: 10px auto; } .shudu li { width: 100%; height: 25px; border-bottom: 1px solid; border-bottom-color: #ccc; } .shudu li:nth-of-type(3n) { border-bottom-color: #000; } .shudu li span { float: left; width: 25px; height: 25px; line-height: 25px; text-align: center; border-right: 1px solid; border-right-color: #ccc; } .shudu li span:nth-of-type(3n) { border-right-color: #000; } .input { color: #08e; } .err { color: red; } </style> </head> <body> <div><label for="">计时:</label><span id="time"></span></div> <div class="shudu"> <ul> </ul> </div> <script src="js/jquery-1.12.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var sd = { blockNum: 30, //空白格子的数量 sdArr: [], //生成的数组 cacheArr: [], //保存数组 init: function(blockNum) { sd.blockNum = blockNum || sd.blockNum; timer = setInterval(timing, 1000); sd.createDom(); sd.createSdArr(); sd.createBlock(sd.blockNum); sd.checkOne(); }, getRandom(n) { //生成随机正整数 return Math.floor(Math.random() * n + 1); }, checkOne: function() { //单个检查 $(".shudu ul span[contenteditable=true]").keyup(function(event) { var val = $(this).html(), idx = $(this).data().index + ""; var reStr = /^[1-9]{1}$/; if(!reStr.test(val)) { $(this).html(''); } else { var i = parseInt(idx.split('')[0]), j = parseInt(idx.split('')[1]); var xArr = sd.getXArr(j, sd.sdArr), yArr = sd.getYArr(i, sd.sdArr), palace = sd.getPalace(i, j, sd.sdArr); var beExistArr = xArr.concat(yArr, palace); if($.inArray(+val, beExistArr) > -1) { $(this).removeClass('input').addClass('err'); } else { $(this).removeClass('err').addClass('input'); sd.sdArr[parseInt(i + '' + j)] = +val; var done = $(".shudu ul span.input").length, err = $(".shudu ul span.err").length; if(done == sd.blockNum && err == 0) { //检查空白格是否全部输入 window.clearInterval(timer); alert('good! 用时:'+$("#time").html()+''); $("#time").html('00:00:00'); sec = 0; min = 0; hour = 0; sd.init(sd.blockNum); } } } }); }, createBlock: function(num) { //随机空白格 var blockArr = []; var numArr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; for(var k = 0; k < num; k++) { var n; do { n = parseInt(numArr[sd.getRandom(9) - 1] + '' + numArr[sd.getRandom(9) - 1]); } while ($.inArray(n, blockArr) > -1); //如果已经选过此格子,再随机选一次 blockArr.push(n); var dom = $(".shudu ul span[data-index='" + n + "']"); dom.attr('contenteditable', true).text(''); //元素可编辑并置空 sd.sdArr[n] = "block"; } }, createDom: function() { //生成格子 var ul = $(".shudu ul").empty(); for(var i = 0; i < 9; i++) { var span = ''; for(var j = 0; j < 9; j++) { span += '<span data-index="' + (j + 1) + "" + (i + 1) + '"></span>'; } var li = '<li>' + span + '</li>'; ul.append(li); } }, createSdArr: function() { //生成数独数组 sd.sdArr = []; sd.diagonal(2, 2); sd.diagonal(5, 5); sd.diagonal(8, 8); var allNum = [1, 2, 3, 4, 5, 6, 7, 8, 9]; for(var i = 1; i <= 9; i++) { for(var j = 1; j <= 9; j++) { if(!sd.sdArr[parseInt(i + '' + j)]) { var xArr = sd.getXArr(j, sd.sdArr), //获取所在行数字 yArr = sd.getYArr(i, sd.sdArr), //获取所在列数字 palace = sd.getPalace(i, j, sd.sdArr); //获取所在宫数字 var beExistArr = xArr.concat(yArr, palace); var ableArr = sd.arrMinus(allNum, beExistArr); //得到可用数字 if(ableArr.length == 0) { sd.createSdArr(); return; } var theOne = ableArr[sd.getRandom(ableArr.length) - 1]; sd.sdArr[parseInt(i + '' + j)] = theOne; } } } sd.cacheArr = sd.sdArr; var spans = $(".shudu ul span"); $.each(spans, function(i, v) { var idx = parseInt(v.dataset.index); if(sd.sdArr[idx]){ v.innerText = sd.sdArr[idx]; } }); }, diagonal: function(x, y) { //对角线三宫随机数字 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(x + '' + y); 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++) { sd.sdArr[thIndexArr[a]] = sortedNumArr[a]; } }, 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; }, getPalace: function(i, j, sdArr) { //获取所在宫 var arr = []; var cenNum = sd.getPalaceCenter(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; }, getPalaceCenter: 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; }, arrMinus: function(arr1, arr2) { var resArr = [], len = arr1.length; for(var i = 0; i < len; i++) { if($.inArray(arr1[i], arr2) < 0) { resArr.push(arr1[i]); } } return resArr; } } $("#time").html('00:00:00'); var sec = 0, //秒 min = 0, //分 hour = 0; //时 var timer; function treg(arg){ return arg < 10 ? ("0" + arg) : arg; } function timing() { var time = treg(hour) + ":" +treg(min) + ":" + treg(sec); $("#time").html(time); sec++; if(sec > 59) { sec = 0; min++; } if(min > 59) { sec = 0; hour++; } } sd.init(35); </script> </body> </html>