Remaking the popular and loved game of Minesweeper 💣

Inspired by googles project called proxx, I set off on creating my own minesweeper clone. The result is a progressive web app for an great in app experience.

Alec Di Vito
Alec Di Vito 7 min read
Remaking the popular and loved game of Minesweeper 💣
Bomb Finder: My clone of minesweeper

Minesweeper is a game of sweeping a grid of boxes to find all bomb contained in the world without getting blown up! Players pick squares slowly to discover which are safe and try to not pick the boxes which explode. The goal of the game is to clear the board of all safe squares as fast as possible. Along the way, you can set flags on boxes which you believe are bombs to help assist you complete the game faster.

But I'll be honest, Minesweeper was before my time, and I remember a child hood of opening the game, promptly dying and thus believing that it was a stupid game. So why clone it? Because google made a pretty cool clone over at https://proxx.app. That made me interested to see if I could produce something similar while I was still in school.

A small project, with small scope. Perfect for a student with too much time on his hands.

Bomb Finder: A Minesweeper and proxx clone
Try your best to navigate a grid of bombs without stepping on one! Track your results and install! A web based clone of minesweeper with insperation from the Google Chrome Labs proxx app.

Don't want to read the article, but you want to play some Minesweeper, I get it! Go for it!

Overall I treated it as an exercise to learn more about react and HTML canvas. I was able to use many Browser API's and configure the website as a PWA (Progressive Web App). I would extend the game by adding in pages for templates, statistics and settings for changing game play look and feel.

GitHub - AlecDivito/bomb-finder: A minesweeper clone inspired after Google’s version called proxx.app. Extend by adding more functionality.
A minesweeper clone inspired after Google’s version called proxx.app. Extend by adding more functionality. - AlecDivito/bomb-finder

Code is more interesting than my writing? I think so to 😉

Goals and Technology

This project was mainly to take a deep dive into PWA and getting used to using IndexDB. I wanted to be able to get a high lighthouse score as well so that I know the app worked well in all browsers. By saving the data in IndexDB, I could somewhat grantee that data of games would be saved even if the website or PWA was closed.

🥲
In practice, even when I downloaded the website to my phone, it would sometimes clear the IndexDB.

I decided to build the app with React and Typescript. The app was created with create-react-app which I believe in 2024, is not recommended anymore.

Design process

I'm not a designer but I try to follow a workflow for all of my projects. I initially
started by writing my goals I want for the project and all the pages I wanted in the app. After, I got started on a simple designs of how I would expect it too look on each page.

Sitemap going over all the available routes the user could navigate

Development initially started with the project using React's smaller alternative
Preact. While using Preact I rolled my own webpack build configuration but I had trouble figuring out a way to support code-splitting as well as use typescript. These frustrations lasted about a week and after unsuccessfully getting code splitting to work I decided to switch to react create-react-app (CRA) because of my pervious experience with it. CRA allowed me to quickly get the project started and focus on the design of my components and pages.

After I had added a majority of the required feature's, I went back and filled
the app out with a very basic design, using a very sad colour palette of shades.

I think the final result speaks for itself!

0:00
/0:56

Video of me playing (lose and win) of minesweeper.

Difficulties and Challenges

How to update the game board and persist data in the PWA

Bomb Finder was pretty simple to create but because of some of the features that I was trying to implement into the program I ran into issues. One issue was when developing the game board to support hundreds to thousands of pieces. My initial implementation drew ever piece on the HTML canvas board. This method had very poor performance because it was rendering boxes that were off screen.

I solved this by using 2 techniques that I found on MDN:

  1. Using an invisible offscreen HTML Canvas (Sadly not OffscreenCanvas) I drew one rotating square, then copy and pasted the invisible canvas into the games canvas. This worked great and solved my problem on desktop where I had a lot of power but on mobile I was still having issues with my framerate.
  2. On mobile, the view was very zoomed in, so I updated my render loop to not draw any of the squares that were not within the viewport of the user. This allowed me to get to acceptable frame rates for mobile users as well.

Aside from rendering problems, there was also challenged with finding a solution to read and write data to IndexDB effectivity. I wanted to build an abstraction around accessing the data in IndexDB. The code I came up with is a little large to move into this blog post, but I ended up being able to write code like the following below.

@Table("settings")
class Preferences {
    @Field("settings", true)
    readonly id: string;

    @Field("settings")
    defaultCellSize: number = 35;
    ...
}

The Table and Field are typescript decorations that called a static instance of my MetaDataStorage. This is was basically my data access layer to IndexDB.

export function Table(name: string) {
  return function (target: Function) {
    MetaDataStorage.getInstance().addTable(name);
  };
}

export function Field(tableName: string, isPrimary: boolean = false) {
  return function (target: any, propertyName: string) {
    MetaDataStorage.getInstance().addField(tableName, propertyName, isPrimary);
  };
}

The MetaDataStorage is an in singleton, in-memory class that records all the Table class names and Field names when the web page loads. The metadata is can then be used when Querying data.

Let's take a look at a subset of the Query.save function to understand how it's able to use the metadata on the class to save the data.

static async save<T extends IDBTable>(obj: T): Promise<boolean> {
  // ... check if things exist ...

  // 1. Get the metadata of a class (fields to save in the database)
  let metaData = MetaDataStorage.getInstance().getMetaData(obj.tableName);
  
  // 2. Build up an object from the metadata
  const save: any = {};
  Object.keys(obj)
    .filter((key) => metaData!.fields.includes(key))
    .forEach((key) => (save[key] = (obj as any)[key]));

  // 3. Execute the transaction to save the new record
  const request = query
    .database!.transaction(obj.tableName, "readwrite")
    .objectStore(obj.tableName)
    .put(save);

  // ... error handling ...
}

The result of this work is that I could then call static methods on my ORM layer to work with persistent data. For example, below is a function from the Game model that I call when a user creates a new game.

public static async AddGame(id: string) {
    const s = await Statistics.GetStats(Query.sanitizeId(id));
    s.inprogress += 1;
    s.name = id;
    s.id = Query.sanitizeId(id);
    return await Query.save(s);
}

I spent a lot of time trying to get the class name from the @Table decorator. But I was never able to get it work. Thus, I went with the singleton implementation to track metadata.

The following are a collection of screenshots that were collected during the process of using the web app in my own time.

Project Screen Shots

At the end of the day this project is complete and I'm quite proud to show it off. I learned a lot about chrome dev tools, profiling and building abstractions. It was a lot of fun creating the animations and working with canvas2d. Using Reacts CRA helped a lot to quickly get the project running. I feel as though I hit all my goals I wanted to hit and am really proud of that!

If you have enjoyed the article, why not give the game a try to! I have recently updated it to run on my HomeLab using a brand spanking new Docker image. Click the link below to give it a try!

Bomb Finder: A Minesweeper and proxx clone
Try your best to navigate a grid of bombs without stepping on one! Track your results and install! A web based clone of minesweeper with insperation from the Google Chrome Labs proxx app.

Read the article and now you want to play the game? Go for it!