实验内容
(1)迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
(2)要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示;
(3)设计交互友好的游戏图形界面。
实验平台
通过高级程序设计语言(JAVA)实现。
相关知识
①JAVA Swing GUI图形界面窗口开发
Swing是JAVA为图形界面应用开发提供的一组工具包,是JAVA基础类的一部分。它包含了构建图形界面(GUI)的各种组件,如:窗口、标签、按钮、文本框等。
②深度优先算法(DFS)
深度优先遍历图的方法是,从图中某顶点v出发:
a.访问顶点v;
b.依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
c.若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。
③A*算法
(1)把起点加入 open list 。
(2) 重复如下过程:
a. 遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。
b. 把这个节点移到 close list 。
c. 对当前方格的 84个相邻方格的每一个方格
◆ 如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。
◆ 如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。
◆ 如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更 好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。如果你的 open list 是按 F 值排序的话,改变后你可能需要重新排序。
d. 停止,当你
◆ 把终点加入到了 open list 中,此时路径已经找到了,或者
◆ 查找终点失败,并且 open list 是空的,此时没有路径。
(3)保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。
具体步骤
①显示迷宫的图形界面
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i <= NUM; i++) {
g.drawLine(padding + i * width, padding, padding + i * width, padding + NUM * width);
}
for (int j = 0; j <= NUM; j++) {
g.drawLine(padding, padding + j * width, padding + NUM * width, padding + j * width);
}
g.setColor(this.getBackground());
for (int i = NUM - 1; i >= 0; i--) {
for (int j = NUM - 1; j >= 0; j--) {
Lattice f = maze[i][j].getF();
if (f != null) {
int fx = f.getX(), fy = f.getY();
clearFence(i, j, fx, fy, g);
}
}
}
g.drawLine(padding, padding + 1, padding, padding + width - 1);
int last = padding + NUM * width;
g.drawLine(last, last - 1, last, last - width + 1);
g.setColor(Color.red);
g.fillOval(getCenterX(ballY) - width / 3, getCenterY(ballX) - width / 3, width / 2, width / 2);
if (drawPath == true)
drawPath(g);
}
private void drawPath(Graphics g) {
Color PATH_COLOR = Color.red, BOTH_PATH_COLOR = this.getBackground();
if (drawPath == true)
g.setColor(PATH_COLOR);
else
g.setColor(this.getBackground());
Lattice p = maze[NUM - 1][NUM - 1];
while (p.getF() != null) {
p.setFlag(2);
p = p.getF();
}
g.fillOval(getCenterX(p) - width / 3, getCenterY(p) - width / 3, width / 2, width / 2);
p = maze[ballX][ballY];
while (p.getF() != null) {
if (p.getFlag() == 2) {
p.setFlag(3);
g.setColor(BOTH_PATH_COLOR);
}
g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getF()), getCenterY(p.getF()));
p = p.getF();
}
g.setColor(PATH_COLOR);
p = maze[NUM - 1][NUM - 1];
while (p.getF() != null) {
if (p.getFlag() == 3)
break;
g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getF()), getCenterY(p.getF()));
p = p.getF();
}
}
②生成随机的迷宫
private void createMaze() {
Random random = new Random();
int x = 0;
int y = 0;
Stack<Lattice> s = new Stack<Lattice>();
Lattice p = maze[x][y];
Lattice neis[] = null;
s.push(p);
while (!s.isEmpty()) {
p = s.pop();
p.setFlag(Lattice.YES);
neis = getNeis(p);
int ran = Math.abs(random.nextInt()) %
4;
for (int a = 0; a <= 3; a++) {
ran++;
ran %= 4;
if (neis[ran] == null || neis[ran].getFlag() == Lattice.YES)
continue;
s.push(neis[ran]);
neis[ran].setF(p);
}
}
}
③移动玩家走迷宫
private void move(int c) {
int tx = ballX, ty = ballY;
switch (c) {
case KeyEvent.VK_LEFT:
ty--;
break;
case KeyEvent.VK_RIGHT:
ty++;
break;
case KeyEvent.VK_UP:
tx--;
break;
case KeyEvent.VK_DOWN:
tx++;
break;
case KeyEvent.VK_SPACE:
if (drawPath == true) {
drawPath = false;
} else {
drawPath = true;
}
break;
default:
}
if (!isOutOfBorder(tx, ty)
&& (maze[tx][ty].getF() == maze[ballX][ballY] || maze[ballX][ballY].getF() == maze[tx][ty])) {
ballX = tx;
ballY = ty;
}
}
private void setKeyListener() {
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
move(c);
repaint();
checkWin();
}
});
}
④用深度优先算法求解迷宫(A*不太会)
private void init() {
for (int i = 0; i <= NUM - 1; i++)
for (int j = 0; j <= NUM - 1; j++) {
maze[i][j].setF(null);
maze[i][j].setFlag(Lattice.NO);
}
ballX = 0;
ballY = 0;
drawPath = false;
createMaze();
this.setFocusable(true);
repaint();
}
private void checkWin() {
if (ballX == NUM - 1 && ballY == NUM - 1) {
JOptionPane.showMessageDialog(null, "YOU WIN !", "你走出了迷宫。", JOptionPane.PLAIN_MESSAGE);
init();
}
}
private Lattice[] getNeis(Lattice p) {
final int[] adds = { -1, 0, 1, 0, -1 };// 顺序为上右下左
if (isOutOfBorder(p)) {
return null;
}
Lattice[] ps = new Lattice[4];// 顺序为上右下左
int xt;
int yt;
for (int i = 0; i <= 3; i++) {
xt = p.getX() + adds[i];
yt = p.getY() + adds[i + 1];
if (isOutOfBorder(xt, yt))
continue;
ps[i] = maze[xt][yt];
}
return ps;
}