package com.zy.common.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.core.common.SpringUtils; import com.core.exception.CoolException; import com.zy.asrs.entity.BasMap; import com.zy.asrs.service.BasMapService; import com.zy.common.model.NavigateNode; import com.zy.core.enums.MapNodeType; import java.util.*; /** * A*算法实现类 */ public class NavigateSolution { // Open表用优先队列 public PriorityQueue Open = new PriorityQueue(); //Close表用普通的数组 public ArrayList Close = new ArrayList(); //用来存放已经出现过的结点。 Map bestGMap = new HashMap<>(); public List> getStationMap() { BasMapService basMapService = SpringUtils.getBean(BasMapService.class); BasMap basMap = basMapService.selectOne(new EntityWrapper().eq("lev", 1)); if (basMap == null) { throw new CoolException("地图不存在"); } List> dataList = JSON.parseObject(basMap.getData(), List.class); List> navigateNodeList = new ArrayList<>(); for (int i = 0; i < dataList.size(); i++) { List row = dataList.get(i); List navigateNodeRow = new ArrayList<>(); for (int j = 0; j < row.size(); j++) { JSONObject map = row.get(j); NavigateNode navigateNode = new NavigateNode(i, j); String nodeType = map.getString("type"); if(nodeType == null) { navigateNode.setValue(MapNodeType.DISABLE.id); }else if(nodeType.equals("devp") || nodeType.equals("merge")){ navigateNode.setValue(MapNodeType.NORMAL_PATH.id); JSONObject valueObj = JSON.parseObject(map.getString("value")); List directionList = valueObj.getJSONArray("direction").toJavaList(String.class); navigateNode.setDirectionList(directionList); }else { navigateNode.setValue(MapNodeType.DISABLE.id); } navigateNode.setNodeType(nodeType); navigateNode.setNodeValue(map.getString("value")); navigateNodeRow.add(navigateNode); } navigateNodeList.add(navigateNodeRow); } return navigateNodeList; } public NavigateNode astarSearchJava(List> map, NavigateNode start, NavigateNode end) { // 清理上次搜索的状态,防止复用实例导致记录残留 this.Open.clear(); this.Close.clear(); this.bestGMap.clear(); //把第一个开始的结点加入到Open表中 this.Open.add(start); //主循环 while (Open.size() > 0) { //取优先队列顶部元素并且把这个元素从Open表中删除 NavigateNode current_node = Open.poll(); if (current_node.getX() == end.getX() && current_node.getY() == end.getY()) {//找到目标结点就返回 return current_node; } //将这个结点加入到Close表中 Close.add(current_node); //对当前结点进行扩展,得到一个四周结点的数组 ArrayList neighbour_node = extend_current_node(map, current_node); //对这个结点遍历,看是否有目标结点出现 for (NavigateNode node : neighbour_node) { // 已在关闭列表中的节点不再处理,避免父链反复改写形成环 if (Close.contains(node)) { continue; } // G + H + E (对启发函数增加去拐点方案calcNodeExtraCost) int gCost = calcNodeExtraCost(current_node, node, end); //进行计算对G, F, H 等值 node.setG(1 + gCost); node.init_node(current_node, end); node.setH(calcNodeCost(node, end)); node.setF(node.getG() + node.getH()); String key = node.getX() + "_" + node.getY(); Integer recordedG = bestGMap.get(key); // 仅当找到更小的代价时才更新与入Open,避免等价代价反复入队导致父链抖动 if (recordedG == null || node.getG() < recordedG) { bestGMap.put(key, node.getG()); Open.add(node); } } } //如果遍历完所有出现的结点都没有找到最终的结点,返回null return null; } public ArrayList extend_current_node(List> map, NavigateNode current_node) { //获取当前结点的x, y int x = current_node.getX(); int y = current_node.getY(); //如果当前结点的邻结点合法,就加入到neighbour_node ArrayList neighbour_node = new ArrayList(); if(current_node.getValue() == MapNodeType.DISABLE.id) { return neighbour_node; } List directionList = current_node.getDirectionList(); if(directionList == null || directionList.size() == 0) { return neighbour_node; } for(String direction : directionList) { if(direction.equals("top")) { NavigateNode node = getValidNavigateNode(map, x - 1, y); if(node != null) { neighbour_node.add(node); } }else if(direction.equals("bottom")) { NavigateNode node = getValidNavigateNode(map, x + 1, y); if(node != null) { neighbour_node.add(node); } }else if(direction.equals("left")) { NavigateNode node = getValidNavigateNode(map, x, y - 1); if(node != null) { neighbour_node.add(node); } }else if(direction.equals("right")) { NavigateNode node = getValidNavigateNode(map, x, y + 1); if(node != null) { neighbour_node.add(node); } } } return neighbour_node; } public NavigateNode getValidNavigateNode(List> map, int x, int y) { if (x < 0 || x >= map.size() || y < 0 || y >= map.get(0).size()) { return null; } NavigateNode node = map.get(x).get(y); if(node.getValue() == MapNodeType.DISABLE.id) { return null; } return node; } public NavigateNode findStationNavigateNode(List> map, int stationId) { for(int x = 0; x < map.size(); x++) { for(int y = 0; y < map.get(0).size(); y++) { NavigateNode node = map.get(x).get(y); if("devp".equals(node.getNodeType())) { JSONObject valueObj = JSON.parseObject(node.getNodeValue()); if(valueObj.getInteger("stationId") == stationId) { return node; } } } } return null; } //------------------A*启发函数------------------// //计算通过现在的结点的位置和最终结点的位置计算H值(曼哈顿法:坐标分别取差值相加) private int calcNodeCost(NavigateNode node1, NavigateNode node2) { return Math.abs(node2.getX() - node1.getX()) + Math.abs(node2.getY() - node1.getY()); } //去除拐点算法,给直线增加优先级 private int calcNodeExtraCost(NavigateNode currNode, NavigateNode nextNode, NavigateNode endNode) { // 第一个点或直线点 if (currNode.getFather() == null || nextNode.getX() == currNode.getFather().getX() || nextNode.getY() == currNode.getFather().getY()) { return 0; } // 拐向终点的点 if (nextNode.getX() == endNode.getX() || nextNode.getY() == endNode.getY()) { return 1; } // 普通拐点 /* 拐点判断逻辑 拿到父节点和下一节点 通过判断父节点和下一节点的x数据和y数据都不相同时,则表明当前坐标是一个拐点 */ return 1; } //------------------A*启发函数-end------------------// }