动手学Javascript(1)——PopStar

PopStar是一款很流行的手机游戏。它的基本规则是在某个方块上单击,如果该方块周围有和它颜色一样的方块,那么这些方块都被选中。之后在选中方块的某一个上再次单击,所有选中的方块就会消失。

如下图所示,7个绿色的方块被选中(选中的方块被白色的框包围):


在选中的方块再次单击,这些绿色的方块就会消失。如果这些方块的上方就其他方块,上方的方块会掉下来。如果某一列全被消失,那么右边的列会自动左移。7块选中的绿色方块消失之后的效果如下图所示:


HTML代码如下所示:

<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8 />
        <title>PopStar</title>
        <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
        <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
        <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
        <script src="PopStar.js" type="text/javascript">
        </script>
        <link rel="stylesheet" type="text/css" href="PopStar.css" />
    </head>
    <body>
        <h1>Welcome to PopStar</h1>
        <div id="totalScore">Total Score: 0</div>
        <div id="currentScore">Click to Select.</div>
        <div id="mainCanvas">
        </div>
        <div id="notification">
            <div id="message"></div>
        </div>
    </body>
</html>

从上述代码可以看出,我们将会用到JQuery及jQuery UI的相关功能。

CSS的代码如下所示:

#mainCanvas {
    background-color: black;
     300px;
    height: 300px;
}

.block {
    position: fixed;
    height: 28px;
     28px;
    border-radius: 4px;
}

.selected {
    height: 26px;
     26px;
    border-color: white;
    border-style: solid;
    border- 1px;
}

CSS主要定义了游戏的背景,以及方块(选中或未选中)的外观。

游戏的功能主要用Javascript实现,如下所示:

$(document).ready(function () {
    var sharedData;
    initGame();
    startGame();

    function initGame() {
        sharedData = {};
        sharedData.size = 10;

        var canvas = $('#mainCanvas');
        var width = parseInt(canvas.css('width'));
        var height = parseInt(canvas.css('height'));
        if (width != height) {
            alert('the canvas should be a square.');
        }
        sharedData.canvas = canvas;
        sharedData.blockLength = width / sharedData.size;
        sharedData.matrix = initMatrix();

        $('#notification').dialog({
             400,
            resizable: false,
            modal: true,
            buttons: [{ text: "Ok", click: function () { $(this).dialog("close"); location.reload(); } }]
        });
    }

    function startGame() {
        sharedData.size = 10;
        sharedData.status = 0; // possible values: 0, 1
        sharedData.totalScore = 0;

        clearMatrix();

        initBlocks(sharedData);
        moveBlocks();

        clearCurrentScore();
        clearTotalScore();
        hideNotification();

        if (isGameOver()) {
            showNotification();
        }
    }

    function initBlocks(data) {
        var size = data.size;
        var row, col, value;
        var singleColor = size * size / 4;
        var divString;
        var newRow, newCol, index;

        $('.block').each(function () {
            $(this).remove();
        });

        var randomArray = getRandomArray(data.size);

        for (row = 0; row < size; ++row) {
            for (col = 0; col < size; ++col) {
                if (row * size + col < singleColor) {
                    value = 1;
                }
                else if (row * size + col < singleColor * 2) {
                    value = 2;
                }
                else if (row * size + col < singleColor * 3) {
                    value = 3;
                }
                else {
                    value = 4;
                }
                
                index = randomArray[row * size + col];
                newRow = Math.floor(index / size);
                newCol = index % size;

                divString = '<div class="block" currow="0" curcol="0" nextrow="' + newRow.toString()
                    + '" nextcol="' + newCol.toString() + '" value="' + value.toString() + '"></div>';
                $('#mainCanvas').append(divString);
            }
        }
    }

    function getRandomArray(size) {
        var total = size * size;
        var array = new Array(total);
        var i, index, temp;
        
        for (i = 0; i < array.length; ++i) {
            array[i] = i;
        }
        
        for (i = array.length - 1; i > 0; --i) {
            index = Math.floor(Math.random() * total);

            temp = array[i];
            array[i] = array[index];
            array[index] = temp;
        }
        
        return array;
    }

    function moveBlocks() {
        $('.block').each(function () {
            var nextRow = $(this).attr('nextrow');
            var nextCol = $(this).attr('nextcol');
            var value = $(this).attr('value');
            var parentPos = $(this).parent().offset();
            var top = parseInt(nextRow) * sharedData.blockLength + parentPos.top + 1;
            var left = parseInt(nextCol) * sharedData.blockLength + parentPos.left + 1;
            var cssTop = top.toString() + 'px';
            var cssLeft = left.toString() + 'px';
            var colors = ['#aaaaaa', '#3333cc', '#33cc33', '#cc3333', '#cccc33'];

            $(this).animate({
                top: cssTop,
                left: cssLeft
            },
            500);

            $(this).css({
                'background-color': colors[parseInt(value)]
            });

            $(this).attr('currow', nextRow);
            $(this).attr('curcol', nextCol);
        });
    }

    $('.block').click(function () {
        var clickedBlock = getClickedBlockPos($(this));
        var sameColorBlocks;

        clearMatrix();
        sameColorBlocks = getBlocksWithSameColor(clickedBlock.row, clickedBlock.col);
        updateStatus(clickedBlock.row, clickedBlock.col, sameColorBlocks);
    });

    function updateStatus(row, col, sameColorBlocks) {
        if (sameColorBlocks.length > 1) {
            if (sharedData.status == 0) {
                sharedData.status = 1;
                setSelectedBlocks(sameColorBlocks);
            }
            else {
                if (isClickAgain(row, col, sameColorBlocks)) {
                    moveMatrix(sharedData.matrix, sameColorBlocks);
                    sharedData.status = 0;
                    updateTotalScore(sharedData, sameColorBlocks.length);
                    clearSelectedBlocks();
                    moveBlocks();

                    clearMatrix();
                    if (isGameOver()) {
                        showNotification();
                    }
                }
                else {
                    setSelectedBlocks(sameColorBlocks);
                }
            }
        }
        else if (sharedData.status == 1) {
            sharedData.status = 0;
            clearSelectedBlocks();
        }
    }

    function moveMatrix(matrix, sameColorBlocks) {
        moveMatrixDown(matrix, sameColorBlocks);
        moveMatrixLeft(matrix);
        deleteBlocks();
        updateBlockPosition();
    }

    function updateBlockPosition() {
        $('.block').each(function () {
            var curRow = parseInt($(this).attr('currow'));
            var curCol = parseInt($(this).attr('curcol'));

            var moveDown = sharedData.matrix[curRow][curCol].moveDown;
            var moveLeft = sharedData.matrix[curRow][curCol].moveLeft;

            var nextRow = curRow + moveDown;
            var nextCol = curCol - moveLeft;

            $(this).attr('nextrow', nextRow.toString());
            $(this).attr('nextcol', nextCol.toString());
        });
    }

    function deleteBlocks() {
        $('.block').each(function () {
            var curRow = parseInt($(this).attr('currow'));
            var curCol = parseInt($(this).attr('curcol'));
    
            if (sharedData.matrix[curRow][curCol].value == 0) {
                $(this).remove();
            }
        });
    }

    function moveMatrixDown(matrix, toBeDeleted) {
        var i, j;

        toBeDeleted.sort(sortPosition);

        for (i = 0; i < toBeDeleted.length; ++i) {
            matrix[toBeDeleted[i].row][toBeDeleted[i].col].value = 0;

            for (j = toBeDeleted[i].row ; j >= 0; --j) {
                matrix[j][toBeDeleted[i].col].moveDown += 1;
            }
        }
    }

    function moveMatrixLeft(matrix) {
        for (i = 0; i < matrix.length; ++i) {
            if (isColumnBlank(matrix, i)) {
                moveColumnsLeft(matrix, i);
            }
        }
    }

    function moveColumnsLeft(matrix, col) {
        var i, j;
        for(i = 0; i < matrix.length; ++i) {
            for (j = col + 1; j < matrix.length; ++j)
                matrix[i][j].moveLeft += 1;
            }
    }

    function isColumnBlank(matrix, col) {
        var row;
        for (row = 0; row < matrix.length; ++row) {
            if (matrix[row][col].value != 0)
                return false;
        }

        return true;
    }

    function sortPosition(pos1, pos2) {
        if ((pos1.col < pos2.col) || (pos1.col == pos2.col && pos1.row > pos2.row))
            return -1;
        if (pos1.col == pos2.col && pos1.row == pos2.row)
            return 0;
        return 1;
    }

    function setSelectedBlocks(sameColorBlocks) {
        $('.block').each(function () {
            if ($(this).hasClass('selected')) {
                $(this).removeClass('selected');
            }
        });

        $('.block').each(function () {
            var curRow = parseInt($(this).attr('currow'));
            var curCol = parseInt($(this).attr('curcol'));

            if(isSelected(sameColorBlocks, curRow, curCol) && !$(this).hasClass('selected')) {
                $(this).addClass('selected');
            }
        });

        setCurrentScore(sameColorBlocks.length);
    }

    function isSelected(sameColorBlocks, row, col) {
        for (var i = 0; i < sameColorBlocks.length; ++i) {
            if (sameColorBlocks[i].row == row && sameColorBlocks[i].col == col) {
                return true;
            }
        }

        return false;
    }

    function clearSelectedBlocks() {
        $('.block').each(function () {
            if ($(this).hasClass('selected')) {
                $(this).removeClass('selected');
            }
        });

        clearCurrentScore();
    }

    function getClickedBlockPos(block) {
        var curRow = parseInt(block.attr('currow'));
        var curCol = parseInt(block.attr('curcol'));

        return {
            row: curRow,
            col: curCol
        };
    }

    function initMatrix() {
        var i, j;
        var size = sharedData.size;
        var rows = new Array(size);

        for (i = 0; i < size; ++i) {
            rows[i] = new Array(size);
            for (j = 0; j < size; ++j) {
                rows[i][j] = {
                    value: 0,
                    moveDown: 0,
                    moveLeft: 0
                };
            }
        }

        return rows;
    }

    function clearMatrix() {
        var i, j;
        var size = sharedData.size;
        var matrix = sharedData.matrix;

        for (i = 0; i < size; ++i) {
            for (j = 0; j < size; ++j) {
                matrix[i][j] = {
                    value: 0,
                    moveDown: 0,
                    moveLeft: 0
                };
            }
        }

        $('.block').each(function () {
            var value = parseInt($(this).attr('value'));
            var curRow = parseInt($(this).attr('currow'));
            var curCol = parseInt($(this).attr('curcol'));
            sharedData.matrix[curRow][curCol].value = value;
        });
    }

    function getBlocksWithSameColor(row, col) {
        var matrix = sharedData.matrix;
        var value = matrix[row][col].value, curValue;
        var matrixSize = matrix[0].length;
        var visited = new Array();
        var top, curRow, curCol, preRow, preCol;
        var sameColor = new Array();
        var i;

        var flag = new Array(matrixSize * matrixSize);
        for (i = 0; i < flag.length; ++i) {
            flag[i] = false;
        }

        addBlockWithSameColor(matrix, visited, sameColor, flag, row, col);

        while (visited.length > 0) {
            top = visited.pop();

            // left
            curRow = top.row;
            curCol = top.col - 1;
            addBlockWhenWithSameColor(matrix, value, visited, sameColor, flag, curRow, curCol);

            // right
            curRow = top.row;
            curCol = top.col + 1;
            addBlockWhenWithSameColor(matrix, value, visited, sameColor, flag, curRow, curCol);

            // up
            curRow = top.row - 1;
            curCol = top.col;
            addBlockWhenWithSameColor(matrix, value, visited, sameColor, flag, curRow, curCol);

            // down
            curRow = top.row + 1;
            curCol = top.col;
            addBlockWhenWithSameColor(matrix, value, visited, sameColor, flag, curRow, curCol);
        }

        return sameColor;
    }

    function addBlockWithSameColor(matrix, visited, sameColor, flag, row, col) {
        var cell = {
            row: row,
            col: col
        };

        visited.push(cell);
        sameColor.push(cell);
        flag[row * matrix[0].length + col] = true;
    }

    function addBlockWhenWithSameColor(matrix, value, visited, sameColor, flag, curRow, curCol) {
        var matrixSize = matrix.length;
        var isOnBoundaryOrDiffColor = curRow >= 0 && curRow < matrixSize &&
            curCol >= 0 && curCol < matrixSize &&
            matrix[curRow][curCol].value == value;
        if (isOnBoundaryOrDiffColor && !flag[curRow * matrixSize + curCol]) {
            addBlockWithSameColor(matrix, visited, sameColor, flag, curRow, curCol);
        }
    }

    function isClickAgain(row, col, sameColor) {
        var result = false;

        $('.block').each(function () {
            var curRow = parseInt($(this).attr('currow'));
            var curCol = parseInt($(this).attr('curcol'));

            if(isSelected(sameColor, curRow, curCol) && $(this).hasClass('selected')) {
                result = true;
            }
        });

        return result;
    }

    function setCurrentScore(num) {
        var score = getScore(num);
        $('#currentScore').html('Selection Score: ' + score.toString());
    }

    function clearCurrentScore() {
        $('#currentScore').html('Click to Slect.');
    }

    function clearTotalScore() {
        $('#totalScore').html('Total Score: 0');
    }

    function getScore(num) {
        return 5 * num * num;
    }

    function updateTotalScore(data, num) {
        var score = getScore(num);
        data.totalScore += score;
        $('#totalScore').html('Total Score: ' + data.totalScore.toString());
    }

    function hideNotification() {
        $('#notification').dialog('close');
    }

    function showNotification() {
        $('#message').html('Game over. Click Ok to restart.');
        $('#notification').dialog('open');
    }

    function isGameOver() {
        var over = true;
        var curRow, curCol;
        var sameColor;

        $('.block').each(function () {
            if (over) {
                curRow = parseInt($(this).attr('currow'));
                curCol = parseInt($(this).attr('curcol'));

                sameColor = getBlocksWithSameColor(curRow, curCol);
                if (sameColor.length > 1) {
                    over = false;
                }
            }
        });

        return over;
    }
});

如果你对上述代码感兴趣,也可以到 http://download.csdn.net/detail/haitaohe/6702475处下载。

原文地址:https://www.cnblogs.com/riasky/p/3471579.html