main.cc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Example of world building, display, and successor computation for the artificial
  2. // intelligence path-finding lab
  3. //
  4. // Author: Didier LIME
  5. // Adapted by : Jovian HERSEMEULE
  6. // Date: 2018-10-03
  7. #include <iostream>
  8. #include <list>
  9. #include <cstdlib>
  10. #include <ctime>
  11. #include <stack>
  12. #include <queue>
  13. using namespace std;
  14. // Default parameters
  15. #define DEFAULT_LENGTH 50
  16. #define DEFAULT_HEIGHT 15
  17. #define DEFAULT_PROBABILITY 0.2
  18. // Tile codes
  19. #define FREE 0
  20. #define WALL 1
  21. #define ORIGIN 2
  22. #define TARGET 3
  23. #define DISCOVERED 4
  24. #define TRACE 5
  25. unsigned int identifyTile(unsigned int y, unsigned int x, unsigned int l) {
  26. return y * l + x;
  27. }
  28. class World
  29. {
  30. private:
  31. // Number of columns
  32. unsigned int l;
  33. // Number of lines
  34. unsigned int h;
  35. // Size of the array
  36. const unsigned int size;
  37. // Unidimensional array for tiles
  38. int* board;
  39. public:
  40. // Constructor
  41. World(unsigned int l_, unsigned int h_, double p)
  42. :l(l_), h(h_), size(l_ * h_)
  43. {
  44. board = new int[size]();
  45. // Add walls to the first and last columns
  46. for (unsigned int i = 0; i < h; i++)
  47. {
  48. board[i * l] = WALL;
  49. board[i * l + l - 1] = WALL;
  50. }
  51. // Add walls to the first and last lines
  52. for (unsigned int j = 0; j < l; j++)
  53. {
  54. board[j] = WALL;
  55. board[(h - 1) * l + j] = WALL;
  56. }
  57. for (unsigned int i = 0; i < h; i++)
  58. {
  59. for (unsigned int j = 0; j < l; j++)
  60. {
  61. // add a wall in this tile with probability p and provided that it is neither
  62. // the starting tile nor the goal tile
  63. if ((double) rand() / RAND_MAX < p && !(i == 1 && j == 1) && !(i == h - 2 && j == l - 2))
  64. {
  65. board[i * l + j] = WALL;
  66. }
  67. }
  68. }
  69. }
  70. // Copy constructor
  71. World(const World& other)
  72. :l(other.l), h(other.h), size(other.size)
  73. {
  74. board = new int[size]();
  75. for (int k(0); k < size; k++) {
  76. board[k] = other.board[k];
  77. }
  78. }
  79. // Display the world
  80. void display()
  81. {
  82. for (unsigned int i = 0; i < h; i++)
  83. {
  84. for (unsigned int j = 0; j < l; j++)
  85. {
  86. switch (board[identifyTile(i, j, l)])
  87. {
  88. case FREE:
  89. cout << " ";
  90. break;
  91. case WALL:
  92. cout << "#";
  93. break;
  94. case ORIGIN:
  95. cout << "o";
  96. break;
  97. case TARGET:
  98. cout << "T";
  99. break;
  100. case DISCOVERED:
  101. cout << ":";
  102. break;
  103. case TRACE:
  104. cout << "+";
  105. break;
  106. }
  107. }
  108. cout << endl;
  109. }
  110. }
  111. // compute the successors of tile number i in world w
  112. // we return the number n of valid successors
  113. // the actual list is in array r where only the first n
  114. // elements are significant
  115. unsigned int successors(unsigned int i, unsigned int r[4])
  116. {
  117. unsigned int n = 0;
  118. if (i >= 0 && i < size && board[i] != WALL)
  119. {
  120. // if i is a correct tile number (inside the array and not on a wall)
  121. // look in the four adjacent tiles and keep only those with no wall
  122. const unsigned int moves[] = { i - 1, i + 1, i - l, i + l};
  123. for (unsigned int k = 0; k < 4; k++)
  124. {
  125. if (board[moves[k]] != WALL)
  126. {
  127. r[n] = moves[k];
  128. n++;
  129. }
  130. }
  131. }
  132. return n;
  133. }
  134. // Mark a list of points in the world
  135. void markAll(const list<unsigned int>& path, int value = TRACE) {
  136. for (auto tile : path) {
  137. markOne(tile, value);
  138. }
  139. }
  140. // Mark a point in the world
  141. void markOne(unsigned int tile, int value = ORIGIN) {
  142. board[tile] = value;
  143. }
  144. // Depth-first search
  145. // starting from tile number origin, find a path to tile number t
  146. // return true if such a path exists, false otherwise
  147. // if it exists the path is given in variable path (hence the reference &)
  148. const bool dfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
  149. {
  150. bool targetIsReached = false;
  151. bool explored[size];
  152. unsigned int previous[size];
  153. for (unsigned int k(0); k < size; k ++) {
  154. explored[k] = false;
  155. previous[k] = k;
  156. }
  157. explored[origin] = true;
  158. stack<unsigned int> open;
  159. open.push(origin);
  160. int current;
  161. int neighbour;
  162. unsigned int succs[4];
  163. unsigned int nbSuccs;
  164. do {
  165. current = open.top();
  166. open.pop();
  167. nbSuccs = successors(current, succs);
  168. for (unsigned int i(0); i < nbSuccs; i++) {
  169. neighbour = succs[i];
  170. if (!explored[neighbour]) {
  171. open.push(neighbour);
  172. explored[neighbour] = true;
  173. previous[neighbour] = current;
  174. }
  175. }
  176. // Current tile is now processed
  177. discovered.push_back(current);
  178. // Stop if target found
  179. targetIsReached = (current == target);
  180. } while (!targetIsReached && !open.empty());
  181. // Remove origin and target
  182. if (!discovered.empty() && discovered.front() == origin)
  183. discovered.pop_front();
  184. if (!discovered.empty() && discovered.back() == target)
  185. discovered.pop_back();
  186. // Build path
  187. if (targetIsReached) {
  188. do {
  189. path.push_back(current);
  190. current = previous[current];
  191. } while (current != origin);
  192. path.pop_front();
  193. }
  194. return targetIsReached;
  195. }
  196. // Breadth-first search
  197. // starting from tile number origin, find a path to tile number t
  198. // return true if such a path exists, false otherwise
  199. // if it exists the path is given in variable path (hence the reference &)
  200. const bool bfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
  201. {
  202. bool targetIsReached = false;
  203. bool explored[size];
  204. unsigned int previous[size];
  205. for (unsigned int k(0); k < size; k ++) {
  206. explored[k] = false;
  207. previous[k] = k;
  208. }
  209. explored[origin] = true;
  210. queue<unsigned int> open;
  211. open.push(origin);
  212. int current;
  213. int neighbour;
  214. unsigned int succs[4];
  215. unsigned int nbSuccs;
  216. do {
  217. current = open.front();
  218. open.pop();
  219. nbSuccs = successors(current, succs);
  220. for (unsigned int i(0); i < nbSuccs; i++) {
  221. neighbour = succs[i];
  222. if (!explored[neighbour]) {
  223. open.push(neighbour);
  224. explored[neighbour] = true;
  225. previous[neighbour] = current;
  226. }
  227. }
  228. // Current tile is now processed
  229. discovered.push_back(current);
  230. // Stop if target found
  231. targetIsReached = (current == target);
  232. } while (!targetIsReached && !open.empty());
  233. // Remove origin and target
  234. if (!discovered.empty()) {
  235. discovered.pop_front();
  236. if (!discovered.empty())
  237. discovered.pop_back();
  238. }
  239. // Build path
  240. if (targetIsReached) {
  241. do {
  242. path.push_back(current);
  243. current = previous[current];
  244. } while (current != origin);
  245. path.pop_front();
  246. }
  247. return targetIsReached;
  248. }
  249. void showResults(bool exitFound, const list<unsigned int>& discovered, const list<unsigned int>& path) {
  250. markAll(discovered, DISCOVERED);
  251. if (!path.empty()) {
  252. markAll(path, TRACE);
  253. }
  254. display();
  255. // Display DFS results
  256. if (exitFound)
  257. cout << "SUCCESS !" << endl;
  258. else
  259. cout << "FAILURE ..." << endl;
  260. cout << discovered.size() << " tiles discovered;" << endl;
  261. cout << path.size() << " path length." << endl;
  262. }
  263. };
  264. int main()
  265. {
  266. // Initialise the random number generator
  267. srand(time(0));
  268. // Create a world
  269. const unsigned int l(DEFAULT_LENGTH), h(DEFAULT_HEIGHT);
  270. const double wallProbability(DEFAULT_PROBABILITY);
  271. World w(l, h, wallProbability);
  272. unsigned int start(identifyTile(1, 1, l));
  273. unsigned int end(identifyTile(h - 2, l - 2, l));
  274. // Display it
  275. cout << endl << "Generated world" << endl;
  276. w.markOne(start, ORIGIN);
  277. w.markOne(end, TARGET);
  278. w.display();
  279. // 1
  280. cout << endl << "Depth-First Search" << endl;
  281. World dfsWorld(w);
  282. list<unsigned int> dfsPath;
  283. list<unsigned int> dfsDiscovered;
  284. bool dfsExitFound = dfsWorld.dfs(start, end, dfsPath, dfsDiscovered);
  285. dfsWorld.showResults(dfsExitFound, dfsDiscovered, dfsPath);
  286. // 2
  287. cout << endl << "Breadth-First Search" << endl;
  288. World bfsWorld(w);
  289. list<unsigned int> bfsPath;
  290. list<unsigned int> bfsDiscovered;
  291. bool bfsExitFound = bfsWorld.bfs(start, end, bfsPath, bfsDiscovered);
  292. bfsWorld.showResults(bfsExitFound, bfsDiscovered, bfsPath);
  293. // End
  294. return 0;
  295. }