文章目录
- 游戏思路
- 游戏代码
- 游戏优化
- 试玩路径
本文章是小游戏制作系列,其他系列-数独篇章
游戏思路
数独游戏的规律是,同一行,同一列,每3*3的9宫格中只能存在1-9的数字,不能重复。
步骤1:知道游戏规则之后,我们可以开始着手创建一个符合游戏规则的数独棋盘(它的本质是一个二维数组)。
步骤2:根据选择的难度随机挖空对应数目的格子,留给用户填写。简单模式挖空4格子,中等模式挖空6格子等。
步骤3:用户点击提交时判断做一个简单的输入验证(是否输入为空,是否正填写1-9范围的数字)。
步骤4:通过步骤3的初步验证之后,验证用户填写完的数独数组是否是有效的。(有两种方案,方案一是保步骤1的数独数组与现在的数独数组做对比;方案二:将现在新的组成的数独数组通过一个检验是否有效数独的方法校验),此处选择的是方案二。
步骤5:根据步骤4的校验结果,判断是否通过游戏,并进行对应的提示。通过之后得分改变。
最终游戏界面如下:
游戏代码
html代码
js代码重点方法
// 画布初始化
function init(contentId){// 创建一个二维数据 9*9来进行画布渲染for(let i=0;i<9;i++){sdArr[i] = new Array(9).fill(0)}// 创建数独二维数组// 数独规则,每一行,每一列数字不能重复(1-9)。每一个3*3的正方形格子中,数字不能重复(1-9)for(let row=0;row<9;row++){let column = 0;let numArr = [...numArray];let newObj = new Map();while(column<9){if(column == 0){newObj = new Map();}let cellItem = Math.floor(Math.random()*numArr.length);newObj[column]?'':newObj[column] = new Set();newObj[column].add(numArr[cellItem])if(!rule(numArr[cellItem],row,column)){ if(numArr.length == newObj[column].size){// 表示陷入死循环中,需要重新开始计算这一对数据numArr = [...numArray];column=0;sdArr[row] = new Array(9).fill(0)}}else{sdArr[row][column] = numArr[cellItem];numArr.splice(cellItem,1)column++;}}}hollowing(contentId,sdArr);
}
/*** @description 生成数独数组时需要判断数独的合理性,一行不能出现重复数字,一列不能出现重复数字,一个3*3方格不能出现重复的数字
*/
function rule(number,row,column){// 一列for(let i=0;i<9;i++){if( sdArr[i][column] == number || sdArr[row][i] == number){return false;}}// 一个3*3let rowStart = Math.floor(row/3)*3;let cloumStart = Math.floor(column/3)*3;let rowS = row%3;if(rowS != 0){for(let i=0;i<3;i++){for(let j=0;j<3;j++){if(sdArr[rowStart+i][cloumStart+j] == number){return false}}}}return true;
}
/*** @description 用于检验是否是一个合法的数独数组* */
function submitRule(Arr){let blockSet = {};let blockFlag =true;for(let i=0;i<9;i++){let columSet = new Set();let rowIndex = Math.floor(i/3)*3;for(let j=0;j<9;j++){let columnIndex = Math.floor(j/3)*3;columSet.add(Arr[i][j]);if(i%3 == 0 && j%3 == 0){blockSet[rowIndex+''+columnIndex] = new Set();}blockSet[rowIndex+''+columnIndex].add(Arr[i][j]);}let rowSet = new Set(Arr[i]);if(rowSet.size<9 || columSet.size<9){return false;}}for(item in blockSet){if(item.size<9){blockFlag = false;}}return blockFlag
}
/*** @description 用于渲染游戏区域* @param{contentId} 需要渲染的游戏区域的元素id* @param{Arr} 用于渲染游戏区域的数组对象* */
function renderGame(contentId,Arr){const content = document.getElementById(contentId);let str = '';for(let i=0;i<9;i++){str +=`<div class='canvas-row'>`for(let j=0;j<9;j++){if(Arr[i][j]){str += `<span class='cell'>${Arr[i][j]}</span>`}else{str += `<span class='cell cell${i}${j} cell-color' contenteditable="true">${Arr[i][j]}</span>`}}str +=`</div>`}content.innerHTML = str;
}
/*** @description 进行游戏挖空,根据选定难度来* */ let hollowingArr = [];
function hollowing(contentId,Arr){let num = difficulty[difficultyType].nums;//提取难度对应的空缺位数while(num>0){// 取随机数let row = Math.floor(Math.random()*9)let colum = Math.floor(Math.random()*9)if(hollowingArr.length == 0){hollowingArr.push({row,colum});Arr[row][colum] = ''num--;}else{let hasRow = hollowingArr.some((item)=>{return item.row == row && item.colum == colum});if(!hasRow){hollowingArr.push({row,colum});Arr[row][colum] = ''num--;}}}renderGame(contentId,Arr);
}
/*** @description 游戏可编辑块输入内容捕捉* */ function editable(className){// 获取所有填写的cell元素,组合数组元素let num = difficulty[difficultyType].nums;//提取难度对应的空缺位数let flag = true;for(i=0;i<num;i++){const editE = document.getElementsByClassName(className+hollowingArr[i].row+hollowingArr[i].colum)[0];if(editE.innerText){if(numArray.includes(editE.innerText)){sdArr[hollowingArr[i].row][hollowingArr[i].colum] = editE.innerText;}else{alert("请正确填写第"+(parseInt(hollowingArr[i].row)+1)+'行,第'+(parseInt(hollowingArr[i].colum)+1)+'列内容');flag = false;return false;}}else{alert("第"+(parseInt(hollowingArr[i].row)+1)+'行,第'+(parseInt(hollowingArr[i].colum)+1)+'列未填写');flag = false;return false;}}return flag;}
游戏优化
不过现在多次重新开始游戏,可能是init方法里面的算法问题,可能会存在卡顿现象。后续还需要进行算法优化
界面优化(2023-1-6):在选中用户点击可输入块进行输入时,界面上提示该列,该行,该3*3九宫格范围,让用户比较明显的知道要对比的数据。我们需要css中准备一个class类名‘cell-item’,然后在事件判断过程中,动态的添加到元素上,展示效果如图。
重点代码如下:
/*** @description 输入过程中,样式动态修改
*/
function editCell(){document.getElementById("canvasContent").addEventListener("click", function(e) {// 检查事件源e.targe是否为目标if(e.target && e.target.className.includes('cell-color')) {let elementData = e.target.getAttribute('data');// 获取到当前点击的行列坐标let row = elementData[0];let colum = elementData[2];// 现在需要做样式渲染let cellList = document.getElementsByClassName("cell");for(let i=0;i<cellList.length;i++){cellList[i].classList.remove("cell-item")// 同行同列开始渲染颜色if((i>=row*9 && i<(row*9+9)) || i%9 == colum){cellList[i].classList.add("cell-item")}// 3*3格子开始渲染颜色if(Math.floor(i/27) == Math.floor(row/3)){if(Math.floor((i%9)/3)>=Math.floor(colum/3) && Math.floor((i%9)/3)<Math.floor(colum/3)+1){cellList[i].classList.add("cell-item")}}}}});
}
github地址
试玩路径
试玩路径