下面先上效果图
上图为最终效果,界面没美化,有点丑将就将就,,,,哈哈
不多说,上代码看注释已经注释好多,扫雷主要难度在于计算但前点击周围你的安全区域,我的想法是:递归循环计算当前点击的上、下、左、右4个格子,如果遇到空白则继续,如果遇到周围有雷则停止。
由中心点往外扩散,在扩散中肯定回遇到已经处理过的格子,则跳过continue。。。。
html代码
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>扫雷游戏</title> <style> *{padding:0;margin:0;} .main{ margin: 10px 0 0 10px; } .head{ } .body{ margin: 10px; } .minefield, .minefield tr, .minefield td{border:1px solid #000;} .minefield td{ color:#00f; 20px; height: 20px; text-align: center; cursor: pointer; } /*.minefield td:hover{background-color:#ccc;}*/ /*初始化颜色*/ .init-color{background-color: #ddd;} /*打开颜色*/ .oper-color{background-color: #fff;} /*标志颜色*/ .flag-color{background-color: #00FF00;} /*雷颜色*/ .mine-color{background-color: #f00;} fieldset{ padding: 5px; 800px; font-size: 14px; } legend{ font-size: 12px; color: #00f;; } .info{ font-size: 12px; color: #00f; } </style> </head> <body> <div class="main"> <div class="head"> <fieldset> <legend>难度</legend> <fieldset> <legend><input type="radio" name="define" checked="checked" value="1"/>系统定义</legend> <label title="9x9,10"><input type="radio" name="diff" checked="checked" value="1"/>初级</label> <label title="16x16,40"><input type="radio" name="diff" value="2"/>中级</label> <label title="16x30,99"><input type="radio" name="diff" value="3"/>高级</label> </fieldset> <fieldset> <legend><input type="radio" name="define" value="0"/>自定义</legend> <label>行数(9~24):<input type="text" id="rowNum" value="9"/></label> <label>列数(9~30):<input type="text" id="colNum" value="9"/></label> <label>雷数(10~668):<input type="text" id="mineNum" value="10"/></label> </fieldset> <button type="button" id="start">走你</button> <div class="body" id="minefield"></div> <div class="info"> <label>用时:<span id="useTime">0</span>秒</label> <span id="gameEnd"></span> </div> </fieldset> </div> </div> <script src="jquery-1.9.1.min.js"></script>
<script src="minefield.jquery.js"></script> <script> $(function(){ $('#start').bind('click', function(){ var data = {}, define = $('input[name="define"]:checked').val(), diff = $('input[name="diff"]:checked').val(), rowNum = $.trim($('#rowNum').val()), colNum = $.trim($('#colNum').val()), mineNum = $.trim($('#mineNum').val()); // 选择系统定义 if(define == '1'){ switch(diff){ case '1': data = { rowNum: 9, colNum: 9, mineNum: 10 } break; case '2': data = { rowNum: 16, colNum: 16, mineNum: 40 } break; case '3': data = { rowNum: 16, colNum: 30, mineNum: 99 } break; } }else{ // 选择自定义 if(9 > rowNum || rowNum > 24){ alert('o no~~最多只能是24行,9~24!!'); return ; } if(9 > colNum || colNum > 30){ alert('擦最多只能是30列,9~30!!'); return ; }
// 这里根据列、行计算最多雷数怎么计算?希望高手指点一二~~ if(9 > mineNum || mineNum > 668 || mineNum > (rowNum*colNum-1) ){ alert('干,这是要死的节奏啊,弄这么多雷!!'); return ; } if(rowNum && !isNaN(rowNum)){ data.rowNum = parseInt(rowNum); } if(colNum && !isNaN(colNum)){ data.colNum = parseInt(colNum); } if(mineNum && !isNaN(mineNum)){ data.mineNum = parseInt(mineNum); } }
// 清除下面计时器
clearTimeout(timeFlag); $('#gameEnd').empty();
// 游戏启动时调用 data.gameStart = function(){ useTime(1); $('#gameEnd').html('进行中....加油~使劲~'); } data.gameEnd = function(flag){ clearTimeout(timeFlag); var time = $('#useTime').text(); var str = ''; if(flag){
// 游戏通过后可以根据条件给玩家定义一些级别 if(time <= 20){ str = '你雷逼'; } if(20 < time < 50){ str = '你牛逼!'; } if(50<=time<100){ str = '恭喜你过关!'; } if(time >= 100){ str = '终于过了,不容易啊~~~'; } $('#gameEnd').html('<span style="color:#0f0;">'+str+'</span>'); }else{ $('#gameEnd').html('<span style="color:#f00;">真是彩笔~~</span>'); } }
// 启动游戏 $('#minefield').minefield(data); }); // 初始化 $('#start').click(); });
// 计时器 var timeFlag; function useTime(time){ $('#useTime').text(time); timeFlag = setTimeout(function(){ useTime(time+1); },1000); } </script> </body> </html>
minefield.jquery.js 脚本代码
/** * minefield.jquery.js * 扫雷插件 * CBQ 343330602@qq.com */ (function(window, $){ //扩展array indexOf方法 if(!Array.prototype.indexOf){ Array.prototype.indexOf = function(v){ var i , len; for(i=0,len=this.length;i<len;i++){ if(this[i] == v){ return i; } } return -1; } } function Minefield( options, dom ){ this._default = { init: true, // 是否初始化 rowNum: 9, // 多少行 colNum: 9, // 多少列 mineNum: 10, // 累的个数 gameStart: function(){}, gameEnd: function(){} }; this.settings = $.extend(true, this._default, options); // 用户配置 this.$dom = $(dom); // 当前插入扫雷dom对象 this.$table = $('<table class="minefield" border="1" cellpadding="1" cellspacing="1"><tbody></tbody></table>'); // 扫雷对象 this.mineData = []; // 雷分布数组(包括所有对象) this.minePosition = []; // 存储雷区位置(只有雷区) this.mineNumData = []; // 每个格子的雷数 this.gameStart = false; this.gameOver = false; this.tempSafeArea = []; this._init(); // 初始化 } // 扩展prototype对象 $.extend(Minefield.prototype,{ // 初始化函数 _init: function(){ var me = this; // 创建地雷分布区域 me._createMineData(); // 创建用户交互界面 me._createGame(); // 创建事件监听 me._addEvent(); }, // 创建雷的分布区域 _createMineData: function(){ var me=this, i=0, j=0, rowNum = me.settings.rowNum, colNum = me.settings.colNum, max = rowNum * colNum, mineNum = me.settings.mineNum, mineData = me.mineData, minePosition = me.minePosition, mine; for(i=0; i<max; i++){ mineData.push(0); } // 生成雷 for(j=0;j<mineNum;){ mine = Math.floor(Math.random()*(max-1)); //如果生成的雷已经存在,则重新生成 if(minePosition.indexOf(mine) == -1){ minePosition.push(mine); mineData[mine] = 1; j++; } } }, // 创建用户交互界面 _createGame: function(){ var me=this, i=0, j=0,index = 0, rowNum = me.settings.rowNum, colNum = me.settings.colNum, mineData = me.mineData, mineNumData = me.mineNumData, trs=[], tds=[], isMine = 0 ; for(i=0; i<rowNum; i++){ tds.splice(0, colNum); for(j=0; j<colNum; j++){ isMine = mineData[index]; // 获取每个格子的旁边的地雷数量,如果格子本身就是地雷则为 -1 if(isMine == 1){ mineNumData[index] = -1; }else{ mineNumData[index] = me.getMineNumber(j, i); } // tds.push('<td class="init-color"> '+index+','+isMine+','+mineNumData[index]+'</td>'); // tds.push('<td class="init-color"> '+isMine+','+mineNumData[index]+'</td>'); tds.push('<td class="init-color"> </td>'); index++; } trs.push('<tr>' + tds.join('') + '</tr>'); } me.$table.find('tbody').append(trs.join('')); me.$dom.empty().append(me.$table); }, // 绑定事件 _addEvent: function(){ var me = this;
// 只为table添加事件,这样可以减少多余不必要的事件,如果给每个格子添加点击事件那要污染多少 me.$table .bind('click', function(e){ var target = e.target, $currTr, currIndex,mineNum, name = target.nodeName.toLowerCase(), allSafeArea; var $currTd; if(name == 'td'){
// 如果游戏已经结束则无需继续往下走 if(!me.gameStart){ me.gameStart = true; me.settings.gameStart ? me.settings.gameStart():''; } $currTd = $(target); // 当前点击格子 $currTr = $currTd.parent(); // 当前点击行
// 如果格子已经被点击过,则无需再做操作 if($currTd.hasClass('open-color') || $currTd.hasClass('flag-color') || me.gameOver){ return ; } // 计算当前点击位置 currIndex = (当前行 * 列数) + 当前位置 ,因为使用table布局所以点击没行格子都会从0开始 currIndex = ($currTr.index() * me.settings.colNum) + $currTd.index(); mineNum = me.mineNumData[currIndex]; switch(mineNum){ case 0 : // 安全区 // 清空安全区,重新计算 me.tempSafeArea.splice(0,me.tempSafeArea.length);
// 计算当前点击格子旁边的安全格子 me._getLRUDSafeArea($currTd.index(), $currTr.index());
// 修改安全格子背景颜色 me._setSafeBgColor( me.tempSafeArea ); if(me._isWin()){ // 获胜事件 me.settings.gameEnd ? me.settings.gameEnd(true):''; } break; case -1 : // 雷区 $currTd.addClass('mine-color'); me.gameOver = true; me._showMine(); // 失败事件 me.settings.gameEnd ? me.settings.gameEnd(false):''; break; default: // 其他 $currTd.removeClass('init-color').addClass('open-color').text(mineNum); if(me._isWin()){ // 获胜事件 me.settings.gameEnd ? me.settings.gameEnd(true):''; } break; } } }) .bind('contextmenu', function(e){ // 右击事件,用于标识自己认为的雷区,或解除 e.stopPropagation(); var target = e.target, name = target.nodeName.toLowerCase(); var $currTd; if(name == 'td' && !me.gameOver){ $currTd = $(target); if( $currTd.hasClass('init-color') ){ $currTd.removeClass('init-color').addClass('flag-color'); return false; } if( $currTd.hasClass('flag-color') ){ $currTd.removeClass('flag-color').addClass('init-color'); return false; } } return false; }) ; }, // 获取每个单元的上、下、左、右、左上、右上、左下、右下八个位置的雷分布 getMineNumber: function( x, y ){ var me = this, mineData = me.mineData, rowNum = me.settings.rowNum, colNum = me.settings.colNum, index = y * colNum + x, upNum = parseInt(index)-parseInt(colNum), downNum = parseInt(index)+parseInt(colNum), mineNum = 0; // 获取左值,如果是第一个值则不计算 if(x > 0 && mineData[index - 1] == 1){ mineNum += 1; } // 获取右值,如果是每行的最后一个值则不计算 if(x<(colNum-1) && mineData[index + 1] == 1){ mineNum += 1; } // 获取上方值,如果是第一行则不计算 if(y>0 && mineData[upNum] == 1){ mineNum += 1; } // 获取下方值,如果是第一行则不计算 if(y<(rowNum-1) && mineData[downNum] == 1){ mineNum += 1; } // 获取左上角一个值,如果是第一个值则不计算 if(x>0 && y>0 && mineData[upNum-1] == 1){ mineNum += 1; } // 获取右上角一个值,如果是第一个值则不计算 if(x<(colNum-1) && y>0 && mineData[upNum+1] == 1){ mineNum += 1; } // 获取左下角一个值,如果是第一个值则不计算 if(x>0 && y<(rowNum-1) && mineData[downNum-1] == 1){ mineNum += 1; } // 获取右下角一个值,如果是第一个值则不计算 if(x<(colNum-1) && y<(rowNum-1) && mineData[downNum+1] == 1){ mineNum += 1; } return mineNum; },
// 获取当前格子的上(Up)、下(Down)、左(Left)、右(Right)安全区 _getLRUDSafeArea: function(x, y){ var me = this, mineNumData = me.mineNumData, rowNum = parseInt(me.settings.rowNum), colNum = parseInt(me.settings.colNum), index = y * colNum + x, mineNum; // 如果已经登记过的安全区,则不再查看 if(me.tempSafeArea.indexOf(index) != -1){ return ; } mineNum = mineNumData[index]; // 记录安全区 me.tempSafeArea.push(index); // 如果但前安全区周边没有任何危险,则查看上下左右区域是否安全 if(mineNum == 0 ){ // 左边 if(x>0){ me._getLRUDSafeArea(x-1, y); } // 右边 if(x<(colNum-1)){ me._getLRUDSafeArea(x+1, y); } // 上边 if(y>0){ me._getLRUDSafeArea(x, y-1); } // 下边 if(y<(rowNum-1)){ me._getLRUDSafeArea(x, y+1); } } }, _setSafeBgColor: function( allSafeArea ){ var me = this, mineNumData = me.mineNumData, i, len, $tds = me.$table.find('td'), $td; for(i=0,len=allSafeArea.length; i<len; i++){ $td = $($tds.get(allSafeArea[i])); if($td.hasClass('init-color')){ $td.removeClass('init-color').addClass('open-color').html(mineNumData[allSafeArea[i]] || ' '); } } },
// 每次点击判断是否赢了 _isWin: function(){ var me = this; if(me.$table.find('.init-color,.flag-color').size() == me.settings.mineNum){ return true; } return false; },
// 显示所有雷区 _showMine: function(){ var me = this, i, len, minePosition = me.minePosition, $tds = me.$table.find('td'); for(i=0,len=minePosition.length; i<len; i++){ $($tds[minePosition[i]]).addClass('mine-color'); } } }); // jquery插件 $.fn.minefield = function( options ){ return this.each(function(index, dom){ new Minefield( options, dom ); }); }; })(window, jQuery);