Search code examples
javascriptjqueryhtmlrecursionminesweeper

JQuery Grid Recursion for Minesweeper (click adjacent tiles)


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.


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.