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;
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;
public int H;
public void calcF() {
this.F = this.G + this.H;
}
}
public class Maze extends JPanel implements KeyListener {
final static int wall = 0;
final static int road = 1;
static int[][] mMap;
boolean[][] visit;
static int num = 21;
int width = 21;
Node start = new Node(1, 1);
Node end = new Node(num - 2, num - 2);
Node dangqian;
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<>();
Maze(){
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;
dangqian = 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;
dangqian = 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(dangqian); //将当前格压入栈
while(!path.empty()) {
ArrayList<Node> mNei=notVisitedNei(dangqian);
if(mNei.size()==0){//如果该格子没有可访问的邻接格,则跳回上一个格子
dangqian = path.pop();
continue;
}
next = mNei.get(new Random().nextInt(mNei.size()));//随机选取一个邻接格
int x = next.x;
int y = next.y;
if(visit[x][y]){//如果该节点被访问过,则回到上一步继续寻找
dangqian = path.pop();
}
else{//否则将当前格压入栈,标记当前格为已访问,并且在迷宫地图上移除障碍物
path.push(next);
visit[x][y] = true;
mMap[x][y] = road;
mMap[(dangqian.x + x) / 2][(dangqian.y + y) / 2] = road; //打通当前格与下一格
dangqian = 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.green);
g.setColor(Color.black);
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);
}
}
}
g.setColor(Color.BLUE);//画移动的轨迹
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);
if(drawpath){//画出A*算法求得的路径
g.setColor(Color.yellow);
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.fillOval(10+(num-1)*width+width/4, 10+width*(num-2)+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) {
Object[] options = {"再来一局", "结束"};
int response = JOptionPane.showOptionDialog(this, "恭喜通关","Game Over", JOptionPane.YES_NO_OPTION,
JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
if (response == 0) {
init();
} else {
System.exit(0);
}
}
}
//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 upX = currentNode.x;
int upY = currentNode.y - 1;
if (!isOutOfBorder(upX, upY) && !exists(closeList, upX, upY) && mMap[upX][upY]==1) {
arrayList.add(new Node(upX, upY));
}
int downX = currentNode.x;
int downY = currentNode.y + 1;
if (!isOutOfBorder(downX, downY) && !exists(closeList, downX, downY) && mMap[downX][downY]==1) {
arrayList.add(new Node(downX, downY));
}
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);
while (openList.size()>0){
Node currentNode = findMinFNodeInOpenList();// 遍历 open list ,查找 F值最小的节点,把它作为当前要处理的节点
openList.remove(currentNode);
closeList.add(currentNode);
ArrayList<Node> neighborNodes = findNeighborNodes(currentNode);
for (Node node : neighborNodes) {
if (exists(openList, node)) {
foundPoint(currentNode, node);
} else {
notFoundPoint(currentNode, endNode, node);
}
}
if (find(openList, endNode) != null) {//如果找到尾节点,则返回尾节点
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 Maze();
JFrame frame = new JFrame("迷宫(按空格显示最优路径)");
frame.getContentPane().add(p);//添加画布
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用 System exit 方法退出应用程序。
frame.setSize(480, 500);//设置窗口大小
frame.setLocation(550, 150);
frame.setVisible(true);
}
}