2

I am building a Minesweeper game with vanilla JavaScript. I have a Grid class that handles logic on the grid, a Space class that acts basically as a container for space properties, and a Game class that handles game logic.

In the Grid class, I have a method called openAdjoiningSpaces. The intended behaviour of this method is to open all connected spaces on the board in each direction starting with the clicked empty space, and terminating when the next space borders a mine. If you aren't familiar with Minesweeper you can see this behaviour at http://minesweeperonline.com/.

The openAdjoiningSpaces method calls another method, getBorderingSpaces. This method accepts the clicked space as an argument and returns an array that contains each bordering space, including spaces that touch at the corners.

When openAdjoiningSpaces is called, it first gets the bordering spaces that don't contain a mine and puts them in an array called toOpen. The method then uses a while loop. In the while loop, each space in toOpen is opened, and then for each space that is opened, its bordering spaces are obtained and if they are empty, they are added to an array called next. At the end of the loop, if the next array has a length greater than 0, the toOpen variable is reassigned to next, otherwise the loop terminates.

This should cause the desired behaviour, but it isn't. Each time I rewrite it in another way, it either crashes the browser (even though it is not an infinite loop), or it only opens up the surrounding spaces of the clicked space and then terminates. Here is the method itself:

    openAdjoiningSpaces(space)
    {
        if (space.isEmpty) {

            let loop = true;

            var toOpen = [ ...this.getBorderingSpaces(space).filter(space => !space.hasMine)];

            while (loop) {

                const next = [];

                toOpen.forEach(space => {

                    this.openSpace(space.id);

                    const neighbouring = this.getBorderingSpaces(space);

                    neighbouring.forEach(space => {
                        if (!space.hasMine && space.isEmpty) {
                            next.push(space);
                        }
                    });
                });

                if (next.length > 0) {
                    toOpen = next;
                    break;
                } else {
                    loop = false;
                }

            }
        }
    }

I have been trying to create this space opening behavior for two days now. Each time I get close to succeeding, the grid still displays some kind of buggy behavior. All this afternoon I have continually tried to rewrite this method in different ways, to no avail. I'm at my wits end and really need some outside input. Please don't hesitate to ask me to clarify anything about the code. One thing I should clarify is that Space objects have two separate properties hasMine and isEmpty. The first just refers to whether the space has a mine or not. The second refers to whether it contains a number. Maybe a design flaw, I don't know but I thought I should mention that.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
  • Just to clarify, the while loop should go on until `next.length === 0`? If that's the case, on your code you're always ending the loop either way (`break` breaks out the loop), so you should remove the `break`. In any case, could you include the code of `this.getBorderingSpaces`? Even better if you can also add a few test "spaces" and the expected result. – SrThompson Dec 29 '19 at 22:24
  • It helps to post valid code, there appears to be a missing keyword in the first line. – RobG Dec 29 '19 at 22:25
  • @RobG There is no missing keyword. Methods in JavaScript omit the `function` keyword. – Michael Alexander Dec 29 '19 at 22:29
  • The code **as posted** contains syntax errors. – RobG Dec 29 '19 at 22:29
  • I can't post the `getBorderingSpaces` method here because it's too long. However, it does correctly obtain all spaces that touch the target spaces. – Michael Alexander Dec 29 '19 at 22:30
  • @MichaelAlexander please add the extra code to the question, it's awkward to read it from a comment – SrThompson Dec 29 '19 at 22:33
  • @RobG Thanks for that really useful comment. Very appreciated. – Michael Alexander Dec 29 '19 at 22:33
  • @SrThompson I'm not sure what specifically would be useful to show you. Instead I am going to link the whole class from github for you: https://github.com/michaelacook/minesweeper/blob/master/src/classes/Grid.js – Michael Alexander Dec 29 '19 at 22:36

1 Answers1

0

It was a bit tricky to find, but there is a bug in openAdjoiningSpaces. When you add items to next you never check if the current space is already opened. This caused the isOpen array to get larger every iteration, making the loop never ending. Add this to your code and remove the break after toOpen = next

Change this

toOpen.forEach(space => {

                this.openSpace(space.id);

                const neighbouring = this.getBorderingSpaces(space);

                neighbouring.forEach(space => {
                    if (!space.hasMine && space.isEmpty) {
                        next.push(space);
                    }
                });
            });

For This

toOpen.forEach(space => {

                this.openSpace(space.id);

                const neighbouring = this.getBorderingSpaces(space);

                neighbouring.forEach(space => {
                    if (!space.hasMine && space.isEmpty && space.status !== 'open') {
                        next.push(space);
                    }
                });
            });
SrThompson
  • 5,568
  • 2
  • 17
  • 25
  • Thanks for your effort. That did fix a bug, but the method still does not produce the desired outcome. Method still only opens the spaces that directly touch the clicked space, rather than fanning out. – Michael Alexander Dec 30 '19 at 00:21
  • Nevermind, I removed the `break` statement. Desired behaviour *almost* reached. The only problem now is that it only opens all empty spaces. I should actually keep opening spaces until it finds a space that borders a mine, then open that space and terminate. Getting closer though. I'm encouraged. – Michael Alexander Dec 30 '19 at 00:25
  • 1
    Thanks again for your help. If you're interested, here's the clone: https://michaelacook.github.io/minesweeper/ It's about 98% done. – Michael Alexander Jan 01 '20 at 00:30