A couple weeks ago, I started playing minesweeper after downloading a game app on my phone. It got me thinking, had been a while since making the last small browser game, could be fun to try recreating this one, complete with the distinctive styling of the original Windows version.

The game

It’s a minimal implementation, HTML/CSS/build-free Javascript without any external dependencies (code here. Attempted to take the chance to learn a bit about web component by building the game as a simple one.

Click/tap reveals cells, right-click/long tap marks cells with flags.

Styling 3D effect borders

To recreate the retro look, CSS has some interesting possibilities for border-style to give 3D effects:

solid
inset
outset
border
ridge

The outset border can be used to create the game grid itself:

.grid {
    border-collapse: separate;
    border-spacing: 0;
}
.grid tr td {
    width: 40px;
    height: 40px;
    background: lightgray;
    border: 8px outset white;
    box-sizing: border-box;
}

First thought was a similar 3D effect for the containing game frame could be achieved by simply collapsing the borders, but it looked rubbish and broke the 3D effect (1). After some experimentation, the trick was to have an outer outset frame around the whole game, and inset border on the 3 inner panels to create the 3D effect (2), ensuring border-spacing: 0 (3):

(As fun as using nested tables for layout is, I rewrote using divs – the same principle works there too, using inner inset and outer outset borders in tandem).

Game logic

The game logic is separated from the visualisation. Random mines are placed with field represents a state of every cell in the game grid (whether it is a mine, a number or empty). Each click reveals more about the world state to the user. revealed is what is known to the user of the grid.

One interesting logic aspect is when clicking on an empty cell not adjacent to a mine. Then, all adjacent non-mine cells should also be revealed. For example, the state below could have been created by a single click on a currently revealed empty cell:

Minesweeper flood fill example

This is an example of a flood-fill. This was implemented here recursively:

floodFill(x, y) {
    let curr = [x, y];
    // if this cell is already revealed or out of bounds, do nothing
    if (this.revealed[curr] || this.outOfBounds(x, y)) {
        return;
    }
    if (this.field[curr] === undefined) {
        // field is empty, need to flood-fill and reveal adjacent empty cells
        this.revealed[curr] = MinesweeperGame.FIELD_EMPTY;
        for (let [dx, dy] of MinesweeperGame.NEIGHBOUR_OFFSETS)
            this.floodFill(x + dx, y + dy)
    } else if (typeof this.field[curr] == "number") {
        // field is a number cell, should reveal
        this.revealed[curr] = this.field[curr];
    }
}

There are a few features of the game left unimplemented still, such as chording.

Playing online

Stumbled on minesweeper.online – pretty fully featured site with the game, your statistics, tournaments and other challenges. Didn’t realise people played competitively. Up for a challenge?