DS_WeightedGraph.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. /// \file DS_WeightedGraph.h
  11. /// \internal
  12. /// \brief Weighted graph.
  13. /// \details I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently).
  14. ///
  15. #ifndef __WEIGHTED_GRAPH_H
  16. #define __WEIGHTED_GRAPH_H
  17. #include "DS_OrderedList.h"
  18. #include "DS_Map.h"
  19. #include "DS_Heap.h"
  20. #include "DS_Queue.h"
  21. #include "DS_Tree.h"
  22. #include "RakAssert.h"
  23. #include "RakMemoryOverride.h"
  24. #ifdef _DEBUG
  25. #include <stdio.h>
  26. #endif
  27. #ifdef _MSC_VER
  28. #pragma warning( push )
  29. #endif
  30. /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
  31. /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
  32. namespace DataStructures
  33. {
  34. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  35. class RAK_DLL_EXPORT WeightedGraph
  36. {
  37. public:
  38. static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<node_type>(node_type(),node_type());}
  39. WeightedGraph();
  40. ~WeightedGraph();
  41. WeightedGraph( const WeightedGraph& original_copy );
  42. WeightedGraph& operator= ( const WeightedGraph& original_copy );
  43. void AddNode(const node_type &node);
  44. void RemoveNode(const node_type &node);
  45. void AddConnection(const node_type &node1, const node_type &node2, weight_type weight);
  46. void RemoveConnection(const node_type &node1, const node_type &node2);
  47. bool HasConnection(const node_type &node1, const node_type &node2);
  48. void Print(void);
  49. void Clear(void);
  50. bool GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT);
  51. bool GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT );
  52. unsigned GetNodeCount(void) const;
  53. unsigned GetConnectionCount(unsigned nodeIndex) const;
  54. void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const;
  55. node_type GetNodeAtIndex(unsigned nodeIndex) const;
  56. protected:
  57. void ClearDijkstra(void);
  58. void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT);
  59. DataStructures::Map<node_type, DataStructures::Map<node_type, weight_type> *> adjacencyLists;
  60. // All these variables are for path finding with Dijkstra
  61. // 08/23/06 Won't compile as a DLL inside this struct
  62. // struct
  63. // {
  64. bool isValidPath;
  65. node_type rootNode;
  66. DataStructures::OrderedList<node_type, node_type> costMatrixIndices;
  67. weight_type *costMatrix;
  68. node_type *leastNodeArray;
  69. // } dijkstra;
  70. struct NodeAndParent
  71. {
  72. DataStructures::Tree<node_type>*node;
  73. DataStructures::Tree<node_type>*parent;
  74. };
  75. };
  76. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  77. WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph()
  78. {
  79. isValidPath=false;
  80. costMatrix=0;
  81. }
  82. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  83. WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::~WeightedGraph()
  84. {
  85. Clear();
  86. }
  87. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  88. WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph( const WeightedGraph& original_copy )
  89. {
  90. adjacencyLists=original_copy.adjacencyLists;
  91. isValidPath=original_copy.isValidPath;
  92. if (isValidPath)
  93. {
  94. rootNode=original_copy.rootNode;
  95. costMatrixIndices=original_copy.costMatrixIndices;
  96. costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ );
  97. leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(costMatrixIndices.Size(), _FILE_AND_LINE_ );
  98. memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
  99. memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
  100. }
  101. }
  102. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  103. WeightedGraph<node_type, weight_type, allow_unlinkedNodes>& WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::operator=( const WeightedGraph& original_copy )
  104. {
  105. adjacencyLists=original_copy.adjacencyLists;
  106. isValidPath=original_copy.isValidPath;
  107. if (isValidPath)
  108. {
  109. rootNode=original_copy.rootNode;
  110. costMatrixIndices=original_copy.costMatrixIndices;
  111. costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ );
  112. leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(costMatrixIndices.Size(), _FILE_AND_LINE_ );
  113. memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
  114. memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
  115. }
  116. return *this;
  117. }
  118. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  119. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddNode(const node_type &node)
  120. {
  121. adjacencyLists.SetNew(node, RakNet::OP_NEW<DataStructures::Map<node_type, weight_type> >( _FILE_AND_LINE_) );
  122. }
  123. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  124. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveNode(const node_type &node)
  125. {
  126. unsigned i;
  127. DataStructures::Queue<node_type> removeNodeQueue;
  128. removeNodeQueue.Push(node, _FILE_AND_LINE_ );
  129. while (removeNodeQueue.Size())
  130. {
  131. RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_);
  132. // Remove this node from all of the other lists as well
  133. for (i=0; i < adjacencyLists.Size(); i++)
  134. {
  135. adjacencyLists[i]->Delete(node);
  136. #ifdef _MSC_VER
  137. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
  138. #endif
  139. if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0)
  140. removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i), _FILE_AND_LINE_ );
  141. }
  142. }
  143. ClearDijkstra();
  144. }
  145. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  146. bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::HasConnection(const node_type &node1, const node_type &node2)
  147. {
  148. if (node1==node2)
  149. return false;
  150. if (adjacencyLists.Has(node1)==false)
  151. return false;
  152. return adjacencyLists.Get(node1)->Has(node2);
  153. }
  154. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  155. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddConnection(const node_type &node1, const node_type &node2, weight_type weight)
  156. {
  157. if (node1==node2)
  158. return;
  159. if (adjacencyLists.Has(node1)==false)
  160. AddNode(node1);
  161. adjacencyLists.Get(node1)->Set(node2, weight);
  162. if (adjacencyLists.Has(node2)==false)
  163. AddNode(node2);
  164. adjacencyLists.Get(node2)->Set(node1, weight);
  165. }
  166. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  167. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveConnection(const node_type &node1, const node_type &node2)
  168. {
  169. adjacencyLists.Get(node2)->Delete(node1);
  170. adjacencyLists.Get(node1)->Delete(node2);
  171. #ifdef _MSC_VER
  172. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
  173. #endif
  174. if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node
  175. {
  176. if (adjacencyLists.Get(node1)->Size()==0)
  177. RemoveNode(node1); // Will also remove node1 from the adjacency list of node2
  178. if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0)
  179. RemoveNode(node2);
  180. }
  181. ClearDijkstra();
  182. }
  183. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  184. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Clear(void)
  185. {
  186. unsigned i;
  187. for (i=0; i < adjacencyLists.Size(); i++)
  188. RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_);
  189. adjacencyLists.Clear();
  190. ClearDijkstra();
  191. }
  192. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  193. bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT)
  194. {
  195. path.Clear(false, _FILE_AND_LINE_);
  196. if (startNode==endNode)
  197. {
  198. path.Insert(startNode, _FILE_AND_LINE_);
  199. path.Insert(endNode, _FILE_AND_LINE_);
  200. return true;
  201. }
  202. if (isValidPath==false || rootNode!=startNode)
  203. {
  204. ClearDijkstra();
  205. GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT);
  206. }
  207. // return the results
  208. bool objectExists;
  209. unsigned col,row;
  210. weight_type currentWeight;
  211. DataStructures::Queue<node_type> outputQueue;
  212. col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists);
  213. if (costMatrixIndices.Size()<2)
  214. {
  215. return false;
  216. }
  217. if (objectExists==false)
  218. {
  219. return false;
  220. }
  221. node_type vertex;
  222. row=costMatrixIndices.Size()-2;
  223. if (row==0)
  224. {
  225. path.Insert(startNode, _FILE_AND_LINE_);
  226. path.Insert(endNode, _FILE_AND_LINE_);
  227. return true;
  228. }
  229. currentWeight=costMatrix[row*adjacencyLists.Size() + col];
  230. if (currentWeight==INFINITE_WEIGHT)
  231. {
  232. // No path
  233. return true;
  234. }
  235. vertex=endNode;
  236. outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_);
  237. row--;
  238. #ifdef _MSC_VER
  239. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
  240. #endif
  241. while (1)
  242. {
  243. while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight)
  244. {
  245. if (row==0)
  246. {
  247. path.Insert(startNode, _FILE_AND_LINE_);
  248. for (col=0; outputQueue.Size(); col++)
  249. path.Insert(outputQueue.Pop(), _FILE_AND_LINE_);
  250. return true;
  251. }
  252. --row;
  253. }
  254. vertex=leastNodeArray[row];
  255. outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_);
  256. if (row==0)
  257. break;
  258. col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists);
  259. currentWeight=costMatrix[row*adjacencyLists.Size() + col];
  260. }
  261. path.Insert(startNode, _FILE_AND_LINE_);
  262. for (col=0; outputQueue.Size(); col++)
  263. path.Insert(outputQueue.Pop(), _FILE_AND_LINE_);
  264. return true;
  265. }
  266. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  267. node_type WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeAtIndex(unsigned nodeIndex) const
  268. {
  269. return adjacencyLists.GetKeyAtIndex(nodeIndex);
  270. }
  271. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  272. unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeCount(void) const
  273. {
  274. return adjacencyLists.Size();
  275. }
  276. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  277. unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionCount(unsigned nodeIndex) const
  278. {
  279. return adjacencyLists[nodeIndex]->Size();
  280. }
  281. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  282. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const
  283. {
  284. outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex);
  285. outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex);
  286. }
  287. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  288. bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT )
  289. {
  290. // Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable
  291. DataStructures::List<node_type> path;
  292. DataStructures::WeightedGraph<node_type, weight_type, allow_unlinkedNodes> outGraph;
  293. bool res;
  294. unsigned i,j;
  295. for (i=0; i < inputNodes->Size(); i++)
  296. {
  297. res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT);
  298. if (res && path.Size()>0)
  299. {
  300. for (j=0; j < path.Size()-1; j++)
  301. {
  302. // Don't bother looking up the weight
  303. outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT);
  304. }
  305. }
  306. }
  307. // Copy the graph to a tree.
  308. DataStructures::Queue<NodeAndParent> nodesToProcess;
  309. DataStructures::Tree<node_type> *current;
  310. DataStructures::Map<node_type, weight_type> *adjacencyList;
  311. node_type key;
  312. NodeAndParent nap, nap2;
  313. outTree.DeleteDecendants();
  314. outTree.data=startNode;
  315. current=&outTree;
  316. if (outGraph.adjacencyLists.Has(startNode)==false)
  317. return false;
  318. adjacencyList = outGraph.adjacencyLists.Get(startNode);
  319. for (i=0; i < adjacencyList->Size(); i++)
  320. {
  321. nap2.node=RakNet::OP_NEW<DataStructures::Tree<node_type> >( _FILE_AND_LINE_ );
  322. nap2.node->data=adjacencyList->GetKeyAtIndex(i);
  323. nap2.parent=current;
  324. nodesToProcess.Push(nap2, _FILE_AND_LINE_ );
  325. current->children.Insert(nap2.node, _FILE_AND_LINE_);
  326. }
  327. while (nodesToProcess.Size())
  328. {
  329. nap=nodesToProcess.Pop();
  330. current=nap.node;
  331. adjacencyList = outGraph.adjacencyLists.Get(nap.node->data);
  332. for (i=0; i < adjacencyList->Size(); i++)
  333. {
  334. key=adjacencyList->GetKeyAtIndex(i);
  335. if (key!=nap.parent->data)
  336. {
  337. nap2.node=RakNet::OP_NEW<DataStructures::Tree<node_type> >( _FILE_AND_LINE_ );
  338. nap2.node->data=key;
  339. nap2.parent=current;
  340. nodesToProcess.Push(nap2, _FILE_AND_LINE_ );
  341. current->children.Insert(nap2.node, _FILE_AND_LINE_);
  342. }
  343. }
  344. }
  345. return true;
  346. }
  347. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  348. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT)
  349. {
  350. if (adjacencyLists.Size()==0)
  351. return;
  352. costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ );
  353. leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(adjacencyLists.Size(), _FILE_AND_LINE_ );
  354. node_type currentNode;
  355. unsigned col, row, row2, openSetIndex;
  356. node_type adjacentKey;
  357. unsigned adjacentIndex;
  358. weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight;
  359. DataStructures::Map<node_type, weight_type> *adjacencyList;
  360. DataStructures::Heap<weight_type, node_type, false> minHeap;
  361. DataStructures::Map<node_type, weight_type> openSet;
  362. for (col=0; col < adjacencyLists.Size(); col++)
  363. {
  364. // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck
  365. costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_);
  366. }
  367. for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++)
  368. costMatrix[col]=INFINITE_WEIGHT;
  369. currentNode=startNode;
  370. row=0;
  371. currentNodeWeight=0;
  372. rootNode=startNode;
  373. // Clear the starting node column
  374. if (adjacencyLists.Size())
  375. {
  376. adjacentIndex=adjacencyLists.GetIndexAtKey(startNode);
  377. for (row2=0; row2 < adjacencyLists.Size(); row2++)
  378. costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0;
  379. }
  380. while (row < adjacencyLists.Size()-1)
  381. {
  382. adjacencyList = adjacencyLists.Get(currentNode);
  383. // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight.
  384. for (col=0; col < adjacencyList->Size(); col++)
  385. {
  386. edgeWeight=(*adjacencyList)[col];
  387. adjacentKey=adjacencyList->GetKeyAtIndex(col);
  388. adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey);
  389. adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex];
  390. if (currentNodeWeight + edgeWeight < adjacentNodeWeight)
  391. {
  392. // Update the weight for the adjacent node
  393. for (row2=row; row2 < adjacencyLists.Size(); row2++)
  394. costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight;
  395. openSet.Set(adjacentKey, currentNodeWeight + edgeWeight);
  396. }
  397. }
  398. // Find the lowest in the open set
  399. minHeap.Clear(true,_FILE_AND_LINE_);
  400. for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++)
  401. minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_);
  402. /*
  403. unsigned i,j;
  404. for (i=0; i < adjacencyLists.Size()-1; i++)
  405. {
  406. for (j=0; j < adjacencyLists.Size(); j++)
  407. {
  408. RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]);
  409. }
  410. RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]);
  411. RAKNET_DEBUG_PRINTF("\n");
  412. }
  413. */
  414. if (minHeap.Size()==0)
  415. {
  416. // Unreachable nodes
  417. isValidPath=true;
  418. return;
  419. }
  420. currentNodeWeight=minHeap.PeekWeight(0);
  421. leastNodeArray[row]=minHeap.Pop(0);
  422. currentNode=leastNodeArray[row];
  423. openSet.Delete(currentNode);
  424. row++;
  425. }
  426. /*
  427. #ifdef _DEBUG
  428. unsigned i,j;
  429. for (i=0; i < adjacencyLists.Size()-1; i++)
  430. {
  431. for (j=0; j < adjacencyLists.Size(); j++)
  432. {
  433. RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]);
  434. }
  435. RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]);
  436. RAKNET_DEBUG_PRINTF("\n");
  437. }
  438. #endif
  439. */
  440. isValidPath=true;
  441. }
  442. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  443. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::ClearDijkstra(void)
  444. {
  445. if (isValidPath)
  446. {
  447. isValidPath=false;
  448. RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_);
  449. RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_);
  450. costMatrixIndices.Clear(false, _FILE_AND_LINE_);
  451. }
  452. }
  453. template <class node_type, class weight_type, bool allow_unlinkedNodes>
  454. void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Print(void)
  455. {
  456. #ifdef _DEBUG
  457. unsigned i,j;
  458. for (i=0; i < adjacencyLists.Size(); i++)
  459. {
  460. //RAKNET_DEBUG_PRINTF("%i connected to ", i);
  461. RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString());
  462. if (adjacencyLists[i]->Size()==0)
  463. RAKNET_DEBUG_PRINTF("<Empty>");
  464. else
  465. {
  466. for (j=0; j < adjacencyLists[i]->Size(); j++)
  467. // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) );
  468. RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) );
  469. }
  470. RAKNET_DEBUG_PRINTF("\n");
  471. }
  472. #endif
  473. }
  474. }
  475. #ifdef _MSC_VER
  476. #pragma warning( pop )
  477. #endif
  478. #endif
粤ICP备19079148号