main.cc 8.6 KB

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