BFS

BFS-广度优先搜索-解决最短路径的算法之一。

  1. 什么是BFS
  2. BFS可以解决什么问题
  3. 使用BFS模拟最短路线
  4. 在游戏中使用

BFS是一种简便的图的搜索方法之一,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

例如我从(0,0)点到(3,3)点,沿着A路线需要6步,沿着B需要8步。目标简单,最短路径可一眼看出,假如路线复杂,添加障碍物,而且路线的选择交给机器(没有思想),如果有一个固定的算法规律来指引,就可以给出路线来参考。


image.png

BFS搜寻的大致思路:
最先找到原则,举一个钓鱼的例子,假如鱼漂是路径的目的地,这时你向河水投入一枚石子,假如石子的位置为路径的起点。石子到鱼漂的最短路径就是石子荡起的波纹触碰到鱼漂的位置。BFS图搜寻的思路大致类似,以起点为开始向四处扩散,直到找到目标。


image.png

BFS可以解决两类问题:

  1. 从A出发有前往B的路径吗?(波纹扩散到边界没有触碰到鱼漂,则没有)
  2. 从A出发前往B的那条路径最短?(最先触碰到鱼漂的波纹路径最短)

使用BFS算法模拟最短路径。

需要用到最短路径的场景很多,
比如:我想找到一名微信好友,经过哪些好友的推荐可以快速找到?

  1. 我发现我有这个好友, 那就0步找到。
  2. 我发现通过我直接好友的推荐可以找到,那就一步可以找到。
  3. 我发现通过我直接好友的直接好友可以找到,那就两步找到。
  4. ... ...
  5. 找到后,再通过一步一步回溯就可以找到该最短路径。
    如下,我通过好友的好友的好友找到了D1好友,然后通过D1 --> A的回溯找到最近。(A - B1 - C2 - D1)
    image.png

在游戏中使用
吃到食物的最短路径:
部分代码如下:

private void autoV3(){
        /**
         *  TODO 未完成
         *
         * 1.可以找到吃食物的路线
         *      1.吃到食物后,可以找到自己的尾巴
         *          移动一步
         *      2.吃到食物后,找不到自己的尾巴
         *           沿最远路径想尾巴移动一步
         * 2. 找不到食物的路线
         *      1. 可以找到尾巴
         *          沿着最远向尾部移动一步
         *      2. 找不到尾巴
         *          按存活时间最长移动一步
         */
        // 将snake位置放到list中,用于调用bfs
        snakeList.addFirst(new Point(snakeX[0],snakeY[0]));
        for(int i = 1;i< len;i++){
            snakeList.addLast(new Point(snakeX[i],snakeY[i]));
        }
        // 路线
        LinkedList dirList = sdfMinStep.bfs(snakeList,new Point(pointX,pointY));
        System.out.println("计算路线:"+dirList);
        if(dirList == null){
//            setGameStat("AI AM LOST");
//            return;
            dirList = sdfMaxStep.bfs(snakeList,snakeList.getLast());
            if(dirList == null){
                setGameStat("AI AM LOST");
                return;
            }
        }
        System.out.println("计算步数:"+dirList.size());
        // 先按照路线模拟走一遍,
        // 模拟snake数据
        LinkedList tempSnakeList = new LinkedList<>(snakeList);
        // 计算模拟之后的snake位置
        int stepNum = dirList.size();
        for(int i = 0;i < stepNum;i++){
            tempSnakeList.addFirst(dirList.get(i).getPoint());
            if(i != stepNum - 1){
                tempSnakeList.removeLast();
            }
        }
        System.out.println("模拟吃到食物后的snake位置:"+tempSnakeList);
        // 计算模拟之后的snake是可以走到snake尾
        LinkedList tempDirList = sdfMinStep.bfs(tempSnakeList,tempSnakeList.getLast());
        System.out.println("模拟的snake到snake尾的路径:"+tempDirList);
        if(tempDirList == null){
            // 向snake尾走一步 TODO 按最远走
            // 更新路线
            dirList = sdfMaxStep.bfs(snakeList,snakeList.getLast());
            System.out.println("向snake尾走一步:"+dirList);
            if(dirList == null){
                setGameStat("AI AM LOST");
                return;
            }
        }
        System.out.println("模拟的snake到snake尾的路径:"+tempDirList.size());
        snakeList.clear();
        System.out.println("准备路线:"+dirList);
        System.out.println("执行步数:"+dirList.size());
        if(dirList.size() > 0){
            System.out.println("执行方向:"+dirList.getFirst());
            move(dirList.getFirst().getDir());
        }
    }
// 由于不想修改snake.move方法
    // 预返回一条带方向的list,便于调用
    public LinkedList bfs(LinkedList snakeList, Point targetPoint){
        if(snakeList.size() < 1){
            return null;
        }
//        snakeList.removeLast();
        Point startPoint = snakeList.getFirst();
        if(isTarget(startPoint,targetPoint)){
            return null;
        }
        // 用于存储到达目标时的信息
        PointDto tempTargetPoint = null;
        int step = 0;
        LinkedList allList = new LinkedList<>();
        LinkedList parentList = new LinkedList<>();
        LinkedList childList = new LinkedList<>();
        PointDto pointDto = new PointDto(startPoint,null,0);

        allList.add(pointDto);
        parentList.add(pointDto);
        childList.add(pointDto);
        while (!parentList.isEmpty()){
            step ++;
            parentList.clear();
            parentList.addAll(childList);
            childList.clear();
            A:for (PointDto p:parentList) {
                // 1. 边界,终点,
                // 2. 已存在
                Point thisPoint = p.getPoint();
                // 上 下 左 右
                PointDto pointU = new PointDto(thisPoint.getX(),thisPoint.getY() - 1,step,"U");
                PointDto pointD = new PointDto(thisPoint.getX(),thisPoint.getY() + 1,step,"D");
                PointDto pointL = new PointDto(thisPoint.getX() - 1,thisPoint.getY(),step,"L");
                PointDto pointR = new PointDto(thisPoint.getX() + 1,thisPoint.getY(),step,"R");
                List udlrPointList = new LinkedList<>();
                udlrPointList.add(pointD);
                udlrPointList.add(pointL);
                udlrPointList.add(pointR);
                udlrPointList.add(pointU);
                for (PointDto pointUDLR:udlrPointList) {
                    if(isSuitPoint(pointUDLR,allList,snakeList)){
                        pointUDLR.setParent(p);
                        childList.add(pointUDLR);
                        allList.add(pointUDLR);
                        if(isTarget(pointUDLR.getPoint(),targetPoint)){
                            tempTargetPoint = pointUDLR;
                            allList.removeLast();
                            childList.removeLast();
                        }
                    }
                }
            }
        }
        System.out.println("长度:"+snakeList.size());
        System.out.println("位置:"+snakeList);
        if(allList.size() < 1){
            return null;
        }
        if(tempTargetPoint == null){
            return null;
        }
        System.out.println("计算所有位置:"+allList.getLast());
        System.out.println(tempTargetPoint);
        return getDirList(tempTargetPoint);
    }

    /**
     * 方向路线
     */
    private LinkedList getDirList(PointDto pointDto){
        if(pointDto.getParent() == null){
            return null;
        }
        LinkedList dirList = new LinkedList<>();
        while(pointDto.getParent() != null){
            dirList.addFirst(new StepInfo(pointDto.getDir(),pointDto.getPoint()));
            pointDto = pointDto.getParent();
        }
        return dirList;
    }
    /**
     * 是否终点
     * @return
     */
    private boolean isTarget(Point point,Point targetPoint){
        return point.equals(targetPoint);
    }

    /**
     * 判断是否节点合适
     * @param list
     * @return
     */
    private boolean isSuitPoint(PointDto PointUDLR,List list,LinkedList snakeList) {
        // 界限内
        Point point = PointUDLR.getPoint();
        if(0 <= point.getX() && point.getX() < mapWidth && 0 <= point.getY() && point.getY() < mapHeight){
            // 阻碍物
            for(Point snakePoint : snakeList){
                if(snakePoint.equals(point)){
                    return false;
                }
            }
            // 没有被统计过
            for(PointDto pointDto:list){
                if(pointDto.getPoint().equals(point)){
                    return false;
                }
            }
            return true;
        }
        return false;
    }

效果图:

image.png

GIF.gif

你可能感兴趣的:(BFS)