Recursive function functions faultily with repeated parsing of a Minesweeper tile

53 Views Asked by At

I've been making an implementation of Minesweeper. It would be complete if not one thing: sometimes the function responsible for recursion doesn't increment the variable responsible for tracking the amount of tiles uncovered, required for the game to end with positive outcome.

Major functions' roles:

  • RevealAll(win, id) - Used for revealing every item on the board in scenarios of victory and loss. Also used for bomb flag manipulation;
  • generate(initX, initY) - Used for placing bombs on the board and providing starter safe space (every first square clicked will yield a zero)
  • numberize() - Used for enriching the board with numbers, telling about the amount of mines in adjacent tiles;
  • revealDeadSpaces(i, j) - Used for recursively revealing zeros and non-bomb fields;
  • parseInput(i, j) - This is, most probably, the culprit. Used for basic data migration from the sapper javascript table to the game board and placing flags.

Data from the present console.logs led me to assume, that revealDeadSpaces, by triggering the parseInput function on already parsed cells, most probably is the reason of incorrect incrementation. The pivot of the situation is one line:

if(element.innerHTML != '' || element.classList.contains('td-flag')) return;

By definition, it's responsible for ending the function when an element is either flagged or its inner HTML is empty (every element of which contents are hidden doesn't have text embedded). This line causes the lower balance. However, removing it makes the variable higher than it's supposed to be, leading to the game ending prematurely.

What I've tried:

  • Removal and experimenting with the if's parameters;
  • Experimenting with mechanisms related to flagging;
  • Reaching out to some external sources.

Unfortunately, these only left me with more questions than answers. My best guess is the problem lies within the parameters, but I cannot invent any working solution.

The provided debug in the console shows how uncovered behaves in parseInput and the current cell's address - its row and column respectively.

Violet is the global color for flags. Click on the violet box in the upper right corner to enable flagging - while on, it's green.

let uncovered = 0,
  max = 0,
  flagMode = false;
window.onload = () => {
  function revealAll(win, id = null) {
    let element;
    if (id)
      element = document.getElementById(id);
    if ((!flagMode && element !== undefined && !element.classList.contains("td-flag")) || win) {
      for (let i = 1; i < trueSize - 1; i++)
        for (let j = 1; j < trueSize - 1; j++) {
          const cellkey = document.getElementById("cell-" + i + "-" + j);
          cellkey.classList.remove("td-unclicked");
          if (!cellkey.classList.contains("td-flag")) {
            if (sapper[i][j] != 'b')
              cellkey.innerHTML = sapper[i][j];
            else
              cellkey.classList.add("td-bomb");
            flag.onclick = null;
          } else if (sapper[i][j] != 'b')
            setInterval(() => {
              cellkey.classList.toggle("td-flag");
              if (cellkey.innerHTML == '') cellkey.innerHTML = sapper[i][j];
              else cellkey.innerHTML = '';
            }, 1000);
          else
            setInterval(() => {
              cellkey.classList.toggle("td-flag");
              cellkey.classList.toggle("td-bomb");
            }, 1000);
        }
      if (win === false) setTimeout(() => {
        location.reload()
      }, 6000);
    }
    if (flagMode) element.classList.toggle("td-flag");
  }

  function revealDeadSpaces(i, j) {
    const cellkey = document.getElementById('cell-' + i + '-' + j);
    cellkey.innerHTML = sapper[i][j];
    if (i === 0 || i === trueSize - 1 || j === 0 || j === trueSize - 1 || sapper[i][j] != 0 || !cellkey.classList.contains("td-unclicked")) {
      cellkey.classList.remove("td-unclicked");
      return;
    }
    cellkey.classList.remove("td-unclicked");
    for (let x = i - 1; x < i + 2; x++)
      for (let y = j - 1; y < j + 2; y++) {
        if (sapper[x][y] == 0)
          revealDeadSpaces(x, y);
        else
          parseInput(x, y);
      }
  }

  function parseInput(i, j) {
    console.log(uncovered);
    console.log(i + " " + j);
    let element = document.getElementById("cell-" + i + "-" + j);
    if (element.innerHTML != '' || element.classList.contains('td-flag')) return;
    if (flagMode && element.innerHTML == '') element.classList.toggle("td-flag");
    if (!flagMode && sapper[i][j] != 'b' && !element.classList.contains("td-flag")) {
      element.onclick = null;
      element.innerHTML = sapper[i][j];
      uncovered++;
      if (uncovered == max) {
        for (let i = 1; i < trueSize - 1; i++)
          for (let j = 1; j < trueSize - 1; j++)
            document.getElementById("cell-" + i + "-" + j).classList.add("anim-rainbow");
        revealAll(true);
      } else if (sapper[i][j] == 0) revealDeadSpaces(i, j);
      else element.classList.remove("td-unclicked");
    }
  }

  function returnRand() {
    return Math.floor(Math.random() * (trueSize - 2) + 1);
  }
  let size = prompt("Size: ", 10);
  if (size == null) size = 10;
  let parsedPrompt = parseInt(size),
    trueSize = parsedPrompt + 2,
    maxBombs = Math.floor(parsedPrompt * parsedPrompt * 0.2);
  max = size * size - Math.floor(size * size * 0.2);
  let sapper = [];
  for (let i = 0; i < trueSize; i++) {
    sapper[i] = [];
    for (let j = 0; j < trueSize; j++)
      sapper[i][j] = 0;
  }
  console.log("PRE");
  console.log(sapper);
  for (let i = 0; i < trueSize; i++) {
    let row = document.createElement("tr"),
      rowkey = "row-" + i;
    row.classList.add("fx");
    row.id = rowkey;
    if (i == 0 || i == trueSize - 1) row.classList.add("dp-none");
    document.getElementById("sapper-core").appendChild(row)
    for (let j = 0; j < trueSize; j++) {
      let cell = document.createElement("td"),
        cellkey = "cell-" + i + "-" + j,
        cellClasses = ["td", "fx-center", "fx", "td-unclicked", "bg-full", "button"],
        size = 40 / parsedPrompt;
      cell.setAttribute("style", "width: " + size + "vw; height: " + size + "vw;")
      for (let k = 0; k < cellClasses.length; k++)
        cell.classList.add(cellClasses[k]);
      if (j == 0 || j == trueSize - 1) cell.classList.add("dp-none");
      cell.id = cellkey;
      document.getElementById(rowkey).appendChild(cell);
    }
  }

  function generate(initX = false, initY = false) {
    if (!initX)
      for (let j = 1; j < trueSize - 1; j++)
        for (let k = 1; k < trueSize - 1; k++)
          document.getElementById("cell-" + j + '-' + k).onclick = () => {
            generate(j, k)
          };
    else {
      for (let i = 0; i < maxBombs; i++) {
        let x = returnRand(),
          y = returnRand();
        while (sapper[x][y] == 'b' || (initX && ((x + 1 == initX && y + 1 == initY) || (x + 1 == initX && y == initY) || (x + 1 == initX && y - 1 == initY) || (x == initX && y + 1 == initY) || (x == initX && y == initY) || (x == initX && y - 1 == initY) || (x - 1 == initX && y + 1 == initY) || (x - 1 == initX && y == initY) || (x - 1 == initX && y - 1 == initY)))) {
          x = returnRand();
          y = returnRand();
        }
        const cellkey = document.getElementById("cell-" + x + "-" + y);
        sapper[x][y] = 'b';
        cellkey.onclick = () => {
          revealAll(false, cellkey.id)
        };
      }
      console.log("GEN");
      console.log(sapper);
      numberize();
      parseInput(initX, initY);
    }
  }
  generate();

  function numberize() {
    for (let i = 1; i < trueSize - 1; i++)
      for (let j = 1; j < trueSize - 1; j++) {
        const cellkey = document.getElementById("cell-" + i + "-" + j);
        if (sapper[i][j] != 'b') {
          let number = 0;
          for (let x = i - 1; x < i + 2; x++)
            for (let y = j - 1; y < j + 2; y++)
              if (sapper[x][y] == 'b')
                number++;
          sapper[i][j] = number;
          if (number > 0) cellkey.classList.add("td-" + number);
          cellkey.onclick = () => {
            parseInput(i, j)
          };
        }
      }
    console.log("NUM");
    console.log(sapper);
  }
  const flag = document.getElementById("flag")

  function toggleFlag() {
    if (flagMode) flagMode = false;
    else flagMode = true;
    for (let i = 1; i < trueSize - 1; i++)
      for (let j = 1; j < trueSize - 1; j++) {
        const cellkey = document.getElementById("cell-" + i + "-" + j);
        if (cellkey.classList.contains("td-flag"))
          cellkey.classList.toggle("td-unclicked");
      }
    flag.classList.toggle("td-flag");
    flag.classList.toggle("td-flag2");
  }
  flag.onclick = () => {
    toggleFlag()
  };
}
.body {
  min-width: 70vw;
  min-height: 100vh;
  margin: 0;
}

.table {
  border: 4px grey ridge;
}

.td {
  font: 1.5vw monospace;
  font-weight: bold;
}

.button {
  background-color: grey;
}

.flag {
  width: 6vw;
  height: 6vw;
  background-color: violet;
  position: fixed;
  top: 3vw;
  right: 3vw;
}

.bg-full {
  background-size: cover;
}

.td-unclicked {
  cursor: pointer;
}

.td-flag {
  background-color: violet;
}

.td-flag2 {
  background-color: green;
}

.td-1 {
  color: blue;
}

.td-2 {
  color: green;
}

.td-3 {
  color: red;
}

.td-4 {
  color: darkblue;
}

.td-5 {
  color: darkred;
}

.td-6 {
  color: #007e7d;
}

.td-7 {
  color: black;
}

.td-8 {
  color: slategray;
}

.td-bomb {
  background: red, grey;
  background-size: 60%, cover;
  background-repeat: no-repeat;
  background-position: center center, center center;
}

.fx {
  display: flex;
}

.fx-center {
  align-items: center;
  justify-content: center;
}

.anim-rainbow {
  animation: rainbow 3s ease-in-out infinite;
}

@keyframes rainbow {
  12.5% {
    color: red !important;
  }
  25% {
    color: orange;
  }
  37.5% {
    color: yellow;
  }
  50% {
    color: green;
  }
  62.5% {
    color: blue;
  }
  75% {
    color: indigo;
  }
  87.5% {
    color: violet;
  }
  100% {
    color: red !important;
  }
}

.dp-none {
  display: none;
}
<body class="body fx fx-center">
  <img class="dp-none" src="flag2.png" alt="flaga 2">
  <table id="sapper-core" class="table"></table>
  <div id="flag" class="table flag bg-full td-unclicked"></div>
</body>

0

There are 0 best solutions below