GUI(2) 迷宫问题

迷宫

  • 转载自:http://zpsailor.iteye.com/blog/651141
  • 使用swing组件的一个项目
  • awt 和 util 包里的list冲突,util不能写成util.*;
  • @SuppressWarnings(“serial”) //告诉编译器忽略指定的警告,不用在编译完成后出现警告信息
  • public class Maze extends JFrame implements ActionListener 自身就是监听器
  • 结合了图的深度优先遍历,高效的产生复杂的迷宫
  • 二维数组,0、1矩阵表示迷宫
  • 使用java.awt.Timer类来代替时间的线程
  • 使用java.awt.Canvas类来描述每个迷宫格子,每次只渲染两个小格字就可以了

迷宫生成算法

  • 既然要通过图的深度优先遍历来生成迷宫,我们首先需要的是一张图,这张图的节点中,要包含迷宫的开始点和结束点。在我们的算法中,最初构造了这样一张图:
  • 《GUI(2) 迷宫问题》
  • 在这张图中,每一个白色小块表示图中的一个节点,而横向和竖向上的红色方块是这个图的边。在整个图中,白色的部分表示通道,而红色部分表示的是墙壁。我们规定左上角是迷宫的入口,右下角是迷宫的出口。在这种情况下,我们只需要从入口处开始,采用深度优先遍历方法,遍历这个图,在遍历的过程中,当从一个节点移动到另一个节点的过程中,将其移动过程中的边变成通道,这样遍历完图后,就将产生一个类似下图的迷宫:
  • 《GUI(2) 迷宫问题》
  • 这样,我们的迷宫便生成好了。在这个过程中,需要用到递归算法和回溯法。

走迷宫的算法

  • 在走迷宫的过程中,需要用到两个列表结构willvisit和 comed,分别记录了当前可以移动到的位置和已经走过的位置。在willvisit中不包含comed中的记录的位置。这样利用回溯法,在当前位置的willvisit中随机选取一个位置移动,并将移动过的位置记录到comed的尾部。当当前所在位置不能产生下一个移动时,将当前位置添加到comed的头部,从comed的尾部取出一个位置,然后将这个位置作为当前位置。再判断能否移动下一步,依次这样移动,直到移动到出口处。

jframe.setDefaultCloseOperation(int operation):

  • 设置用户在此窗体上发起 “close” 时默认执行的操作。方法中的参数解释如下:
  • 为“0”或DO_NOTHING_ON_CLOSE:
  • (在 WindowConstants 中定义):不执行任何操作;要求程序在已注册的WindowListener 对象的 windowClosing 方法中处理该操作。
  • 比如实例程序代码中更改为f.setDefaultCloseOperation(f. DO_NOTHING_ON_CLOSE);或者f.setDefaultCloseOperation(0),然后查看效果,可以发现窗口无法关闭,下面是相同测试方法,不再解释了。
  • 为“1”或HIDE_ON_CLOSE
  • 调用任意已注册的 WindowListener 对象后自动隐藏该窗体。此时没有关闭程序,只是将程序界面隐藏了。可以打开任务管理器,可以看到一个叫“java.exe”的进程(如果调试运行了多个java程序,则会看到多个“java.exe”的进程),如果此时用EditPlus测试程序,会发现当单击窗口的关闭按钮关闭窗口后,却无法再次对程序进行调试,因为程序线程没有关闭,在任务管理器中关闭java.exe(如果有多个“java.exe”的进程,则先都关闭掉,再来测试该问题)基础后,EditPlus才可以重新编译改程序。
  • 为“2”或DISPOSE_ON_CLOSE
  • 调用任意已注册 WindowListener 的对象后自动隐藏并释放该窗体。但继续运行应用程序,释放了窗体中占用的资源。
  • 为“3”EXIT_ON_CLOSE(在 JFrame 中定义):
  • 使用 System exit 方法退出应用程序。仅在应用程序中使用。结束了应用程序。
  • 默认情况下,该值被设置为 HIDE_ON_CLOSE。

@SuppressWarnings(“serial”)

  • 简介:java.lang.SuppressWarnings是J2SE 5.0中标准的Annotation之一。可以标注在类、字段、方法、参数、构造方法,以及局部变量上。
  • 作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。
  • 使用:
  • @SuppressWarnings(“”)
  • @SuppressWarnings({})
  • @SuppressWarnings(value={})

  • 根据sun的官方文档描述:

  • value – 将由编译器在注释的元素中取消显示的警告集。允许使用重复的名称。忽略第二个和后面出现的名称。出现未被识别的警告名不是 错误:编译器必须忽略无法识别的所有警告名。但如果某个注释包含未被识别的警告名,那么编译器可以随意发出一个警告。
  • 各编译器供应商应该将它们所支持的警告名连同注释类型一起记录。鼓励各供应商之间相互合作,确保在多个编译器中使用相同的名称。
  • @SuppressWarnings(“unchecked”)
  • 告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
  • @SuppressWarnings(“serial”)
  • 如果编译器出现这样的警告信息:The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long 使用这个注释将警告信息去掉。
  • @SuppressWarnings(“deprecation”)
  • 如果使用了使用@Deprecated注释的方法,编译器将出现警告信息。 使用这个注释将警告信息去掉。
  • @SuppressWarnings(“unchecked”, “deprecation”)
  • 告诉编译器同时忽略unchecked和deprecation的警告信息。
  • @SuppressWarnings(value={“unchecked”, “deprecation”})
  • 等同于@SuppressWarnings(“unchecked”, “deprecation”)

Maze.java

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.TimerTask; //这里不能写成java.util.*; 因为awt包里面也有List,会冲突
import javax.swing.*;
@SuppressWarnings("serial") // 告诉编译器忽略指定的警告,不用在编译完成后出现警告信息
public class Maze extends JFrame implements ActionListener {
    private int ROW = 25;// ROW 和COL目前暂定只能是奇数
    private int COL = 25;
    private JPanel panel;  
    private JPanel northPanel;  
    private JPanel centerPanel;  
    private MazeGrid grid[][];  
    private JButton restart;  
    private JButton dostart;  
    private List<String> willVisit = new ArrayList<String>(); // 记录了当前可以移动到的位置
                                                              // ,在willvisit中不包含comed中的记录的位置
    private List<String> visited = new ArrayList<String>();
    private LinkedList<String> comed = new LinkedList<String>(); // 已经走过的位置
    private long startTime;
    private long endTime;

    public Maze() {
        init();
        this.setTitle("回溯法--走迷宫");
        this.add(panel);
        this.pack(); // 不加pack就只剩标题栏了
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 用户单击窗口的关闭按钮时程序执行的操作
    }

    public void init() {
        panel = new JPanel();  
        northPanel = new JPanel();  
        centerPanel = new JPanel();  
        panel.setLayout(new BorderLayout());  
        restart = new JButton("重新生成迷宫");  
        dostart = new JButton("开始走迷宫");  
        grid = new MazeGrid[ROW][COL];  

        centerPanel.setLayout(new GridLayout(ROW, COL, 1, 1));
        centerPanel.setBackground(new Color(0, 0, 0));
        northPanel.add(restart);
        northPanel.add(dostart);

        dostart.addActionListener(this);
        restart.addActionListener(this);
        for (int i = 0; i < grid.length; i++)
            for (int j = 0; j < grid[i].length; j++) {
                if (j % 2 == 0 && i % 2 == 0)
                    grid[i][j] = new MazeGrid(true, 20, 20);
                else
                    grid[i][j] = new MazeGrid(false, 20, 20);
            }
        grid[0][0].setVisited(true);
        grid[0][0].setPersonCome(true);
        grid[0][0].setStart(true);
        visited.add("0#0");
        grid[ROW - 1][COL - 1].setEnd(true);
        grid = createMap(grid, 0, 0);
        for (int i = 0; i < grid.length; i++)
            for (int j = 0; j < grid[i].length; j++) {
                grid[i][j].repaint();
                centerPanel.add(grid[i][j]);
            }

        panel.add(northPanel, BorderLayout.NORTH);
        panel.add(centerPanel, BorderLayout.CENTER);
    }
    public MazeGrid[][] createMap(MazeGrid mazeGrid[][], int x, int y) {
        int visitX = 0;
        int visitY = 0;
        if (x - 2 >= 0) {
            if (!mazeGrid[x - 2][y].isVisited()) {
                willVisit.add((x - 2) + "#" + y);
            }
        }
        if (x + 2 < COL) {
            if (!mazeGrid[x + 2][y].isVisited()) {
                willVisit.add((x + 2) + "#" + y);
            }
        }
        if (y - 2 >= 0) {
            if (!mazeGrid[x][y - 2].isVisited()) {
                willVisit.add(x + "#" + (y - 2));
            }
        }
        if (y + 2 < ROW) {
            if (!mazeGrid[x][y + 2].isVisited()) {
                willVisit.add(x + "#" + (y + 2));
            }
        }
        if (!willVisit.isEmpty()) {
            int visit = (int) (Math.random() * willVisit.size());
            String id = willVisit.get(visit);
            visitX = Integer.parseInt(id.split("#")[0]);
            visitY = Integer.parseInt(id.split("#")[1]);
            mazeGrid[(visitX + x) / 2][(visitY + y) / 2].setMark(true);

            mazeGrid[visitX][visitY].setVisited(true);
            if (!visited.contains(id)) {// 将这个点加到已访问中去
                visited.add(id);
            }
            willVisit.clear();
            createMap(mazeGrid, visitX, visitY);
        } else {
            if (!visited.isEmpty()) {
                String id = visited.remove(visited.size() - 1);// 取出最后一个元素
                visitX = Integer.parseInt(id.split("#")[0]);
                visitY = Integer.parseInt(id.split("#")[1]);
                mazeGrid[visitX][visitY].setVisited(true);
                createMap(mazeGrid, visitX, visitY);
            }
        }
        return mazeGrid;
    }

    public String goMaze(MazeGrid mazeGrid[][], int x, int y) {
        int comeX = 0;
        int comeY = 0;
        // left
        if (x - 1 >= 0) {
            if (mazeGrid[x - 1][y].isMark()) {
                if (!comed.contains((x - 1) + "#" + y))
                    willVisit.add((x - 1) + "#" + y);
            }
        }
        // right
        if (x + 1 < COL) {
            if (mazeGrid[x + 1][y].isMark()) {
                if (!comed.contains((x + 1) + "#" + y))
                    willVisit.add((x + 1) + "#" + y);
            }
        }
        // up
        if (y - 1 >= 0) {
            if (mazeGrid[x][y - 1].isMark()) {
                if (!comed.contains(x + "#" + (y - 1)))
                    willVisit.add(x + "#" + (y - 1));
            }
        }
        // down
        if (y + 1 < ROW) {
            if (mazeGrid[x][y + 1].isMark()) {
                if (!comed.contains(x + "#" + (y + 1)))
                    willVisit.add(x + "#" + (y + 1));
            }
        }
        if (!willVisit.isEmpty()) {
            int visit = (int) (Math.random() * willVisit.size());
            String id = willVisit.get(visit);
            comeX = Integer.parseInt(id.split("#")[0]);
            comeY = Integer.parseInt(id.split("#")[1]);
            mazeGrid[x][y].setPersonCome(false);
            mazeGrid[comeX][comeY].setPersonCome(true);
            mazeGrid[x][y].repaint();
            mazeGrid[comeX][comeY].repaint();
            willVisit.clear();
            comed.add(x + "#" + y);
        } else {
            if (!comed.isEmpty()) {
                String id = comed.removeLast();
                comeX = Integer.parseInt(id.split("#")[0]);
                comeY = Integer.parseInt(id.split("#")[1]);
                mazeGrid[x][y].setPersonCome(false);
                mazeGrid[comeX][comeY].setPersonCome(true);
                mazeGrid[x][y].repaint();
                mazeGrid[comeX][comeY].repaint();
                comed.addFirst(x + "#" + y);
            }
        }
        return comeX + "#" + comeY;
    }

    int comeX = 0;
    int comeY = 0;

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("重新生成迷宫")) {
            refreshMap(grid);
        } else if (e.getActionCommand().equals("开始走迷宫")) {
            startTime = System.currentTimeMillis();
            dostart.setVisible(false);
            restart.setText("禁止刷新");
            int delay = 1000;
            int period = 500;// 循环间隔
            java.util.Timer timer = new java.util.Timer();
            timer.scheduleAtFixedRate(new TimerTask() {
                public void run() {
                    if (grid[ROW - 1][COL - 1].isPersonCome()) {
                        endTime = System.currentTimeMillis();
                        JOptionPane.showMessageDialog(null, "已经走出迷宫,耗时" + (endTime - startTime) / 1000 + "秒", "消息提示",
                                JOptionPane.ERROR_MESSAGE);
                        this.cancel();
                        restart.setText("重新生成迷宫");
                    } else {


                        String id = goMaze(grid, comeX, comeY);
                        comeX = Integer.parseInt(id.split("#")[0]);
                        comeY = Integer.parseInt(id.split("#")[1]);
                    }
                }
            }, delay, period);
        }
    }

    public void refreshMap(MazeGrid mazeGrid[][]) {
        comeX = 0;
        comeY = 0;
        willVisit.clear();
        visited.clear();
        comed.clear();
        this.remove(panel);
        init();
        this.add(panel);
        this.pack();
        this.setVisible(true);
    }

    public static void main(String args[]) {
        long start = System.currentTimeMillis();
        new Maze();
        long end = System.currentTimeMillis();
        System.out.println("使用ArrayList生成迷宫耗时:" + (end - start) + "毫秒");
    }
}

MazeGrid.java

import java.awt.*;
@SuppressWarnings("serial")  
public class MazeGrid extends Canvas {  //Canvas控件代表一个矩形区域,应用程序可以画的东西,或者可以由用户创建的接收输入。
                                    //用法类似与Frame
    private boolean mark;// 标记是否是通路,TRUE为通路,FALSE为不通 
    private boolean isVisited;// 标记是否是访问过的,这是在生成迷宫的时候判断的。 
    private boolean isPersonCome;// 标记是否已经走过 
    private boolean isStart;// 判断是否为入口 
    private boolean isEnd;// 判断是否为出口 
    public MazeGrid() {  
        this.setBackground(new Color(120, 0, 0));  
        this.setSize(25, 25);  
    }  
    public MazeGrid(boolean mark, int width, int height) {  
        this.mark = mark;  
        this.setSize(width, height);  
        if (mark == true) {  
            this.setBackground(new Color(255, 255, 255));  
        } else {  
            this.setBackground(new Color(120, 0, 0));  
        }  
    }  
    public boolean isMark() {  
        return mark;  
    }  
    public void setMark(boolean mark) {  
        this.mark = mark;  
    }  
    public void paint(Graphics g) {  
        if (this.mark) {  
            if (this.isStart || this.isEnd) {  
                this.setBackground(new Color(255,0,0));  
            } else  
                this.setBackground(new Color(255, 255, 255));  
        } else {  
            this.setBackground(new Color(120, 0, 0));  
        }  
        if (this.isPersonCome) {  
            g.setColor(Color.BLACK);  
            g.fillOval(2, 2, 15, 15);  
        }  
    }  
    public boolean isVisited() {  
        return isVisited;  
    }  
    public void setVisited(boolean isVisited) {  
        this.isVisited = isVisited;  
    }  
    public boolean isPersonCome() {  
        return isPersonCome;  
    }  
    public void setPersonCome(boolean isPersonCome) {  
        this.isPersonCome = isPersonCome;  
    }  
    public boolean isStart() {  
        return isStart;  
    }  
    public void setStart(boolean isStart) {  
        this.isStart = isStart;  
    }  
    public boolean isEnd() {  
        return isEnd;  
    }  
    public void setEnd(boolean isEnd) {  
        this.isEnd = isEnd;  
    }  
}  
    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/u014015972/article/details/49783599
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞