1

I want to generate random mines with a button ".btn" for my Minesweeper Game but I only managed to hide the tiles. I can't figure out why calling getMinePositions(boardSize, numberOfMines) doesn't work - mines are not generated after clicking the button, they stay in the same place as before. I also want to unable the stopProp with button, right now if a mine is revealed (game is lost) I can't start a new game with button because I can't click on the tiles. Any help would be appreciated :-)

const TILE_STATUSES = {
  HIDDEN: "hidden",
  MINE: "mine",
  NUMBER: "number",
};

function createBoard(boardSize, numberOfMines) {
  const board = [];
  const minePositions = getMinePositions(boardSize, numberOfMines);

  for (let x = 0; x < boardSize; x++) {
    const row = [];
    for (let y = 0; y < boardSize; y++) {
      const element = document.createElement("div");

      element.dataset.status = TILE_STATUSES.HIDDEN;

      document.querySelector(".btn").addEventListener("click", function () {
        // 1.Hide the tiles - working just fine :)
        element.dataset.status = TILE_STATUSES.HIDDEN;

        // 2.Generate new mines - not working, mines stay in the same place as before. Only refreshing the page creates new mines. - Proper behaviour new mines should be generated when button is clicked.
        getMinePositions(boardSize, numberOfMines);

        // 3.No idea how to disable checkGameEnd function from "./script.js" with this button :( - Proper behaviour if mine is revealed and game is lost, click button to start new game (disable stopImmediatePropagation).
      });

      const tile = {
        element,
        x,
        y,
        mine: minePositions.some(positionMatch.bind(null, { x, y })),
        get status() {
          return this.element.dataset.status;
        },
        set status(value) {
          this.element.dataset.status = value;
        },
      };

      row.push(tile);
    }
    board.push(row);
  }
  return board;
}

function revealTile(tile) {
  if (tile.status !== TILE_STATUSES.HIDDEN) {
    return;
  }
  if (tile.mine) {
    tile.status = TILE_STATUSES.MINE;
    return;
  }
  tile.status = TILE_STATUSES.NUMBER;
}

function checkLose(board) {
  return board.some((row) => {
    return row.some((tile) => {
      return tile.status === TILE_STATUSES.MINE;
    });
  });
}

function getMinePositions(boardSize, numberOfMines) {
  const positions = [];

  while (positions.length < numberOfMines) {
    const position = {
      x: randomNumber(boardSize),
      y: randomNumber(boardSize),
    };

    if (!positions.some(positionMatch.bind(null, position))) {
      positions.push(position);
    }
  }

  return positions;
}

function positionMatch(a, b) {
  return a.x === b.x && a.y === b.y;
}

function randomNumber(size) {
  return Math.floor(Math.random() * size);
}

const BOARD_SIZE = 5;
const NUMBER_OF_MINES = 2;

const board = createBoard(BOARD_SIZE, NUMBER_OF_MINES);
const boardElement = document.querySelector(".board");

board.forEach((row) => {
  row.forEach((tile) => {
    boardElement.append(tile.element);
    tile.element.addEventListener("click", () => {
      revealTile(tile);
      checkGameEnd();
    });
    tile.element.addEventListener("contextmenu", (e) => {
      e.preventDefault();
    });
  });
});

boardElement.style.setProperty("--size", BOARD_SIZE);

//  Disable this when click ".btn"

function checkGameEnd() {
  const lose = checkLose(board);

  if (lose) {
    boardElement.addEventListener("click", stopProp, { capture: true });
    boardElement.addEventListener("contextmenu", stopProp, { capture: true });
  }

  if (lose) {
    board.forEach((row) => {
      row.forEach((tile) => {
        if (tile.mine) revealTile(tile);
      });
    });
  }
}

function stopProp(a) {
  a.stopImmediatePropagation();
}
* {
  box-sizing: border-box;
}

body {
  font-family: serif;
  background-color: #000000da;
}

.btn {
  font: inherit;
  font-size: 20px;
  font-family: Arial;
  background-color: rgb(31, 30, 30);
  color: rgba(255, 255, 255, 0.842);
  border: 1px solid #2bbe06;
  position: absolute;
  cursor: pointer;
  border-radius: 2px;
  width: 230px;
  height: 30px;
  left: 20px;
  top: 40%;
}

.board {
  display: inline-grid;
  grid-template-columns: repeat(var(--size), 100px);
  grid-template-rows: repeat(var(--size), 100px);
  gap: 20px;
  background-color: rgb(31, 30, 30);
  padding-top: 30px;
  padding-left: 40px;
}

.board > * {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
  user-select: none;
  border-radius: 5px;
  border: 2px solid rgba(255, 217, 0, 0.507);
  box-shadow: 0 0 4px rgb(246, 246, 67);
}

.board > [data-status="hidden"] {
  background-color: rgba(255, 255, 255, 0.068);
  cursor: pointer;
}

.board > [data-status="mine"] {
  background-color: rgba(255, 0, 0, 0.63);
  box-shadow: 0 0 5px rgb(255, 0, 0);
  border: 2px solid rgb(255, 255, 255);
}

.board > [data-status="number"] {
  background-color: rgba(38, 255, 0, 0.466);
  border: 2px solid rgb(255, 217, 0);
  box-shadow: 0 0 5px rgb(255, 255, 255);
}

.bOx {
  width: 270px;
  height: 640px;
  border-radius: 5px;
  position: absolute;
  left: 550px;
  bottom: 660px;
  top: 200px;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.13);
  background-color: rgb(31, 30, 30);
}

.card {
  width: 660px;
  height: 640px;
  left: 825px;
  top: 50px;
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.13);
  position: absolute;
  margin: 20px auto;
  border-radius: 5px;
  margin-top: 150px;
  margin-right: 350px;
  background-color: rgb(31, 30, 30);
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <script src="script.js" type="module"></script>
  </head>
  <body>
    <div class="card">
      <div class="board"></div>
    </div>
    <div class="bOx">
      <button class="btn">Start</button>
    </div>
  </body>
</html>
Bagnom
  • 21
  • 3
  • Please explain "_doesn't work._" - what error conditions are in the console? If no errors what is the behavior you are seeing that makes you believe it "_doesn't work_". – Randy Casburn Dec 05 '21 at 05:11
  • No errors in the console. The mines don't generate after I click the button, they stay in the same place as before. – Bagnom Dec 05 '21 at 13:13
  • You'll need to provide a [example] or some type of debugging details. Include the relevant HTML, CSS along with your JavaScript that "doesn't work". Good luck to you. – Randy Casburn Dec 05 '21 at 15:46
  • Sorry for being such a noob, I'm new to stack overflow and this project is harder than I thought... – Bagnom Dec 05 '21 at 17:08
  • No prob. There are a couple of issues that, when resolved, will lead to other issues that need to be resolved as well. First, in the `.btn` event handler you are not using the return value of `getMinePositions()`. Second, you are creating 15 event handlers for your single `.btn` element. You should always refrain from creating event handlers in a loop - especially when there is only one button. So every time you click that button, the event handler runs 15 times. As I said, using the return statement and moving the event handler declaration out of the loops will lead to other issues to resolve – Randy Casburn Dec 05 '21 at 19:10
  • FYI: Rather than creating/duplicating handlers in a loop (for every element), consider [Event Delegation](https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation) – Randy Casburn Dec 05 '21 at 19:11

0 Answers0