main.cc 9.0 KB

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