Creating a Minesweeper Game in SolidJS - Score, Timer and Game State
Tags: solidjs, tutorial, gamedev, javascript
Published: 2023-03-31 10:25:50

Welcome to the 3rd installment of the “Creating a Minesweeper Game in SolidJS” post series. In the [first part](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-the-board-3p5h), we constructed the game board using a flat array and mathematical operations. In the [second part](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-the-zero-opening-a46), we concentrated on the recursive iteration process required to implement the "zero-opening" functionality. 
In this post, we aim to incorporate gamification elements into the game to make it more engaging and enjoyable for users.

We're going to spice things up by adding a mine counter to keep track of those pesky bombs and help you keep count of the ones you've marked. Plus, we'll introduce a game-ending logic and a handy new Timer component that will keep track of how long you've been playing until the bombs start detonating or you've marked all the mines. 

We have plenty to do, so let’s get on to it!

***

*Hey! for more content like the one you're about to read check out <a href="https://twitter.com/mattibarzeev?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">@mattibarzeev</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> on Twitter* :beers:

***

The code can be found in this GitHub repository:
https://github.com/mbarzeev/solid-minesweeper

## The Mines Counter
The Minesweeper game has a game panel, which holds, among other things, the mines counter.
The counter starts from the initial number of total mines on the board, and each time the player marks a tile, the mine count is decreased accordingly, even if the mark is not actually on a Tile which has a mine in it.

We first render the game panel in the header:

```jsx
<header class={styles.header}>
               <div class={styles.gamePanel}></div>
               <div class={styles.board}>
                   <For each={tilesArray()}>
                       {(tileData: TileData) => (
                           <Tile data={tileData} onTileContextMenu={toggleTileMark} onTileClicked={onTileClicked} />
                       )}
                   </For>
               </div>
           </header>
```

In order to show the remaining mines count we need to create a solid’s signal so it can be reactive:

```javascript
const TOTAL_MINES = 50;
const [remainingMines, setRemainingMines] = createSignal<number>(TOTAL_MINES);
```

And the rendering will be:

```javascript
<div class={styles.gamePanel}>
                   <span>{remainingMines()}</span>
               </div>
```

Now, in order to set the number of total remaining mines correctly we need to update it each time the player toggle the marking and I would like to do that as a side effect of when the tiles array is being modified. 
In order to achieve that we need to “listen” for changes in the `tilesArray` and inspect the marked tiles in order to calculate the remaining mines. It may appear not optimized but it will make our game work in a deterministic manner with a game state - by that I mean that if we give the game a state it will render correctly. 

For that we shall use Solid’s “createEffect()”:

```javascript
createEffect(() => {
   const markedTiles = tilesArray().filter((tile) => tile.isMarked);
   setRemainingMines(TOTAL_MINES - markedTiles.length);


 });
```

This effect will trigger each time the tilesArray signal is changed, filter out the tiles which are marked, and deduct them from the TOTAL_MINES to get the remaining mines number.

## Detonating a Mine
When the player accidentally opens a tile which has a mine, the game is over.
Here is the code for the tile clicked handler:

```javascript
const onTileClicked = (index: number) => {
   const tile: TileData = tilesArray()[index];
   // If the tile is marked, un-mark it
   tile.isMarked = false;
   // If the tile has a mine, it's game over
   if (tile.value === MINE_VALUE) {
       gameOver();
   } else {
       let indices = [index];
       const tileValue = tile.value;
       if (tileValue === 0) {
           // get the indices that need to be opened...
           indices = getTotalZeroTilesIndices(index);
       }
       openTiles(indices);
   }
};
```

You can see that when we detect that the value is a “MINE_VALUE” we call the `gameOver()` function.
What we want to do in the “gameOver” function is to detonate the clicked mine and then the rest of the mines on the board.

We start by adding another property to the TileData, which is `isDetonated:boolean;`:

```javascript
export type TileData = {
   index: number;
   value: TileValue;
   isOpen: boolean;
   isMarked: boolean;
   isDetonated: boolean;
};
```

This will help us indicate that the tile is detonated. We obviously initiate it to “false”:

```javascript
// Convert the boardArray to TilesArray
const [tilesArray, setTilesArray] = createSignal<TileData[]>(
   boardArray.map((item, index) => ({
       index,
       value: getTileValue(index),
       isOpen: false,
       isMarked: false,
       isDetonated: false,
   }))
);
```

We also add a css class in case the Tile is detonated, in the Tile component:

```javascript
classList={{[styles.exposed]: data.isOpen || data.isMarked, [styles.detonated]: data.isDetonated}}
```

```css
.value.detonated {
   display: block;
   background-color: red;
}
```

Now, when the player detonates a mine, we mark all the tiles with mines in them as “detonated” and we open them all, so when the game is over the entire game board is exposed:

```javascript
const gameOver = () => {
   setTilesArray((prevArray) => {
       const newArray = prevArray.map((tile) => {
           if (tile.value === MINE_VALUE) {
               return {...tile, isDetonated: true, isOpen: true};
           }
           return {...tile, isOpen: true};
       });


       return newArray;
   });
};
```

And here is the result so far:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjgxbfie4n66xnodgu1g.png)

## Finding all the mines
It’s time to handle the situation where the player finds all the mines. In other words, it means that each Tile that has a mine in it is marked with a flag, and that the number of marked Tiles is equal to the total number of mines in the board.

In the `createEffect` we previously made we add the following code that checks if the game is won:

```javascript
createEffect(() => {
  if (isGameOver) return;


   const markedTiles = tilesArray().filter((tile) => tile.isMarked);
   setRemainingMines(TOTAL_MINES - markedTiles.length);


   // If the marked tiles are actually mines and they equal to the total number
   // of mines in the board, the game is won
   if (markedTiles.length === TOTAL_MINES) {
       try {
           markedTiles.forEach((tile) => {
               if (tile.value !== MINE_VALUE) {
                   throw new Error();
               }
           });
           gameWon();
       } catch (error) {
           // Do nothing, the game is not won
       }
   }
});
```

In short, when the number of marked tiles is equal to the number of total mines we check if they are all mines. If they are not, we break the loop. If they are, we call the “gameWon()” method.

Notice that we check the “isGameOver” before we do anything. This is to prevent an infinite cycle when we want to do something to the Tiles array when the game is over, like in the `gameWon` function, where we open the entire Tiles.

Here is the code for the `gameWon` function:

```javascript
const gameWon = () => {
   isGameOver = true;
   setTilesArray((prevArray) => {
       const newArray = prevArray.map((tile) => {
           return {...tile, isOpen: true};
       });


       return newArray;
   });
};
```

## The Timer
It’s time to add the timer.
For now it will be “dumb” - it will start at the refresh of the page and will stop when the game is over for some reason.

We start with a Signal and an interval for the timer:

```javascript
let timerInterval: number;
const [timerSeconds, setTimerSeconds] = createSignal(0);
const startTimer = () => {
   timerInterval = setInterval(() => {
       setTimerSeconds(timerSeconds() + 1);
   }, 1000);
};
```

And we call the `startTimer()` on the main App file.

For the Timer we build a dedicated component which displays the time in a nice format. Notice that the “seconds” prop that is passed to the component is not a number but is actually a Solid’s Accessor type:

```javascript
import {Accessor} from 'solid-js';


const Timer = ({seconds}: {seconds: Accessor<number>}) => {
   return <div>{getDisplayTimeBySeconds(seconds())}</div>;
};


const getDisplayTimeBySeconds = (seconds: number) => {
   const min = Math.floor(seconds / 60);
   const sec = seconds % 60;
   return `${getDisplayableTime(min)}:${getDisplayableTime(sec)}`;
};


function getDisplayableTime(timeValue: number): string {
   return timeValue < 10 ? `0${timeValue}` : `${timeValue}`;
}


export default Timer;


```

We use this component like this in the main App file:

```javascript
<div class={styles.scoring}>
                   <span>{remainingMines()}</span>
                   <Timer seconds={timerSeconds} />
               </div>
```

And when the game is over for any reason, we clear the interval. It might be better to have a game state and have effects to it, but we will save that, and others, to the refactor phase.

Well, I think we made it and achieved our goals for this post!

So what do we have so far?

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajvyqrscrq8auitwkvk5.png)

We have a mine counter, we have a timer, we have a way to indicate when the game is over or won, and even got a nice bomb emoji for the mines (couldn’t find a mine, sorry). Not too bad 🙂

Stay tuned for the next post where we will put some modals and attempt to complete and deploy the game!

The code can be found in this GitHub repository:
https://github.com/mbarzeev/solid-minesweeper 

This article is one of a 4 parts post series:
* [Creating a Minesweeper Game in SolidJS - The Board](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-the-board-3p5h)
* [Creating a Minesweeper Game in SolidJS - The Zero-Opening](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-the-zero-opening-a46)
* [Creating a Minesweeper Game in SolidJS - Score, Timer and Game State](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-score-timer-and-game-state-56n0)
* [Creating a Minesweeper Game in SolidJS - Completing The Game](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-completing-the-game-3f8p)

***

*Hey! for more content like the one you've just read check out <a href="https://twitter.com/mattibarzeev?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">@mattibarzeev</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> on Twitter* :beers:

<small><small><small></small></small></small>