前端小练习:扫雷
总叙
一个简单的前端小练习。扫雷游戏的规则大家都知道,如果要用一个前端页面的形式实现这个游戏,由于扫雷盘的大小和地雷数量都是用户输入的,并且地雷的位置需要随机生成,因此需要嵌入大量JavaScript代码来辅助动态生成。相信对前端开发初学者和入门一段时间的前端开发者来说都是一次非常合适的练手。
功能展示
用户可以自定义扫雷游戏的规模。进行游戏时,用户可以左键单击一个未翻开的地块,如果埋有地雷则游戏失败;否则将该地块翻开,展示其周围8个地块中埋藏的地雷数量。
初始界面
游戏中界面
游戏结束界面
参考实现
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>扫雷</title>
</head>
<style>
body {
width: 100%;
height: 100%; /* 会在JavaScript代码中显式设定为浏览器窗口高度 */
margin: 0;
}
</style>
<body>
<div style="display: flex; align-items: center; width: 100%; height: 100%;">
<div style="margin: auto;" id="input">
</div>
<div style="margin: auto;" id="main">
</div>
</div>
</body>
<script>
function onWindowChange() {
document.body.style.height = `${window.innerHeight}px`;
}
window.addEventListener("resize", onWindowChange);
onWindowChange();
let flag = false; // 若为真,说明本局游戏已经结束
let M = 10, N = 12, C = 3; // M行N列,C个地雷
let array = buildArray();
// 0:未翻开,无雷
// 1:未翻开,有雷
// 2:已翻开,无雷
function buildArray() {
let array = Array.from(
{ length: M },
function() {
return Array.from(
{ length: N },
function() {
return 0;
}
)
}
);
function sum() {
let count = 0;
for (let i = 0; i < M; i += 1) {
for (let j = 0; j < N; j += 1) {
count += array[i][j];
}
}
return count;
}
while (sum() < C) {
const val = Math.floor(Math.random() * M * N);
const row = Math.floor(val / N);
const col = val - row * N;
array[row][col] = 1;
}
return array;
}
function onInputClick() {
const inputM = document.getElementById("input-m");
const inputN = document.getElementById("input-n");
const inputC = document.getElementById("input-c");
M = Number.parseInt(inputM.value);
N = Number.parseInt(inputN.value);
C = Number.parseInt(inputC.value);
if (Number.isNaN(M) || Number.isNaN(N) || Number.isNaN(C)) {
window.alert("非整数输入值!");
return;
}
if (M <= 0 || N <= 0 || C > M * N / 2) {
window.alert("输入值范围非法!\n检查行列数为正整数,\n且地雷数不超过格子总数的一半");
return;
}
newGame();
}
function buildInput() {
const inputDiv = document.getElementById("input");
let inputDivInnerHTML = "";
inputDivInnerHTML += `<div><label>行 数:<input type="text" value="${M}" id="input-m" /></label></div>`;
inputDivInnerHTML += `<div><label>列 数:<input type="text" value="${N}" id="input-n" /></label></div>`;
inputDivInnerHTML += `<div><label>地雷数:<input type="text" value="${C}" id="input-c" /></label></div>`;
inputDivInnerHTML += "<div style=\"text-align: center; margin: 2px;\"><input type=\"button\" οnclick=\"onInputClick()\" value=\"新游戏\" /></div>";
inputDiv.innerHTML = inputDivInnerHTML;
}
function getId(i, j) {
return `generated_block_id_${i}_${j}`;
}
function getNum(i, j) {
let count = 0;
function judge(i, j) {
if (i < 0 || i >= M || j < 0 || j >= N) {
return 0;
} else if (array[i][j] == 1) {
return 1;
} else {
return 0;
}
}
[-1, 0, 1].forEach(function(_i) {
[-1, 0, 1].forEach(function(_j) {
if (_i != 0 || _j != 0) {
count += judge(i + _i, j + _j);
}
});
});
return count;
}
function checkWin() {
let result = true;
array.forEach(function(row) {
row.forEach(function(value) {
if (value === 0) {
result = false;
}
});
});
return result;
}
function showLoss() {
for (let i = 0; i < M; i += 1) {
for (let j = 0; j < N; j += 1) {
if (array[i][j] === 1) {
const id = getId(i, j);
const block = document.getElementById(id);
block.style.backgroundColor = "red";
block.style.color = "white";
block.innerText = "×";
}
}
}
}
function onClick(i, j) {
if (flag) {
} else if (i < 0 || i >= M || j < 0 || j >= N) {
} else if (array[i][j] === 1) {
flag = true;
showLoss();
window.alert("你输了!");
} else if (array[i][j] === 2) {
} else if (array[i][j] === 0) {
array[i][j] = 2;
let block = document.getElementById(getId(i, j));
block.style.backgroundColor = "rgba(0, 0, 0, 0)";
const num = getNum(i, j);
block.innerText = `${num}`;
if (checkWin()) {
flag = true;
window.alert("你赢了!");
}
if (num === 0) {
for (let _i = -1; _i <= 1; _i += 1) {
for (let _j = -1; _j <= 1; _j += 1) {
onClick(_i + i, _j + j);
}
}
}
}
}
function buildMain() {
const mainDiv = document.getElementById("main");
let mainInnerHTML = "";
for (let i = 0; i < M; i += 1) {
var oneLine = "<div>";
for (let j = 0; j < N; j += 1) {
const style = "width: 1rem;"
+ " height: 1rem;"
+ " background-color: gray;"
+ " display: inline-block;"
+ " text-align: center;"
+ " margin: 2px;";
const oneBlock = `<div style="${style}" οnclick="onClick(${i}, ${j})" id="${getId(i, j)}"></div>`;
oneLine += oneBlock;
}
oneLine += "</div>";
mainInnerHTML += oneLine;
}
mainDiv.innerHTML = mainInnerHTML;
}
function newGame() {
flag = false;
array = buildArray();
buildInput();
buildMain();
}
newGame();
</script>
</html>