Minesweeper

In this example below you will see how to do a Minesweeper with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by matixc, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright matixc ©
  • HTML
  • CSS
  • JavaScript
    

<div class="box">
  <div class="container" id="container">

  </div>
  <button id="start"><i class="fa fa-refresh fa-3x" aria-hidden="true"></i></button>
  <p class="marked" id="marked">0 </p>
  <button id="options"><i class="fa fa-cog fa-3x" aria-hidden="true"></i></button>

</div>

<div class="pop1" id="pop1">
  <i class="fa fa-arrows-h fa-2x block" aria-hidden="true"></i><input type="range" id="width" value="10" min="10" max="40">
  <p id="width-value">10</p><br>
  <i class="fa fa-arrows-v fa-2x block" aria-hidden="true"></i> <input type="range" id="height" value="10" min="10" max="40">
  <p id="height-value">10</p><br>
  <i class="fa fa-bomb fa-2x block" aria-hidden="true"></i> <input type="range" id="mines" value="10" min="10" max="100">
  <p id="mines-value">10</p><br>
  <button id="play"><i class="fa fa-play fa-2x block" aria-hidden="true"></i> </button>
</div>

<div class="pop2 hidden" id="pop2">
  BOOM
</div>

/*Downloaded from https://www.codeseek.co/matixc/minesweeper-GmRKPo */
    body {
  display: flex;
  justify-content: center;
  text-align: center;
  font-family: 'Open Sans', sans-serif;
}

div {
  margin: 0;
  padding: 0;
}

p {
  margin: 3px 0;
}

.row:last-child {
  margin-bottom: 2px;
}

.container {
  border: solid gray 1px;
  overflow-x: hiden;
}

.cube {
  display: inline-block;
  text-align: center;
  font-weight: 800;
  line-height: 20px;
  width: 20px;
  height: 20px;
  border: solid white 1px;
  font-size: 20px;
  vertical-align: top;
}

.hide {
  background: #4FC3F7;
}

.mark {
  background: #f44336;
}

.hide:hover {
  -webkit-filter: grayscale(100%) ;
}

.row {
  height: 20px;
  white-space: nowrap;
}

.pop1 {
  background: white;
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  margin: 0 auto;
  z-index: 1;
}

.hidden {
  visibility: hidden;
}

.marked {
  display: inline-block;
  border: solid #4FC3F7 3px;
  font-size: 20px;
  padding: 5px;
}

.block {
  display: block;
}

button {
  background: white;
  border: solid #4FC3F7 3px;
  border-radius: 2px;
  padding: 4px;
}

input[type="range"]{
  -webkit-appearance:none;
  width:200px;
  height:2px;
  background: #4FC3F7;
  background-position:center;
  background-repeat:no-repeat;
}

input[type="range"]::-webkit-slider-thumb{
  -webkit-appearance:none;
  width:20px;
  height:20px;
  border-radius: 100%;
  background: #434343;
  position:relative;
  border: 3px solid #4FC3F7;
  z-index:3;
  cursor: pointer;
}

.pop2 {
  background: rgba(255,255,255,0.92);
  position: fixed;
  width: 100vw;
  height: 100%;
  max-width: 100%;
  z-index: 3;
  font-size: 50px;
  font-weight: 600;
  text-align: center;
  vertical-align: middle;
  line-height: 100vh;
  top: 0;
  left: 0;
}


.blue {
  color: blue;
}
.green {
  color: green;
}
.red {
  color: red;
}
.purple {
  color: rgb(128,0,128);
}
.black {
  color: black;
}
.maroon {
  color: rgb(165,42,42);
}
.gray {
  color: gray;
}
.turquoise {
  color: turqoise;
}


/*Downloaded from https://www.codeseek.co/matixc/minesweeper-GmRKPo */
    let width = 10; 
let height = 10;
let mines = 10;
let board = [];
let marked = 0;
let hide = width * height;
let state = "play";
// just to test how it wokr:
const id = (i,j) => document.getElementById(`${i}-${j}`);

function generateGrid() {
  for (let i = 0; i < height; i++) {
    let row = `<div class="row">`;
    for (let j = 0; j < width; j++) {
      row += `<div class="cube hide" id="${i}-${j}">
  </div>`;
    }
    row += `</div>`;
    document.getElementById("container").innerHTML += row;
  }
}

function generateBoard() {
  board = [];
  for (let i = 0; i < height; i++) {
    let row = [];
    for (let j = 0; j < width; j++) {
      row.push(0);
    }
    board.push(row);
  }
}

function setMines() {
  let minesToSet = mines;
  while (minesToSet > 0) {
    let x = Math.floor(Math.random() * width);
    let y = Math.floor(Math.random() * height);
    if (board[y][x] === 0) {
      board[y][x] = "X";
      minesToSet--;
    }
  }
}

function calculateBoard() {
  for (let i = 0; i < height; i++) {
    for (let j = 0; j < width; j++) {
      if (board[i][j] !== "X") {
        let sum = 0;
        if (i > 0 && j > 0 && board[i - 1][j - 1] === "X") {
          sum++;
        }
        if (j > 0 && board[i][j - 1] === "X") {
          sum++;
        }
        if (i < height - 1 && j > 0 && board[i + 1][j - 1] === "X") {
          sum++;
        }
        if (i > 0 && board[i - 1][j] === "X") {
          sum++;
        }
        if (i < height - 1 && board[i + 1][j] === "X") {
          sum++;
        }
        if (i > 0 && j < width - 1 && board[i - 1][j + 1] === "X") {
          sum++;
        }
        if (j < width - 1 && board[i][j + 1] === "X") {
          sum++;
        }
        if (i < height - 1 && j < width - 1 && board[i + 1][j + 1] === "X") {
          sum++;
        }
        if (sum > 0) {
          board[i][j] = sum;
        } else {
          board[i][j] = " ";
        }
      }
    }
  }
}

function eventListeners() {
  for (let i = 0; i < height; i++) {
    for (let j = 0; j < width; j++) {
      document.getElementById(`${i}-${j}`).addEventListener("click", boardLeftClick.bind(null, i, j));

   // don't know how to pass ev as parameter so I leave function inside eventListener   
      document.getElementById(`${i}-${j}`).addEventListener(
        "contextmenu",
        function(ev) {
          ev.preventDefault();
          if (state === "play") {
            if (
              document.getElementById(`${i}-${j}`).classList.contains("hide")
            ) {
              if (
                document.getElementById(`${i}-${j}`).classList.contains("mark") 
              ) {
                document.getElementById(`${i}-${j}`).classList.remove("mark");
                marked--;
                document.getElementById("marked").innerHTML = mines - marked;
              } else {
                document.getElementById(`${i}-${j}`).classList.add("mark");
                marked++;
                document.getElementById("marked").innerHTML = mines - marked;
              }
            }
          }
          return false;
        },
        false
      );
    }
  }
}

function boardLeftClick(i, j) {
          if (state === "play") {
            if (!document.getElementById(`${i}-${j}`).classList.contains("mark")) {
              if (board[i][j] === "X") {
                document.getElementById(`${i}-${j}`).classList.add("mark");
                lost();
              }
              // removeEventListener does work so i go around by checking if state play and classname.match /mark/
              document.getElementById(`${i}-${j}`).removeEventListener("click", boardLeftClick.bind(null, i, j));
              if (emptyUsed.indexOf(`${i}-${j}`) < 0) {
                hideCount();
              }
              document.getElementById(`${i}-${j}`).classList.remove("hide");
              id(i,j).innerHTML = board[i][j];
              addColor(i, j);
              if (board[i][j] === " ") {
                checkEmpty(i, j);
              }
              winCheck();
            }
          }
        }

function checkEmpty(y, x) {
  let time = 5;
  if (board[y][x] === " ") {
    for (let i = y - 1; i <= y + 1; i++) {
      for (let j = x - 1; j <= x + 1; j++) {
        if (
          j >= 0 &&
          i >= 0 &&
          j < width &&
          i < height &&
          document.getElementById(`${i}-${j}`).classList.contains("hide") &&
          !document.getElementById(`${i}-${j}`).classList.contains("mark")
        ) {
          document.getElementById(`${i}-${j}`).innerHTML = board[i][j];
          addColor(i, j);
          document.getElementById(`${i}-${j}`).classList.remove("hide");
          // hideCount()
          if (emptyUsed.indexOf(`${i}-${j}`) < 0) {
            hideCount();
            checkEmpty(i, j);
          }
          emptyUsed.push(`${i}-${j}`);
        }
      }
    }
  }
}

function addColor(i,j) {
  var colors = ["blue","green","red","purple","black","maroon","gray","turquoise"];
  if (board[i][j] > 0 && board[i][j] < 9) {
    document.getElementById(`${i}-${j}`).classList.add(colors[board[i][j] - 1]);
  }
}

function pushData() {
  for (let i = 0; i < height; i++) {
    for (let j = 0; j < width; j++) {
      document.getElementById(`${i}-${j}`).innerHTML = board[i][j];
      addColor(i, j);
      document.getElementById(`${i}-${j}`).classList.remove("hide");
    }
  }
}

function hideCount() {
  hide--;
}

function lost() {
  state = "lost";
  document.getElementById("pop2").innerHTML = "BOOM";
  document.getElementById("pop2").classList.remove("hidden");
  for (let i = 0; i < height; i++) {
    for (let j = 0; j < width; j++) {
      if (board[i][j] === "X") {
        document.getElementById(`${i}-${j}`).classList.remove("hide");
        document.getElementById(`${i}-${j}`).innerHTML = "X";
      }
    }
  }
}

function winCheck() {
  if (mines == hide && state === "play") {
    document.getElementById("pop2").innerHTML = "You Win";
    document.getElementById("pop2").classList.remove("hidden");
    state = "win";
    for (let i = 0; i < height; i++) {
      for (let j = 0; j < width; j++) {
        if (board[i][j] === "X") {
          document.getElementById(`${i}-${j}`).classList.add("mark");
        }
      }
    }
  }
}

function getValues() {
  width = document.getElementById("width").value;
  height = document.getElementById("height").value;
  mines = document.getElementById("mines").value;
  if (height * width < mines - 1) {
    mines = height * width - 1;
  }
}

function newGame() {
  getValues();
  board = [];
  emptyUsed = [];
  hide = width * height;
  state = "play";
  marked = 0;
  document.getElementById("container").innerHTML = "";
  document.getElementById("marked").innerHTML = mines - marked;
  generateGrid();
  generateBoard();
  setMines();
  calculateBoard();
  eventListeners();
  //pushData()
}


document.getElementById("start").addEventListener("click", function() {
  newGame();
});

document.getElementById("play").addEventListener("click", function() {
  newGame();
  document.getElementById("pop1").classList.add("hidden");
});

document.getElementById("options").addEventListener("click", function() {
  document.getElementById("pop1").classList.remove("hidden");
});

document.getElementById("pop2").addEventListener("click", function() {
  this.classList.add("hidden");
});

//   POP1
const inputs = document.querySelectorAll("input");

function update() {
  document.getElementById(`${this.id}-value`).innerHTML = this.value;
  getValues();
  if (width * height < 401) {
    document.getElementById("mines").max = width * height - 1;
    document.getElementById("mines-value").innerHTML = document.getElementById(
      "mines"
    ).value;
  } else {
    document.getElementById("mines").max = 400;
    document.getElementById("mines-value").innerHTML = document.getElementById(
      "mines"
    ).value;
  }
}

inputs.forEach(input => input.addEventListener("change", update));
inputs.forEach(input => input.addEventListener("mousemove", update));


Comments