基于A*算法的迷宫游戏开发——计算机软件实习(项目三)

基于A*算法的迷宫游戏开发

一、项目概述

1.1项目目标和主要内容

1)迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
2)要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。
3)设计交互友好的游戏图形界面。

1.2项目的主要功能

1)可以随机生成迷宫并使用A*算法求解迷宫
2)并未添加在行走路径上留下痕迹的功能

二、项目分析

1)深度优先遍历生成迷宫地图
2)搭建GUI迷宫地图
3)A*算法寻路

三、算法分析

3.1深度优先遍历初始化地图

主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底…,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。
如树的先序(中序,后序)遍历都是深度优先遍历

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);
            }
        }
    }

3.2深度优先搜索算法生成迷宫

(1)首先找到初始节点A,
(2)依此从A未被访问的邻接点出发,对图进行深度优先遍历
(3)若有节点未被访问,则回溯到该节点,继续进行深度优先遍历
(4)直到所有与顶点A路径想通的节点都被访问过一次,举个例子,在下方 的无向连通图中,假设我们要从起始点A出发,使用深度优先搜索算法进行搜索,首先访问A->B->E,走不通了,回溯到A起始点,走第二个 分支节点B,路径为A->C->F->H->G->D,走不通了,再回溯到起始点A,发现所有的节点都已经走过一次了,因此本次搜索结束。

在这里插入图片描述

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);// 递归
            }
        }
    }

A*算法寻路

1)建立开放和关闭列表

	private final ArrayList<Node> openList = new ArrayList<Node>();
    private final ArrayList<Node> closeList = new ArrayList<Node>();

2)把起点加入 open list

	openList.add(startNode);

3)遍历 open list ,查找 F值最小的节点,把它作为当前要处理的节点
从open list中移除
把这个节点移到 close list
遍历四个邻居
如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。
如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。如果你的 open list 是按 F 值排序的话,改变后你可能需要重新排序。
当你把终点加入到了 open list 中,此时路径已经找到了,或者
查找终点失败,并且 open list 是空的,此时没有路径。
保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径

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);
    }

四、绘制GUI

4.1绘图

public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.fillRect(20, 80, 420, 420);
        g.drawImage(bj, 0, 0, this);
        g.setColor(Color.blue);
        g.setFont(new Font("华文正楷", Font.BOLD, 40));
        g.drawString("迷宫游戏", 120, 50);

        // 画迷宫
        for (int i = 0; i < Maez.LabId.length; i++) {
            for (int j = 0; j < Maez.LabId[0].length; j++) {
                if (Maez.LabId[i][j] == 0) {
                    g.drawImage(wall, 20 + i * 20, 80 + j * 20, this);
                }
            }
        }

        // 画A*路径
        if (ans) {
            g.setColor(Color.pink);
            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.yellow);
        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);// 画角色
        }
    }

4.2监听

4.2.1 鼠标监听
@Override
    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;
            new Panel();
        }
        if (e.getSource().equals(start)) {
            ans = false;
            isVictory=false;
        }
        repaint();
    }
   

4.2.2键盘监听

@Override
    public void keyTyped(KeyEvent e) {
    }
    @Override
    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();
    }

五、项目源码

源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值