简体   繁体   中英

Prevent Stack Overflow in a Minesweeper Clone

I'm creating a Minesweeper clone. Thus far, I've gotten to revealing adjacent tiles when the tile clicked has zero adjacent mines, below is my method for revealing mines.

struct data
{
    public Button tile;
    public bool mine, flag, clicked;
    public int adjMines;
}

data[,] dat;
//Defaults
Size gridSize = new Size(16, 16);
Size tileSize = new Size(16, 16);
int mines = 40, flags = 0;

bool valid(int x, int y)
{
    return (x >= 0 && y >= 0 && y < gridSize.Height && x < gridSize.Width);
}    


void reveal(Button btn)
{
    btn.BackColor = Color.DimGray;
    start = true;
    btn.Enabled = false;

    //find button clicked, forget everything you ever learned about efficiency.
    for (int i = 0; i < gridSize.Width; i++)
        for (int j = 0; j < gridSize.Height; j++)
            if (dat[i, j].tile == btn)
            {
                if (dat[i, j].adjMines == 0)
                {
                    for (int ii = -1; ii <= 1; ii++)
                        for (int jj = -1; jj <= 1; jj++)
                            if (valid(i + ii, j + jj))
                                reveal(dat[i + ii, j + jj].tile);
                }
                else
                    btn.Text = dat[i, j].adjMines.ToString();
            }
}

I keep getting a StackOverflowException when I run it, which doesn't come as much of a surprise, but I don't know how to fix it without doing away with the struct , which is a requirement. Any ideas?

The problem is that when you "reveal", you reveal all the neighbors. When the neighbor is revealed, it reveals all of its neighbours, including the first one, and now you have an infinite recursion.

The trick is: before you reveal the very first one, make a hash set of "in progress reveals". Before you recurse, add the current button to the "in progress" set. In the reveal method return immediately if the button is already in the in-progress set . You know that all its neighbours are already in the process of being revealed, so there's no work to do.

It would probably help if you didn't recursively try to reveal the same tile:

                for (int ii = -1; ii <= 1; ii++)
                    for (int jj = -1; jj <= 1; jj++)
                        if (valid(i + ii, j + jj) && !(ii == 0 && jj == 0))
                            reveal(dat[i + ii, j + jj].tile);

Note the !(ii == 0 && jj == 0) test added - that will stop one of the stack overflow reasons. Apart from that, you also need to mark the node as 'enabled' before going recursive, it'll keep bouncing back and forth otherwise.

It's not a really efficient method you've chosen to implement, but it should work with this fix.

It looks like you are calling reveal from inside reveal. Reveal goes through each tile. You are calling it for all adjacent tiles which then goes through each tile and then calls all adjacent tiles. I would suggest looking through your use of recursion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM