Minesweeper Implementation using P5.js

Originally Written on:- 14th January, 2019

Img Source:- Wikipedia



P5.js

p5.js is a JavaScript library that starts with the original goal of Processing—to make coding accessible for artists, designers, educators, and beginners—and reinterprets this for today's web.

Using the original metaphor of a software sketchbook, p5.js has a full set of drawing functionality. However, you're not limited to your drawing canvas, you can think of your whole browser page as your sketch! For this, p5.js has addon libraries that make it easy to interact with other HTML5 objects, including text, input, video, webcam, and sound.

Please see the page for instructions for setting up your editing environment and project.


I am using Brackets. Download it here

Full source code is in my Github

Getting into the implementation
  
We want to build a game using the  P5.js library to make the famous game : minesweeper. Now, it has been a long time since I have played minesweeper but I think I remember how it works. Minesweeper is a single-player puzzle video game. The objective of the game is to clear a rectangular board containing hidden "mines" or bombs without detonating any of them, with help from clues about the number of neighbouring mines in each field. The game originates from the 1960s, and has been written for many computing platforms in use today. The player is initially presented with a grid of undifferentiated squares. Some randomly selected squares, unknown to the player, are designated to contain mines.

We want to create a grid in this case and we want to make sure that we keep a few things in mind.




   1)     Cell state
   2)     We also need to see that whether a mine is in a cell or not
   3)     Coordinates that represent the cell:- x, y, w, h.
   4)     We also need to find out whether the cell is revealed or not.

 In the game itself, we are going to click on a cell. When we click on a cell,  it will either be a number, or have a bunch of neighboring cells with no mine. Or we, can click on a cell and find a mine in it, in that case the game will be over.
Therefore, it is either going to be a mine, a number or a number and a bunch of empty cells.

Create two windows:- cell.js and sketch.js. Cell.js will be used as separate JavaScript file. There are too many functions and just to access the two in your html file add  cell.js .

We need to store the cells in a data structure. We can make a 2D array for that-> to store the rows and the columns . Another important thing to note here is that each of the cells in the grid has a pixel size of 10x10. We can create a 2D array and it creates the whole array for us and then we can just return it. So under sketch.js




function make2DArray(cols, rows) {
  var arr = new Array(cols);
  for (var i = 0; i < arr.length; i++) {
    arr[i] = new Array(rows);
  }
  return arr;
}


Next, in sketch.js we need to make the Game board.
function setup() {
  createCanvas(401, 401);
  cols = floor(width / w);
  rows = floor(height / w);
  grid = make2DArray(cols, rows);
  for (var i = 0; i < cols; i++) {
    for (var j = 0; j < rows; j++) {
      grid[i][j] = new Cell(i, j, w);
    }
  }

In the above piece of code, w is a global  variable through which we can control the size of the pixel value. That is why for the rows and the columns, we divide it by the width and height. We use floor, so that we get round numbers. Suppose if the grid is 400 x 400 . Then we will have 40 rows x 40 columns.

Attach functions to a prototype:  here it is show. We need to attach the functions to the prototype. We have to use constructor function to create every object and every object will have a set of methods. Therefore, in cell.js


Cell.prototype.show = function() {
  stroke(0);
  noFill();
  rect(this.x, this.y, this.w, this.w);
  if (this.revealed) {
    if (this.bee) {
      fill(127);
      ellipse(this.x + this.w * 0.5, this.y + this.w * 0.5, this.w * 0.5);
    } else {
      fill(200);
      rect(this.x, this.y, this.w, this.w);
      if (this.neighborCount > 0) {
        textAlign(CENTER);
        fill(0);
        text(this.neighborCount, this.x + this.w * 0.5, this.y + this.w - 6);


We need to make the mines visible. In this case, we are considering the mines as bees. We are filling the unnecessary cells and moving the pixels back and forth to fill up the blank neighboring cells. We have also tried to fit the mines in the middle of the cell

Next up, we are also counting the number of neighbors, so as to avoid looking into those that are either already revealed, yet to be revealed or have mines in them. For this reason, we are going to use the floodfill algorithm Here . Code after the previous snippet:


Cell.prototype.countBees = function() {
  if (this.bee) {
    this.neighborCount = -1;
    return;
  }
  var total = 0;
  for (var xoff = -1; xoff <= 1; xoff++) {
    var i = this.i + xoff;
    if (i < 0 || i >= cols) continue;

    for (var yoff = -1; yoff <= 1; yoff++) {
      var j = this.j + yoff;
      if (j < 0 || j >= rows) continue;

      var neighbor = grid[i][j];
      if (neighbor.bee) {
        total++;
      }
    }
  }
  this.neighborCount = total;
}Cell.prototype.contains = function(x, y) {
  return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.w);
}

Cell.prototype.reveal = function() {
  this.revealed = true;
  if (this.neighborCount == 0) {
    // flood fill time
    this.floodFill();
  }
}Cell.prototype.floodFill = function() {
  for (var xoff = -1; xoff <= 1; xoff++) {
    var i = this.i + xoff;
    if (i < 0 || i >= cols) continue;

    for (var yoff = -1; yoff <= 1; yoff++) {
      var j = this.j + yoff;
      if (j < 0 || j >= rows) continue;

      var neighbor = grid[i][j];
      // Note the neighbor.bee check was not required
      if (!neighbor.revealed) {
        neighbor.reveal();
      }
    }
  }
}

We also want to know how many mines are neighbouring the cells.  We use Xoffset and Yoffset. After we create the neighbours, we want to check all of it. We count the neighbours, but we do not wank to count ourselves. We need to take care of the edges as well.  After we are done, we need to delete that spot, so that it no longer is an option.

After adding a few other P5.js for interactivity, we would be done. For more information on this game watch the video by Daniel Shiffman  here.


My Results






Credits Go to Daniel Shiffman. I followed his tutorial and used his code.

Comments