项目准备
需求分析
1.prime算法生成迷宫
2.A*算法实现自动寻路
编程语言及开发工具
JAVA,IDEA
项目软件概述
界面
功能
1.能随机生成迷宫
2.自动寻路功能
代码
Main
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Remake(R) Help(H)");
frame.setSize(365,385);
Maze maze = new Maze();
frame.add(maze);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
CreateMaze
import java.util.Random;
class CreateMaze {
public static int L;
public static int[][] map;
private static int size;
CreateMaze(int l) {//保证行数和列数是奇数
size=2*l+1;
L=l;
map = new int[size][size];
}
public static int[][] Init() {
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
map[i][j] = 0;// 0 为墙 1为路
for (int i = 0; i < L; i++)
for (int j = 0; j < L; j++)
map[2 * i + 1][2 * j + 1] = 1;
Prime();
return map;
}
public static void Prime() {
int[] Y, N;
int sum = L * L;
Y = new int[sum];
N = new int[sum];
int[] LR = {-1, 1, 0, 0};
int[] UD = {0, 0, 1, -1};
int[] S = {-1, 1, L, -L};
for (int i = 0; i < sum; i++) {
Y[i] =0;
N[i] = 0;
}
// 起点
Random rd = new Random();
int random = rd.nextInt(sum);
Y[0] = random;
N[random] = 1;
int count = 0;
while (true) {
int x = random % L;//坐标
int y = random / L;
int direction = -1;//保存方向,若没有方向可走,就默认为-1
int i = 0;
while (++i < 5) {
int dir = rd.nextInt(4);
int repos;
int x1, y1;
repos = random + S[dir];
x1 = x + LR[dir];
y1 = y + UD[dir];
if (y1 >= 0 && x1 >= 0 && x1 < L && y1 < L && repos >= 0 && repos < sum
&& N[repos] != 1) {
N[repos] = 1;
Y[++count] = repos;
random = repos;
direction = dir;
map[2 * x + 1 + LR[dir]][2 * y + 1 + UD[dir]] = 1;
break;
} else {
if (count == sum - 1)
return;
}
}
if (direction < 0) {
random = Y[rd.nextInt(count + 1)];
}
}
}
}
A*
import java.util.ArrayList;
import java.util.List;
class A {
public static int[][] NODES;//定义一个迷宫单元数组
public int STEP = 10;//设每一步的权值为10
private final ArrayList<Node> openList = new ArrayList<>();//维护一个开放列表
private final ArrayList<Node> closeList = new ArrayList<>();//维护一个关闭列表
A(int[][] map) {
NODES=map;//初始化迷宫单元为新生成的对应地图,把Maze类里面生成的地图传给NODES,再在此地图基础上用A*算法寻路径
Node startNode = new Node(Maze.cx, Maze.cy);//起点
Node endNode = new Node(map.length-2, map.length-2);//终点
Node parent = findPath(startNode, endNode); //父节点
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 < NODES.length; i++) {
for (int j = 0; j < NODES.length; j++) {
if (exists(arrayList, i, j)) {
NODES[i][j]=3;//标记关闭列表里的方格为2,为了方便后面在界面画系统寻路路径
}
}
}
}
public static int[][] ans(){
return NODES;
}
//寻找开放列表里F值最小的节点的方法
public Node findMinFNodeInOpneList() {
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<>();
// 只考虑上下左右,不考虑斜对角
int topX = currentNode.x;
int topY = currentNode.y - 1;
if (canReach(topX, topY) && !exists(closeList, topX, topY)) {
arrayList.add(new Node(topX, topY));
}
int bottomX = currentNode.x;
int bottomY = currentNode.y + 1;
if (canReach(bottomX, bottomY) && !exists(closeList, bottomX, bottomY)) {
arrayList.add(new Node(bottomX, bottomY));
}
int leftX = currentNode.x - 1;
int leftY = currentNode.y;
if (canReach(leftX, leftY) && !exists(closeList, leftX, leftY)) {
arrayList.add(new Node(leftX, leftY));
}
int rightX = currentNode.x + 1;
int rightY = currentNode.y;
if (canReach(rightX, rightY) && !exists(closeList, rightX, rightY)) {
arrayList.add(new Node(rightX, rightY));
}
return arrayList;
}
//判断此处坐标是否可达,若超界或者是墙则不可达
public boolean canReach(int x, int y) {
return x >= 0 && x < NODES.length && y >= 0 && y < NODES.length && NODES[x][y] == 1;
}
//A*寻路过程
public Node findPath(Node startNode, Node endNode) {
openList.add(startNode);// 把起点加入 open list
while (openList.size() > 0) {
Node currentNode = findMinFNodeInOpneList();// 遍历 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)) {
foundPoint(currentNode, node);
} else {
notFoundPoint(currentNode, endNode, node);
}
}
if (find(openList, endNode) != null) {
return find(openList, endNode);//找到终点了并返回
}
}
return find(openList, endNode);
}
//在列表里可以找到节点后的情况
private void foundPoint(Node tempStart, Node node) {
int G = calcG(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(node);
node.H = calcH(end, node);
node.calcF();
openList.add(node);
}
//计算G值的方法
private int calcG(Node node) {
int G = STEP;
int parentG = node.parent != null ? node.parent.G : 0;
return G + parentG;
}
//计算H值的方法
private int calcH(Node end, Node node) {
int step = Math.abs(node.x - end.x) + Math.abs(node.y - end.y);
return step * STEP;
}
//找到终点的方法
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;
}
//下面两个是exist方法的重载,判断不同参数情况时节点是否在列表里
public static boolean exists(List<Node> nodes, Node node) {
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) {
for (Node n : nodes) {
if ((n.x == x) && (n.y == y)) {
return true;
}
}
return false;
}
//节点类,定义了每一个节点的属性
public static class Node {
public Node(int x, int y) {
this.x = x;
this.y = y;
}
public int x;
public int y;
public int F;
public int G;
public int H;
public void calcF() {
this.F = this.G + this.H;
}
public Node parent;
}
}
Maze
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Maze extends JPanel implements KeyListener,ActionListener {
public final static int wall = 0;
public final static int current = 2;
static int cx = 1; //当前位置
static int cy = 1;
static int[][] map;
static int i=0;
public static void Map() {
CreateMaze createMaze = new CreateMaze(16);
map = CreateMaze.Init();
map[1][1] = 2;
cx = 1;
cy = 1;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.addKeyListener(this);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
if (map[i][j] == 0) {
g.setColor(Color.BLACK);
g.fillRect(10 * i + 10, 10 * j + 10, 10, 10);
}
else if(map[i][j]==3){
g.setColor(Color.blue);
g.fillOval(10 * i + 12, 10 * j + 12, 5, 5);
}
else if (map[i][j] == 2) {
g.setColor(Color.red);
g.fillRect(10 * i + 10, 10 * j + 10, 10, 10);
}
}
}
g.setColor(Color.white);
g.fillRect(10 , 10 + 10, 10, 10);//画入口
g.fillRect(map.length*10 , map.length*10-10, 10, 10);
g.setColor(Color.red);
if (gameOver()) {
g.setFont(new Font("黑体", Font.BOLD, 30));
g.drawString("游戏胜利,按R重新开始", 20, 180);
}
}
//将A*算法得出的路径返回给map
public void ans() {
A aStart = new A(map);
map = A.ans();
}
public Maze() {
Map();
this.setFocusable(true);
this.addKeyListener(this);
}
//判断游戏结束
public static boolean gameOver() {
return map.length - 2 == cx && map.length - 2 == cy;
}
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
int x = cx, y = cy;
if(!gameOver()) {
if (key == KeyEvent.VK_LEFT && map[x - 1][y] != Maze.wall &&i==0) {//左移
map[x - 1][y] = Maze.current; //更改当前位置到移动到的位置
map[x][y] = 1;//恢复地图原有的图标
cx--;i++;
} else if (key == KeyEvent.VK_RIGHT&&map[x + 1][y] != Maze.wall &&i==0) {
map[x + 1][y] = Maze.current; //右移
map[x][y] = 1;
cx++;i++;
} else if (key == KeyEvent.VK_UP&&map[x][y - 1] != Maze.wall &&i==0) {
map[x][y - 1] = Maze.current; //上移
map[x][y] = 1;
cy--;i++;
} else if (key == KeyEvent.VK_DOWN&&map[x][y + 1] != Maze.wall &&i==0) {
map[x][y + 1] = 2; //下移
map[x][y] = 1;
cy++;i++;
}else if(key == KeyEvent.VK_H&&i==0){ //显示路径
ans();
}else if(key == KeyEvent.VK_R&&i==0){ //重置迷宫
Map();
}
}else if(key == KeyEvent.VK_R&&i==0){ //游戏胜利按下R重新开始
Map();
}
repaint(); //刷新游戏界面
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
i=0;
}
}