Creating a Minesweeper Game in SolidJS - Completing The Game
Tags: solidjs, tutorial, gamedev, typescript
Published: 2023-04-11 12:04:12

Welcome to the **4th** and **final** installment of the “Creating a Minesweeper Game with SolidJS” post series. In the [first part](Creating a Minesweeper Game in SolidJS - Completing The Game), 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 the [third part](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-score-timer-and-game-state-56n0) we add the score, timers and the initial game state.

In this post, our focus will be on developing a GameOverModal component that will appear over a Backdrop in the event of a game over scenario. Our objective is to provide players with the option to either start a new game or restart the one they have just finished playing. Finally, we will deploy the finished product using Netlify. 
Let's get started.

***

*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

## Game Over
There are 2 ways in which the game can be over - one is when the players detonate a mine by mistake and in this case they lose. The other is when the players find all the mines, which in this case - glory.

In our game app we currently have a single “state” which indicates whether the game is over or not:

```javascript
let isGameOver = false;

```

This boolean value is not enough to support the functionality we want. We need something that can indicate the game state and also why it ended - the player won or lost.
For that I will convert this boolean into a string that holds the state for the game-over. It can be either “won”, “lost” or “ongoing”. Let’s put this in an enum and set the game state signal:

```javascript
export enum GAME_STATUS {
   WON = 'won',
   LOST = 'lost',
   ONGOING = 'ongoing',
}


export const [gameState, setGameState] = createSignal<GAME_STATUS>(GAME_STATUS.ONGOING);
```

The game state initializes with “ongoing” value, but when the game is won we wish to change that to “won”.
I’m creating a `gameWon()` function that sets this state and exposes all the tiles:

```javascript
const gameWon = () => {
   setGameState(GAME_STATUS.WON);
   clearInterval(timerInterval);
   setTilesArray((prevArray) => {
       const newArray = prevArray.map((tile) => {
           return {...tile, isOpen: true};
       });


       return newArray;
   });
};
```

I will create a `gameOver()` function for when the game is lost, and in it I will set the game state to “lost”. In addition to that I will expose all the tiles which hold mines in them by setting the tiles data’s `isDetonated` to true:

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


       return newArray;
   });
};

```

Notice that in both methods I’m stopping the time by clearing the `timerInterval` interval.

It is time to see how the game will render according to the game state. I would like to have a modal popup showing when the game is over - let’s do that!

## The Modal and Backdrop
We would like to popup a Modal when the game is over. For that we’re going to use [Portals](https://www.solidjs.com/docs/latest/api#portal).
The first step is to create a Modal component which uses a Portal. This component appends the Modal unto an existing element with “modal” id to it. We will create this element later on. 
As you can see, we can append any content to the Modal component, so this component is agnostic to the content it has:

```javascript
import {ComponentProps} from 'solid-js';
import {Portal} from 'solid-js/web';


interface ModalProps extends ComponentProps<'div'> {}


const Modal = ({children}: ModalProps) => {
   return <Portal mount={document.getElementById('modal') as Node}>{children}</Portal>;
};


export default Modal;
```
I’m also creating a “Backdrop” component to block any interaction behind the opened modal. This component will also blur out the background, focusing the player on the Modal. The Backdrop is also a Portal which appends itself unto and existing element with “backdrop” id to to:

```javascript
import {ComponentProps} from 'solid-js';
import {Portal} from 'solid-js/web';
import styles from './Backdrop.module.css';


interface BackdropProp extends ComponentProps<'div'> {}


const Backdrop = (props: BackdropProp) => {
   return (
       <Portal mount={document.getElementById('backdrop') as Node}>
           <div class={styles.backdrop} />
       </Portal>
   );
};


export default Backdrop;
```
Next, in the main HTML file, I’m adding a div for the “modal” and a div for the “backdrop”:

```html
<body>
       . . . 
 <div id="root"></div>
       <div id="backdrop"></div>
       <div id="modal"></div>
	 . . .
</body>
```

Now, when the gameState is not “ongoing”, let’s bring the Backdrop up. We do that in the main App:

```javascript
{gameState() !== GAME_STATUS.ONGOING && <Backdrop />}
```

And now we have this when the game is over:

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

Not bad… it’s time to create a GameOverModal

## The GameOverModal
For the time being I think that we can settle with a single GameOverModal component which supports both “won” and “lost” states, but obviously if you feel that you need something more complex you can divide it into 2 different components with some common “ground”.

The GameOverModal component is simple - it displays a title according to the game state, displays the elapsed time since the game has started and offers 2 actions, restarting the game or starting a new game. 
Restarting the game is playing the same mines map, while a new game generates a new mines map.

Here is the code for the GameOverModal. Notice that it uses the Modal component and injects its content into it, and I’m using the same Timer component talked about in [previous posts](https://dev.to/mbarzeev/creating-a-minesweeper-game-in-solidjs-score-timer-and-game-state-56n0). I’m also importing the gameState and the timerSeconds so I can render the content accordingly, but if you can also pass these as props if you feel the need: 

```javascript
import {ComponentProps} from 'solid-js';
import {gameState, GAME_STATUS, timerSeconds} from '../App';
import Modal from '../Modal/Modal';
import Timer from '../Timer';
import styles from './GameOverModal.module.css';


interface GameOverModalProps extends ComponentProps<'div'> {
   onPlayAgain?: () => void;
   onNewGame?: () => void;
}


const GameOverModal = ({onPlayAgain, onNewGame}: GameOverModalProps) => {
   return (
       <Modal>
           <div class={styles.GameOverModal}>
               <h1>{gameState() === GAME_STATUS.WON ? `You've made it!` : 'Ka-Boom! :('}</h1>
               <div class={styles.time}>
                   Elapsed time:&nbsp;
                   <Timer seconds={timerSeconds} />
               </div>


               <div class={styles.actionPanel}>
                   <button class={styles.actionBtn} onclick={onPlayAgain}>
                       Play again
                   </button>
                   <button class={styles.actionBtn} onclick={onNewGame}>
                       New game
                   </button>
               </div>
           </div>
       </Modal>
   );
};


export default GameOverModal;
```


When the game ends for some reason, we render the GameOver Modal like so:
```javascript
{gameState() !== GAME_STATUS.ONGOING && (
               <GameOverModal onPlayAgain={restartGame} onNewGame={startNewGame} />
           )}
```
Notice that we’re passing 2 callbacks to the GameOverModal which we will discuss soon.
Here is the result - you see the blurry backdrop and the modal saying that game was lost:

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

(what is that frowning emoji up there you might ask… I’ll tell you later ;)

It is time to deal with the actions offered.

## Play again and New game actions
You might have noticed that our GameOverModal can accept 2 callbacks for `onPlayAgain` and `onNewGame`. Let’s see what they do - 

The `onPlayAgain` refers to a `restartGame` function which looks like this:

```javascript
const restartGame = () => {
   // Hide all the tiles
   setTilesArray((prevArray) => {
       const newArray = prevArray.map((tile) => {
           return {...tile, isOpen: false, isDetonated: false, isMarked: false};
       });


       return newArray;
   });
   startTimer();
   setGameState(GAME_STATUS.ONGOING);
};
```

What this does is to reset the tiles, just by hiding them all again and removing any marks, and then resetting the timer and the game state. That’s it.

Let’s have a look at the `onNewGame` which refers to the `startNewGame` function. Here is its code:

```javascript
const startNewGame = () => {
   let count = 0;
   const boardArray = [...Array(TOTAL_TILES)].fill(0);
   while (count < TOTAL_MINES) {
       const randomCellIndex = Math.floor(Math.random() * TOTAL_TILES);
       if (boardArray[randomCellIndex] !== 1) {
           boardArray[randomCellIndex] = 1;
           count++;
       }
   }


   setTilesArray(
       boardArray.map((item, index, array) => ({
           index,
           value: getTileValue(index, array),
           isOpen: false,
           isMarked: false,
           isDetonated: false,
       }))
   );
   startTimer();
   setGameState(GAME_STATUS.ONGOING);
};
```

This function creates a new board array and from it, it generates the tiles array which we render. In other words, this function creates a new board.
Now that we have this function, we can reuse it when the game first starts as well.

When we have these 2 function, we can add that “smiley/frowning” emoji at the top that can start a new game each time it is pressed:

```javascript
<button class={styles.resetBtn} onclick={startNewGame}>
                       {gameState() === GAME_STATUS.LOST ? '🙁' : '🙂'}
                   </button>
```

And… Yes! I think we can say that our game is pretty much done.
Of course there’s a lot more that can be done with it, but the general flow and logic are in place. Be sure to check the code on GitHub for parts I did not mention here (like CSS etc.)
The code can be found in this GitHub repository:
https://github.com/mbarzeev/solid-minesweeper

Let’s deploy it ;) 

## Deploying the Game
I’m using [Netlify](https://www.netlify.com/) in order to publish the game. This is rather simple - after you create a n account, you define the GitHub repo (in my case) you’d like Netlify to "listen to" and build upon change, define the output directory in which the result artifacts can be found and Boom! - you have it.

**Here it is: https://solid-minesweeper.netlify.app/**

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>