用JS实现2048小游戏

时间:2024-03-11 10:51:51

前言:

一年来一直做得是后端的东西,没有写前端代码,忘干净了,之前也只用jQuery,好歹还能做点效果。想着捡起来一点,要不然枉费了师傅的栽培。

一直在看博客,但没有属于自己的东西。this has to change.

why not start now?俗话说只要代码敲得够快,悲伤就追不上我。

此篇开博,从简单小游戏开始。目的是做出一些可以快点看到效果的东西,捡回一点程序员的信心。

 

先看最终效果:

 

 

1. 2048游戏功能分析:

核心是4个方向的移动的处理。

往一个方向移动,先看是否有数字的合并。一次移动,一行只允许合并一次,且从终点往起点合并。

合并完成之后,将合并后的数字和其他数字移动,贴边。

在空白位置随机生成一个数字2,移动结束。

 

算法实现:

考虑采用二维数组存储4*4个格子中的值,如果移动,只需要更新数组,然后用数组的值来更新dom元素即可。

(这种想法不太面向对象。要是将每个格子都理解为一个对象,不知道好不好做,等以后研究--TODO)

 

2. 要用到的东西

2.1 html页面布局,4*4个方块得整出来

2.2 简单css:不同的数字设置不同颜色

2.3 简单dom操作:取元素,变换显示的数字,改变class

2.4 取随机数算法

2.5 按键事件监听

2.6 一点点交互操作考虑,比如判定游戏失败结束,成功结束,返回上一步。

 

3. 代码结构:

 

较为粗糙,js几乎全部是直接用html引用了,不会其他方式。先实现功能;

几个图片是:返回上一步按钮,上下左右按钮,重新开始按钮。

js区分:

arrayHelper是操作数组的;

domOperation顾文思意,操作dom;

eventListener,处理按键事件

common:页面初始化,随机数生成等

 

4. 核心代码

4.1 向方向【左】移动的逻辑

评论:写的比较死,一步一步,按照规则来即可

// 向一个方向移动,更新数组(左),其他方向也是一样的
function moveLeft(arrayCopy){
    var gameSuccess = false;
    
    // 是否有合并或者移动,如果没有合并也没有移动,就是没有操作那就不要生成新的数字
    var anyChange = false;
    // 每一行,从【左】往【右】对比是否有一样的数字(排除0)--并不需要挨着!中间隔了空气也可以合并
    for (var i=0;i<4;i++){
        var firstIndex = 0;
        var secondIndex = 1;
        while (secondIndex<4){
            if (arrayCopy[i][firstIndex] == 0){
                // 第一个是0,不可能有合并,往后移动
                firstIndex++;
                secondIndex++;
                continue;
            }
            if (arrayCopy[i][secondIndex] == 0){
                // 第二个是0,第二个继续往后找,第一个不变
                secondIndex++;
                // 1. 后面找不到数字,一直往后找,结束循环
                continue;
            }            
            // 有一次一样的数字:计算得到和,放到第一个位置,这一行完了
            else if (arrayCopy[i][firstIndex] == arrayCopy[i][secondIndex]){
                arrayCopy[i][firstIndex] = arrayCopy[i][firstIndex] * 2;
                arrayCopy[i][secondIndex] = 0;
                anyChange = true;
                if (arrayCopy[i][firstIndex] >= 2048){
                    // 得到了2048,游戏结束
                    gameSuccess = true;
                }
                // 2. 后面找到数字,且可以合并,就合并,结束循环
                break;
            }
            else{
                // 3. 后面找到数字,但是不能合并,更新第一个数字为当前数字,第二个数字为下一个数字
                firstIndex = secondIndex;
                secondIndex += 1;
                continue;
            }
        }
    }
    
    // 将每一行数字向【左】挪动
    for (var i=0;i<4;i++){
        var newLine = [];//临时存储从【左】到【右】的非0数字
        var index = 0;
        for (var j=0;j<4;j++){
            if (arrayCopy[i][j] != 0){
                newLine[index] = arrayCopy[i][j];
                index++;
            }
        }
        
        // 用临时存储的数字给数组更新
        for (var m=0;m<index;m++){
            if (arrayCopy[i][m] != newLine[m]){
                anyChange = true;
            }
            arrayCopy[i][m] = newLine[m];
        }
        // 剩余的位置给0
        for (var n=index;n<4;n++){
            if (arrayCopy[i][n] != 0){
                anyChange = true;
            }
            arrayCopy[i][n] = 0;
        }
    }
    
    if (!anyChange){
        console.log("no change after this move!");
        if (!isEmptyCell(arrayCopy)){
            // 本步不能移动,且没有剩余格子,且任意相邻格子没有相同的,就结束游戏
            if (!canMerge(arrayCopy)){
                console.log("Game Over! Try again.");
                return \'fail\';
            }
        }
        if (gameSuccess){
            return \'success\';
        }
        // 没有移动
        return \'unmoved\';
    }
    
    // 给空闲位置设置数字2
    if (isEmptyCell(arrayCopy))
    {
        initOnePosition(arrayCopy);
    }
    
    if (gameSuccess){
        return \'success\';
    }
    return \'moved\';
}

4.2.  通用的移动处理。

因为往一个方向移动,和往其他方向移动处理是一样的,不可能复制以上代码4次吧。

换个方向看:往左移动和往右移动对数组的操作,只是行处理相反,列处理一样;

其他方向移动类似,不值得copy整个方法。

// 通用方法
// 所有移动都转换成一个方向上的移动:例如,都想象成向左移动,只需要倒转数组即可,完成移动再倒转回来!
function move(direction){
    if (flagSuccess){
        if (confirm("You\'ve got your 2048! Start Another One?\n已经通关!开始新游戏?")){
            gameRestart();
        }
        return;
    }
    
    // 移动之前,记录上一步的内容,移动失败之后可以恢复
    var lastStepUnchange = createNewArray();
    updateFrontArray(lastStepUnchange,lastStepArray);
    // 将上一步状态设置为当前的
    updateFrontArray(lastStepArray,arrayInit);
    
    // 将当前的去更新,更新失败则回滚上一步的状态
    // 1. 数组倒转复制
    var arrayCopy = new Array();
    if (direction == \'left\'){
        arrayCopy = copyArrayLeft();
    }else if (direction == \'right\'){
        arrayCopy = copyArrayRight();
    }else if (direction == \'up\'){
        arrayCopy = copyArrayUp();
    }else if (direction == \'down\'){
        arrayCopy = copyArrayDown();
    }
    
    // 2. 都向一个方向移动
    var moveResult = moveLeft(arrayCopy);
    if (\'fail\' == moveResult){
        
        updateFrontArray(lastStepArray,lastStepUnchange);
        // 移动失败,没有任何改变
        if (confirm(\'Game Over! Try again?\n游戏结束,再来一次?\')){
            gameRestart();
            return;
        }else{
            // 最后一步了,允许回退
            clickedLastStep = false;
            return;
        }
        return;
    }
    if (\'unmoved\' == moveResult){
        return;
    }
    
    // 3. 从倒转的数组更新原数组
    if (direction == \'left\'){
        restoreArrayLeft(arrayCopy);
    }else if (direction == \'right\'){
        restoreArrayRight(arrayCopy);
    }else if (direction == \'up\'){
        restoreArrayUp(arrayCopy);
    }else if (direction == \'down\'){
        restoreArrayDown(arrayCopy);
    }
    
    // 4. 根据数字更新页面元素
    updatePageByArray();
    updateColor();
    
    if (\'success\' == moveResult){
        // 因为操作DOM不是实时的,所以要等一段事件操作完成之后再弹出确认框
        setTimeout(function(){
            // 游戏通关,是否重新开始
            if (confirm(\'Congragulations! Start Another Game?\n恭喜通关!是否再来一把?\')){
                gameRestart();
            }else{
                flagSuccess = true;
            }
        },200);
        return;
    }
    
    // 移动完成
    clickedLastStep = false;
}

 

4.3 随机数的生成:

开始做的是生成一个随机数,16个位置的任意一个,如果这个位置已经有人了,就放弃,重新生成一个,直到找到空位为止。

但是到空位变少的时候,这样效率低下。

优化:只在空位中找随机位置。

// 生成随机数,转化成0-15的整数;
function getRamdom(count)
{
    // 0-1
    var r0 = Math.random();
    // 0-16
    var r1 = r0 * count;
    // 只取整数位数的数
    return Math.floor(r1);
}

// 改进:只在当前可用位置中随机找,不用找不可用的位置 
function getOneEmptyCoordinate(array)
{
    // 1. 得到当前数组
    var countEmptyCells = 0;
    var mapIndex2Coordinate = {};
    var coordinates = null;
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            if (array[i][j] == 0){
                countEmptyCells++;
                coordinates = new Array();
                coordinates.push(i);
                coordinates.push(j);
                mapIndex2Coordinate[countEmptyCells] = coordinates;
            }
        }
    }
    // 取可用位置的随机位置
    var random = getRamdom(countEmptyCells);
    return mapIndex2Coordinate[random];
}

 

 

5. 欠缺:

5.1 布局,样式的调整比较嫌麻烦,瞎调的。

5.2 js代码关系--没什么关系,忘了前端是怎么组织的了;

5.3 刷新页面多次,可能出现从初始化异常,可能是文件加载顺序问题,初始化时dom操作比较慢等原因

5.4 看看别人实现思路如何 

-- http://www.cnblogs.com/-lizi/p/8431030.html 这个写的很漂亮

 

6. 总结:

 开发时间:5天下班时间,实际投入:15个小时左右。

 代码规模:js400行;

 

7. 完整代码粘贴

index.html

<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<link href="../css/common.css" rel="stylesheet">

<body>
    <script type="text/javascript" src="../js/common.js"></script>
    <script type="text/javascript" src="../js/eventListener.js"></script>
    <script type="text/javascript" src="../js/domOperation.js"></script>
    <div id="main">
        <div id="refreshDiv" onclick="gameRestart()">
            <span id="refreshButton">
                <img src="../resource/refreshButton.png"  alt="refresh" />
                <a>重新开始</a>
            </span>
        </div>
        <div id="mainTable">
        </div>
        <div id="explation">
            <span>
             2048游戏说明:<br>
             按上下左右方向键可以移动有数字的方块,<br>
             相邻的相同数字的方块往一个方向移动会合并成更大的数字。<br>
             当最大数字出现2048时,游戏胜利!<br>
             温馨提示:你可以使用空格键回退上一步。
            </span>
        </div>
        <div id="rights">
            <span>
                All rights reserved to S.C. Contact me at 857025773@qq.com. 
            </span>
        </div>
    </div>
    
    <!-- 操作 -->
    <div id="keys">
        <div>
            <span id="moveUpButton"onclick="move(\'up\')">
                <img src="../resource/direction.png"  alt="direction" />
            </span>
        </div>
        <div>
            <span id="moveLeftButton"onclick="move(\'left\')">
                <img src="../resource/direction.png"  alt="direction" />
            </span>
            <span id="moveBackButton" onclick="eventSpaceKey()">
                <img src="../resource/moveBack.png"  alt="moveBack" />
            </span>
            <span id="moveRightButton"onclick="move(\'right\')">
                <img src="../resource/direction.png"  alt="direction" />
            </span>
        </div>
        <div>
            <span id="moveDownButton"onclick="move(\'down\')">
                <img src="../resource/direction.png"  alt="direction" />
            </span>
        </div>
    </div>
    
    
    <script type="text/javascript">
        window.onload = function(){
                pageInit();
        }
    </script>
</body>

 

common.css

#main
{
    margin-left:500px;
    margin-top:100px;
    width:404px;
    heignt:400px;
    border: 0px solid gray;
    border-radius: 1px;
}

#keys
{
    margin-left:1150px;
    margin-top:-430px;
    width:303px;
    heignt:300px;
    border: 3px solid green;
    border-radius: 20px;
}


#keys div
{
    display:flex;
    height:75px;
}

#keys div span
{
    height:75px;
    width:100px;
    border: 1px solid gray;
    background:blue;
}

#moveLeftButton img
{
    height:75px;
    width:100px;
    -ms-transform:rotate(180deg); /* IE 9 */
    -moz-transform:rotate(180deg); /* Firefox */
    -webkit-transform:rotate(180deg); /* Safari and Chrome */
    -o-transform:rotate(180deg); /* Opera */
}
#moveRightButton img
{
    height:75px;
    width:100px;
}

#moveUpButton
{
    margin-left:100px;
}
#moveUpButton img
{
    margin-top: -13px;
    margin-left: 12px;
    height:100px;
    width:75px;
    -ms-transform:rotate(270deg); /* IE 9 */
    -moz-transform:rotate(270deg); /* Firefox */
    -webkit-transform:rotate(270deg); /* Safari and Chrome */
    -o-transform:rotate(270deg); /* Opera */
}
#moveDownButton
{
    margin-left:100px;
}
#moveDownButton img
{
    margin-left: 12px;
    margin-top: -13px;
    height:100px;
    width:75px;
    -ms-transform:rotate(90deg); /* IE 9 */
    -moz-transform:rotate(90deg); /* Firefox */
    -webkit-transform:rotate(90deg); /* Safari and Chrome */
    -o-transform:rotate(90deg); /* Opera */
}
#moveBackButton
{
    height:100px;
    width:75px;
    background:yellow !important;
}

#refreshDiv
{
    height:40px;
    width:133px;
    margin-left:140px;
    cursor: pointer;
    background:#ba3537;
    border-radius: 2px;
}

#refreshDiv span
{
    display: contents;
    font-family: \'微软雅黑\';
    margin-left:159px;
    font-size:20;
}
#refreshDiv span a
{
    margin-bottom: 10px;
}


#explation
{
    margin-top:30px;
}
#rights
{
    margin-top:100px;
}
#rights span
{
    font-size:10;
}

#explation span
{
    font-size:14;
}

#mainTable
{
    margin-top:20px;
    background:#f5f5f5;
    border: 2px solid #478dcd;
    border-radius: 3px;
}

#mainTable div
{
    display:flex;
    width:400px;
    heignt:100px;
}

#mainTable span
{
    height:100px;
    width:100px;
    border: 1px solid gray;
    font-size: 50;
    text-align: center;
    font-weight:bold;
    
}

.color0
{
    background:#f5f5f5;
}

.color2
{
    background:#f5c7ad;
}
.color4
{
    background:#ec9362;
}
.color8
{
    background:#e3631e;
}
.color16
{
    background:#c2fcb1;
    font-size: 45 !important;
}
.color32
{
    background:#76f850;
    font-size: 45 !important;
}
.color64
{
    background:#2dad07;
    font-size: 45 !important;
}
.color128
{
    background:#a8a6f9;
    font-size: 40 !important;
}
.color256
{
    background:#4b47f1;
    font-size: 40 !important;
}
.color512
{
    background:#110da4;
    font-size: 40 !important;
}
.color1024
{
    background:#f799ef;
    font-size: 35 !important;
}
.color2048
{
    background:#a30e98;
    font-size: 35 !important;
}

 

common.js

// 拼成了2048,冻结操作,不允许移动了
var flagSuccess = false;
// 刚才点击了回退,不允许重复点击,要移动之后再点击
var clickedLastStep = false;

// 记录上一步,用于回退
var lastStepArray = [
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0]
                     ];

// 数组初始化--正常初始化
var arrayInit = 
    [
     [0,0,0,0],
     [0,0,0,0],
     [0,0,0,0],
     [0,0,0,0]
   ];
//[//调试--将要success
// [0,0,0,0],
// [0,128,0,0],
// [0,0,512,1024],
// [0,0,0,1024]
//];
//[//调试--将要fail
// [1,2,3,4],
// [5,128,6,7],
// [8,9,512,1024],
// [13,22,11,11]
//];



// 页面加载初始化
function pageInit()
{
    addHelper();
    
//    initArray();
    // 页面元素初始化(单元格)
    initElements();
    
    // 初始数据
    initTwoPositions();
    
    // 根据初始数据给单元格填充值
    updatePageByArray();
    
    // 根据单元格的值给单元格设置样式
    updateColor();
}

// 重新开始
function gameRestart(){
    initArray();
    pageInit();
}

function initArray(){
    flagSuccess = false;
    arrayInit = createNewArray();
    clickedLastStep = false;
    lastStepArray = createNewArray();
}


// 给两个位置设置数字2
function initTwoPositions()
{
    initOnePosition(arrayInit);
    initOnePosition(arrayInit);
}
// 给一个位置设置数字2
function initOnePosition(array){
    // 取随机数方式1
//    var num = getRamdom(16);
//    var line = Math.floor(num/4);
//    var col = num % 4;
//    var curNum = array[line][col];
//    if (curNum == 0)
//    {
//        // 成功找到空闲位置
//        array[line][col] = 2;
//        return true;
//    }
//    else{
//        // 递归调用,必须找到一个可用位置才算完
//        return initOnePosition(array);
//    }
    
    // 取随机数方式2
    var coordinate = getOneEmptyCoordinate(array);
    array[coordinate[0]][coordinate[1]] = 2; 
}

function isEmptyCell(arrayCopy)
{
    for (var i=0;i<4;i++)
    {
        for (var j=0;j<4;j++)
        {
            if (arrayCopy[i][j] == 0)
            {
                return true;
            }
        }
    }
    return false;
}

// 生成随机数,转化成0-15的整数;
function getRamdom(count)
{
    // 0-1
    var r0 = Math.random();
    // 0-16
    var r1 = r0 * count;
    // 只取整数位数的数
    return Math.floor(r1);
}

// 改进:只在当前可用位置中随机找,不用找不可用的位置 
function getOneEmptyCoordinate(array)
{
    // 1. 得到当前数组
    var countEmptyCells = 0;
    var mapIndex2Coordinate = {};
    var coordinates = null;
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            if (array[i][j] == 0){
                countEmptyCells++;
                coordinates = new Array();
                coordinates.push(i);
                coordinates.push(j);
                mapIndex2Coordinate[countEmptyCells] = coordinates;
            }
        }
    }
    // 取可用位置的随机位置
    var random = getRamdom(countEmptyCells);
    return mapIndex2Coordinate[random];
}

 

eventListener,js

/**
 *  引用工具类
 */
function addHelper(){
    var newscript = document.createElement(\'script\');  
    newscript.setAttribute(\'type\',\'text/javascript\');  
    newscript.setAttribute(\'src\',\'../js/arrayHelper.js\');  
    document.body.appendChild(newscript);  
}

//捕捉按键事件
document.onkeyup = function(event) {
    event = event || window.event;
    
    if (event.keyCode == 37){//left
        move(\'left\');
    }else if (event.keyCode == 39){//right
        move(\'right\');
    }else if (event.keyCode == 38){//up
        move(\'up\');
    }else if (event.keyCode == 40){//down
        move(\'down\');
    }else if (event.keyCode == 32){//space
        eventSpaceKey();
    }
}

function eventSpaceKey(){
    // 回退上一步
    if (clickedLastStep){
        console.log(\'can not move back again\');
        return;
    }
    moveBack();
    flagSuccess = false;
    clickedLastStep = true;
}

// 回退上一步
function moveBack(){
    // 判断上一步全是0(刚刚初始化)
    var justStarted = true;
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            if (lastStepArray[i][j] != 0){
                justStarted =  false;
            }
        }
    }
    if(justStarted){
        return;
    }
    
    // 1. 用备份的数组给当前数组设置值
    updateFrontArray(arrayInit,lastStepArray);
    
    // 3. 更新页面
    updatePageByArray();
    updateColor();
}

// 通用方法
// 所有移动都转换成一个方向上的移动:例如,都想象成向左移动,只需要倒转数组即可,完成移动再倒转回来!
function move(direction){
    if (flagSuccess){
        if (confirm("You\'ve got your 2048! Start Another One?\n已经通关!开始新游戏?")){
            gameRestart();
        }
        return;
    }
    
    // 移动之前,记录上一步的内容,移动失败之后可以恢复
    var lastStepUnchange = createNewArray();
    updateFrontArray(lastStepUnchange,lastStepArray);
    // 将上一步状态设置为当前的
    updateFrontArray(lastStepArray,arrayInit);
    
    // 将当前的去更新,更新失败则回滚上一步的状态
    // 1. 数组倒转复制
    var arrayCopy = new Array();
    if (direction == \'left\'){
        arrayCopy = copyArrayLeft();
    }else if (direction == \'right\'){
        arrayCopy = copyArrayRight();
    }else if (direction == \'up\'){
        arrayCopy = copyArrayUp();
    }else if (direction == \'down\'){
        arrayCopy = copyArrayDown();
    }
    
    // 2. 都向一个方向移动
    var moveResult = moveLeft(arrayCopy);
    if (\'fail\' == moveResult){
        
        updateFrontArray(lastStepArray,lastStepUnchange);
        // 移动失败,没有任何改变
        if (confirm(\'Game Over! Try again?\n游戏结束,再来一次?\')){
            gameRestart();
            return;
        }else{
            // 最后一步了,允许回退
            clickedLastStep = false;
            return;
        }
        return;
    }
    if (\'unmoved\' == moveResult){
        return;
    }
    
    // 3. 从倒转的数组更新原数组
    if (direction == \'left\'){
        restoreArrayLeft(arrayCopy);
    }else if (direction == \'right\'){
        restoreArrayRight(arrayCopy);
    }else if (direction == \'up\'){
        restoreArrayUp(arrayCopy);
    }else if (direction == \'down\'){
        restoreArrayDown(arrayCopy);
    }
    
    // 4. 根据数字更新页面元素
    updatePageByArray();
    updateColor();
    
    if (\'success\' == moveResult){
        // 因为操作DOM不是实时的,所以要等一段事件操作完成之后再弹出确认框
        setTimeout(function(){
            // 游戏通关,是否重新开始
            if (confirm(\'Congragulations! Start Another Game?\n恭喜通关!是否再来一把?\')){
                gameRestart();
            }else{
                flagSuccess = true;
            }
        },200);
        return;
    }
    
    // 移动完成
    clickedLastStep = false;
}

// 向一个方向移动,更新数组(左),其他方向也是一样的
function moveLeft(arrayCopy){
    var gameSuccess = false;
    
    // 是否有合并或者移动,如果没有合并也没有移动,就是没有操作那就不要生成新的数字
    var anyChange = false;
    // 每一行,从【左】往【右】对比是否有一样的数字(排除0)--并不需要挨着!中间隔了空气也可以合并
    for (var i=0;i<4;i++){
        var firstIndex = 0;
        var secondIndex = 1;
        while (secondIndex<4){
            if (arrayCopy[i][firstIndex] == 0){
                // 第一个是0,不可能有合并,往后移动
                firstIndex++;
                secondIndex++;
                continue;
            }
            if (arrayCopy[i][secondIndex] == 0){
                // 第二个是0,第二个继续往后找,第一个不变
                secondIndex++;
                // 1. 后面找不到数字,一直往后找,结束循环
                continue;
            }            
            // 有一次一样的数字:计算得到和,放到第一个位置,这一行完了
            else if (arrayCopy[i][firstIndex] == arrayCopy[i][secondIndex]){
                arrayCopy[i][firstIndex] = arrayCopy[i][firstIndex] * 2;
                arrayCopy[i][secondIndex] = 0;
                anyChange = true;
                if (arrayCopy[i][firstIndex] >= 2048){
                    // 得到了2048,游戏结束
                    gameSuccess = true;
                }
                // 2. 后面找到数字,且可以合并,就合并,结束循环
                break;
            }
            else{
                // 3. 后面找到数字,但是不能合并,更新第一个数字为当前数字,第二个数字为下一个数字
                firstIndex = secondIndex;
                secondIndex += 1;
                continue;
            }
        }
    }
    
    // 将每一行数字向【左】挪动
    for (var i=0;i<4;i++){
        var newLine = [];//临时存储从【左】到【右】的非0数字
        var index = 0;
        for (var j=0;j<4;j++){
            if (arrayCopy[i][j] != 0){
                newLine[index] = arrayCopy[i][j];
                index++;
            }
        }
        
        // 用临时存储的数字给数组更新
        for (var m=0;m<index;m++){
            if (arrayCopy[i][m] != newLine[m]){
                anyChange = true;
            }
            arrayCopy[i][m] = newLine[m];
        }
        // 剩余的位置给0
        for (var n=index;n<4;n++){
            if (arrayCopy[i][n] != 0){
                anyChange = true;
            }
            arrayCopy[i][n] = 0;
        }
    }
    
    if (!anyChange){
        console.log("no change after this move!");
        if (!isEmptyCell(arrayCopy)){
            // 本步不能移动,且没有剩余格子,且任意相邻格子没有相同的,就结束游戏
            if (!canMerge(arrayCopy)){
                console.log("Game Over! Try again.");
                return \'fail\';
            }
        }
        if (gameSuccess){
            return \'success\';
        }
        // 没有移动
        return \'unmoved\';
    }
    
    // 给空闲位置设置数字2
    if (isEmptyCell(arrayCopy))
    {
        initOnePosition(arrayCopy);
    }
    
    if (gameSuccess){
        return \'success\';
    }
    return \'moved\';
}

// 是否有任意相邻的格子可以合并
function canMerge(arrayCopy){
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            if (j+1 < 4&& arrayCopy[i][j] == arrayCopy[i][j+1]){
                return true;
            }
            if (i+1 < 4 && arrayCopy[i][j] == arrayCopy[i+1][j]){
                return true;
            }
        }
    }
    return false;
}

arrayHelper.js

function createNewArray(){
    var newArray = [
                           [0,0,0,0],
                           [0,0,0,0],
                           [0,0,0,0],
                           [0,0,0,0]
                         ];
    return newArray;
}


// 后面的复制给前面
function updateFrontArray(arr1,arr2)
{
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arr1[i][j] = arr2[i][j];
        }
    }
}

function copyArray(arr){
    var newArray = createNewArray();
    updateFrontArray(newArray,arr);
    return newArray;
}


//左,原方向
function copyArrayLeft(){
    var arrayCopy = copyArray(arrayInit);
    return arrayCopy;
}
function restoreArrayLeft(arrayCopy){
    updateFrontArray(arrayInit,arrayCopy);
}

//右,上下不变,左右颠倒
function copyArrayRight(){
    var arrayCopy = new Array();
    for (var i=0;i<4;i++){
        var copyOneLine = new Array();
        for (var j=0;j<4;j++){
            copyOneLine[3-j] = arrayInit[i][j];
        }
        arrayCopy[i] = copyOneLine;
    }
    return arrayCopy;
}
function restoreArrayRight(arrayCopy){
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arrayInit[i][j] = arrayCopy[i][3-j];
        }
    }
}

//
//从上往下变成从左往右
//从右往左变成从上往下
//(i,j) 对应:(3-j,i)
function copyArrayUp(){
    var arrayCopy = [
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0]
                     ];
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arrayCopy[3-j][i] = arrayInit[i][j];
        }
    }
    return arrayCopy;
}
function restoreArrayUp(arrayCopy){
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arrayInit[i][j] = arrayCopy[3-j][i];
        }
    }
}

//
//从上往下变成从下往上
//从左往右变成从左往右
//(i,j) 对应:(j,3-i)
function copyArrayDown(){
    var arrayCopy = [
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0]
                     ];
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arrayCopy[j][3-i] = arrayInit[i][j];
        }
    }
    return arrayCopy;
}
function restoreArrayDown(arrayCopy){
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            arrayInit[i][j] = arrayCopy[j][3-i];
        }
    }
}

domOperation.js

// 用数组给元素赋值
function updatePageByArray()
{
    var lines = document.getElementById("mainTable").children;
    var lineIndex = 0;
    
    while (lineIndex < lines.length)
    {
        var spanIndex = 0;
        while (spanIndex < lines[lineIndex].children.length)
        {
            var oneSpan = lines[lineIndex].children[spanIndex];
            oneSpan.innerHTML = arrayInit[lineIndex][spanIndex];
            spanIndex++;
        }
        lineIndex++;
    }
}

// 初始化单元格和颜色
function initElements()
{
    var mainTable = document.getElementById("mainTable");
    var str = \'\';
    // 创建行
    for (var i=0;i<4;i++)
    {
        str += "<div id=\'" +"line"+i+"\'>";
        for (var j=0;j<4;j++)
        {
            var num = arrayInit[i][j];
            str += "<span id=\'" +"span"+i+j+"\'>"
                        + num+
                    "</span>";
        }
        str += "</div>";
        mainTable.innerHTML = str;
    }
}

// 根据ID更新颜色
function updateColor(){
    for (var i=0;i<4;i++){
        for (var j=0;j<4;j++){
            var span = document.getElementById("span"+i+j);
            var value = span.innerHTML;
            if (value == \'0\'){
                span.innerHTML = \'\';
            }
            span.className = (" color" + value);
        }
    }
}