一个简单的九宫格排序游戏js实现

游戏介绍

开心或者不开心的时候,写点简单的小东西也是个不错的选择。
这次写的是一个十分简单的九宫格排序游戏,样子是这样的:
九宫格排序游戏
通关的条件很简单:把数字按照从小到大的顺序排列好就可以了。也就是排列成这样:
九宫格排序游戏通关
下面来说一下实现:

实现思路

HTML部分

分层次的说,页面主要分两块:左边是一块名为 game 的 div 区域,用于放置游戏需要的棋盘和小方块;右边一块 div 名为 control,用来显示游戏时间和按钮:
九宫格排序游戏界面布局
上图基本展示了代码的层级关系,具体代码如下所示:

<html>
<head>
    <title>九宫格排序游戏</title>
    <link rel="stylesheet" href="jiugongge.css">
</head>
<body>
    <div id="all"> 
    <!--总体区域-->
        <div id="game">
        <!--九宫格区域-->
            <div id="d1" onclick="move(1)">1</div>
            <div id="d2" onclick="move(2)">2</div>
            <div id="d3" onclick="move(3)">3</div>
            <div id="d4" onclick="move(4)">4</div>
            <div id="d5" onclick="move(5)">5</div>
            <div id="d6" onclick="move(6)">6</div>
            <div id="d7" onclick="move(7)">7</div>
            <div id="d8" onclick="move(8)">8</div>
        </div>
        <div id="control">
        <!--控制按钮区域-->
            <span id="timeText">总用时</span>
            <span id="timer"></span>
            <button id="start" onclick="start()">开始</button>
            <button id="restart" onclick="restart()">重新开始</button>
        </div>
    </div>
    <script src="jiugongge.js"></script>
</body>
</html>

为了不导致误会,这里把棋盘所处的 <div id="game">称之为『大div』(棋盘),把棋子(也就是小方块)所处的<div id="d1">称之为『小div』(一共八个棋子,要留空一块用来移动)。

CSS 部分

CSS部分是最吼的!
为了避免浏览器不同标准导致的意外,我先无脑拷贝一份CSS初始化代码(来自淘宝网)。

body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; } 
  body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; } 
 h1, h2, h3, h4, h5, h6{ font-size:100%; } 
  address, cite, dfn, em, var { font-style:normal; } 
  code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; }
  ul, ol { list-style:none; }
  a { text-decoration:none; } 
  a:hover { text-decoration:underline; }
  sup { vertical-align:text-top; }
  sub{ vertical-align:text-bottom; }
  legend { color:#000; }
  fieldset, img { border:0; }
  button, input, select, textarea { font-size:100%; }
  table { border-collapse:collapse; border-spacing:0; }
/*  样式初始化*/

接下来我把想说的直接写在注释里,值得一提的是,小方块(棋子)滑动的实现用的是 CSS 的transition实现的,比 JS 实现方便到不知道哪里去了(大雾(/ω\))。
完整的CSS代码:

body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0; } 
  body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; } 
 h1, h2, h3, h4, h5, h6{ font-size:100%; } 
  address, cite, dfn, em, var { font-style:normal; } 
  code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:12px; }
  ul, ol { list-style:none; }
  a { text-decoration:none; } 
  a:hover { text-decoration:underline; }
  sup { vertical-align:text-top; }
  sub{ vertical-align:text-bottom; }
  legend { color:#000; }
  fieldset, img { border:0; }
  button, input, select, textarea { font-size:100%; }
  table { border-collapse:collapse; border-spacing:0; }
/*  样式初始化*/

body {           /*浏览器屏幕大小自动适配 */
  width: 100%;
  height: 100%;
}
#all {
  position: relative;
  width: 100%;
  height: 800px;
  margin: 0 auto;     /*居中*/
  margin-top: 20px;   /*我开心才写的,你不喜欢可以删掉*/
}

#game {     /*大div(棋盘)*/
  position: absolute;
  display: inline-block;
  width: 450px;
  height:450px;
  background-color: #66ffcc;  /*初音绿*/
  box-shadow: 0 0 10px #66ffcc;
  margin: 0 auto;   /*居中*/
  top:0px;
  right: 0px;
  bottom: 0px;
  left: 0px;
}

#game div {    /*小div(棋子)*/
  position: absolute;
  width: 149px;
  height: 149px;
  box-shadow: 1px 1px 2px #777;
  background-color: #66ccff; /*天依蓝*/
  color: white;
  text-align: center;
  font-size: 150px;
  line-height: 150px;
  cursor: pointer;
    -webkit-transition: 0.3s;/*浏览器前缀,兼容其他浏览器 safari && chrome*/
       -moz-transition: 0.3s;/*firefox*/
        -ms-transition: 0.3s;/*ie*/
         -o-transition: 0.3s;/*opera*/
            transition: 0.3s;
}

#game div:hover{
    color: #ffe171; /*悬浮变色*/
}

#control {
  width: 150px;
  height: 450px;
  display: inline-block;
  float: right;
  position: absolute;
  left:80%;
}

#control button,span{
    height: 25px;
    font-size: 20px;
    color: #222;
    margin-top: 10px;

}

#start{
    display: inline-block;
    width: 100px;
    background-color: #66ccff;
    text-shadow: 1px 1px 2px #ffe171;/*字体阴影*/
    border-radius: 5px;              /*圆角属性*/
    box-shadow: 2px 2px 5px #4c98f5;/*盒子阴影*/
    text-align: center;             /*文字居中*/
    cursor: pointer;                /*光标变形*/
}

#restart{
    display: inline-block;
    width: 100px;
    background-color: #66ccff;
    text-shadow: 1px 1px 2px #ffe171;
    border-radius: 5px;
    box-shadow: 2px 2px 5px #4c98f5;
    text-align: center;
    cursor: pointer;
}
/*一一确定小div(棋子)的初始位置,本游戏棋盘宽450px*/
#d1{
    left: 0px;
}
#d2{
    left: 150px;
}
#d3{
    left: 300px;
}
#d4{
    top: 150px;
}
#d5{
    top: 150px;
    left: 150px;
}
#d6{
    top: 150px;
    left: 300px;
}
#d7{
    top: 300px;
}
#d8{
    left: 150px;
    top: 300px;
}

JS 部分

JS代码中,主要有这么几个函数:

  • move(id) 传入值是当前被点击的小方块(棋子)的编号(该编号只和棋子本身的数字有关,和棋盘上的位置无关),当小方块(棋子)被点击时,会调用whereCanTo(cur_div)函数来判断是否能移动和能移动到那个位置,并进行移动。
  • whereCanTo(cur_div) 传入值是当前被点击的小方块在棋盘上的位置(和棋子本身的编号无关),返回值是一个数字,表示能移动到的位置,返回0表示不能移动。
  • start() 点击『开始/暂停』按钮后触发,该按钮对应的方法是一个toggle,暂停时调用会开始游戏,进行游戏的时候调用会暂停游戏。该方法会通过setInterval(timer,1000)调用timer()方法,以此实现计时器。

*timer() start()方法每1000毫秒调用一次,以此实现计时器,并把时间显示到 DOM 中。

  • restart() 点击『重新开始』时调用该方法,会把时间计0,然后调用random_d()方法把棋子全部打乱。
  • random_d() 将所有的棋子打乱,实现的思路是模拟人的操作。

全部代码如下:

var time = 0;

var pause = true;  //值为 true 的时候,说明当前状态为暂停

var set_timer;

var d = Array(10); //用于保存小div的编号

var d_direct = new Array(      //记录当前位置的方块可以移动到哪些位置
        [0], //为了逻辑简明,不使用第一项
        [2,4],  //1号位的方块可以移动到2号位和4号位
        [1,3,5],
        [2,6],
        [1,5,7],
        [2,4,6,8],
        [3,5,9],
        [4,8],
        [5,7,9],
        [6,8]
    );

var d_position = new Array(    //保存方块的位置
        [0],       //为了逻辑简明,不使用第一项
        [0,0],     //第一个表示left,第二个表示top,比如第一块的位置为let:0px,top:0px
        [150,0],  //方块的大小为 150px * 150px
        [300,0],
        [0,150],
        [150,150],
        [300,150],
        [0,300],
        [150,300],
        [300,300]
    );

d[1]=1;d[2]=2;d[3]=3;d[4]=4;d[5]=5;d[6]=6;d[7]=7;d[8]=8;d[9]=0;   //默认按照顺序排好,方块只有八块,第九块没有,
                                                                  //所以为0,我们用0表示空白块

function move(id) { //用于移动方块的函数
    var i = 1;
    while(d[i] != id){   // 用于确定当前触发函数的是哪个区域(编号属于大DIV而不是小方块)
        i++;
    } 

    var target_d = 0; //用于保存小方块可以去的区域,0表示不能移动

    target_d = whereCanTo(i);
    //用来找出小DIV可以去的位置,如果返回0,表示不能移动,如果可以移动,则返回可以去的位置编号

    if(target_d != 0){
        d[i] = 0; //如果当前可以移动,则把当前小方块移走,当前位置设置为无方块
        d[target_d] = id; //把目标大DIV设置为被点击的小DIV的编号
        document.getElementById("d"+id).style.left = d_position[target_d][0]+"px";
        document.getElementById("d"+id).style.top = d_position[target_d][1]+"px";
    }

    var finish_flag = true;

    for(var k=1; k<9; ++k){  //用于判断游戏是否通关
        if( d[k] != k){
            finish_flag=false;
            break;
            //如果大DIV保存的编号和它本身的编号不同,则表示还不是全部按照顺序排的,那么设置为false,跳出循环
        }
    }

    if(finish_flag==true){
        if(!pause)
            start();   //此时调用 start() 函数,效果是暂停游戏,
                       //start() 是一个 toggle,暂停时调用会开始游戏,进行游戏的时候调用会暂停游戏
        alert("恭喜通关!");
    }
}

function whereCanTo(cur_div) {  //参数是大div的编号
    var j = 0;
    var move_flag = false; //方块能否移动的标记

    for(j=0; j<d_direct[cur_div].length; j++){
        if (d[ d_direct[cur_div][j] ]== 0) {
            move_flag = true;
            break;
        }
    }
    if(move_flag){
        return d_direct[cur_div][j];
    }
    else{
        return 0;
    }
}

//定时函数,每一秒执行一次
function timer(){
    time+=1;//一秒钟加一,单位是秒
    var min=parseInt(time/60);//把秒转换为分钟
    var sec=time%60;//取余就是秒
    document.getElementById("timer").innerHTML=min+"分"+sec+"秒";//把时间更新显示出来
}

function start(){
    if(pause){
        document.getElementById("start").innerHTML = "暂停"; //当前状态为游戏进行中,把开始按钮切换为暂停 
        pause = false;//暂停标志设置为false
        set_timer = setInterval(timer,1000);//启动定时,每1000毫秒调用一次
    }else{
        document.getElementById("start").innerHTML = "开始";
        pause = true;
        clearInterval(set_timer);
    }
}

function restart(){
    time=0;//把时间设置为0
    random_d();//把方块随机打乱函数
    if(pause)//如果暂停,则开始计时
        start();
}

function random_d(){ //模拟人的操作,为了避免随机的时候直接碰巧通关,所以先走上几步
    move(6);
    move(5);
    move(2);
    move(1);
    move(4);
    var i = 30;   //要是觉得打散的不够彻底,可以多循环几次。
    while(i>0){
        move(parseInt(Math.random()*9));
        i--;
    }
}

window.onload=function(){  //初始化函数,页面加载的时候调用重置函数,重新开始
    restart();
}

实际上,还可以使用另外一种打散的方式,原理是随机调换小div的位置,但是这样做会有相当的概率导致你永远都不能通关,随机打散的代码如下:

function random_d(){
       for(var i=9; i>1; --i){
        var to=parseInt(Math.random()*(i-1)+1);//产生随机数,范围为1到i,不能超出范围,因为没这个id的DIV
        if(d[i]!=0){          //把当前的DIV位置设置为随机产生的DIV的位置
            document.getElementById("d"+d[i]).style.left=d_position[to][0]+"px";
            document.getElementById("d"+d[i]).style.top=d_position[to][1]+"px";
        }

        if(d[to]!=0){           //把随机产生的DIV的位置设置为当前的DIV的位置
            document.getElementById("d"+d[to]).style.left=d_position[i][0]+"px";
            document.getElementById("d"+d[to]).style.top=d_position[i][1]+"px";
        }

        var tem=d[to];         //然后把它们两个的DIV保存的编号对调一下
        d[to]=d[i];
        d[i]=tem;
    }
}

代码部分:

所有的代码都在文章中写出来了,github的地址点此

Comments
Write a Comment