00001 //----------------------------------------------------------------------------- 00002 /** @file tiling.cpp 00003 @see tiling.h 00004 00005 $Id: tiling_8cpp-source.html,v 1.1.1.1 2003/08/07 19:41:39 emarkus Exp $ 00006 $Source: /usr/cvsroot/www_pathfind/libpathfind/0.1.0/doc/tiling_8cpp-source.html,v $ 00007 */ 00008 //----------------------------------------------------------------------------- 00009 00010 #include <assert.h> 00011 #include "tiling.h" 00012 00013 #include <ctype.h> 00014 #include <iostream> 00015 #include <sstream> 00016 #include "search.h" 00017 00018 using namespace std; 00019 using namespace PathFind; 00020 00021 #define MAXLINE 2048 00022 00023 //----------------------------------------------------------------------------- 00024 00025 TilingNodeInfo::TilingNodeInfo() 00026 : m_isObstacle(false), 00027 m_column(-1), 00028 m_row(-1) 00029 { 00030 } 00031 00032 TilingNodeInfo::TilingNodeInfo(bool isObstacle, int row, int column) 00033 : m_isObstacle(isObstacle), 00034 m_column(column), 00035 m_row(row) 00036 { 00037 } 00038 00039 //----------------------------------------------------------------------------- 00040 00041 Tiling::Tiling(Type type, int rows, int columns) 00042 { 00043 init(type, rows, columns); 00044 } 00045 00046 Tiling::Tiling(const Tiling & tiling, int horizOrigin, int vertOrigin, 00047 int width, int height) 00048 { 00049 // init builds everything, except for the obstacles... 00050 init(tiling.getType(), width, height); 00051 // so we now put the obstacles in place 00052 for (int col = 0; col < width; col++) 00053 for (int row = 0; row < height; row++) 00054 { 00055 // get the local node 00056 int localNodeId = getNodeId(row, col); 00057 TilingNodeInfo& localNodeInfo = m_graph.getNodeInfo(localNodeId); 00058 // get the initial tiling node 00059 int nodeId = tiling.getNodeId(vertOrigin + row, horizOrigin + col); 00060 const TilingNodeInfo& nodeInfo = tiling.getNodeInfo(nodeId); 00061 // set obstacle for the local node 00062 if (nodeInfo.isObstacle()) 00063 localNodeInfo.setObstacle(true); 00064 else 00065 localNodeInfo.setObstacle(false); 00066 } 00067 } 00068 00069 Tiling::Tiling(LineReader& reader) 00070 { 00071 int columns = -1; 00072 int rows = -1; 00073 bool typeFound = false; 00074 Type type = TILE; 00075 bool done = false; 00076 while (! done) 00077 { 00078 string line = reader.readLine(); 00079 if (line == "") 00080 continue; 00081 if (line.size() > 0 && line[0] == '#') 00082 continue; 00083 istringstream in(line); 00084 string attribute; 00085 in >> attribute; 00086 if (! in) 00087 throw reader.createError("Missing attribute."); 00088 if (attribute == "type") 00089 { 00090 string typeString; 00091 in >> typeString; 00092 if (! in) 00093 throw reader.createError("Missing type value."); 00094 if (typeString == "tile") 00095 type = TILE; 00096 else if (typeString == "octile") 00097 type = OCTILE; 00098 else if (typeString == "octile_unicost") 00099 type = OCTILE_UNICOST; 00100 else if (typeString == "hex") 00101 type = HEX; 00102 else 00103 throw reader.createError("Invalid type value."); 00104 typeFound = true; 00105 } 00106 else if (attribute == "width") 00107 { 00108 in >> columns; 00109 if (! in || columns <= 0 || columns > LineReader::MAX_LINE) 00110 throw reader.createError("Invalid width."); 00111 } 00112 else if (attribute == "height") 00113 { 00114 in >> rows; 00115 if (! in || rows <= 0) 00116 throw reader.createError("Invalid height."); 00117 } 00118 else if (attribute == "map") 00119 { 00120 done = true; 00121 } 00122 else 00123 throw reader.createError("Unknown attribute."); 00124 } 00125 if (columns == -1 || rows == -1) 00126 throw reader.createError("Map without valid width / height."); 00127 if (! typeFound) 00128 throw reader.createError("Map without type."); 00129 init(type, rows, columns); 00130 readObstacles(reader); 00131 } 00132 00133 void Tiling::addOutEdge(int nodeId, int row, int col, int cost) 00134 { 00135 if (row < 0 || row >= m_rows || col < 0 || col >= m_columns) 00136 return; 00137 m_graph.addOutEdge(nodeId, getNodeId(row, col), TilingEdgeInfo(cost)); 00138 } 00139 00140 void Tiling::clearObstacles() 00141 { 00142 int numberNodes = getNumberNodes(); 00143 for (int nodeId = 0; nodeId < numberNodes; ++nodeId) 00144 { 00145 TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId); 00146 nodeInfo.setObstacle(false); 00147 } 00148 } 00149 00150 void Tiling::createEdges() 00151 { 00152 assert(m_type == TILE 00153 || m_type == OCTILE 00154 || m_type == OCTILE_UNICOST 00155 || m_type == HEX); 00156 for (int row = 0; row < m_rows; ++row) 00157 for (int col = 0; col < m_columns; ++col) 00158 { 00159 int nodeId = getNodeId(row, col); 00160 addOutEdge(nodeId, row - 1, col, COST_ONE); 00161 addOutEdge(nodeId, row + 1, col, COST_ONE); 00162 addOutEdge(nodeId, row, col - 1, COST_ONE); 00163 addOutEdge(nodeId, row, col + 1, COST_ONE); 00164 if (m_type == OCTILE) 00165 { 00166 addOutEdge(nodeId, row + 1, col + 1, COST_SQRT2); 00167 addOutEdge(nodeId, row + 1, col - 1, COST_SQRT2); 00168 addOutEdge(nodeId, row - 1, col + 1, COST_SQRT2); 00169 addOutEdge(nodeId, row - 1, col - 1, COST_SQRT2); 00170 } 00171 else if (m_type == OCTILE_UNICOST) 00172 { 00173 addOutEdge(nodeId, row + 1, col + 1, COST_ONE); 00174 addOutEdge(nodeId, row + 1, col - 1, COST_ONE); 00175 addOutEdge(nodeId, row - 1, col + 1, COST_ONE); 00176 addOutEdge(nodeId, row - 1, col - 1, COST_ONE); 00177 } 00178 else if (m_type == HEX) 00179 { 00180 if (col % 2 == 0) 00181 { 00182 addOutEdge(nodeId, row - 1, col + 1, COST_ONE); 00183 addOutEdge(nodeId, row - 1, col - 1, COST_ONE); 00184 } 00185 else 00186 { 00187 addOutEdge(nodeId, row + 1, col + 1, COST_ONE); 00188 addOutEdge(nodeId, row + 1, col - 1, COST_ONE); 00189 } 00190 } 00191 } 00192 } 00193 00194 void Tiling::createNodes() 00195 { 00196 for (int row = 0; row < m_rows; ++row) 00197 for (int col = 0; col < m_columns; ++col) 00198 { 00199 int nodeId = getNodeId(row, col); 00200 m_graph.addNode(nodeId, TilingNodeInfo(false, row, col)); 00201 } 00202 } 00203 00204 vector<char> Tiling::getCharVector() const 00205 { 00206 vector<char> result; 00207 int numberNodes = getNumberNodes(); 00208 result.reserve(numberNodes); 00209 for (int i = 0; i < numberNodes; ++i) 00210 { 00211 if (m_graph.getNodeInfo(i).isObstacle()) 00212 result.push_back('@'); 00213 else 00214 result.push_back('.'); 00215 } 00216 return result; 00217 } 00218 00219 int Tiling::getHeuristic(int start, int target) const 00220 { 00221 int colStart = start % m_columns; 00222 int colTarget = target % m_columns; 00223 int rowStart = start / m_columns; 00224 int rowTarget = target / m_columns; 00225 int diffCol = abs(colTarget - colStart); 00226 int diffRow = abs(rowTarget - rowStart); 00227 switch (m_type) 00228 { 00229 case HEX: 00230 // Vancouver distance 00231 // See P.Yap: Grid-based Path-Finding (LNAI 2338 pp.44-55) 00232 { 00233 int correction = 0; 00234 if (diffCol % 2 != 0) 00235 { 00236 if (rowTarget < rowStart) 00237 correction = colTarget % 2; 00238 else if (rowTarget > rowStart) 00239 correction = colStart % 2; 00240 } 00241 // Note: formula in paper is wrong, corrected below. 00242 int dist = max(0, diffRow - diffCol / 2 - correction) + diffCol; 00243 return dist * COST_ONE; 00244 } 00245 case OCTILE_UNICOST: 00246 return max(diffCol, diffRow) * COST_ONE; 00247 case OCTILE: 00248 int maxDiff; 00249 int minDiff; 00250 if (diffCol > diffRow) 00251 { 00252 maxDiff = diffCol; 00253 minDiff = diffRow; 00254 } 00255 else 00256 { 00257 maxDiff = diffRow; 00258 minDiff = diffCol; 00259 } 00260 return minDiff * COST_SQRT2 + (maxDiff - minDiff) * COST_ONE; 00261 case TILE: 00262 return (diffCol + diffRow) * COST_ONE; 00263 default: 00264 assert(false); 00265 return 0; 00266 } 00267 } 00268 00269 int Tiling::getMaxCost() const 00270 { 00271 if (m_type == OCTILE) 00272 return COST_SQRT2; 00273 return COST_ONE; 00274 } 00275 00276 int Tiling::getMaxEdges(Type type) 00277 { 00278 switch (type) 00279 { 00280 case HEX: 00281 return 6; 00282 case OCTILE: 00283 case OCTILE_UNICOST: 00284 return 8; 00285 case TILE: 00286 return 4; 00287 } 00288 assert(false); 00289 return 0; 00290 } 00291 00292 int Tiling::getMinCost() const 00293 { 00294 return COST_ONE; 00295 } 00296 00297 int Tiling::getNumberNodes() const 00298 { 00299 return m_rows * m_columns; 00300 } 00301 00302 void Tiling::getSuccessors(int nodeId, int lastNodeId, 00303 vector<Successor>& result) const 00304 { 00305 result.reserve(m_maxEdges); 00306 result.clear(); 00307 const TilingNode& node = m_graph.getNode(nodeId); 00308 const TilingNodeInfo& nodeInfo = node.getInfo(); 00309 if (nodeInfo.isObstacle()) 00310 { 00311 assert(result.size() == 0); 00312 return; 00313 } 00314 const vector<TilingEdge>& edges = node.getOutEdges(); 00315 for (vector<TilingEdge>::const_iterator i = edges.begin(); 00316 i != edges.end(); ++i) 00317 { 00318 int targetNodeId = i->getTargetNodeId(); 00319 assert(isValidNodeId(targetNodeId)); 00320 const TilingNode& targetNode = m_graph.getNode(targetNodeId); 00321 const TilingNodeInfo& targetNodeInfo = targetNode.getInfo(); 00322 if (targetNodeInfo.isObstacle()) 00323 continue; 00324 if (lastNodeId != NO_NODE) 00325 if (pruneNode(targetNodeId, lastNodeId)) 00326 continue; 00327 result.push_back(Successor(targetNodeId, i->getInfo().getCost())); 00328 } 00329 #ifndef NDEBUG 00330 int resultSize = result.size(); 00331 assert(resultSize <= m_maxEdges); 00332 if (lastNodeId != NO_NODE) 00333 switch (m_type) 00334 { 00335 case HEX: 00336 case TILE: 00337 assert(resultSize <= 3); 00338 break; 00339 case OCTILE: 00340 case OCTILE_UNICOST: 00341 assert(resultSize <= 5); 00342 break; 00343 } 00344 #endif 00345 } 00346 00347 void Tiling::init(Type type, int rows, int columns) 00348 { 00349 m_type = type; 00350 m_maxEdges = getMaxEdges(type); 00351 m_rows = rows; 00352 m_columns = columns; 00353 m_graph.clear(); 00354 createNodes(); 00355 createEdges(); 00356 } 00357 00358 bool Tiling::isValidNodeId(int nodeId) const 00359 { 00360 return nodeId >= 0 && nodeId < getNumberNodes(); 00361 } 00362 00363 void Tiling::printFormatted(ostream& o) const 00364 { 00365 printFormatted(o, getCharVector()); 00366 } 00367 00368 void Tiling::printFormatted(ostream& o, const vector<char>& chars) const 00369 { 00370 for (int row = 0; row < m_rows; ++row) 00371 { 00372 for (int col = 0; col < m_columns; ++col) 00373 { 00374 int nodeId = getNodeId(row, col); 00375 o << chars[nodeId]; 00376 } 00377 o << '\n'; 00378 } 00379 } 00380 00381 void Tiling::printFormatted(ostream& o, int start, int target) const 00382 { 00383 vector<char> chars = getCharVector(); 00384 chars[start] = 'S'; 00385 chars[target] = 'T'; 00386 printFormatted(o, chars); 00387 } 00388 00389 void Tiling::printFormatted(ostream& o, const vector<int>& path) const 00390 { 00391 vector<char> chars = getCharVector(); 00392 if (path.size() > 0) 00393 { 00394 for (vector<int>::const_iterator i = path.begin(); 00395 i != path.end(); ++i) 00396 chars[*i] = 'x'; 00397 chars[*path.begin()] = 'T'; 00398 chars[*(path.end() - 1)] = 'S'; 00399 } 00400 printFormatted(o, chars); 00401 } 00402 00403 void Tiling::printLabels(ostream& o, const vector<char>& labels) const 00404 { 00405 assert(labels.size() == static_cast<unsigned int>(getNumberNodes())); 00406 vector<char> chars = getCharVector(); 00407 vector<char>::iterator j = chars.begin(); 00408 for (vector<char>::const_iterator i = labels.begin(); 00409 i != labels.end(); ++i, ++j) 00410 if (*i != ' ') 00411 *j = *i; 00412 printFormatted(o, chars); 00413 } 00414 00415 00416 void Tiling::printPathAndLabels(ostream& o, const vector<int>& path, 00417 const vector<char>& labels) const 00418 { 00419 assert(labels.size() == static_cast<unsigned int>(getNumberNodes())); 00420 00421 vector<char> chars = getCharVector(); 00422 00423 vector<char>::iterator j = chars.begin(); 00424 for (vector<char>::const_iterator i = labels.begin(); 00425 i != labels.end(); ++i, ++j) 00426 if (*i != ' ') 00427 *j = *i; 00428 00429 if (path.size() > 0) 00430 { 00431 for (vector<int>::const_iterator i = path.begin(); 00432 i != path.end(); ++i) 00433 chars[*i] = 'x'; 00434 chars[*path.begin()] = 'T'; 00435 chars[*(path.end() - 1)] = 'S'; 00436 } 00437 00438 printFormatted(o, chars); 00439 } 00440 00441 00442 bool Tiling::pruneNode(int targetNodeId, int lastNodeId) const 00443 { 00444 if (targetNodeId == lastNodeId) 00445 return true; 00446 if (m_type == TILE) 00447 return false; 00448 const TilingNode& lastNode = m_graph.getNode(lastNodeId); 00449 const vector<TilingEdge>& edges = lastNode.getOutEdges(); 00450 for (vector<TilingEdge>::const_iterator i = edges.begin(); 00451 i != edges.end(); ++i) 00452 { 00453 if (i->getTargetNodeId() == targetNodeId) 00454 return true; 00455 } 00456 return false; 00457 } 00458 00459 void Tiling::readObstacles(LineReader& reader) 00460 { 00461 for (int row = 0; row < m_rows; ++row) 00462 { 00463 string line = reader.readLine(); 00464 istringstream in(line); 00465 for (int col = 0; col < m_columns; ++col) 00466 { 00467 bool isObstacle; 00468 char c; 00469 in.get(c); 00470 if (! in) 00471 throw reader.createError("Unexpected end of stream."); 00472 if (c == '@') 00473 isObstacle = true; 00474 else if (c == '.') 00475 isObstacle = false; 00476 else 00477 throw reader.createError("Unknown charcter."); 00478 int nodeId = getNodeId(row, col); 00479 TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId); 00480 if (! nodeInfo.isObstacle()) 00481 nodeInfo.setObstacle(isObstacle); 00482 00483 } 00484 } 00485 } 00486 00487 00488 bool Tiling::conflictDiag(int row, int col, int roff, int coff) 00489 { 00490 // Avoid generating cofigurations like: 00491 // 00492 // @ or @ 00493 // @ @ 00494 // 00495 // that favor one grid topology over another. 00496 if ( (row+roff < 0) || (row+roff >= m_rows) || 00497 (col+coff < 0) || (col+coff >= m_columns) ) 00498 return false; 00499 00500 if ( (m_graph.getNodeInfo(getNodeId(row+roff,col+coff))).isObstacle() ) 00501 { 00502 if ( !m_graph.getNodeInfo(getNodeId(row,col+coff)).isObstacle() && 00503 !m_graph.getNodeInfo(getNodeId(row+roff,col)).isObstacle() ) 00504 return true; 00505 } 00506 return false; 00507 } 00508 00509 00510 void Tiling::setObstacles(float obstaclePercentage, bool avoidDiag ) 00511 { 00512 clearObstacles(); 00513 int numberNodes = getNumberNodes(); 00514 int numberObstacles = static_cast<int>(obstaclePercentage * numberNodes); 00515 for (int count = 0; count < numberObstacles; ) 00516 { 00517 int nodeId = rand() / (RAND_MAX / numberNodes + 1); 00518 TilingNodeInfo& nodeInfo = m_graph.getNodeInfo(nodeId); 00519 if (! nodeInfo.isObstacle()) 00520 { 00521 if ( avoidDiag ) 00522 { 00523 int row = nodeInfo.getRow(); 00524 int col = nodeInfo.getColumn(); 00525 00526 if ( !conflictDiag(row,col,-1,-1) && 00527 !conflictDiag(row,col,-1,+1) && 00528 !conflictDiag(row,col,+1,-1) && 00529 !conflictDiag(row,col,+1,+1) ) 00530 { 00531 nodeInfo.setObstacle(true); 00532 ++count; 00533 } 00534 } 00535 else 00536 { 00537 nodeInfo.setObstacle(true); 00538 ++count; 00539 } 00540 } 00541 } 00542 } 00543 00544 //-----------------------------------------------------------------------------