main.cc 9.5 KB

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