main.cc 9.1 KB

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