首页 > 语言 > JavaScript > 正文

使用纯javascript实现经典扫雷游戏

2024-05-06 16:18:40
字体:大 中 小
来源:转载
供稿:网友

本文给大家分享的是个人刚开始学习javascript的时候写的仿windows经典的扫雷游戏的代码,当时只是写了下来,没加注释,这里补上,有需要的小伙伴可以参考下。

很久以前写的 当时都没写注释的 刚加上了 (尼玛,好多自己都不认识了 ... )

不足的地方就是本来想写个游戏排名的统计的,等有空了再加上(好像每次都这么说 然后就等好久好久...)

还有就是没有实现:点击第一个格子不能是雷的功能

 

 
  1. <style> 
  2. ul{padding:0;list-style:none;} 
  3. #mine{overflow:hidden;width:30px;height:30px;border:1px solid #966;} 
  4. #mine li{float:left;width:30px;height:30px;line-height:30px;text-align:center;font-size:14px;color:#222;} 
  5. #mine .mine_01{background:url(mine.gif) no-repeat;} 
  6. #mine .mine_02{background:url(mine.gif) -30px 0 no-repeat;} 
  7. #mine .mine_03{background:url(mine.gif) -60px 0 no-repeat;} 
  8. #mine .mine_04{background:url(mine.gif) -90px 0 no-repeat;} 
  9. #mine .mine_05{background:url(mine.gif) -120px 0 no-repeat;} 
  10. #mine .mine_06{background:url(mine.gif) -150px 0 no-repeat;} 
  11. #mine .mine_07{background:url(mine.gif) -180px 0 no-repeat;} 
  12.  
  13. #count{font-size:12px;} 
  14. #time{color:#900;font-weight:bold;} 
  15. </style> 
  16. <select id="wh"> 
  17. <option value="8*10">8*10</option> 
  18. <option value="10*10">10*10</option> 
  19. <option value="12*12">12*12</option> 
  20. </select> 
  21. <button id='ready'>重新开始</button> 
  22. <div id="count"> 
  23. 计时: <span id="time">0</span> 
  24. </div> 
  25.  
  26. <ul id="mine"> 
  27.  
  28. </ul> 
  29.  
  30. ie6+ ff oprea 谷歌 
  31. opera 早期版本 默认不支持document.oncontextmenu事件 没有找到好的替代方法 
  32. <script> 
  33. var $ = function(id){return document.getElementById(id)}; 
  34. window.onload=function ready(){ 
  35. var V=$('wh').value.split('*') 
  36. setMineField(Number(V[0]),Number(V[1])); 
  37.  
  38. $('wh').onchange=$('ready').onclick=function(){ 
  39. V=$('wh').value.split('*') 
  40. setMineField(Number(V[0]),Number(V[1])) 
  41. } 
  42. } 
  43. //---------------------------------------------------雷区 
  44. var mineField={ 
  45. _mineW:30, //每一个雷的宽度 必须和样式同步 
  46. _mineH:30, 
  47. _mineFieldBlock:$('mine'), 
  48. _mineFieldEle:$('mine').getElementsByTagName('li'), 
  49. _time:$('time'), 
  50.  
  51. status:0, //雷区状态 0还没开始 1开始了 已经在计时了 2游戏结束了  
  52. mineNum:0, //雷数 
  53. clearPlace:0, //统计扫除的格子; 
  54. x:0, //列 
  55. y:0, //行 
  56. density:0.2, //雷的密度 雷区密度不宜超过0.5 在有些尺寸的雷区下 过高的设置 无法生成mineMap数组 
  57. mineMap:[], //雷区的二维图,0 : 不是雷 -1 : 雷 
  58. time:-1, //计时 s 
  59. debug:false //调试模式 
  60. }//mineField object end 
  61.  
  62. function timedCount(){ 
  63. if(mineField.status!=1){return false} 
  64. mineField.time++; 
  65. mineField._time.innerHTML=mineField.time; 
  66. setTimeout("timedCount()",1000); 
  67. } 
  68. //--------------------------------------对雷的状态设置 
  69. function setThisMine(str,index){ 
  70.  
  71. var allMine=mineField._mineFieldEle; 
  72. //设置雷是否插旗 
  73. if(str=='setOn'){ 
  74. var thisMine=allMine[index]; 
  75. thisMine.on=thisMine.on<2?thisMine.on+1:0; 
  76. if(thisMine.on==1){thisMine.className='mine_03';}//插旗 
  77. else if(thisMine.on==2){thisMine.className='mine_04'}//取消插旗 
  78. else if(thisMine.on==0){thisMine.className='mine_01'}//取消插旗 
  79. return false; 
  80. } 
  81. //显示方格 
  82. if(str=='show'){ 
  83. if(Object.prototype.toString.call(index) === '[object Array]'){//显示一大片 
  84. mineField.clearPlace=mineField.clearPlace+index.length; 
  85. for(var i=0;i<index.length;i++){ 
  86. var thisMine=allMine[index[i]]; 
  87. thisMine.innerHTML=mineField.mineMap[thisMine.index]; 
  88. thisMine.className='mine_02'; 
  89. thisMine.on=3; 
  90. } 
  91. } 
  92. else{//显示单个非雷区域 
  93. mineField.clearPlace++; 
  94. allMine[index].on=3; 
  95. allMine[index].innerHTML=mineField.mineMap[index]; 
  96. allMine[index].className='mine_02'; 
  97. } 
  98. if(mineField.clearPlace==mineField.x*mineField.y-mineField.mineNum){//恭喜你 
  99. alert('恭喜你'); 
  100. for(var i=0;i<allMine.length;i++){ 
  101. if(mineField.mineMap[allMine[i].index]==-1){ 
  102. allMine[i].className='mine_07'; 
  103. } 
  104. } 
  105. mineField.status=2; 
  106. return false; 
  107. } 
  108. } 
  109. //显示全部雷 
  110. if(str=='peng'){ 
  111. for(var i=0;i<allMine.length;i++){ 
  112. if(mineField.mineMap[allMine[i].index]==-1){ 
  113. allMine[i].className=i==index?'mine_06':'mine_05'; 
  114. } 
  115. } 
  116. mineField.status=2; 
  117. } 
  118. } 
  119. //-----------------------------------------------设置行数 和 列数 
  120. function setMineField(a,b){ 
  121. var thisMineFiele=mineField._mineFieldBlock; 
  122. DivBox=document.createElement("div"), 
  123. num=a*b,k=0; 
  124. thisMineFiele.innerHTML=''; 
  125. //雷区参数调整 
  126. mineField.x=a;//有几列 
  127. mineField.y=b;//有几行 
  128. mineField.mineNum=Math.floor(a*b*mineField.density); 
  129. mineField.status=0; 
  130. mineField.time=-1; 
  131. mineField.clearPlace=0; 
  132. mineField.mineMap.length=0; 
  133. mineField._time.innerHTML=0; 
  134.  
  135. //生成雷区地图 
  136. setMineMap(); 
  137. //生成雷区(创建li) 
  138. while(k<num){ 
  139. var newLi=document.createElement("li"); 
  140. if(mineField.debug) newLi.innerHTML=mineField.mineMap[k];//作弊 
  141. newLi.className='mine_01'; 
  142. DivBox.appendChild(newLi); 
  143. k++; 
  144. } 
  145. thisMineFiele.style.height=mineField._mineW*b+'px'; 
  146. thisMineFiele.style.width=mineField._mineH*a+'px'; 
  147. thisMineFiele.innerHTML=DivBox.innerHTML; 
  148. DivBox=null; 
  149. setEvent();//事件 
  150. } 
  151. //-----------------------------------生成雷区地图 
  152. function setMineMap(){ 
  153. var num=mineField.x*mineField.y, 
  154. mineNum=mineField.mineNum, 
  155. Interval=Math.floor(num/mineNum); 
  156. for(var i=0;i<num;i++){ 
  157. if(i%Interval==0&&i<mineNum*Interval){mineField.mineMap[i]=-1;}else{mineField.mineMap[i]=0;}//雷等距离分布与数组中 
  158. } 
  159. mineField.mineMap.sort(function(){ return 0.5 - Math.random()})//打乱数组 
  160.  
  161. //判断方格周围是否都是雷 如果是的话 要重新生成雷区 从而避免成片雷区的存在 
  162. var br=0,//所在行 
  163. x=mineField.x, 
  164. L_T,T,R_T,L,R,L_B,B,R_B; 
  165. for(var i=0;i<num;i++){ 
  166. br=Math.ceil((i+1)/x); 
  167. L_T = i-x-1; 
  168. T = i-x; 
  169. R_T = i-x+1; 
  170. L = i-1; 
  171. R = i+1; 
  172. L_B = i+x-1; 
  173. B = i+x; 
  174. R_B = i+x+1; 
  175. //坐上角 如果在雷区 并且是在上一行 并且他不是雷 就进行下一方格检测 
  176. if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==0){continue} 
  177. if(T >=0&&Math.ceil((T +1)/x)==br-1&&mineField.mineMap[T ]==0){continue} 
  178. if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==0){continue} 
  179.  
  180. if(L>=0 &&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==0){continue} 
  181. if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==0){continue} 
  182.  
  183. if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==0){continue} 
  184. if(B <num&&Math.ceil((B +1)/x)==br+1&&mineField.mineMap[B ]==0){continue} 
  185. if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==0){continue} 
  186.  
  187. setMineMap(); 
  188. return false 
  189. } 
  190. //统计非雷方格周围的雷数 
  191. for(i=0;i<num;i++){ 
  192. if(mineField.mineMap[i]==-1){continue} 
  193. var thisMineNum=0 
  194. br=Math.ceil((i+1)/x); 
  195. L_T = i-x-1; 
  196. T = i-x; 
  197. R_T = i-x+1; 
  198. L = i-1; 
  199. R = i+1; 
  200. L_B = i+x-1; 
  201. B = i+x; 
  202. R_B = i+x+1; 
  203.  
  204. if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==-1){thisMineNum++;} 
  205. if(T >=0&&Math.ceil((T +1)/x)==br-1&&mineField.mineMap[T ]==-1){thisMineNum++;} 
  206. if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==-1){thisMineNum++;} 
  207.  
  208. if(L>=0 &&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==-1){thisMineNum++;} 
  209. if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==-1){thisMineNum++;} 
  210.  
  211. if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==-1){thisMineNum++;} 
  212. if(B <num&&Math.ceil((B +1)/x)==br+1&&mineField.mineMap[B ]==-1){thisMineNum++;} 
  213. if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==-1){thisMineNum++;} 
  214.  
  215. mineField.mineMap[i]=thisMineNum; 
  216. } 
  217. } 
  218. //----------------------------------雷区事件 
  219. function setEvent(){ 
  220. var thisMineFiele=mineField._mineFieldBlock, 
  221. allMine=mineField._mineFieldEle, 
  222. iMax=mineField.x*mineField.y; 
  223. for(var i=0;i<iMax;i++){ 
  224. allMine[i].index=i; 
  225. allMine[i].on=0;//0为默认 1为插旗 2为问号 3为显示 
  226. } 
  227. thisMineFiele.onmouseup=function(e){ 
  228. if(mineField.status==2){return false;} 
  229. if(mineField.status==0){mineField.status=1;timedCount();} 
  230. var e=e||window.event, 
  231. thisObj=e.target||e.srcElement; 
  232. if(thisObj.nodeName=='UL'||thisObj.on==3){return false;} 
  233.  
  234. var Btn=getButton(e); 
  235. if(Btn== 0){//左键 
  236. if(thisObj.on==1){return false;}//插旗子了 就不能点了 
  237.  
  238. if(mineField.mineMap[thisObj.index]==-1){//点雷了 
  239. setThisMine('peng',thisObj.index); 
  240. }else if(mineField.mineMap[thisObj.index]==0){//点到空地了 打开一大片 
  241. //alert('你运气真好') 
  242. var allShowMine=minesShow(thisObj.index); 
  243. setThisMine('show',allShowMine) 
  244. }else{//显示一个格子 
  245. setThisMine('show',thisObj.index) 
  246. } 
  247. } 
  248. if(Btn== 2){//右键 
  249. setThisMine('setOn',thisObj.index); 
  250. } 
  251. } 
  252. } 
  253. //--------------------------------按到空格时 显示一大片 
  254. function minesShow(I){ 
  255. var allMine=mineField._mineFieldEle, 
  256. allShowMine=[I];//保存要显示的雷的下标 
  257.  
  258. allMine[I].on=3; 
  259.  
  260. see(I);//查询下标为I的周围的方格 
  261. function see(allI){ 
  262. var _allI=[]; 
  263. if(Object.prototype.toString.call(allI) === '[object Array]'){ 
  264. for(var i=0;i<allI.length;i++){f(allI[i])} 
  265. } 
  266. else{f(allI)} 
  267. function f(thisI){ 
  268. var text, 
  269. x=mineField.x, 
  270. br, 
  271. num=x*mineField.y, 
  272. L_T,T,R_T,L,R,L_B,B,R_B; 
  273.  
  274. text='_'+allShowMine.join('_')+'_';//用来判断下标是否已经写入数组 
  275. br=Math.ceil((thisI+1)/x); 
  276. L_T = thisI-x-1; 
  277. T = thisI-x; 
  278. R_T = thisI-x+1; 
  279. L = thisI-1; 
  280. R = thisI+1; 
  281. L_B = thisI+x-1; 
  282. B = thisI+x; 
  283. R_B = thisI+x+1; 
  284. //左上角的方格 如果是在雷区 又是在上一行 又是没翻开的格子 又是空格 那么就写如_allI数组 来进行下一次检索 
  285. if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&allMine[L_T].on==0&&mineField.mineMap[L_T] == 0){_allI.push(L_T);} 
  286. if(T >=0&&Math.ceil((T +1)/x)==br-1&&allMine[T ].on==0&&mineField.mineMap[T ] == 0){_allI.push(T);} 
  287. if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&allMine[R_T].on==0&&mineField.mineMap[R_T] == 0){_allI.push(R_T);} 
  288.  
  289. if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&mineField.mineMap[L] == 0){_allI.push(L);} 
  290. if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&mineField.mineMap[R] == 0){_allI.push(R);} 
  291.  
  292. if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&allMine[L_B].on==0&&mineField.mineMap[L_B] == 0){_allI.push(L_B);} 
  293. if(B <num&&Math.ceil((B +1)/x)==br+1&&allMine[B ].on==0&&mineField.mineMap[B ] == 0){_allI.push(B);} 
  294. if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&allMine[R_B].on==0&&mineField.mineMap[R_B] == 0){_allI.push(R_B);} 
  295. //------------------------------------------------ 
  296. //左上角的的格子 如果在雷区 又是在上一行 又是没翻开的格子 又是没写入allShowMine数组的 那就写入吧 并提前标记为翻开的格子 
  297. if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&allMine[L_T].on==0&&text.indexOf('_'+L_T+'_') == -1){allShowMine.push(L_T);allMine[L_T].on=3} 
  298. if(T >=0&&Math.ceil((T +1)/x)==br-1&&allMine[T ].on==0&&text.indexOf('_'+T+'_') == -1){allShowMine.push(T);allMine[T].on=3} 
  299. if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&allMine[R_T].on==0&&text.indexOf('_'+R_T+'_') == -1){allShowMine.push(R_T);allMine[R_T].on=3} 
  300.  
  301. if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&text.indexOf('_'+L+'_') == -1){allShowMine.push(L);allMine[L].on=3} 
  302. if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&text.indexOf('_'+R+'_') == -1){allShowMine.push(R);allMine[R].on=3} 
  303.  
  304. if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&allMine[L_B].on==0&&text.indexOf('_'+L_B+'_') == -1){allShowMine.push(L_B);allMine[L_B].on=3} 
  305. if(B <num&&Math.ceil((B +1)/x)==br+1&&allMine[B ].on==0&&text.indexOf('_'+B+'_') == -1){allShowMine.push(B );allMine[B ].on=3} 
  306. if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&allMine[R_B].on==0&&text.indexOf('_'+R_B+'_') == -1){allShowMine.push(R_B);allMine[R_B].on=3} 
  307.  
  308. if(_allI.length!=0){see(_allI)} 
  309. } 
  310. } 
  311. return allShowMine 
  312. } 
  313. //-------------------------------------- 
  314. document.oncontextmenu=function(){return false;}//禁止右键菜单 
  315. function getButton(e){ 
  316. var Btn; 
  317. if(document.implementation.hasFeature('MouseEvents','2.0')){Btn=e.button;} 
  318. else{ switch(e.button){ 
  319. case 0: 
  320. case 1: 
  321. case 3: 
  322. case 5: 
  323. case 7: 
  324. Btn=0; 
  325. break; 
  326. case 2: 
  327. case 6: 
  328. Btn=2; 
  329. break; 
  330. case 4: 
  331. Btn=9; 
  332. break; 
  333. } 
  334. } 
  335. return Btn; 
  336. } 
  337. </script> 

以上所述就是本文的全部内容了,希望大家能够喜欢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选