Realization of 2048 mini-games with JS

Posted by shaitan on Sun, 12 May 2019 10:16:18 +0200

Preface:

What I have been doing for a year is back-end stuff. I haven't written the front-end code. I forgot about it. I used jQuery before, but I can still do something good. Think of picking up a little, or waste the cultivation of the master.

I've been reading blogs, but I don't have anything of my own. this has to change.

why not start now? As the saying goes, as long as the code knocks fast enough, sadness will not catch up with me.

This blog begins with a simple game. The goal is to make something that can see the effect quickly and to pick up a little bit of programmer confidence.

 

Look first at the final results:

 

 

1. 2048 Game Function Analysis:

The core is the processing of movement in four directions.

Move in one direction and see if there is a merger of numbers. One move, one row is allowed to merge only once, and merge from the end point to the starting point.

After the merge is completed, the merged numbers and other numbers are moved and edged.

A number 2 is generated randomly in the blank position and the movement ends.

 

Algorithmic implementation:

Consider using a two-dimensional array to store values in 4*4 lattices. If you move, you only need to update the array, and then update the dom element with the value of the array.

(The idea is not very object-oriented. If we understand each lattice as an object, we don't know whether it's good or not. We'll study it later--TODO.

 

2. Things to be used

2.1 html page layout, 4*4 squares to be integrated

2.2 Simple css: Different numbers set different colors

2.3 Simple dom operation: take element, change display number, change class

2.4 Random Number Selection Algorithms

2.5 Keyboard Event Monitoring

2.6 A little bit of interaction considerations, such as deciding the end of the game failure, successful end, go back to the previous step.

 

3. Code structure:

 

Rougher, js is almost all referenced directly in html, no other way. First realize the function;

Several pictures are: Return to the previous step button, up and down the left and right buttons, restart the button.

js distinction:

ArayHelper is an array of operations;

domOperation, Gu Wenyi, operates dom;

EvetListener, handle key events

common: page initialization, random number generation, etc.

 

4. Core Code

4.1 Logic of Moving in Direction [Left]

Comment: Write more dead, step by step, according to the rules.

// Move in one direction,Update arrays (left), the same in other directions
function moveLeft(arrayCopy){
    var gameSuccess = false;
    
    // If there is merge or move, if there is no merge or move, if there is no operation, then do not generate new numbers
    var anyChange = false;
    // Each line, from left to right, compares whether there is the same number (excluding 0)--It doesn't need to be next to each other! Air in the middle can also be combined.
    for (var i=0;i<4;i++){
        var firstIndex = 0;
        var secondIndex = 1;
        while (secondIndex<4){
            if (arrayCopy[i][firstIndex] == 0){
                // The first is 0.,There can't be mergers, moving backwards.
                firstIndex++;
                secondIndex++;
                continue;
            }
            if (arrayCopy[i][secondIndex] == 0){
                // The second one is 0, the second one continues to look back, and the first one remains unchanged.
                secondIndex++;
                // 1. You can't find the number at the back. Keep looking back. End the loop.
                continue;
            }            
            // Once there was the same number: calculate the sum, put it in the first place, and that's the end of the line.
            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){
                    // Get 2048, the game is over
                    gameSuccess = true;
                }
                // 2. Find the number later, and can merge, merge, end the cycle
                break;
            }
            else{
                // 3. Find the number later, but can not merge, update the first number to the current number, the second number to the next number.
                firstIndex = secondIndex;
                secondIndex += 1;
                continue;
            }
        }
    }
    
    // Move each line of numbers to the left
    for (var i=0;i<4;i++){
        var newLine = [];//Temporary storage of non-zero numbers from left to right
        var index = 0;
        for (var j=0;j<4;j++){
            if (arrayCopy[i][j] != 0){
                newLine[index] = arrayCopy[i][j];
                index++;
            }
        }
        
        // Update arrays with temporarily stored numbers
        for (var m=0;m<index;m++){
            if (arrayCopy[i][m] != newLine[m]){
                anyChange = true;
            }
            arrayCopy[i][m] = newLine[m];
        }
        // The remaining position is given to 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)){
            // This step can not move, and there is no remaining grid, and any adjacent grid does not have the same, the end of the game.
            if (!canMerge(arrayCopy)){
                console.log("Game Over! Try again.");
                return 'fail';
            }
        }
        if (gameSuccess){
            return 'success';
        }
        // No move
        return 'unmoved';
    }
    
    // Set Number 2 for Free Location
    if (isEmptyCell(arrayCopy))
    {
        initOnePosition(arrayCopy);
    }
    
    if (gameSuccess){
        return 'success';
    }
    return 'moved';
}

4.2. General mobile processing.

Because moving in one direction is the same as moving in other directions, it is impossible to copy the above code four times.

In another direction: move left and right to manipulate arrays, but the row processing is the opposite, the column processing is the same;

Moving in other directions is similar and not worth copy ing the entire method.

// General method
// All the movements are translated into a movement in one direction: for example, they are all imagined to move to the left, only need to reverse the array, complete the movement and then reverse back!
function move(direction){
    if (flagSuccess){
        if (confirm("You've got your 2048! Start Another One?\n It's cleared! Start a new game?")){
            gameRestart();
        }
        return;
    }
    
    // Before moving, record the contents of the previous step, and recover after the failure of the move
    var lastStepUnchange = createNewArray();
    updateFrontArray(lastStepUnchange,lastStepArray);
    // Set the previous step status to the current one
    updateFrontArray(lastStepArray,arrayInit);
    
    // Update the current,Update failure rolls back the status of the previous step
    // 1. Array inversion replication
    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. They all move in one direction.
    var moveResult = moveLeft(arrayCopy);
    if ('fail' == moveResult){
        
        updateFrontArray(lastStepArray,lastStepUnchange);
        // Mobile failure, no change
        if (confirm('Game Over! Try again?\n Game over, one more time?')){
            gameRestart();
            return;
        }else{
            // The last step, let's go back.
            clickedLastStep = false;
            return;
        }
        return;
    }
    if ('unmoved' == moveResult){
        return;
    }
    
    // 3. Update the original array from the inverted array
    if (direction == 'left'){
        restoreArrayLeft(arrayCopy);
    }else if (direction == 'right'){
        restoreArrayRight(arrayCopy);
    }else if (direction == 'up'){
        restoreArrayUp(arrayCopy);
    }else if (direction == 'down'){
        restoreArrayDown(arrayCopy);
    }
    
    // 4. Update page elements based on numbers
    updatePageByArray();
    updateColor();
    
    if ('success' == moveResult){
        // Because operation DOM Not real-time, so wait for an event to complete before popping up the confirmation box
        setTimeout(function(){
            // Game clearance, whether to restart
            if (confirm('Congragulations! Start Another Game?\n Congratulations on clearance! Would you like another one??')){
                gameRestart();
            }else{
                flagSuccess = true;
            }
        },200);
        return;
    }
    
    // Mobile completion
    clickedLastStep = false;
}

 

4.3 Generation of random numbers:

What we started with was to generate a random number, any one of the 16 locations. If the location was already occupied, we would abandon it and regenerate it until we found a vacancy.

But when there are fewer vacancies, it is inefficient.

Optimize: Search for random positions only in vacancies.

// Generate a random number and convert it to 0-15 Integer;
function getRamdom(count)
{
    // 0-1
    var r0 = Math.random();
    // 0-16
    var r1 = r0 * count;
    // Number of integer digits only
    return Math.floor(r1);
}

// Improvement: Random search only in current available locations, not in unavailable locations 
function getOneEmptyCoordinate(array)
{
    // 1. Get the current array
    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;
            }
        }
    }
    // Random Location of Available Location
    var random = getRamdom(countEmptyCells);
    return mapIndex2Coordinate[random];
}

 

 

5. deficiency:

5.1 Layout, style adjustment is relatively troublesome, misleading.

5.2 js code relationship - it doesn't matter, forget how the front end is organized;

5.3 Refresh page many times, may appear from the initialization exception, may be the file loading order problem, initialization dom operation is slow and so on.

5.4 See what other people think about.

http://www.cnblogs.com/-lizi/p/8431030.html

 

6. summary:

Development time: 5 working days, actual investment: about 15 hours.

Code size: js400 lines;

 

7. Full code pasting

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>Restart</a>
            </span>
        </div>
        <div id="mainTable">
        </div>
        <div id="explation">
            <span>
             2048 Game Description:<br>
             Press the left and right direction keys to move the numeric blocks.<br>
             Moving adjacent blocks of the same number in one direction will merge into larger numbers.<br>
             When the maximum number is 2048, the game wins!<br>
             Warm Tip: You can use the space bar to step back.
            </span>
        </div>
        <div id="rights">
            <span>
                All rights reserved to S.C. Contact me at 857025773@qq.com. 
            </span>
        </div>
    </div>
    
    <!-- operation -->
    <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: 'Microsoft YaHei';
    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

// Put together 2048, freeze operation, no movement allowed
var flagSuccess = false;
// Just clicked Back, no repeat clicks allowed, to move and then click
var clickedLastStep = false;

// Record the previous step for regression
var lastStepArray = [
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0],
                       [0,0,0,0]
                     ];

// Array initialization--Normal initialization
var arrayInit = 
    [
     [0,0,0,0],
     [0,0,0,0],
     [0,0,0,0],
     [0,0,0,0]
   ];
//[//Debugging -- will succeed
// [0,0,0,0],
// [0,128,0,0],
// [0,0,512,1024],
// [0,0,0,1024]
//];
//[//Debugging -- will fail
// [1,2,3,4],
// [5,128,6,7],
// [8,9,512,1024],
// [13,22,11,11]
//];



// Page loading initialization
function pageInit()
{
    addHelper();
    
//    initArray();
    // Page element initialization (cells)
    initElements();
    
    // Initial data
    initTwoPositions();
    
    // Fill cells with values based on initial data
    updatePageByArray();
    
    // Style cells according to their values
    updateColor();
}

// Restart
function gameRestart(){
    initArray();
    pageInit();
}

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


// Set the number 2 for two locations
function initTwoPositions()
{
    initOnePosition(arrayInit);
    initOnePosition(arrayInit);
}
// Set the number 2 for a location
function initOnePosition(array){
    // Choose Random Number Mode 1
//    var num = getRamdom(16);
//    var line = Math.floor(num/4);
//    var col = num % 4;
//    var curNum = array[line][col];
//    if (curNum == 0)
//    {
//        // Successfully find a free place
//        array[line][col] = 2;
//        return true;
//    }
//    else{
//        // Recursive calls have to find an available location to complete
//        return initOnePosition(array);
//    }
    
    // Choose Random Number Mode 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;
}

// Generate a random number and convert it to 0-15 Integer;
function getRamdom(count)
{
    // 0-1
    var r0 = Math.random();
    // 0-16
    var r1 = r0 * count;
    // Number of integer digits only
    return Math.floor(r1);
}

// Improvement: Random search only in current available locations, not in unavailable locations 
function getOneEmptyCoordinate(array)
{
    // 1. Get the current array
    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;
            }
        }
    }
    // Random Location of Available Location
    var random = getRamdom(countEmptyCells);
    return mapIndex2Coordinate[random];
}

 

eventListener,js

/**
 *  Reference Tool Class
 */
function addHelper(){
    var newscript = document.createElement('script');  
    newscript.setAttribute('type','text/javascript');  
    newscript.setAttribute('src','../js/arrayHelper.js');  
    document.body.appendChild(newscript);  
}

//Capture keystroke events
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(){
    // Take a step back
    if (clickedLastStep){
        console.log('can not move back again');
        return;
    }
    moveBack();
    flagSuccess = false;
    clickedLastStep = true;
}

// Take a step back
function moveBack(){
    // Judge that the last step is all 0 (just initialized)
    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. Set values for the current array with backup arrays
    updateFrontArray(arrayInit,lastStepArray);
    
    // 3. Update page
    updatePageByArray();
    updateColor();
}

// General method
// All the movements are translated into a movement in one direction: for example, they are all imagined to move to the left, only need to reverse the array, complete the movement and then reverse back!
function move(direction){
    if (flagSuccess){
        if (confirm("You've got your 2048! Start Another One?\n It's cleared! Start a new game?")){
            gameRestart();
        }
        return;
    }
    
    // Before moving, record the contents of the previous step, and recover after the failure of the move
    var lastStepUnchange = createNewArray();
    updateFrontArray(lastStepUnchange,lastStepArray);
    // Set the previous step status to the current one
    updateFrontArray(lastStepArray,arrayInit);
    
    // Update the current,Update failure rolls back the status of the previous step
    // 1. Array inversion replication
    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. They all move in one direction.
    var moveResult = moveLeft(arrayCopy);
    if ('fail' == moveResult){
        
        updateFrontArray(lastStepArray,lastStepUnchange);
        // Mobile failure, no change
        if (confirm('Game Over! Try again?\n Game over, one more time?')){
            gameRestart();
            return;
        }else{
            // The last step, let's go back.
            clickedLastStep = false;
            return;
        }
        return;
    }
    if ('unmoved' == moveResult){
        return;
    }
    
    // 3. Update the original array from the inverted array
    if (direction == 'left'){
        restoreArrayLeft(arrayCopy);
    }else if (direction == 'right'){
        restoreArrayRight(arrayCopy);
    }else if (direction == 'up'){
        restoreArrayUp(arrayCopy);
    }else if (direction == 'down'){
        restoreArrayDown(arrayCopy);
    }
    
    // 4. Update page elements based on numbers
    updatePageByArray();
    updateColor();
    
    if ('success' == moveResult){
        // Because operation DOM Not real-time, so wait for an event to complete before popping up the confirmation box
        setTimeout(function(){
            // Game clearance, whether to restart
            if (confirm('Congragulations! Start Another Game?\n Congratulations on clearance! Would you like another one??')){
                gameRestart();
            }else{
                flagSuccess = true;
            }
        },200);
        return;
    }
    
    // Mobile completion
    clickedLastStep = false;
}

// Move in one direction,Update arrays (left), the same in other directions
function moveLeft(arrayCopy){
    var gameSuccess = false;
    
    // If there is merge or move, if there is no merge or move, if there is no operation, then do not generate new numbers
    var anyChange = false;
    // Each line, from left to right, compares whether there is the same number (excluding 0)--It doesn't need to be next to each other! Air in the middle can also be combined.
    for (var i=0;i<4;i++){
        var firstIndex = 0;
        var secondIndex = 1;
        while (secondIndex<4){
            if (arrayCopy[i][firstIndex] == 0){
                // The first is 0.,There can't be mergers, moving backwards.
                firstIndex++;
                secondIndex++;
                continue;
            }
            if (arrayCopy[i][secondIndex] == 0){
                // The second one is 0, the second one continues to look back, and the first one remains unchanged.
                secondIndex++;
                // 1. You can't find the number at the back. Keep looking back. End the loop.
                continue;
            }            
            // Once there was the same number: calculate the sum, put it in the first place, and that's the end of the line.
            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){
                    // Get 2048, the game is over
                    gameSuccess = true;
                }
                // 2. Find the number later, and can merge, merge, end the cycle
                break;
            }
            else{
                // 3. Find the number later, but can not merge, update the first number to the current number, the second number to the next number.
                firstIndex = secondIndex;
                secondIndex += 1;
                continue;
            }
        }
    }
    
    // Move each line of numbers to the left
    for (var i=0;i<4;i++){
        var newLine = [];//Temporary storage of non-zero numbers from left to right
        var index = 0;
        for (var j=0;j<4;j++){
            if (arrayCopy[i][j] != 0){
                newLine[index] = arrayCopy[i][j];
                index++;
            }
        }
        
        // Update arrays with temporarily stored numbers
        for (var m=0;m<index;m++){
            if (arrayCopy[i][m] != newLine[m]){
                anyChange = true;
            }
            arrayCopy[i][m] = newLine[m];
        }
        // The remaining position is given to 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)){
            // This step can not move, and there is no remaining grid, and any adjacent grid does not have the same, the end of the game.
            if (!canMerge(arrayCopy)){
                console.log("Game Over! Try again.");
                return 'fail';
            }
        }
        if (gameSuccess){
            return 'success';
        }
        // No move
        return 'unmoved';
    }
    
    // Set Number 2 for Free Location
    if (isEmptyCell(arrayCopy))
    {
        initOnePosition(arrayCopy);
    }
    
    if (gameSuccess){
        return 'success';
    }
    return 'moved';
}

// Are there any adjacent lattices that can be merged?
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;
}


// Copy the back to the front
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;
}


//Left, original direction
function copyArrayLeft(){
    var arrayCopy = copyArray(arrayInit);
    return arrayCopy;
}
function restoreArrayLeft(arrayCopy){
    updateFrontArray(arrayInit,arrayCopy);
}

//Right, top and bottom unchanged, left and right reversed
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];
        }
    }
}

//upper
//From top to bottom to left to right
//From right to left to top to bottom
//(i,j) Corresponding:(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];
        }
    }
}

//lower
//From top to bottom to top
//From left to right to left to right
//(i,j) Corresponding:(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

// Assignment of elements with arrays
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++;
    }
}

// Initialize cells and colors
function initElements()
{
    var mainTable = document.getElementById("mainTable");
    var str = '';
    // Create row
    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;
    }
}

// according to ID Update color
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);
        }
    }
}

Topics: Javascript Mobile IE Firefox