vue-minesweeper

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

Thumbnail
This awesome code was written by rhapsodyn, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright rhapsodyn ©
  • HTML
  • CSS
  • JavaScript
    <div id="app">
    <h2>{{result}}</h2>
    <div v-for="(row, i) in cells" :key="i">
      <div v-for="(cell, j) in row" :key="j" 
                                      :class="['cell', cell.isClear ? 'clear' : '']" 
                                      @click="filp(i, j, cell)"
                                      @contextmenu="mark(cell, $event)">
        {{ showCell(cell) }}
      </div>
    </div>
    <div style="margin-top: 40px;">
      <div>
        <label>rowCount:</label>
        <input type="number" v-model="rowCount">
      </div>
      <div>
        <label>bombCount:</label>
        <input type="number" v-model="bombCount">
      </div>
      <button type="button" @click="restart">Restart</button>
    </div>
  </div>

/*Downloaded from https://www.codeseek.co/rhapsodyn/vue-minesweeper-wqPXJG */
    .cell 
  {
    width: 50px;
    display: inline-block;
    line-height: 50px;
    height: 50px;
    text-align: center;
    margin: 3px;
    background-color: beige;
    border: 2px solid gray;
    vertical-align: middle;
  }
  .clear 
  {
    background-color: white;
  }


/*Downloaded from https://www.codeseek.co/rhapsodyn/vue-minesweeper-wqPXJG */
    function getAroundIdx(rowCount, i, j) {
      let indices = [];

      // top-left
      if (i > 0 && j > 0) {
        indices.push([i - 1, j - 1]);
      }
      // top
      if (i > 0) {
        indices.push([i - 1, j]);
      }
      // top-right
      if (i > 0 && j < rowCount - 1) {
        indices.push([i - 1, j + 1]);
      }
      // left
      if (j > 0) {
        indices.push([i, j - 1]);
      }
      // right
      if (j < rowCount - 1) {
        indices.push([i, j + 1]);
      }
      // bottom-left
      if (i < rowCount - 1 && j > 0) {
        indices.push([i + 1, j - 1]);
      }
      // bottom
      if (i < rowCount - 1) {
        indices.push([i + 1, j]);
      }
      // bottom-right
      if (i < rowCount - 1 && j < rowCount - 1) {
        indices.push([i + 1, j + 1]);
      }

      return indices;
    }

    // around === -1 => there is a bomb
    function newBoard(rowCount, bombCount) {
      // init
      let board = [];
      for (let i = 0; i < rowCount; i++) {
        board[i] = [];
        for (let j = 0; j < rowCount; j++) {
          board[i][j] = { around: 0, isMark: false, isClear: false };
        }
      }

      // add bombs
      while (bombCount > 0) {
        let randRow = Math.floor(Math.random() * rowCount);
        let randCol = Math.floor(Math.random() * rowCount);

        if (board[randRow][randCol].around !== -1) {
          board[randRow][randCol].around = -1;
          bombCount--;
        }
      }

      // set around
      for (let i = 0; i < rowCount; i++) {
        for (var j = 0; j < rowCount; j++) {
          if (board[i][j].around === -1) {
            getAroundIdx(rowCount, i, j).forEach(pos => {
              if (board[pos[0]][pos[1]].around !== -1) {
                board[pos[0]][pos[1]].around ++;
              }
            })
          }
        }
      }

      return board;
    }

    function recurClear(row, col, cells, visitedPoses) {
      visitedPoses = visitedPoses || [];

      getAroundIdx(cells.length, row, col).forEach(pos => {
        if (!visitedPoses.some(p => p[0] === pos[0] && p[1] === pos[1])) {
          visitedPoses.push(pos);

          let [x, y] = pos;
          if (cells[x][y].around === 0) {
            cells[x][y].isClear = true;
            recurClear(x, y, cells, visitedPoses);
          } else if (cells[x][y].around > 0) {
            cells[x][y].isClear = true;
          }
        }
      });
    }

    new Vue({
      el: '#app',
      data() {
        const rowCount = 4;
        const bombCount = 2;

        return { 
          bombCount,
          rowCount,
          result: '',
          cells: newBoard(rowCount, bombCount) 
        }
      },
      methods: {
        showCell(cell) {
          if (cell.isMark) {
            return '√';
          } else if (cell.isClear) {
            if (cell.around === -1) {
              return 'X';
            } else if (cell.around > 0) {
              return cell.around;
            } else {
              return '';
            }
          } else {
            return '';
          }
        },
        checkWin() {
          let { cells } = this.$data;
          let length = cells.length;
          let unclearBombCount = 0;

          for (let i = 0; i < length; i++) {
            for (let j = 0; j < length; j++) {
              let cell = cells[i][j];

              // not all clear 
              if (cell.around !== -1 && !cell.isClear) {
                return false;
              }

              if (!cell.isClear && cell.around === -1) {
                unclearBombCount++;
              }
            }
          }

          return unclearBombCount == this.bombCount;
        },
        filp(i, j, cell) {
          if (cell.isMark) {
            cell.isMark = false;
            return;
          }

          cell.isClear = true;

          if (cell.around === -1) {
            this.clearAll();
            this.result = 'Game Over';
            return;
          } else {
            // spread around
            if (cell.around === 0) {
              recurClear(i, j, this.cells);
            }

            if (this.checkWin()) {
              this.clearAll();
              this.result = 'You Win';
            }
          } 
        },
        clearAll() {
          let { cells } = this.$data;
          let length = cells.length;

          for (let i = 0; i < length; i++) {
            for (let j = 0; j < length; j++) {
              cells[i][j].isClear = true;
            }
          }
        },
        mark(cell, event) {
          cell.isMark = !cell.isMark;

          event.preventDefault();
        },
        restart() {
          if (this.bombCount >= this.rowCount * this.rowCount) {
            alert('Too many bombs');
            return;
          }
          
          this.cells = newBoard(this.rowCount, this.bombCount);
          this.result = '';
        }
      }
    })

Comments