使用纯javascript实现经典扫雷游戏
很久以前写的当时都没写注释的刚加上了(尼玛,好多自己都不认识了...)
不足的地方就是本来想写个游戏排名的统计的,等有空了再加上(好像每次都这么说然后就等好久好久...)
还有就是没有实现:点击第一个格子不能是雷的功能
<style>
ul{padding:0;list-style:none;}
#mine{overflow:hidden;width:30px;height:30px;border:1pxsolid#966;}
#mineli{float:left;width:30px;height:30px;line-height:30px;text-align:center;font-size:14px;color:#222;}
#mine.mine_01{background:url(mine.gif)no-repeat;}
#mine.mine_02{background:url(mine.gif)-30px0no-repeat;}
#mine.mine_03{background:url(mine.gif)-60px0no-repeat;}
#mine.mine_04{background:url(mine.gif)-90px0no-repeat;}
#mine.mine_05{background:url(mine.gif)-120px0no-repeat;}
#mine.mine_06{background:url(mine.gif)-150px0no-repeat;}
#mine.mine_07{background:url(mine.gif)-180px0no-repeat;}
#count{font-size:12px;}
#time{color:#900;font-weight:bold;}
</style>
<selectid="wh">
<optionvalue="8*10">8*10</option>
<optionvalue="10*10">10*10</option>
<optionvalue="12*12">12*12</option>
</select>
<buttonid='ready'>重新开始</button>
<divid="count">
计时:<spanid="time">0</span>
</div>
<ulid="mine">
</ul>
ie6+ffoprea谷歌
opera早期版本默认不支持document.oncontextmenu事件没有找到好的替代方法
<script>
var$=function(id){returndocument.getElementById(id)};
window.onload=functionready(){
varV=$('wh').value.split('*')
setMineField(Number(V[0]),Number(V[1]));
$('wh').onchange=$('ready').onclick=function(){
V=$('wh').value.split('*')
setMineField(Number(V[0]),Number(V[1]))
}
}
//---------------------------------------------------雷区
varmineField={
_mineW:30,//每一个雷的宽度必须和样式同步
_mineH:30,
_mineFieldBlock:$('mine'),
_mineFieldEle:$('mine').getElementsByTagName('li'),
_time:$('time'),
status:0,//雷区状态0还没开始1开始了已经在计时了2游戏结束了
mineNum:0,//雷数
clearPlace:0,//统计扫除的格子;
x:0,//列
y:0,//行
density:0.2,//雷的密度雷区密度不宜超过0.5在有些尺寸的雷区下过高的设置无法生成mineMap数组
mineMap:[],//雷区的二维图,0:不是雷-1:雷
time:-1,//计时s
debug:false//调试模式
}//mineFieldobjectend
functiontimedCount(){
if(mineField.status!=1){returnfalse}
mineField.time++;
mineField._time.innerHTML=mineField.time;
setTimeout("timedCount()",1000);
}
//--------------------------------------对雷的状态设置
functionsetThisMine(str,index){
varallMine=mineField._mineFieldEle;
//设置雷是否插旗
if(str=='setOn'){
varthisMine=allMine[index];
thisMine.on=thisMine.on<2?thisMine.on+1:0;
if(thisMine.on==1){thisMine.className='mine_03';}//插旗
elseif(thisMine.on==2){thisMine.className='mine_04'}//取消插旗
elseif(thisMine.on==0){thisMine.className='mine_01'}//取消插旗
returnfalse;
}
//显示方格
if(str=='show'){
if(Object.prototype.toString.call(index)==='[objectArray]'){//显示一大片
mineField.clearPlace=mineField.clearPlace+index.length;
for(vari=0;i<index.length;i++){
varthisMine=allMine[index[i]];
thisMine.innerHTML=mineField.mineMap[thisMine.index];
thisMine.className='mine_02';
thisMine.on=3;
}
}
else{//显示单个非雷区域
mineField.clearPlace++;
allMine[index].on=3;
allMine[index].innerHTML=mineField.mineMap[index];
allMine[index].className='mine_02';
}
if(mineField.clearPlace==mineField.x*mineField.y-mineField.mineNum){//恭喜你
alert('恭喜你');
for(vari=0;i<allMine.length;i++){
if(mineField.mineMap[allMine[i].index]==-1){
allMine[i].className='mine_07';
}
}
mineField.status=2;
returnfalse;
}
}
//显示全部雷
if(str=='peng'){
for(vari=0;i<allMine.length;i++){
if(mineField.mineMap[allMine[i].index]==-1){
allMine[i].className=i==index?'mine_06':'mine_05';
}
}
mineField.status=2;
}
}
//-----------------------------------------------设置行数和列数
functionsetMineField(a,b){
varthisMineFiele=mineField._mineFieldBlock;
DivBox=document.createElement("div"),
num=a*b,k=0;
thisMineFiele.innerHTML='';
//雷区参数调整
mineField.x=a;//有几列
mineField.y=b;//有几行
mineField.mineNum=Math.floor(a*b*mineField.density);
mineField.status=0;
mineField.time=-1;
mineField.clearPlace=0;
mineField.mineMap.length=0;
mineField._time.innerHTML=0;
//生成雷区地图
setMineMap();
//生成雷区(创建li)
while(k<num){
varnewLi=document.createElement("li");
if(mineField.debug)newLi.innerHTML=mineField.mineMap[k];//作弊
newLi.className='mine_01';
DivBox.appendChild(newLi);
k++;
}
thisMineFiele.style.height=mineField._mineW*b+'px';
thisMineFiele.style.width=mineField._mineH*a+'px';
thisMineFiele.innerHTML=DivBox.innerHTML;
DivBox=null;
setEvent();//事件
}
//-----------------------------------生成雷区地图
functionsetMineMap(){
varnum=mineField.x*mineField.y,
mineNum=mineField.mineNum,
Interval=Math.floor(num/mineNum);
for(vari=0;i<num;i++){
if(i%Interval==0&&i<mineNum*Interval){mineField.mineMap[i]=-1;}else{mineField.mineMap[i]=0;}//雷等距离分布与数组中
}
mineField.mineMap.sort(function(){return0.5-Math.random()})//打乱数组
//判断方格周围是否都是雷如果是的话要重新生成雷区从而避免成片雷区的存在
varbr=0,//所在行
x=mineField.x,
L_T,T,R_T,L,R,L_B,B,R_B;
for(vari=0;i<num;i++){
br=Math.ceil((i+1)/x);
L_T=i-x-1;
T=i-x;
R_T=i-x+1;
L=i-1;
R=i+1;
L_B=i+x-1;
B=i+x;
R_B=i+x+1;
//坐上角如果在雷区并且是在上一行并且他不是雷就进行下一方格检测
if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==0){continue}
if(T>=0&&Math.ceil((T+1)/x)==br-1&&mineField.mineMap[T]==0){continue}
if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==0){continue}
if(L>=0&&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==0){continue}
if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==0){continue}
if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==0){continue}
if(B<num&&Math.ceil((B+1)/x)==br+1&&mineField.mineMap[B]==0){continue}
if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==0){continue}
setMineMap();
returnfalse
}
//统计非雷方格周围的雷数
for(i=0;i<num;i++){
if(mineField.mineMap[i]==-1){continue}
varthisMineNum=0
br=Math.ceil((i+1)/x);
L_T=i-x-1;
T=i-x;
R_T=i-x+1;
L=i-1;
R=i+1;
L_B=i+x-1;
B=i+x;
R_B=i+x+1;
if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==-1){thisMineNum++;}
if(T>=0&&Math.ceil((T+1)/x)==br-1&&mineField.mineMap[T]==-1){thisMineNum++;}
if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==-1){thisMineNum++;}
if(L>=0&&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==-1){thisMineNum++;}
if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==-1){thisMineNum++;}
if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==-1){thisMineNum++;}
if(B<num&&Math.ceil((B+1)/x)==br+1&&mineField.mineMap[B]==-1){thisMineNum++;}
if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==-1){thisMineNum++;}
mineField.mineMap[i]=thisMineNum;
}
}
//----------------------------------雷区事件
functionsetEvent(){
varthisMineFiele=mineField._mineFieldBlock,
allMine=mineField._mineFieldEle,
iMax=mineField.x*mineField.y;
for(vari=0;i<iMax;i++){
allMine[i].index=i;
allMine[i].on=0;//0为默认1为插旗2为问号3为显示
}
thisMineFiele.onmouseup=function(e){
if(mineField.status==2){returnfalse;}
if(mineField.status==0){mineField.status=1;timedCount();}
vare=e||window.event,
thisObj=e.target||e.srcElement;
if(thisObj.nodeName=='UL'||thisObj.on==3){returnfalse;}
varBtn=getButton(e);
if(Btn==0){//左键
if(thisObj.on==1){returnfalse;}//插旗子了就不能点了
if(mineField.mineMap[thisObj.index]==-1){//点雷了
setThisMine('peng',thisObj.index);
}elseif(mineField.mineMap[thisObj.index]==0){//点到空地了打开一大片
//alert('你运气真好')
varallShowMine=minesShow(thisObj.index);
setThisMine('show',allShowMine)
}else{//显示一个格子
setThisMine('show',thisObj.index)
}
}
if(Btn==2){//右键
setThisMine('setOn',thisObj.index);
}
}
}
//--------------------------------按到空格时显示一大片
functionminesShow(I){
varallMine=mineField._mineFieldEle,
allShowMine=[I];//保存要显示的雷的下标
allMine[I].on=3;
see(I);//查询下标为I的周围的方格
functionsee(allI){
var_allI=[];
if(Object.prototype.toString.call(allI)==='[objectArray]'){
for(vari=0;i<allI.length;i++){f(allI[i])}
}
else{f(allI)}
functionf(thisI){
vartext,
x=mineField.x,
br,
num=x*mineField.y,
L_T,T,R_T,L,R,L_B,B,R_B;
text='_'+allShowMine.join('_')+'_';//用来判断下标是否已经写入数组
br=Math.ceil((thisI+1)/x);
L_T=thisI-x-1;
T=thisI-x;
R_T=thisI-x+1;
L=thisI-1;
R=thisI+1;
L_B=thisI+x-1;
B=thisI+x;
R_B=thisI+x+1;
//左上角的方格如果是在雷区又是在上一行又是没翻开的格子又是空格那么就写如_allI数组来进行下一次检索
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);}
if(T>=0&&Math.ceil((T+1)/x)==br-1&&allMine[T].on==0&&mineField.mineMap[T]==0){_allI.push(T);}
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);}
if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&mineField.mineMap[L]==0){_allI.push(L);}
if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&mineField.mineMap[R]==0){_allI.push(R);}
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);}
if(B<num&&Math.ceil((B+1)/x)==br+1&&allMine[B].on==0&&mineField.mineMap[B]==0){_allI.push(B);}
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);}
//------------------------------------------------
//左上角的的格子如果在雷区又是在上一行又是没翻开的格子又是没写入allShowMine数组的那就写入吧并提前标记为翻开的格子
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}
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}
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}
if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&text.indexOf('_'+L+'_')==-1){allShowMine.push(L);allMine[L].on=3}
if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&text.indexOf('_'+R+'_')==-1){allShowMine.push(R);allMine[R].on=3}
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}
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}
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}
if(_allI.length!=0){see(_allI)}
}
}
returnallShowMine
}
//--------------------------------------
document.oncontextmenu=function(){returnfalse;}//禁止右键菜单
functiongetButton(e){
varBtn;
if(document.implementation.hasFeature('MouseEvents','2.0')){Btn=e.button;}
else{switch(e.button){
case0:
case1:
case3:
case5:
case7:
Btn=0;
break;
case2:
case6:
Btn=2;
break;
case4:
Btn=9;
break;
}
}
returnBtn;
}
</script>
以上所述就是本文的全部内容了,希望大家能够喜欢。