Question

I have created a javascript version of minesweeper for a project, but I am trying to introduce an additional piece of functionality to better mimic the desktop version I remember playing.

My full code can be found at: jsfiddle

The particular function I am having difficulty with is the "clickTile()" function. The base requirement is to add the class 'selected' as well as the number of adjacent mines to the html for the clicked tile. The additional requirement I am trying to resolve is recursive autoclicking of tiles that do not have adjacent mines.

The function as it stands is:

function clickTile($tile) {
if ($tile.hasClass('mine')) {
    alert('game over');
    $('.mine').css("background", "red");
} else if (!$tile.hasClass("selected")){
    mines = getNumberOfAdjacentMines($tile);
    $tile.addClass("selected");
    $tile.html(mines);

    //recursively call clickTile function as long as there are no adjacent mines
    if (mines <= 0) {

        height = $('#field .row').length;
        width = $('#field .row .tile').length / height;
        rowNum = $tile.parent().prevAll('div').length
        colNum = $tile.prevAll('div').length

        $above = $tile    // td
        .parent()      // tr
        .parent()      // table or tbody
        .children('.row')
        .eq(rowNum - 1) // the row above this one
        .children('.tile')
        .eq(colNum)    // in the same column as this
        ;

        $below = $tile    // td
        .parent()      // tr
        .parent()      // table or tbody
        .children('.row')
        .eq(rowNum + 1) // the row below this one
        .children('.tile')
        .eq(colNum)    // in the same column as this
        ;

        if (rowNum > 0) {
            if (!$above.hasClass('mine') && !$above.hasClass('selected')){
                clickTile($above);
            }
        }

        if (colNum < (width-1)){
            if(!$tile.next().hasClass('selected')) {
                clickTile($tile.next());
            }
        }

        if (rowNum < (height-1)){
            if(!$below.hasClass('mine') && !$below.hasClass('selected')) {
                clickTile($below);
            }
        }

        if (colNum > 0){
            if (!$tile.prev().hasClass('selected')) {
                clickTile($tile.prev());
            }
        }

    }
}


}

To me, this SHOULD work to recursively click all tiles with 0 adjacent mines, but it does not. Can anyone help me understand where I may be going wrong with this? The behavior is very finicky.

Was it helpful?

Solution

The problem with your code is that the variables are defined without the var keyword, so they are defined in the global scope.

In the clickTile function, you should replace

// rowNum is defined in the global scope, 
// Its value is overwritten with each recursive call
rowNum = $tile.parent().prevAll('div').length

with

// rowNum has the function as a scope, 
// its keeps different rowNum values for each recursive function call
var rowNum = $tile.parent().prevAll('div').length

The rowNum (and other variables) values are overwritten by recursive calls, so further conditions will behave incorrectly

if (rowNum > 0) { // rowNum can be anything at this point
    // The condition is randomly met
}

Add the var keyword to your variables, and your code should work as expected.

More about javascript scopes here.

OTHER TIPS

Well, you had some trouble with selectors which were causing a too much recursion error, it was an endless loop sometimes. I tried to find the error and in some cases you sent to the recursive click function an empty selector, so it was no working properly. I did not like those selectors so made my own way of getting above, below, next and previous elements and it worked fine! Here is the code:

function clickTile($tile) {
var tiles=$(".tile");
var rows=8;
var columns=8;
var index=tiles.index($tile);    

if($tile.length ==0) return false;
if ($tile.hasClass('mine')) {
    alert('game over');
    $('.mine').css("background", "red");
} else if (!$tile.hasClass("selected")){
    mines = getNumberOfAdjacentMines($tile);

    $tile.html(mines);
    $tile.addClass("selected");

    //recursively call clickTile function as long as there are no adjacent mines
    if (mines <= 0) {

        if (index >= rows) {
            $above=$(tiles.get(index-rows));
            if (!$above.hasClass('mine') && !$above.hasClass('selected')){
                clickTile($above);
            }
        }

        if ((index+1)%columns !=0 ){
            $next=$(tiles.get(index+1))
            if(!$next.hasClass('selected')) {
                clickTile($next);
            }
        }

        if (index+rows < (rows*columns) ){
            $below=$(tiles.get(index+rows));
            if(!$below.hasClass('mine') && !$below.hasClass('selected')) {
                clickTile($below);
            }
        }

        if ((index+1)%columns != 1){
            $prev=$(tiles.get(index-1))
            if (!$prev.hasClass('selected')) {
                clickTile($prev);
            }
        }

    }
}

}

And here is a shorter version for travelling neighbours:

        if (mines <= 0) {

            var posInRow=(index+1)%columns;
            var neighbours=[];
            if(index >= rows) neighbours.push(index-rows);//up
            if(index+rows < (rows*columns) ) neighbours.push(index+rows);//down
            if(posInRow != 0) neighbours.push(index+1);//right
            if(posInRow != 1) neighbours.push(index-1);//left
            for(var i=0;i<neighbours.length;i++){
                var neighbour=$(tiles.get(neighbours[i]));
                if (!neighbour.hasClass('mine') && !neighbour.hasClass('selected')){
                    clickTile(neighbour);
                }
            }

        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top