【问题标题】:Recursive algorithm with Minesweeper not working as expected使用扫雷的递归算法未按预期工作
【发布时间】:2015-05-11 22:43:48
【问题描述】:

我正在尝试理解递归,因此正在尝试为扫雷游戏编写递归函数。我的程序没有游戏界面,只是为了理解。

这是我的理解:

我有一个给定大小的网格,将用数字填充。为简单起见,我假设 0 代表我的,网格中的空单元格将由数字 9 表示。这是我的网格:
0 1 9 9 9
1 1 9 9 9
9 9 9 9 9
9 9 9 1 1
9 9 9 1 0

这是我的递归算法。我想我已经输入了正确的边界条件,但似乎是进入无限递归的算法:

基本情况:
如果网格坐标小于 0,即 x grid.length-1 OR y > grid.length-1
返回
其他
如果网格显示 0 或 9 以外的数字,则显示数字,即 grid[x][y]!=0 && grid[x][y]!=9
else 如果网格显示 0 表示它是地雷,则显示 Game over
其他
所有周围 8 个单元格的递归案例,因为所有周围单元格都可以安全打开

这里有一些代码显示我在算法中列出的内容:

public static void playGame(int[][]grid, int x, int y){
        if(x > grid.length-1 || x <0 || y > grid.length-1 || y<0){
            return;
        }else{
            if(grid[x][y] !=0 && grid[x][y]!=9){
                //means this is a valid number to display to user
                System.out.println("opening cell:"+x+","+y+" "+grid[x][y]);
            }else if(grid[x][y] == 0){
                System.out.println("Game over!");
                //might want to show all mine locations
            }else{
                //all 8 cells are safe to open 
                playGame(grid,x-1,y-1); //left
                playGame(grid,x,y-1); //up
                playGame(grid,x,y+1);//down
                playGame(grid,x+1,y);//diagonal
                playGame(grid,x-1,y); //diagonal
                playGame(grid,x+1,y-1);//diagonal
                playGame(grid,x+1,y+1);//right
                playGame(grid,x-1,y+1);//diagonal

            }
        }
    }

我将单元格作为 2,2 传递,它给了我 java.lang.StackOverflowError。 有人可以在这里指导我吗。我对算法或递归的理解是否存在问题,或者我可能引入了一些错误。我无法发现问题。

【问题讨论】:

  • “1”代表什么?
  • 1 表示该单元格周围有 1 个地雷(用 0 表示)。
  • 尝试在方法顶部打印 grid.length, x, y?它可能会提供一些线索。
  • 我在程序中有一些打印语句会继续打印:opening cell:1,0 1. 我在想,当它打开空白单元格时,它会回到它开始的地方并继续进行递归。我应该包括一些东西来访问单元格吗?我没有看到任何人这样做,所以持怀疑态度。
  • 这个算法看起来正确吗?我想先检查我是否遗漏了任何条件?

标签: algorithm recursion minesweeper


【解决方案1】:

根据一般性建议,在调试器中单步调试未按预期工作的代码始终是一个好主意,然后再解决给出的具体问题。

您的实现问题似乎是,打开空字段,您不跟踪已访问的字段。导致在相同的两个域之间振荡的递归中运行越来越深的问题。

鉴于您示例中的数据,我用叉号 x 和顶部的调用堆栈标记了实际访问的字段,以向您展示我在说什么。

让我们从您的示例中给出的单元格 2、2 开始:

playGame(grid, 2, 2)
0 1 9 9 9
1 1 9 9 9
9 9 x 9 9
9 9 9 1 1
9 9 9 1 0

代码最终会调用 playGame(grid,x,y-1); //up

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
0 1 9 9 9
1 1 x 9 9
9 9 9 9 9
9 9 9 1 1
9 9 9 1 0

我们将再次联系playGame(grid,x,y-1); //up

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
    playGame(grid, 2, 0)
0 1 x 9 9
1 1 9 9 9
9 9 9 9 9
9 9 9 1 1
9 9 9 1 0

由于该字段仍为空,我们再次致电playGame(grid,x,y-1); //up

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
    playGame(grid, 2, 0)
      playGame(grid, 2, -1)

这次我们将返回并调用playGame(grid,x,y+1); //down

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
    playGame(grid, 2, 0)
     playGame(grid, 2, 1)
0 1 9 9 9
1 1 x 9 9
9 9 9 9 9
9 9 9 1 1
9 9 9 1 0

由于此字段为空,因此下一步将调用playGame(grid,x,y-1); //up

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
    playGame(grid, 2, 0)
      playGame(grid, 2, 1)
        playGame(grid, 2, 0)
0 1 x 9 9
1 1 9 9 9
9 9 9 9 9
9 9 9 1 1
9 9 9 1 0

从现在开始,我们将重复已经观察到的步骤,直到所有堆栈空间都用完:

playGame(grid, 2, 2)
  playGame(grid, 2, 1)
    playGame(grid, 2, 0)
      playGame(grid, 2, 1)
        playGame(grid, 2, 0)
          playGame(grid, 2, 1)
            playGame(grid, 2, 0)
               ...

【讨论】:

  • 我同意你的观点,我想我必须确保我不会再次访问同一个单元格。我之前确实尝试过,因为我怀疑这是问题所在。做了一个小的调整,用值 -1 标记访问的单元格。如果 grid[x][y] = -1 更新了基本情况以返回但仍然导致相同的错误。我会更深入地研究一下,看看我能不能让它发挥作用。
【解决方案2】:

感谢大家提供建议。在得到一些建议后,算法似乎需要进行一次修改。我们必须确保在访问空单元格后对其进行标记。这将防止算法进入无限递归。
这是更新的算法和代码,如果您认为有任何改进,请告诉我:
基本情况:
如果网格坐标小于 0,即 x grid.length-1 OR y > grid.length-1
返回
其他
如果网格显示的数字不是 0 或 9 ,则显示数字,即 grid[x][y]!=0 && grid[x][y]!=9
else 如果网格显示 0 表示它是地雷,则显示 Game over
其他
所有周围 8 个单元格的递归案例,因为所有周围单元格都可以安全打开。 “此时请确保每个单元格都被标记为已访问”
这是按预期工作的更新代码。

public static void playGame(int[][]grid, int x, int y){
        System.out.println("Currently working with cell :x: "+x +" and y :"+y);
        if(x > grid.length-1 || x <0 || y > grid.length-1 || y<0 ){
            return;
        }
        else{
            if(grid[x][y] !=0 && grid[x][y]!=9) {
                //means this is a valid number to display to user
                System.out.println("opening cell:"+x+","+y+" "+grid[x][y]);
            }else if(grid[x][y] == 0){
                System.out.println("Game over!");
                //might want to show all mine locations
            }else if(grid[x][y] == 9){
                //mark this cell visited.
                grid[x][y] = -1;
                //all 8 cells are safe to open 
                playGame(grid,x-1,y-1); //left
                playGame(grid,x,y-1); //diagonal left
                playGame(grid,x,y+1);
                playGame(grid,x+1,y);
                playGame(grid,x-1,y);
                playGame(grid,x+1,y-1);
                playGame(grid,x+1,y+1);
                playGame(grid,x-1,y+1);

            }
        }
    }

【讨论】:

    【解决方案3】:

    grid.length 返回数组中元素的总数,而不是数组“边”的长度。对于 5x5 数组,您基本上可以接受 x 和 y 均小于 25 的任何坐标值。

    您也无法阻止先前已解决的单元格被读取到堆栈中 - 您将所有 8 个单元格都添加到堆栈中,无论它们之前是否已解决。这种情况一直持续到您的堆栈超出可用内存,并且您收到堆栈溢出错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-12
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多