前几天上班的时候,u大姐发现对面的后台大哥在玩纸牌游戏,开始嘲笑他玩这么古老的游戏,于是我们聊到了扫雷,然后开始思考扫雷的游戏原理,接着用闲暇时间,用js写了一个简单的扫雷小游戏。
游戏原理:点击方格出现数字,根据数字推算哪块方格有雷,找到该局设定的所有
的雷,游戏胜利,误掀雷方格,游戏结束。
思考一:该如何随机生成雷?
我设定的是100雷,格子16X30共480个方块。要均匀且随机的在这个480个小方格中生成100颗雷,毋庸置疑肯定需要Math.random()随机函数。我用的都是很笨的方法,480个方格id带着各自的行列信息,存入数组当中,然后在数组长度范围中随机出100个索引值,在这些索引对应的方格中放入地雷。
思考二:如何判断每个方格四周的地雷数量
每个方格id都带有自己的行列信息,比如方格 行列为m,n,周围八个方格的id为(m-1,n-1)(m-1,n)(m-1,n+1)(m,n-1)(m,n+1)(m+1,n-1)(m+1,n)(m+1,n+1),于是根据这些信息计算这八个方格中有雷的方格个数,最后相加填入方格(m,n)中。按照这个规则遍历380个方格就行,除去那一百颗雷。
以上基本完成了扫雷游戏的一大半,接下来是对玩家操作的思考。
思考三:点击空白区域,经典扫雷游戏会自动展开一片相连的无雷区域。
自动展开,是个连锁反应,于是想到迭代,假设点到的是一个空白方格,我们开始判断它周围的八个格子是否存在不是雷的格子,是的话则掀开,如果是数字,掀开后停止迭代,如果是空白格则继续判断这个格子的另外八个格子,循环往复下去,直到该片相连的空白区域都被掀开。
思考四:如何结束游戏
第一,掀到雷游戏结束,第二找出全部的雷,游戏胜利,全部雷找出需要已判断的雷与设定的雷数量一致,且未掀开的格子数量正好要等于设定的雷的数量(这样保证雷都是正确的,如果不正确肯定在这之前就已经炸了)
以上是对整个游戏原理的思考。下面附代码。
css:
table {
margin: 0 auto;
}
table td {
position: relative;
cursor: pointer;
border: 1px solid #000;
}
table td div {
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
box-sizing: border-box;
font-size: 24px;
font-weight: 700;
}
table td label {
width: 30px;
height: 30px;
position: absolute;
top: 0px;
left: 0px;
background: #f0f0f0;
}
table td img {
width: 30px;
height: 30px;
position: absolute;
top: 0px;
left: 0px;
}
.text1 {
color: #01ABDC;
}
.text2 {
color: #399b39;
}
.text3 {
color: #FD482C;
}
.text4 {
color: #FF0000;
}
.text5 {
color: #720838;
}
.text6 {
color: #720838;
}
.text7 {
color: #720838;
}
.text8 {
color: #720838;
}
html
<div id="table" class="tablediv">
<p><span style="float:left">剩余:<b id="surplus"></b></span>
<span style="float:right">用时:<b id="timer">0</b>s</span>
</p>
</div>
js
<script type="text/javascript" src="js/jquery-1.8.0.min.js"></script>
<script type="text/javascript">
var choseMine=[];//玩家判断出来的雷,放入的数组
var correctMine=[];//正确的雷数组
$(function() {
creatMine(100, 16,30);
clickOpen();
setInterval(function(){
var timer=$("#timer").text()*1;
timer++;
$("#timer").text(timer);
},1000)
/*右键的默认事件禁止*/
document.getElementById("table").oncontextmenu = function(e){
return false;
}
})
/*生成雷*/
function creatMine(mineNums, row, col) {
var tablehtml = [];
var td = []
$("#surplus").text(mineNums);
tablehtml.push("<table class='minetab' cellspacing='0' cellpadding='0' >");
for(var i = 0; i < row; i++) {
tablehtml.push("<tr>");
for(var j = 0; j < col; j++) {
/*添加自定义属性,mine是否有雷,text该方格周围有几个雷,label为遮罩层*/
tablehtml.push("<td class='tdm' text='0' row='" + i + "' col='" + j + "' mine='0' id='td" + i + "-" + j + "'><div></div><label></label></td>")
td.push("#td" + i + "-" + j);
}
tablehtml.push("</tr>");
}
tablehtml.push("</table>");
$("#table").append(tablehtml.join(''));
for(var m = 0; m < mineNums; m++) {
var a = Math.round(Math.random() * (td.length - 1));
$(td[a]).attr({
"mine": "1",
"text": "mine"
}).find("div").text("☹");
correctMine.push(td[a]);
td.splice(a, 1);
}
computerMineNums()
}
/*计算每个方格周围有多少雷*/
function computerMineNums() {
var td = $(".tdm");
td.each(function() {
c = 1;
var nums = 0;
if($(this).attr("mine") != "1") {
var i = $(this).attr("row") * 1,
j = $(this).attr("col") * 1;
var arry = [];
arry[0] = '#td' + (i - 1) + '-' + (j - 1),
arry[1] = '#td' + (i - 1) + '-' + (j),
arry[2] = '#td' + (i - 1) + '-' + (j + 1),
arry[3] = '#td' + (i) + '-' + (j - 1),
arry[4] = '#td' + (i) + '-' + (j + 1),
arry[5] = '#td' + (i + 1) + '-' + (j - 1),
arry[6] = '#td' + (i + 1) + '-' + (j),
arry[7] = '#td' + (i + 1) + '-' + (j + 1);
for(var m = 0; m < 8; m++) {
if($(arry[m]).length != 0 && $(arry[m]).attr('mine') == "1") {
nums = nums + 1;
}
};
$(this).attr({
"text": nums
});
nums = nums == 0 ? '' : nums;
$(this).find("div").text(nums).attr({
"class": "text" + nums
})
} else {
return;
}
})
}
/*鼠标左右键点击事件*/
function clickOpen() {
var td = $(".tdm");
td.mousedown(function(e) {
if(3 == e.which) {
if($(this).find("img").length==0){
$(this).append("<img src='img/flag.png'/>")
var s=$("#surplus").text()*1;
s--
$("#surplus").text(s);
choseMine.push("#"+$(this).attr("id"));
}else{
$(this).find("img").remove();
var s=$("#surplus").text()*1;
s++
$("#surplus").text(s);
var index=$.inArray("#"+$(this).attr("id"),choseMine);
choseMine.splice(index,1);
}
gameEnd();
} else if(1 == e.which) {
gameEnd();
var t = $(this);
if(t.attr("mine") == "0") {
t.find("label").remove();
if(t.attr("text") == "0") {
autoSpread(t)
}
} else {
gameOver();
}
}
})
td.dblclick(function(){
sureToOpen($(this))
})
}
/*如果点开空的自动展开*/
function autoSpread(This) {
var i = This.attr("row") * 1,
j = This.attr("col") * 1;
var arry = [];
arry[0] = '#td' + (i - 1) + '-' + (j - 1),
arry[1] = '#td' + (i - 1) + '-' + (j),
arry[2] = '#td' + (i - 1) + '-' + (j + 1),
arry[3] = '#td' + (i) + '-' + (j - 1),
arry[4] = '#td' + (i) + '-' + (j + 1),
arry[5] = '#td' + (i + 1) + '-' + (j - 1),
arry[6] = '#td' + (i + 1) + '-' + (j),
arry[7] = '#td' + (i + 1) + '-' + (j + 1);
for(var m = 0; m < arry.length; m++) {
var a = $(arry[m]);
if(a.find("label").length > 0) {
/*text==0表示该方格周围无雷,继续迭代*/
if(a.attr("text") == "0") {
a.find("label").remove();
autoSpread(a);
gameEnd();
} else {
if(a.attr("text") != "mine") {
a.find("label").remove();
gameEnd();
}
}
}
};
}
/*当玩家确定该区域已经没有雷的时候,双击数字掀开剩下围绕的地板*/
function sureToOpen(t){
var i = t.attr("row") * 1,
j = t.attr("col") * 1;
var nums=t.attr("text")*1;
var findnums=0;//只有当你判断出来的雷的数量已经等于该方格数字时才可以双击掀开
var arry = [];
arry[0] = '#td' + (i - 1) + '-' + (j - 1),
arry[1] = '#td' + (i - 1) + '-' + (j),
arry[2] = '#td' + (i - 1) + '-' + (j + 1),
arry[3] = '#td' + (i) + '-' + (j - 1),
arry[4] = '#td' + (i) + '-' + (j + 1),
arry[5] = '#td' + (i + 1) + '-' + (j - 1),
arry[6] = '#td' + (i + 1) + '-' + (j),
arry[7] = '#td' + (i + 1) + '-' + (j + 1);
for(var m = 0; m < arry.length; m++) {
var a = $(arry[m]);
if(a.find("img").length==1){
findnums++
}
};
if(findnums==nums){
for(var m = 0; m < arry.length; m++) {
var a = $(arry[m]);
if(a.find("img").length==0){
a.find("label").remove();
/*如果判断失误,会掀到有雷的格子,游戏结束*/
if(a.attr("mine")=="1"){
gameOver();
}
if(a.attr("text")=="0"){
autoSpread(a)
}
}
};
}else{
return;
}
}
/*游戏失败的时候,显示出所有的雷*/
function showAllmine(){
correctMine.forEach(item=>{
$(item).find("label").remove();
});
}
function gameOver(){
showAllmine();
alert("GAME OVER!");
$("#table").append("<div style='position: absolute;height: 100%;width:100%;background:rgba(0,0,0,0.5);left:0;top:0;text-align: center;'><a style='font-size:30px;color:#fff;line-height:300px;' href='javascript:window.location.reload()'>重玩</a></div>");
}
/*判断游戏胜利*/
function gameEnd(){
if(choseMine.length==correctMine.length && $(".minetab").find("label").length==correctMine.length){
alert("游戏胜利");
}
}
游戏截图
想法都是很直接很粗略就只实现效果,没有深入思考相关优化等问题,如有感兴趣的朋友欢迎提出建议。