00001
00002
00003
00004
00005
00006
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
00050 init(tiling.getType(), width, height);
00051
00052 for (int col = 0; col < width; col++)
00053 for (int row = 0; row < height; row++)
00054 {
00055
00056 int localNodeId = getNodeId(row, col);
00057 TilingNodeInfo& localNodeInfo = m_graph.getNodeInfo(localNodeId);
00058
00059 int nodeId = tiling.getNodeId(vertOrigin + row, horizOrigin + col);
00060 const TilingNodeInfo& nodeInfo = tiling.getNodeInfo(nodeId);
00061
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
00231
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
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
00491
00492
00493
00494
00495
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