启发式寻路

ub

启发式寻路代码

输入起点和终点,在地图中查找到最短路径

github代码:https://github.com/chengdongba/SearchRoad.git

整体思路:

AStart.java
从起点开始,向起点周围8个方向(上下左右,斜上下左右)搜索可以走的格子(Node),用开放列表openList存放,
从开放列表中找出离终点最近的点,放入关闭列表(closelist),然后从这个点继续向周围8个方向扩张,直到到达终点,路径就是closeList里面的点相连

地图绘制

ShowMapView.java
继承View重写onTouchEvent和onDraw方法
在onTouchEvent中实现选择起点和终点的绘制
用一个flag计数器,点一下就++,0表示还没有选,1表示选了起点,2表示选的终点

在onDraw里面实现地图的绘制
地图用二维数组建模
0表示草地
1表示墙
2表示路径
根据二维数组中的数据来绘制地图,用三张图片分别表示草地,墙和路径
注意: 二维数组和屏幕坐标系是相反的,二维数组的x轴上的数据表示的是列,即屏幕坐标系的y轴

package com.dqchen.www.searchroad;

import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.dqchen.www.searchroad.map_util.MapInfo;
import com.dqchen.www.searchroad.map_util.MapUtils;
import com.dqchen.www.searchroad.map_util.Node;
import com.dqchen.www.searchroad.map_util.ShowMapView;

import static com.dqchen.www.searchroad.map_util.MapUtils.map;
import static com.dqchen.www.searchroad.map_util.MapUtils.printMap;
import static com.dqchen.www.searchroad.map_util.MapUtils.touchFlag;

public class MainActivity extends AppCompatActivity {

    ShowMapView showMapView;
    Handler handler = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        handler = new Handler(Looper.getMainLooper());

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        showMapView = findViewById(R.id.show);

    }

    public void cal(View view) {
        MapInfo mapInfo = new MapInfo(map, map[0].length, map.length, new Node(MapUtils.startCol, MapUtils.startRow), new Node(MapUtils.endCol, MapUtils.endRow));
        Log.i("dechen","start="+MapUtils.startCol+" "+MapUtils.startRow);
        Log.i("dechen","end="+MapUtils.endCol+" "+MapUtils.endRow);
        new Astar().start(mapInfo);
        new MoveThread(showMapView).start();
    }

    public void reset(View view) {
        MapUtils.path = null;
        MapUtils.result.clear();
        MapUtils.touchFlag = 0;
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                if (map[i][j]==2){
                    map[i][j]=0;
                }
            }
        }
        showMapView.invalidate();
    }

    class MoveThread extends Thread{
        ShowMapView showMapView;
        public MoveThread(ShowMapView showMapView){
            this.showMapView = showMapView;
        }

        @Override
        public void run() {
            while (MapUtils.result.size()>0){
                Log.i("dqchen",MapUtils.result.size()+" ");
                Node node = MapUtils.result.pop();
                map[node.coord.y][node.coord.x] = 2;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        showMapView.invalidate();
                    }
                });

                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                map[node.coord.y][node.coord.x] = 0;
            }
            touchFlag = 0;
        }
    }
}

package com.dqchen.www.searchroad;

import android.graphics.Path;

import com.dqchen.www.searchroad.map_util.Coord;
import com.dqchen.www.searchroad.map_util.MapInfo;
import com.dqchen.www.searchroad.map_util.MapUtils;
import com.dqchen.www.searchroad.map_util.Node;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * Created by Jett on 2018/10/31.
 */

class Astar {
    public final static int DIRECT_VALUE = 10;
    public final static int OBLIQUE_VALUE = 14;
    //开放列表  白格子
    private Queue<Node> openList = new PriorityQueue<Node>();
    //关闭列表  黄格子
    private List<Node> closeList = new ArrayList<Node>();

    /**
     * 计算H值
     */
    private int calcH(Coord end, Coord coord) {
        return Math.abs(end.x - coord.x) + Math.abs(end.y - coord.y);
    }

    /**
     * 判断是否为终点
     */
    private boolean isEndNode(Coord end, Coord coord) {
        return coord != null && end.equals(coord);
    }

    /**
     * 从open中查找
     */
    private Node findNodeInOpen(Coord coord) {
        if (coord == null || openList.isEmpty()) return null;
        for (Node node : openList) {
            if (node.coord.equals(coord)) {
                return node;
            }
        }
        return null;
    }

    /**
     * 判断是否在close中
     */
    private boolean isCoordInClose(Coord coord) {
        return coord != null && isCoordInClose(coord.x, coord.y);
    }

    private boolean isCoordInClose(int x, int y) {
        if (closeList.isEmpty()) return false;
        for (Node node : closeList) {
            if (node.coord.x == x && node.coord.y == y) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断一个位置是否可以选择
     */
    private boolean canAddNodeToOpen(MapInfo mapInfo, int x, int y) {
        //是否在地图中
        if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.height) return false;
        //判断是否能过
        if (mapInfo.map[y][x] == MapUtils.WALL) return false;
        //判断是否已经被 选择过的
        if (isCoordInClose(x, y)) return false;
        return true;
    }

    /**
     * 算法开始
     */
    public void start(MapInfo mapInfo) {
        if (mapInfo == null) return;
        //清空以前的所有的内容
        openList.clear();
        closeList.clear();
        //开始搜索
        openList.add(mapInfo.start);
        //开始向周围扩展
        moveNodes(mapInfo);
    }

    private void moveNodes(MapInfo mapInfo) {
        while (!openList.isEmpty()) {
            if (isCoordInClose(mapInfo.end.coord)) {//如果已经是终点了
                //统计结果
                calcPath(mapInfo.map, mapInfo.end);
            }
            //把open中最小的一个取出来 ,放到close中
            Node current = openList.poll();
            closeList.add(current);
            //开始扩展
            addNeighborNodeInOpen(mapInfo, current);
        }
    }

    private void calcPath(int[][] map, Node end) {
        MapUtils.path = new Path();
        if (end != null) {
            MapUtils.path.moveTo(end.coord.x * 80 + 40, end.coord.y * 80 + 40);
        }
        while (end != null) {//把结果入栈
            MapUtils.path.lineTo(end.coord.x * 80 + 40, end.coord.y * 80 + 40);
            MapUtils.result.push(end);
            end = end.parent;
        }
    }

    private void addNeighborNodeInOpen(MapInfo mapInfo, Node current) {
        int x = current.coord.x;
        int y = current.coord.y;
        addNeighborNodeInOpen(mapInfo, current, x - 1, y, DIRECT_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x, y - 1, DIRECT_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x + 1, y, DIRECT_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x, y + 1, DIRECT_VALUE);

        addNeighborNodeInOpen(mapInfo, current, x + 1, y + 1, OBLIQUE_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x - 1, y - 1, OBLIQUE_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x + 1, y - 1, OBLIQUE_VALUE);
        addNeighborNodeInOpen(mapInfo, current, x - 1, y + 1, OBLIQUE_VALUE);
    }

    /**
     * 核心移动功能
     */
    private void addNeighborNodeInOpen(MapInfo mapInfo, Node current, int x, int y, int directValue) {
        if (canAddNodeToOpen(mapInfo, x, y)) {//是否可以通过
            Node end = mapInfo.end;//地图的终点
            Coord coord = new Coord(x, y);//需要填入的位置
            int g = current.g + directValue;
            Node child = findNodeInOpen(coord);
            if (child == null) {//如果能填入数字
                int h = calcH(end.coord, coord);
                if (isEndNode(end.coord, coord)) {//如果到了终点
                    child = end;
                    child.parent = current;
                    child.g = g;
                    child.h = h;
                } else {//否则
                    child = new Node(coord, current, g, h);
                }
                openList.add(child);
            } else {
                //如果已经填过的数据,不用理会
            }

        }
    }
}
package com.dqchen.www.searchroad.map_util;

/**
 * Created by Administrator on 2019/2/16.
 * 地图上坐标点的实例类
 */

public class Coord {

    public int x;
    public int y;

    public Coord(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (obj instanceof Coord) {
            Coord o = (Coord) obj;
            return x == o.x && y == o.y;
        } else {
            return false;
        }
    }
}

package com.dqchen.www.searchroad.map_util;

import android.util.Log;

/**
 * Created by Administrator on 2019/2/16.
 *
 * 地图实例类
 *
 * int[][] map : 二维数组地图
 * int width : 宽度
 * int height : 高度
 * Node start : 起点
 * Node end : 终点
 *
 */

public class MapInfo {

    public int[][] map;
    public int width;
    public int height;
    public Node start;
    public Node end;

    public MapInfo(int[][] map, int width, int height, Node start, Node end) {
        this.map = map;
        this.width = width;
        this.height = height;
        this.start = start;
        this.end = end;
        Log.i("dqchen","地图初始化成功");
    }
}

package com.dqchen.www.searchroad.map_util;

import android.graphics.Path;

import java.util.Stack;

/**
 * Created by Administrator on 2019/2/16.
 *
 * 地图模型类
 *
 */

public class MapUtils {

    public static int startRow = 0;
    public static int startCol = 0;
    public static int endRow = 0;
    public static int endCol = 0;
    public static int touchFlag = 0;
    public static Stack<Node> result = new Stack<>();
    public static Path path;

    public final static int WALL = 1; //  障碍
    public final static int PATH = 2; // 路径


    public static int[][] map = {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1},
            {0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
            {1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
            {1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
            {0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
            {1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1},
            {0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1},
            {0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1},
            {1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1},
            {1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1},
            {0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0},
            {1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1},
            {1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1},
            {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1}
    };

    public static int mapRowSize = map.length;
    public static int mapColSize = map[0].length;


    /**
     * 打印地图
     */
    public static void printMap(int[][] maps) {
        for (int i = 0; i < maps.length; i++) {
            for (int j = 0; j < maps[i].length; j++) {
                System.out.print(maps[i][j] + " ");
            }
            System.out.println();
        }
    }

}

package com.dqchen.www.searchroad.map_util;

import android.support.annotation.NonNull;

/**
 * Created by Administrator on 2019/2/16.
 * 路径结点实例类
 * <p>
 * Coord coord: 坐标
 * Node parent : 父节点
 * int g : 起点到当前节点的实际代价
 * int h : 当前节点到终点的预估代价
 */

public class Node implements Comparable<Node> {

    public Coord coord;
    public Node parent;
    public int g;
    public int h;

    public Node(int x, int y) {
        this.coord = new Coord(x, y);
    }

    public Node(Coord coord, Node parent, int g, int h) {
        this.coord = coord;
        this.parent = parent;
        this.g = g;
        this.h = h;
    }

    @Override
    public int compareTo(@NonNull Node o) {
        if (o == null) {
            return -1;
        } else {
            if (o.g + o.h > g + h) {
                return -1;
            } else if (o.g + o.h < g + h) {
                return 1;
            } else {
                return 0;
            }
        }
    }

}

package com.dqchen.www.searchroad.map_util;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.dqchen.www.searchroad.R;

/**
 * Created by Administrator on 2019/2/16.
 *
 * 地图绘制实例类
 *
 */

public class ShowMapView extends View {
    public ShowMapView(Context context) {
        super(context);
    }

    public ShowMapView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ShowMapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ShowMapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        float x = event.getX();
        float y = event.getY();

        //地图每格大小为80*80,屏幕x,y和数组x,y是相反的

        int row = (int) (y/80);
        int col = (int) (x/80);

        if (MapUtils.map[row][col]==0){
            MapUtils.touchFlag++;
            if (MapUtils.touchFlag==1){
                MapUtils.startRow = row;
                MapUtils.startCol = col;
                MapUtils.map[row][col] = 2;
            }else if (MapUtils.touchFlag==2){
                MapUtils.endRow = row;
                MapUtils.endCol = col;
                MapUtils.map[row][col] = 2;
            }
        }

        this.invalidate();

        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLUE);

        for (int i = 0; i < MapUtils.map.length; i++) {
            for (int j = 0; j < MapUtils.map[i].length; j++) {
                if (MapUtils.map[i][j]==0){
                    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.route);
                    canvas.drawBitmap(bitmap,j*80,i*80,paint);
                }else if (MapUtils.map[i][j]==1){
                    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),R.drawable.wall);
                    canvas.drawBitmap(bitmap,j*80,i*80,paint);
                }else if (MapUtils.map[i][j]==2){
                    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),R.drawable.path);
                    canvas.drawBitmap(bitmap,j*80,i*80,paint);
                }
            }
        }

        if (MapUtils.path!=null){
            canvas.drawPath(MapUtils.path,paint);
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容