001 package com.croftsoft.core.ai.astar; 002 003 import java.util.*; 004 005 import com.croftsoft.core.lang.NullArgumentException; 006 007 /********************************************************************* 008 * The A* algorithm. 009 * 010 * @version 011 * 2003-05-09 012 * @since 013 * 2002-04-21 014 * @author 015 * <a href="https://www.croftsoft.com/">David Wallace Croft</a> 016 *********************************************************************/ 017 018 public final class AStar 019 ////////////////////////////////////////////////////////////////////// 020 ////////////////////////////////////////////////////////////////////// 021 { 022 023 private final Cartographer cartographer; 024 025 private final List openNodeInfoSortedList; 026 027 private final Map nodeToNodeInfoMap; 028 029 // 030 031 private NodeInfo bestNodeInfo; 032 033 private double bestTotalCost; 034 035 private NodeInfo goalNodeInfo; 036 037 private boolean listEmpty; 038 039 ////////////////////////////////////////////////////////////////////// 040 ////////////////////////////////////////////////////////////////////// 041 042 public AStar ( Cartographer cartographer ) 043 ////////////////////////////////////////////////////////////////////// 044 { 045 NullArgumentException.check ( this.cartographer = cartographer ); 046 047 nodeToNodeInfoMap = new HashMap ( ); 048 049 openNodeInfoSortedList = new LinkedList ( ); 050 } 051 052 ////////////////////////////////////////////////////////////////////// 053 ////////////////////////////////////////////////////////////////////// 054 055 public boolean isGoalFound ( ) { return goalNodeInfo != null; } 056 057 public boolean isListEmpty ( ) { return listEmpty; } 058 059 public Iterator getPath ( ) 060 ////////////////////////////////////////////////////////////////////// 061 { 062 List pathList = new LinkedList ( ); 063 064 NodeInfo nodeInfo = goalNodeInfo; 065 066 if ( nodeInfo == null ) 067 { 068 nodeInfo = bestNodeInfo; 069 } 070 071 while ( nodeInfo != null ) 072 { 073 NodeInfo parentNodeInfo = nodeInfo.getParentNodeInfo ( ); 074 075 if ( parentNodeInfo != null ) 076 { 077 pathList.add ( 0, nodeInfo.getNode ( ) ); 078 } 079 080 nodeInfo = parentNodeInfo; 081 } 082 083 return pathList.iterator ( ); 084 } 085 086 public Object getFirstStep ( ) 087 ////////////////////////////////////////////////////////////////////// 088 { 089 NodeInfo nodeInfo = goalNodeInfo; 090 091 if ( nodeInfo == null ) 092 { 093 nodeInfo = bestNodeInfo; 094 } 095 096 Object node = null; 097 098 while ( nodeInfo != null ) 099 { 100 NodeInfo parentNodeInfo = nodeInfo.getParentNodeInfo ( ); 101 102 if ( parentNodeInfo != null ) 103 { 104 node = nodeInfo.getNode ( ); 105 } 106 107 nodeInfo = parentNodeInfo; 108 } 109 110 return node; 111 } 112 113 ////////////////////////////////////////////////////////////////////// 114 ////////////////////////////////////////////////////////////////////// 115 116 public void reset ( Object startNode ) 117 ////////////////////////////////////////////////////////////////////// 118 { 119 goalNodeInfo = null; 120 121 listEmpty = false; 122 123 openNodeInfoSortedList.clear ( ); 124 125 nodeToNodeInfoMap.clear ( ); 126 127 NodeInfo nodeInfo = new NodeInfo ( startNode ); 128 129 nodeToNodeInfoMap.put ( startNode, nodeInfo ); 130 131 openNodeInfoSortedList.add ( nodeInfo ); 132 133 bestTotalCost = Double.POSITIVE_INFINITY; 134 } 135 136 ////////////////////////////////////////////////////////////////////// 137 ////////////////////////////////////////////////////////////////////// 138 139 public boolean loop ( ) 140 ////////////////////////////////////////////////////////////////////// 141 { 142 if ( openNodeInfoSortedList.isEmpty ( ) ) 143 { 144 listEmpty = true; 145 146 return false; 147 } 148 149 NodeInfo nodeInfo 150 = ( NodeInfo ) openNodeInfoSortedList.remove ( 0 ); 151 152 Object node = nodeInfo.getNode ( ); 153 154 if ( cartographer.isGoalNode ( node ) ) 155 { 156 if ( ( goalNodeInfo == null ) 157 || ( nodeInfo.getCostFromStart ( ) 158 < goalNodeInfo.getCostFromStart ( ) ) ) 159 { 160 goalNodeInfo = nodeInfo; 161 } 162 163 return false; 164 } 165 166 Iterator iterator = cartographer.getAdjacentNodes ( node ); 167 168 while ( iterator.hasNext ( ) ) 169 { 170 Object adjacentNode = iterator.next ( ); 171 172 double newCostFromStart 173 = nodeInfo.getCostFromStart ( ) 174 + cartographer.getCostToAdjacentNode ( node, adjacentNode ); 175 176 NodeInfo adjacentNodeInfo 177 = ( NodeInfo ) nodeToNodeInfoMap.get ( adjacentNode ); 178 179 if ( adjacentNodeInfo == null ) 180 { 181 adjacentNodeInfo = new NodeInfo ( adjacentNode ); 182 183 nodeToNodeInfoMap.put ( adjacentNode, adjacentNodeInfo ); 184 185 openNodeInfoSortedList.add ( adjacentNodeInfo ); 186 } 187 else if ( 188 adjacentNodeInfo.getCostFromStart ( ) <= newCostFromStart ) 189 { 190 continue; 191 } 192 193 adjacentNodeInfo.setParentNodeInfo ( nodeInfo ); 194 195 adjacentNodeInfo.setCostFromStart ( newCostFromStart ); 196 197 double totalCost = newCostFromStart 198 + cartographer.estimateCostToGoal ( adjacentNode ); 199 200 adjacentNodeInfo.setTotalCost ( totalCost ); 201 202 if ( totalCost < bestTotalCost ) 203 { 204 bestNodeInfo = adjacentNodeInfo; 205 206 bestTotalCost = totalCost; 207 } 208 209 Collections.sort ( openNodeInfoSortedList ); 210 } 211 212 return true; 213 } 214 215 ////////////////////////////////////////////////////////////////////// 216 ////////////////////////////////////////////////////////////////////// 217 }