【问题标题】:Minesweeper expansion algorithm扫雷扩展算法
【发布时间】:2018-02-17 02:54:02
【问题描述】:

我用 JavaScript 制作了一个简单的扫雷游戏,它运行良好,只是当我点击一个没有地雷的大区域的中间时,它不会清除整个区域,只会清除我点击的位置。

已经有其他问题了,但是我检查生成数字的方式(我相信)不同,因此应该更具体地针对它制定解决方案,而不是更改代码以使其看起来更像其他人所做的那样。

这是一张可以更好地解释这种情况的图片(带有实际代码中未显示的其他颜色):

蓝色是用户单击的位置,然后它应该检查垂直和水平(深绿色)是否这些位置周围有 0 个地雷以展开(绿色)直到地雷(橙色)足够接近的边界(黄色)。

我试图使代码可读且易于理解:

(function() {

  var minesweeper = document.createElement('div');
  var positions = [];
  var playing = true;

  function random(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }

  function end() {
    this.onclick = null;
    if ( playing ) {
      playing = false;
      this.style.backgroundColor = 'rgb(255, 0, 0)';
      alert('Game over.');
    }
  }

  function update() {
    this.onclick = null;
    if ( playing ) {
      this.style.backgroundColor = 'rgb(0, 255, 0)';
      this.className = 'safe';
      let mines = 0;
      let element = this.previousElementSibling;
      if ( element ) {
        if ( element.className == 'mine' && this.style.top == element.style.top ) mines++;
        for ( let i = 0; i < 8; i++ ) {
          element = element.previousElementSibling;
          if ( !element ) break;
        }
        if ( element ) {
          if ( element.className == 'mine' && this.style.top != element.style.top ) mines++;
          element = element.previousElementSibling;
          if ( element ) {
            if ( element.className == 'mine' ) mines++;
            element = element.previousElementSibling;
            if ( element )
              if ( element.className == 'mine' && (parseInt(this.style.top) - parseInt(element.style.top)) == 9 ) mines++;
          }
        }
      }
      element = this.nextElementSibling;
      if ( element ) {
        if ( element.className == 'mine' && this.style.top == element.style.top ) mines++;
        for ( let i = 0; i < 8; i++ ) {
          element = element.nextElementSibling;
          if ( !element ) break;
        }
        if ( element ) {
          if ( element.className == 'mine' && this.style.top != element.style.top ) mines++;
          element = element.nextElementSibling;
          if ( element ) {
            if ( element.className == 'mine' ) mines++;
            element = element.nextElementSibling;
            if ( element )
              if ( element.className == 'mine' && (parseInt(element.style.top) - parseInt(this.style.top)) == 9 ) mines++;
          }
        }
      }
      this.innerText = mines;
      if ( minesweeper.querySelectorAll('div.safe').length == 90 ) {
        playing = false;
        alert('Victory.');
      }
    }
  }

  minesweeper.style.backgroundColor = 'rgb(0, 0, 0)';
  minesweeper.style.fontSize = '7vmin';
  minesweeper.style.textAlign = 'center';
  minesweeper.style.userSelect = 'none';
  minesweeper.style.position = 'absolute';
  minesweeper.style.left = 'calc(50vw - 45.5vmin)';
  minesweeper.style.top = 'calc(50vh - 45.5vmin)';
  minesweeper.style.width = '91vmin';
  minesweeper.style.height = '91vmin';

  for ( let i = 0; i < 10; i++ ) {
    for ( let j = 0; j < 10; j++ ) {
      const n = i * 10 + j;
      positions[n] = document.createElement('div');
      positions[n].style.backgroundColor = 'rgb(255, 255, 255)';
      positions[n].style.position = 'absolute';
      positions[n].style.left = (j * 8 + j + 1) + 'vmin';
      positions[n].style.top = (i * 8 + i + 1) + 'vmin';
      positions[n].style.width = '8vmin';
      positions[n].style.height = '8vmin';
      minesweeper.appendChild(positions[n]);
    }
  }

  for ( let i = 0; i < 11; i++ ) {
    const empty = minesweeper.querySelectorAll('div:not(.mine)');
    if ( i == 10 ) {
      for ( let j = 0; j < 90; j++ ) {
        empty[j].onclick = update;
      }
      break;
    }
    const n = random(0, (empty.length - 1));
    empty[n].className = 'mine';
    empty[n].onclick = end;
  }

  document.body.style.margin = '0px';
  document.body.appendChild(minesweeper);

})();

我检查数字所在位置周围的位置的方法是使用previousElementSiblingnextElementSibling

【问题讨论】:

  • 看看这个Mouse program in Turbo CPP 这是我在 VGA MS-DOS 和 Turbo C++ 下扫雷的古老尝试,所以忽略图形和鼠标的东西......它可以自己解决所以人类只需点击几次就可以决定什么是基于机会的。它可能会激发您的一些想法...

标签: javascript algorithm


【解决方案1】:

干得好!我挂了半个小时测试这个游戏:)

要清除点击位置周围的区域(如果该位置周围有 0 个地雷),您可以递归地为其所有邻居调用 update 函数。

我稍微修改了您的代码:创建了所有非我的邻居的 negihbors 数组,并为每个邻居调用了 update.call(neighbor)

(function() {

    var minesweeper = document.createElement('div');
    var positions = [];
    var playing = true;

    function random(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    }

    function end() {
        this.onclick = null;
        if (playing) {
            playing = false;
            const mines = minesweeper.querySelectorAll('div.mine');
            for (let mine of mines) mine.style.backgroundColor = 'rgb(255, 0, 0)';
            alert('Game over.');
        }
    }

    function update() {
        this.onclick = null;
        if (playing && !this.className.length) {
            this.style.backgroundColor = 'rgb(0, 255, 0)';
            this.className = 'safe';
            let neighbors = [];
            let mines = 0;
            let element = this.previousElementSibling;
            if (element) {
                if (this.style.top === element.style.top) {
                    if (element.className === 'mine') mines++;
                    else neighbors.push(element);
                }
                for (let i = 0; i < 8; i++) {
                    element = element.previousElementSibling;
                    if (!element) break;
                }
                if (element) {
                    if (this.style.top !== element.style.top) {
                        if (element.className === 'mine') mines++;
                        else neighbors.push(element);
                    }
                    element = element.previousElementSibling;
                    if (element) {
                        if (element.className === 'mine') mines++; else neighbors.push(element);
                        element = element.previousElementSibling;
                        if (element) {
                            if (parseInt(this.style.top) - parseInt(element.style.top) === 9 ) {
                                if (element.className === 'mine') mines++;
                                else neighbors.push(element);
                            }
                        }
                    }
                }
            }
            element = this.nextElementSibling;
            if (element) {
                if (this.style.top === element.style.top) {
                    if (element.className === 'mine') mines++;
                    else neighbors.push(element);
                }
                for (let i = 0; i < 8; i++) {
                    element = element.nextElementSibling;
                    if (!element) break;
                }
                if (element) {
                    if (this.style.top !== element.style.top) {
                        if (element.className === 'mine') mines++;
                        else neighbors.push(element);
                    }
                    element = element.nextElementSibling;
                    if (element) {
                        if (element.className === 'mine') mines++; else neighbors.push(element);
                        element = element.nextElementSibling;
                        if (element) {
                            if (parseInt(element.style.top) - parseInt(this.style.top) === 9 ) {
                                if (element.className === 'mine') mines++;
                                else neighbors.push(element);
                            }
                        }
                    }
                }
            }
            this.innerText = mines;
            if (mines === 0) {
                for (let neighbor of neighbors) update.call(neighbor);
            }
            if (minesweeper.querySelectorAll('div.safe').length === 90) {
                playing = false;
                alert('Victory.');
            }
        }
    }

    minesweeper.style.backgroundColor = 'rgb(0, 0, 0)';
    minesweeper.style.fontSize = '7vmin';
    minesweeper.style.textAlign = 'center';
    minesweeper.style.userSelect = 'none';
    minesweeper.style.position = 'absolute';
    minesweeper.style.left = 'calc(50vw - 45.5vmin)';
    minesweeper.style.top = 'calc(50vh - 45.5vmin)';
    minesweeper.style.width = '91vmin';
    minesweeper.style.height = '91vmin';

    for (let i = 0; i < 10; i++) {
        for (let j = 0; j < 10; j++) {
            const n = i * 10 + j;
            positions[n] = document.createElement('div');
            positions[n].style.backgroundColor = 'rgb(255, 255, 255)';
            positions[n].style.position = 'absolute';
            positions[n].style.left = (j * 8 + j + 1) + 'vmin';
            positions[n].style.top = (i * 8 + i + 1) + 'vmin';
            positions[n].style.width = '8vmin';
            positions[n].style.height = '8vmin';
            minesweeper.appendChild(positions[n]);
        }
    }

    for (let i = 0; i < 11; i++) {
        const empty = minesweeper.querySelectorAll('div:not(.mine)');
        if (i === 10) {
            for (let j = 0; j < 90; j++) {
                empty[j].onclick = update;
            }
            break;
        }
        const n = random(0, (empty.length - 1));
        empty[n].className = 'mine';
        empty[n].onclick = end;
    }

    document.body.style.margin = '0px';
    document.body.appendChild(minesweeper);

})();

【讨论】:

  • 感谢您抽出宝贵时间。这看起来就是我想要的。我还没有对代码进行分析,但是稍后有时间我会更好地查看它。从你的解释我明白你做了什么。很久没有玩真正的扫雷了,但我有一种小小的感觉,它不应该像this那样对角扩展(不确定)。
  • @user7393973 是的,我对对角线也有一些疑问。但似乎在真实游戏中它确实extend diagonally。无论如何,您可以通过不将对角邻居添加到 neighbors 数组来简单地避免它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-02
  • 2021-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多