用 JS 做一個數獨游戲(二)
在 上一篇博客 中,我們通過 Node 運行了我們的 JavaScript 代碼,在控制台中打印出來生成好的數獨終盤。為了讓我們的數獨游戲能有良好的體驗,這篇博客將會為生成好的數獨終盤做一個比較完善的界面。最終的效果如下:
你也可以訪問網頁上的 demo 進行數獨游戲的體驗。
完善挖洞算法
上一篇博客 中提到過挖洞算法,實際上那並不完整,因為算法里面只有生成數獨終盤的部分,並沒有進行挖洞處理(也就是隱藏部分格子)。為了補充完整挖洞的算法,我們在 Game
對象里面加上隨機隱藏格子的代碼:
// class Game
/**
* 挖去一部分格子,將屬性設為隱藏
*/
digBoard() {
let dig = 0, block;
for(let i = 0; i < 3; i++) {
for(let j = 0; j < 3; j++) {
for( let k = 0; k < this.digTimes; k++) {
block = this.board.getBlockGrids(i, j);
dig = Math.floor( Math.random() * 9 );
if( block[dig].isVisible() ) {
// avoid duplicated hiding
block[dig].setVisible(false);
}
}
}
}
// Utils.printAll(this.board);
}
實際上就是很簡單的取隨機數,在每個 block
塊(一個塊是一個 3x3 的大方格)中進行 n 次循環,每次循環都將隨機的數作為索引,修改塊中的 grid
對象的 visible 屬性,將其設為隱藏。
挖洞法比較簡單,通過預設的三種難度:
Game.DifficutyEasy = 1;
Game.DifficutyNormal = 2;
Game.DifficutyHard = 3;
每種難度隱藏不同數目的格子,然后只要將其顯示在界面上即可。
編寫界面代碼
界面是用網頁的方式實現的,主要的 html 代碼如下:
<div align="center">
<div id="gamediv" align="center">
</div>
<div>
<p id="result-label" class="result-normal"></p>
</div>
<hr />
<div id="time">
<p id="time-label">00:00:00</p>
</div>
<div id="difficuty">
<input type="radio" name="difficuty" value="1" onmouseup="changeDifficuty(this.value)" checked="checked" />Easy
<input type="radio" name="difficuty" value="2" onmouseup="changeDifficuty(this.value)" /> Normal
<input type="radio" name="difficuty" value="3" onmouseup="changeDifficuty(this.value)" /> Hard
</div>
<div id="buttons">
<button onclick="genBoard()" type="button">Restart game</button>
<button onclick="tu.startTimer()" type="button">Start Game</button>
</div>
</div>
<script src="./NumberPlaceCore.js"></script>
<script src="./game.js"></script>
預留了一個 div 用於顯示數獨棋盤。有用時記錄,兩個按鈕,和難度選擇。
數獨棋盤的顯示是由 JavaScript 代碼完成的。首先查找頁面中是否已有數獨棋盤,若已有棋盤,則先將其刪除,再重新創建,這樣做是為了重新開始游戲后保證頁面中只有一個棋盤。
let tBoard;
tBoard = document.getElementById("board");
if( tBoard ) {
tBoard.remove();
}
tBoard = document.createElement("table");
然后通過循環依次創建各個格子,對於未顯示的值的格子,將其用一個 input
組件表示,留給玩家填數字,最后將填充好的格子添加到預覽的 div 中:
let tr, td, grid, value;
let ginput;
for(let i = 0; i < 9; i++) {
tr = document.createElement("tr");
for(let j = 0; j < 9; j++) {
td = document.createElement("td");
value = g.getValueAt(new Number(i), new Number(j));
td.setAttribute("class", "grid-show");
if( value ) {
td.innerHTML = value;
}
else {
ginput = document.createElement("input");
inputs.push(ginput);
// ... 省略部分代碼
td.appendChild(ginput);
}
tr.appendChild(td);
}
tBoard.appendChild(tr);
}
gamediv.appendChild(tBoard);
其中有個 inputs
數組用於記錄待填的格子,每當玩家向格子中填一個數,就會調用函數 placeGrid
,將玩家填寫的值傳遞給底層的 board
對象。每次填寫數字時,都會判斷一次是否所有的待填格子都已經填充完畢:
function checkInputs() {
let valid = true;
inputs.forEach( e => {
if( !e.value ) {
valid = false;
}
});
return valid;
}
若該函數返回 true 的話,那么就應該提示用於游戲結束,給出結果,例如:
總結
這一部分其實比較簡單,涉及到較多的內容是通過 JavaScript 代碼對 DOM 進行操作。但是這部分代碼仍然有些不足:
-
計時工具必須要手動點擊
Start Game
按鈕才會開始計時,可以考慮做成玩家進入界面時就開始計時,或者開始填充第一個數時計時。 -
缺乏一些提示,可以在提高待填格子數目的情況下,通過某個操作(比如說點擊幫助按鈕顯示某個格子的值)來降低游戲難度,提高可玩性。
-
挖洞法的方法是隨機的,不能確定是否在挖完之后的棋盤上填充數字時只有唯一解。
至此,一個簡單的數獨游戲就完成了。