实验三 迷宫生成和自动寻路
一、 实验要求
1、迷宫随机生成
2、玩家走迷宫,留下足迹
3、系统用A*算法寻路,输出路径
二、前期准备
解决迷宫问题要用到两个算法,深度优先遍历(DFS)生成迷宫,A*算法寻路。那么首先要对这两种算法有清晰的了解。
1.深度优先遍历(DFS)
基于深度优先遍历的随机迷宫生成,我自己的理解简而言之可以分为以下几步:
a. 建立一张地图,用二维数组表示,地图上全是障碍物。然后再创建一个用来表示每个格子是否被访问过的二维数组。再创建一个用来表示路径的栈结构。
b.随机选择地图上的一点,为了方便我初始点直接取的是左上角即坐标表示为(0,0)的格子。终点的话因为不涉及到交互就暂时没有。
c.查找当前格子的邻接格(注意,这里的邻接格子都是还未被访问的)。随机选择一个邻接格子为下一 格,当前格移动到下一格,标记当前格为已访问,将当前格压入路径栈中。一直重复第三步操作。
d.在第三步操作中,如果当前格子不存在可访问的邻接格,则将栈顶的元素弹出,即退回上一步操作,如果栈为空,则结束程序,打印结果。
2.A*算法
公式表示为: f(n)=g(n) h(n),
其中, f(n) 是从初始状态经由状态n到目标状态的代价估计,
g(n) 是在状态空间中从初始状态到状态n的实际代价,
h(n) 是从状态n到目标状态的最佳路径的估计代价。
(对于路径搜索问题,状态就是图中的节点,代价就是距离)
-
import java.util.ArrayList;
-
import java.util.List;
-
-
class AStart {
-
public static int[][] NODES;//定义一个迷宫单元数组
-
public int STEP = 10;//设每一步的权值为10
-
private ArrayList<Node> openList = new ArrayList<Node>();//维护一个开放列表
-
private ArrayList<Node> closeList = new ArrayList<Node>();//维护一个关闭列表
-
-
public AStart() {
-
NODES=Maze.LabId;//初始化迷宫单元为新生成的对应地图,把Maze2类里面生成的地图传给NODES,再在此地图基础上用A*算法寻路径
-
Node startNode = new Node(1, 1);//起点
-
Node endNode = new Node(19, 19);//终点
-
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;
-
}
-
-
//打印有路径的地图,在控制台输出查看
-
System.out.println("\n" "打印有路径的地图:");
-
for (int i = 0; i < NODES.length; i ) {
-
for (int j = 0; j < NODES.length; j ) {
-
if (exists(arrayList, i, j)) {
-
NODES[i][j]=2;//标记关闭列表里的方格为2,为了方便后面在界面画系统寻路路径
-
}
-
System.out.print(NODES[i][j] " ");
-
}
-
System.out.println();
-
}
-
}
-
-
//寻找开放列表里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<Node>();
-
// 只考虑上下左右,不考虑斜对角
-
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) {
-
if (x >=0 && x < NODES.length && y >=0 && y < NODES.length && NODES[x][y]==1) {
-
return true;
-
}
-
return false;
-
}
-
-
-
//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(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);
-
}
-
-
-
//计算G值的方法
-
private int calcG(Node start, 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;
-
}
-
}
-
import java.util.Random;
-
-
public class Maze {
-
public static int row = 10;// 初始地图有路的迷宫单元行数
-
public static int column = 10;// 初始地图有路的迷宫单元列数
-
public static int r = 2 * row 1;// 迷宫单元行数,保证是奇数
-
public static int c = 2 * column 1;// 迷宫单元列数,保证是奇数
-
public static int[][] LabId;// 存放迷宫的数组,迷宫单元数组
-
Random rand = new Random();
-
-
public Maze() {
-
LabId = new int[r][c];
-
System.out.println("初始化地图:");
-
for (int i = 0; i < r; i ) {
-
for (int j = 0; j < c; j ) {
-
LabId[i][j] = 0;// 将所有格子都设为墙, 0 为墙 1为路
-
if (i % 2 == 1 && j % 2 == 1)// 将奇数行奇数列设为路,1为路,0为墙
-
LabId[i][j] = 1;
-
System.out.print(LabId[i][j] " ");// 打印初始化地图,在控制台输出查看
-
}
-
System.out.println();
-
}
-
-
// 调用深度优先搜索算法
-
accLabDFS();
-
System.out.println("\n" "深度优先算法生成的迷宫:");
-
for (int i = 0; i < r; i ) {
-
for (int j = 0; j < c; j ) {
-
System.out.print(LabId[i][j] " ");// 打印生成的深度优先算法生成的迷宫,在控制台输出查看
-
}
-
System.out.println();
-
}
-
}
-
-
// 实现深度优先算法
-
public void accLabDFS() {
-
int[] lab;// 访问队列
-
int count = row * column;// 所有的迷宫单元数,不包括墙
-
lab = new int[count];
-
for (int i = 0; i < count; i )
-
lab[i] = 0;// 设置所有单元都为未访问过的,0表示未访问过,1表示已访问过
-
for (int v = 0; v < count; v ) {// 从第0个点开始遍历
-
if (lab[v] != 1) {// 如果该单元还未被访问,则递归调用深度优先算法遍历
-
DFS(lab, v);
-
}
-
}
-
}
-
-
-
// 使用DFS算法,借助递归思想访问某一顶点v,找v点附近且未被访问的点w,在找w附近未被访问的点(循环...),直到没有继续能找下去的点,
-
// 依次退回最近被访问的点,如果还有该顶点的其他邻居没有被访问,就从邻居点开始继续搜索,把相邻的部分格子打通
-
public void DFS(int[] LabG, int v) {
-
LabG[v] = 1;// 访问顶点
-
int[] neighbor = { v row, v - row, v - 1, v 1 };// 该点的四个邻居 上下左右
-
int[] offR = { 0, 0, -1, 1 }, offC = { 1, -1, 0, 0 };// Row上个方向的偏移 Column上各方向的偏移,上下左右
-
int[] tag = { -1, -1, -1, -1 };// 记录打通位置
-
int n = 0;// 打通的次数
-
while (n < 4) {// 上下左右四个方向都遍历,
-
int i = rand.nextInt(4);// 随机打通一个方向
-
if (tag[i] == 1)
-
continue;// 进入下一轮循环
-
tag[i] = 1;// 打通墙,设为1
-
n ;
-
int w = neighbor[i];// 定义一个该方向上的邻居
-
if (w > LabG.length - 1 || w < 0)
-
continue; // w不存在,即该方向上没有邻居
-
-
// 取出现在的v点的位置
-
int x = v % row;
-
int y = v / row;
-
-
// 遍历到四个边界时再往边界方向就没有邻居了,进入下一轮循环
-
if (i == 0 && y == column - 1)
-
continue;// 上方向
-
if (i == 1 && y == 0)
-
continue;// 下方向
-
if (i == 2 && x == 0)
-
continue;// 左方向
-
if (i == 3 && x == row - 1)
-
continue;// 右方向
-
-
// 如果该点有未访问的邻居,则把该点与其邻居间的墙打通,即相邻的格子中间的位置放1
-
if (LabG[w] == 0) {
-
LabId[2 * x 1 offR[i]][2 * y 1 offC[i]] = 1;
-
DFS(LabG, w);// 递归
-
}
-
}
-
}
-
}
-
import java.awt.Color;
-
import java.awt.Font;
-
import java.awt.Graphics;
-
import java.awt.event.KeyEvent;
-
import java.awt.event.KeyListener;
-
import java.awt.event.MouseEvent;
-
import java.awt.event.MouseListener;
-
import java.awt.image.BufferedImage;
-
import java.io.File;
-
import java.io.IOException;
-
import javax.imageio.ImageIO;
-
import javax.swing.JButton;
-
import javax.swing.JPanel;
-
-
public class Panel extends JPanel implements MouseListener, KeyListener{
-
-
Maze M = new Maze();//定义一个Maze类对象,生成地图
-
AStart A = new AStart();//定义一个AStart类,画出迷宫路径
-
-
private JPanel jp = new JPanel();
-
private JButton answer = new JButton("显示路径");
-
private JButton hide = new JButton("隐藏路径");
-
private JButton reset = new JButton("地图更新");
-
private JButton exit = new JButton("退出游戏");
-
private JButton start = new JButton("开始游戏");
-
BufferedImage wall = null;
-
BufferedImage bj = null;
-
BufferedImage victory = null;
-
BufferedImage my = null;
-
-
int myx = 1;// 定义角色横坐标并初始化
-
int myy = 1;// 定义角色纵坐标
-
int endx;// 定义终点横纵坐标
-
int endy;
-
boolean isStarted = false;
-
boolean isVictory = false;
-
boolean ans = false;// 用于显示路径
-
-
public Panel() throws IOException {
-
this.setName("小熊猫走迷宫");// 设置标题
-
this.setLayout(null);
-
answer.setBounds(470, 130, 90, 30);
-
hide.setBounds(470, 210, 90, 30);
-
reset.setBounds(470, 290, 90, 30);
-
exit.setBounds(470, 370, 90, 30);
-
start.setBounds(470, 450, 90, 30);
-
answer.addMouseListener(this);
-
hide.addMouseListener(this);
-
reset.addMouseListener(this);
-
exit.addMouseListener(this);
-
start.addMouseListener(this);
-
start.addKeyListener(this);
-
this.add(jp);
-
this.add(start);
-
this.add(answer);
-
this.add(hide);
-
this.add(reset);
-
this.add(exit);
-
wall = ImageIO.read(new File("images/Wall.png"));// 放墙的图片
-
bj = ImageIO.read(new File("images/bj.png"));// 放窗口背景图片
-
my = ImageIO.read(new File("images/my.png"));// 放小熊猫的图片
-
victory=ImageIO.read(new File("images/victory.jpg"));
-
try {
-
victory=ImageIO.read(new File("images/victory.jpg"));
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
-
// 画组件
-
public void paintComponent(Graphics g) {
-
super.paintComponent(g);
-
g.fillRect(20, 80, 420, 420);
-
g.drawImage(bj, 0, 0, this);
-
g.setColor(Color.green);
-
g.setFont(new Font("宋体", Font.CENTER_BASELINE, 40));
-
g.drawString("小狗走迷宫", 120, 50);
-
-
// 画迷宫
-
for (int i = 0; i < M.LabId.length; i ) {
-
for (int j = 0; j < M.LabId[0].length; j ) {
-
if (M.LabId[i][j] == 0) {
-
g.drawImage(wall, 20 i * 20, 80 j * 20, this);
-
}
-
}
-
}
-
-
// 画A*路径
-
if (ans) {
-
g.setColor(Color.green);
-
for (int i = 0; i < A.NODES.length; i ) {
-
for (int j = 0; j < A.NODES[0].length; j ) {
-
if (A.NODES[i][j] == 2) {
-
g.fillOval(20 20 * i, 80 20 * j, 18, 18);
-
}
-
}
-
}
-
}
-
-
g.setColor(Color.white);
-
g.fillRect(40, 100, 20, 20);// 画起点
-
g.fillRect(400, 460, 20, 20);// 画终点
-
g.drawImage(my, 20 20 * myx, 80 20 * myy, this);// 画角色
-
-
// 判断是否到达终点
-
if (this.myx ==19 && this.myy ==19) {
-
isVictory = true;
-
}
-
-
// 画游戏胜利界面
-
if (isVictory) {
-
g.drawImage(victory, 60,180, this);// 画角色
-
}
-
}
-
-
// 鼠标监听
-
-
public void mouseClicked(MouseEvent e) {
-
if (e.getSource().equals(answer)) {
-
ans = true;
-
isVictory=false;
-
}
-
if (e.getSource().equals(hide)) {
-
ans = false;
-
isVictory=false;
-
}
-
if (e.getSource().equals(reset)) {
-
ans = false;
-
isVictory=false;
-
myx = 1;
-
myy = 1;
-
try {
-
new Panel();
-
} catch (IOException ex) {
-
ex.printStackTrace();
-
}
-
}
-
if (e.getSource().equals(exit)) {
-
System.exit(0);
-
}
-
if (e.getSource().equals(start)) {
-
ans = false;
-
isVictory=false;
-
myx = 1;// 开始游戏,角色从起点开始出发
-
myy = 1;
-
}
-
repaint();
-
}
-
-
// 键盘监听
-
-
public void keyTyped(KeyEvent e) {
-
}
-
-
public void keyPressed(KeyEvent e) {
-
int k = e.getKeyCode();
-
if (k == KeyEvent.VK_SPACE) {
-
System.out.print("按下空格");
-
}
-
if (k == KeyEvent.VK_LEFT && A.NODES[myx - 1][myy] != 0 && myx - 1 >= 1) {
-
myx--;
-
}
-
if (k == KeyEvent.VK_RIGHT && A.NODES[myx 1][myy] != 0 && myx 1 <= 21) {
-
myx ;
-
}
-
if (k == KeyEvent.VK_UP && A.NODES[myx][myy - 1] != 0 && myy - 1 >= 1) {
-
myy--;
-
}
-
if (k == KeyEvent.VK_DOWN && A.NODES[myx][myy 1] != 0 && myy 1 <= 21) {
-
myy ;
-
}
-
repaint();
-
}
-
-
public void keyReleased(KeyEvent e) {
-
}
-
-
public void mousePressed(MouseEvent e) {
-
}
-
-
public void mouseReleased(MouseEvent e) {
-
}
-
-
public void mouseEntered(MouseEvent e) {
-
}
-
-
public void mouseExited(MouseEvent e) {
-
}
-
}
-
import java.awt.*;
-
import java.io.IOException;
-
import javax.swing.JFrame;
-
public class Test {
-
public static void main(String[] args) throws IOException {
-
JFrame frame = new JFrame();//新建窗口
-
int width = Toolkit.getDefaultToolkit().getScreenSize().width;// 取得屏幕宽度
-
int height = Toolkit.getDefaultToolkit().getScreenSize().height;// 取得屏幕高度
-
frame.setSize(600, 600);// 设置窗体大小
-
frame.setLocation((width - 600) / 2, (height - 600) / 2);// 设置窗体出现大小
-
frame.setResizable(false);// 设置窗体大小不可变
-
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 设置窗体关闭方式
-
frame.add(new Panel());
-
frame.setFocusable(true);//为屏幕添加焦点
-
frame.setVisible(true);// 设置窗体可视
-
frame.requestFocus();
-
}
-
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfhbeg
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13