递归方法StackOverflow错误 - 扫雷

时间:2015-05-12 05:15:11

标签: recursion stack-overflow minesweeper

我正在为扫雷游戏编写递归方法,并且我在清除空格的递归方法中遇到stackOverflow错误。检查周围空间中的3个但仅在检查所有8个时都不会发生错误。你能帮忙找出问题吗?

堆栈跟踪是:

Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
    at java.awt.Component.firePropertyChange(Component.java:8419)
    at javax.swing.AbstractButton.setText(AbstractButton.java:306)
    at Minesweeper.Minesweeper.showTile(Minesweeper.java:105)
    at Minesweeper.Minesweeper.clearEmpty(Minesweeper.java:137)
    at Minesweeper.Minesweeper.clearEmpty(Minesweeper.java:177)

班级:

public class Minesweeper implements ActionListener {

    JFrame frame = new JFrame("Minesweeper");
    JButton reset = new JButton("Reset");
    JButton solve = new JButton("Solve");
    JToggleButton[][] buttons = new JToggleButton[20][20];
    int[][] counts = new int [20][20];
    Container grid = new Container();
    final int MINE = 10;

    public static void main(String[] args)
    {
        new Minesweeper();
    }

    public Minesweeper()
    {
        frame.setSize(600, 600);
        frame.setLayout(new BorderLayout());
        frame.add(reset, BorderLayout.NORTH);
        frame.add(solve, BorderLayout.SOUTH);
        reset.addActionListener(this);
        solve.addActionListener(this);
        grid.setLayout(new GridLayout(20, 20));
        for (int r = 0; r < buttons.length; r++) {
            for (int c = 0; c < buttons[0].length; c++) {
                buttons[r][c] = new JToggleButton();
                buttons[r][c].addActionListener(this);
                grid.add(buttons[r][c]);
                buttons[r][c].setSize(frame.getWidth() / 20, frame.getHeight() / 22);
            }
        }
        frame.add(grid,BorderLayout.CENTER);
        addRandomMines();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public void addRandomMines()
    {
        ArrayList<Integer> mineList = new ArrayList<Integer>();
        for (int x = 0; x < counts.length; x++) {
            for (int y = 0; y < counts[0].length; y++){
                mineList.add((x*100)+y);
            }
        }
        counts = new int[20][20];
        for (int i = 0; i < 30; i++) {
            int choice = (int)(Math.random()*mineList.size());
            counts[mineList.get(choice)/100][mineList.get(choice)%100] = MINE;
            mineList.remove(choice);
        }


        for (int x = 0; x < counts.length; x++) {
            for (int y = 0; y < counts[0].length; y++){
                if (counts[x][y]!=MINE) {
                    int mineCount = 0;
                    if (x > 0 && y > 0 && counts[x - 1][y - 1] == MINE)
                        mineCount++;
                    if (y > 0 && counts[x][y - 1] == MINE)
                        mineCount++;
                    if (x > 0 && counts[x - 1][y] == MINE)
                        mineCount++;
                    if (x < counts.length - 1 && counts[x + 1][y] == MINE)
                        mineCount++;
                    if (y < counts.length - 1 && counts[x][y + 1] == MINE)
                        mineCount++;
                    if (x < counts.length - 1 && y < counts.length - 1 && counts[x + 1][y + 1] == MINE)
                        mineCount++;
                    if (x > 0 && y < counts.length - 1 && counts[x - 1][y + 1] == MINE)
                        mineCount++;
                    if (x < counts.length - 1 && y > 0 && counts[x + 1][y - 1] == MINE)
                        mineCount++;
                    counts[x][y] = mineCount;
                }
            }
        }
    }

    public void showTile(int r, int c)
    {
        if (counts[r][c] == 0) {
            buttons[r][c].setText("");
            buttons[r][c].setSelected(true);
        }
        else if (counts[r][c]==MINE) {
            buttons[r][c].setForeground(Color.red);
            buttons[r][c].setText("X");
            buttons[r][c].setSelected(true);
        }
        else {
            buttons[r][c].setText(counts[r][c] + "");
            if (counts[r][c]==1)
                buttons[r][c].setForeground(Color.blue);
            else if (counts[r][c]==2)
                buttons[r][c].setForeground(Color.magenta);
            else if (counts[r][c]==3)
                buttons[r][c].setForeground(Color.green);
            buttons[r][c].setSelected(true);
        }
    }

    public void lostGame() {
        for (int x = 0; x < buttons.length; x++) {
            for (int y = 0; y < buttons[0].length; y++) {
               if (counts[x][y]==MINE) {
                   showTile(x, y);
               }
            }
        }
    }

    public void clearEmpty(ArrayList<Integer> toClear)
    {
        if (toClear.size()==0){
            return;
        }
        else {
            int x = toClear.get(0)/100;
            int y = toClear.get(0)%100;
            toClear.remove(0);
            if (counts[x][y]==0) {
                if (x > 0 && y > 0) {
                    showTile(x-1,y-1);
                    if (counts[x-1][y-1]==0)
                        toClear.add((x-1)*100 + (y-1));
                }
                if (y > 0) {
                    showTile(x,y-1);
                    if (counts[x][y-1]==0)
                        toClear.add(x*100 + (y-1));
                }
                if (x <counts.length-1 && y > 0) {
                    showTile(x+1,y-1);
                    if (counts[x+1][y-1]==0)
                        toClear.add((x+1)*100 + (y-1));
                }
                if (x > 0) {
                    showTile(x-1,y);
                    if (counts[x-1][y]==0)
                        toClear.add((x-1)*100 + y);
                }
                if (x <counts.length-1 && y > 0) {
                    showTile(x+1,y);
                    if (counts[x+1][y]==0)
                        toClear.add((x+1)*100 + y);
                }
                if (x > 0 && y < counts[0].length-1) {
                    showTile(x-1,y+1);
                    if (counts[x-1][y+1]==0)
                        toClear.add((x-1)*100 + (y+1));
                }
                if (y < counts[0].length-1) {
                    showTile(x,y+1);
                    if (counts[x][y+1]==0)
                        toClear.add(x*100 + (y+1));
                }
                if (x <counts.length-1 && y < counts[0].length-1) {
                    showTile(x+1,y+1);
                    if (counts[x+1][y+1]==0)
                        toClear.add((x+1)*100 + (y+1));
                }
            }
            clearEmpty(toClear);
        }
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if (event.getSource().equals(reset)) {
            for (int r = 0; r < buttons.length; r++) {
                for (int c = 0; c < buttons[0].length; c++) {
                    buttons[r][c].setSelected(false);
                    buttons[r][c].setText("");
                }
            }
            addRandomMines();
        } else if (event.getSource().equals(solve)) {

        } else {
            for (int r = 0; r < buttons.length; r++) {
                for (int c = 0; c < buttons[0].length; c++) {
                    if (event.getSource().equals(buttons[r][c])) {
                        if (counts[r][c] == MINE) {
                            showTile(r, c);
                            lostGame();
                        }
                        else if (counts[r][c] == 0) {
                            ArrayList<Integer> toClear = new ArrayList<Integer>();
                            toClear.add(r*100+c);
                            clearEmpty(toClear);
                        }
                        else {
                            showTile(r, c);
                        }
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

我认为你使用了错误的算法......

尝试使用迭代而不是递归方法。

正如User404已经提到的,你当前的算法使列表不断增长......

对于您的实施:您有400个瓷砖。假设(最坏情况)所有图块都是空的,您只需调用方法i = str.indexOf('x'); while(i >= 0) { System.out.println(i); i = str.indexOf('x', i+1); } 一次。您将发现所有8个邻居都是空的,因此您将此8个邻居添加到列表中,同时仅删除第一个邻居。现在再次将数组传递给方法(第二次调用),并再次为第一个条目找到8个邻居。因此,您的第3个电话会有一个包含15个图块的列表。

真正的问题

通过这种方式,您永远不会结束,因为您永远不会检查当前已检查的磁贴是否已被清除,但您只会添加到列表中,而不是删除它。

<强>解决方案

至少应检查要添加到列表中的图块是否已清除或已在列表中。

您的示例是一个明确的示例,为什么应谨慎使用递归算法,因为有时终止很困难,而且您必须注意不要多次执行任何工作。