BFS

作者: 夏睡醒了秋 | 来源:发表于2019-11-30 19:19 被阅读0次

    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<StepInfo> 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<Point> 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<StepInfo> 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<StepInfo> bfs(LinkedList<Point> 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<PointDto> allList = new LinkedList<>();
            LinkedList<PointDto> parentList = new LinkedList<>();
            LinkedList<PointDto> 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<PointDto> 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<StepInfo> getDirList(PointDto pointDto){
            if(pointDto.getParent() == null){
                return null;
            }
            LinkedList<StepInfo> 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<PointDto> list,LinkedList<Point> 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

          本文链接:https://www.haomeiwen.com/subject/igoowctx.html