Node.class
class Node {
public int x,y;
public Node parent;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
public int F;//F为估价函数
public int G;//G代表的是从初始位置Start沿着已生成的路径到指定待检测结点移动开销
public int H;//H表示待检测结点到目标节点B的估计移动开销
public void calcF() {
this.F = this.G + this.H;
}//计算估价函数
}
MyMaze.class
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Stack;
public class MyMaze extends JPanel implements KeyListener {
//生成地图用到变量
final static int wall =0; //代表墙
final static int road =1; //代表空地
static int num = 21; //迷宫长度
int width = 20; //迷宫宽度
static int [][] mMap; //迷宫
boolean[][] visit; //用来标记某一格是否被访问过
Node start = new Node(1,1); //开始节点
Node end = new Node(num-2,num-2);//结束节点
Node cur; //当前格
Node next; //下一格
Stack<Node> path = new Stack<>();//记录生成地图时遍历的顺序
//走迷宫时用到的变量
private Node movePerson;
List<Integer> xPath=new ArrayList<>();//记录迷宫中行进的轨迹
List<Integer> yPath=new ArrayList<>();
private boolean drawPath = false;
//A*算法使用到的变量
public int sValue = 10;//设每一步的权值为10
private ArrayList<Node> openList = new ArrayList<>();//维护一个开放列表
private ArrayList<Node> closeList = new ArrayList<>();//维护一个关闭列表
//初始化,初始化迷宫参数
MyMaze(){
mMap = new int [num][num];
visit = new boolean[num][num];
for (int i = 0; i < num; i = i+2) {//初始化地图的空格
for (int j = 0; j < num; j=j+2){
mMap[i][j] = wall;//其余均为墙
visit[i][j] = false;
}
}
for (int i = 1; i < num; i = i+2) {//初始化地图的空格
for (int j = 1; j < num; j=j+2){
mMap[i][j] = road;//奇数行奇数列的格子均为路
visit[i][j] = false;
}
}
visit[start.x][start.y] = true;
mMap[start.x][start.y] = road;
cur = start; //将当前格标记为开始格
movePerson=new Node(start.x-1,start.y);
drawPath=false;
createMaze();
this.addKeyListener(this);
this.setFocusable(true);
}
public void init(){//第一轮结束后,再次初始化地图
mMap = new int [num][num];
visit = new boolean[num][num];
for (int i = 0; i < num; i = i+2) {//初始化地图的空格
for (int j = 0; j < num; j=j+2){
mMap[i][j] = wall;//其余均为墙
visit[i][j] = false;
}
}
for (int i = 1; i < num; i = i+2) {//初始化地图的空格
for (int j = 1; j < num; j=j+2){
mMap[i][j] = road;//奇数行奇数列的格子均为路
visit[i][j] = false;
}
}
visit[start.x][start.y] = true;
mMap[start.x][start.y] = road;
cur = start; //将当前格标记为开始格
movePerson=new Node(start.x-1,start.y);//设置移动的起始点
drawPath=false;
xPath.clear();//清空行走的路径坐标
yPath.clear();
openList.clear();
closeList.clear();
createMaze();
this.setFocusable(true);
repaint();
}
//深度优先遍历
void createMaze() {
path.push(cur); //将当前格压入栈
while(!path.empty()) {
ArrayList<Node> mNei=notVisitedNei(cur);
if(mNei.size()==0){//如果该格子没有可访问的邻接格,则跳回上一个格子
cur = path.pop();
continue;
}
next = mNei.get(new Random().nextInt(mNei.size()));//随机选取一个邻接格
int x = next.x;
int y = next.y;
if(visit[x][y]){//如果该节点被访问过,则回到上一步继续寻找
cur = path.pop();
}
else{//否则将当前格压入栈,标记当前格为已访问,并且在迷宫地图上移除障碍物
path.push(next);
visit[x][y] = true;
mMap[x][y] = road;
mMap[(cur.x + x) / 2][(cur.y + y) / 2] = road; //打通当前格与下一格
cur = next;//当前格等于下一格
}
}
mMap[start.x-1][start.y]=1;//设置入口
mMap[end.x+1][end.y]=1;//设置出口
}
public ArrayList<Node> notVisitedNei(Node node)//寻找未访问的邻接节点
{
int []nei={2,0,-2,0,2};
ArrayList<Node> list = new ArrayList<Node>();
for(int i = 0; i < nei.length-1; i++)
{
int x = node.x + nei[i];
int y = node.y + nei[i+1];
if( x >= 0 && x < num && y >= 0 && y < num)
{
if(!visit[x][y])//未访问,则入数组
list.add(new Node(x,y));
}
}
return list;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(Color.lightGray);//画墙
for(int i=0;i<num;i++){
for(int j=0;j<num;j++){
if(mMap[i][j]==0){
g.fillRect(10+i*width,10+j*width,width,width);
}
}
}
if(drawPath){//画出A*算法求得的路径
g.setColor(Color.MAGENTA);
Node parent = findPath(start, end); //父节点
ArrayList<Node> arrayList = new ArrayList<Node>();
while (parent != null) {
arrayList.add(new Node(parent.x, parent.y));
parent = parent.parent;
}
for (int i = 0; i <num; i++) {
for (int j = 0; j < num; j++) {
if (exists(arrayList, i, j)) {
g.fillOval(10+i*width+width/4 , 10+j*width+width/4,
width / 2, width / 2);
}
}
}
}
g.setColor(Color.PINK);//画移动的轨迹
for(int i=0;i<xPath.size();i++){
g.fillOval(10+xPath.get(i)*width+width/4 , 10+yPath.get(i)*width+width/4,
width / 2, width / 2);
}
g.setColor(Color.RED);//画点的移动
g.fillOval(10+movePerson.x*width+width/4 , 10+movePerson.y*width+width/4,
width / 2, width / 2);
}
private boolean isOutOfBorder(int x, int y) {//越界检测
if (x > num-1 || y >num-1 || x < 0 || y < 0) {
return true;
}
return false;
}
private void checkWin() {//通关检测
if (movePerson.x==num-1 && movePerson.y==num-2) {
JOptionPane.showMessageDialog(null, "Congratulation! " +"\n", "My Maze",
JOptionPane.PLAIN_MESSAGE);
init();
}
}
//A*算法
public Node findMinFNodeInOpenList() {//寻找最小移动开销的节点
Node tempNode = openList.get(0);
for (Node node : openList) {
if (node.F < tempNode.F) {
tempNode = node;
}
}
return tempNode;
}
public ArrayList<Node> findNeighborNodes(Node currentNode) {//找上下左右四个方向的邻居节点
ArrayList<Node> arrayList = new ArrayList<Node>();
int topX = currentNode.x;
int topY = currentNode.y - 1;
if (!isOutOfBorder(topX, topY) && !exists(closeList, topX, topY) && mMap[topX][topY]==1) {
arrayList.add(new Node(topX, topY));
}
int bottomX = currentNode.x;
int bottomY = currentNode.y + 1;
if (!isOutOfBorder(bottomX, bottomY) && !exists(closeList, bottomX, bottomY) && mMap[bottomX][bottomY]==1) {
arrayList.add(new Node(bottomX, bottomY));
}
int leftX = currentNode.x - 1;
int leftY = currentNode.y;
if (!isOutOfBorder(leftX, leftY) && !exists(closeList, leftX, leftY) && mMap[leftX][leftY]==1) {
arrayList.add(new Node(leftX, leftY));
}
int rightX = currentNode.x + 1;
int rightY = currentNode.y;
if (!isOutOfBorder(rightX, rightY) && !exists(closeList, rightX, rightY) && mMap[rightX][rightY]==1) {
arrayList.add(new Node(rightX, rightY));
}
return arrayList;
}
public Node findPath(Node startNode, Node endNode) {
openList.add(startNode);// 把起点加入 open list
while (openList.size() > 0) {
Node currentNode = findMinFNodeInOpenList();// 遍历 open list ,查找 F值最小的节点,把它作为当前要处理的节点
openList.remove(currentNode);// 从open list中移除
closeList.add(currentNode);// 把这个节点移到 close list
ArrayList<Node> neighborNodes = findNeighborNodes(currentNode);//寻找邻居节点
for (Node node : neighborNodes) {
if (exists(openList, node)) {//如果邻居节点在open列表中
foundPoint(currentNode, node);//更新列表中父节点和估价函数信息
} else {
notFoundPoint(currentNode, endNode, node);//如果邻居节点不在open列表中,则将该点加入open列表中
}
}
if (find(openList, endNode) != null) {//如果找到尾节点,则返回尾节点
return find(openList, endNode);
}
}
// return find(openList, endNode);
return null;
}
private void foundPoint(Node tempStart, Node node) {
int G = calcG(tempStart, node);
if (G < node.G) {
node.parent = tempStart;
node.G = G;
node.calcF();
}
}
private void notFoundPoint(Node tempStart, Node end, Node node) {
node.parent = tempStart;
node.G = calcG(tempStart, node);
node.H = calcH(end, node);
node.calcF();
openList.add(node);
}
private int calcG(Node start, Node node) {
int G = sValue;
int parentG = node.parent != null ? node.parent.G : 0;
return G + parentG;
}
private int calcH(Node end, Node node) {
int step = Math.abs(node.x - end.x) + Math.abs(node.y - end.y);
return step * sValue;
}
public static Node find(List<Node> nodes, Node point) {
for (Node n : nodes)
if ((n.x == point.x) && (n.y == point.y)) {
return n;
}
return null;
}
public static boolean exists(List<Node> nodes, Node node) {//判断节点是否存在某一list中
for (Node n : nodes) {
if ((n.x == node.x) && (n.y == node.y)) {
return true;
}
}
return false;
}
public static boolean exists(List<Node> nodes, int x, int y) {//判断节点是否存在某一list中
for (Node n : nodes) {
if ((n.x == x) && (n.y == y)) {
return true;
}
}
return false;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {//响应键盘
int keyCode = e.getKeyCode();
int x=movePerson.x;
int y=movePerson.y;
switch (keyCode){
case KeyEvent.VK_SPACE: //空格键选择是否路径的显示的情况
if (drawPath) {
drawPath = false;
} else {
drawPath = true;
}
repaint();
break;
case KeyEvent.VK_LEFT: //根据键盘控制移动
x--;
break;
case KeyEvent.VK_RIGHT:
x++;
break;
case KeyEvent.VK_UP:
y--;
break;
case KeyEvent.VK_DOWN:
y++;
break;
}
if(!isOutOfBorder(x,y)&&mMap[x][y]==1){
xPath.add(movePerson.x);
yPath.add(movePerson.y);
movePerson.x=x;
movePerson.y=y;
}
repaint();
checkWin();
}
@Override
public void keyReleased(KeyEvent e) {
}
public static void main(String[] args) {
JPanel p = new MyMaze();
Image icon=Toolkit.getDefaultToolkit().getImage("maze.png");//设置最小化的图标
JFrame frame = new JFrame("MY MAZE(按空格键显示或隐藏路径提示)");
frame.setIconImage(icon);
frame.getContentPane().add(p);//添加画布
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用 System exit 方法退出应用程序。
frame.setSize(460, 480);//设置窗口大小
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
|