main.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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 <utility> // pair
  14. #include <functional> // greater
  15. #include <limits>
  16. #include <unistd.h>
  17. using namespace std;
  18. // Default parameters
  19. #define DEFAULT_LENGTH 50
  20. #define DEFAULT_HEIGHT 15
  21. #define DEFAULT_PROBABILITY 0.2
  22. #define DEFAULT_ANIMATION true
  23. #define DEFAULT_ANIMATION_DELAY 50000 // us
  24. #define DEFAULT_COLOR_ENABLE true
  25. // Tile codes
  26. #define FREE 0
  27. #define WALL 1
  28. #define ORIGIN 2
  29. #define TARGET 3
  30. #define DISCOVERED 4
  31. #define TRACE 5
  32. unsigned int identifyTile(unsigned int y, unsigned int x, unsigned int l) {
  33. return y * l + x;
  34. }
  35. void locateTile(unsigned int id, unsigned int l, unsigned int &x, unsigned int &y) {
  36. y = id / l;
  37. x = id - l * y;
  38. }
  39. unsigned int delta(unsigned int c1, unsigned int c2) {
  40. if (c1 < c2)
  41. return c2 - c1;
  42. else
  43. return c1 - c2;
  44. }
  45. void moveCursorUp(const unsigned int& h) {
  46. cout << "\033[" << h << 'A';
  47. }
  48. class World
  49. {
  50. private:
  51. // Number of columns
  52. unsigned int l;
  53. // Number of lines
  54. unsigned int h;
  55. // Size of the array
  56. unsigned int size;
  57. // Unidimensional array for tiles
  58. int* board;
  59. // Number of empty tiles
  60. unsigned int tileQuantity;
  61. public:
  62. // Default constructor
  63. World()
  64. :l(0), h(0), size(0), board(nullptr), tileQuantity(0)
  65. {}
  66. // Copy constructor
  67. World(const World& other)
  68. :l(other.l), h(other.h), size(other.size), tileQuantity(other.tileQuantity)
  69. {
  70. board = new int[size]();
  71. for (int k(0); k < size; k++) {
  72. board[k] = other.board[k];
  73. }
  74. }
  75. // Random generation
  76. void generate(unsigned int l_, unsigned int h_, double p)
  77. {
  78. // Error if board exists
  79. if (board != nullptr)
  80. cerr << "Can't generate world, board already exists" << endl;
  81. // Initialize attributes
  82. l = l_;
  83. h = h_;
  84. size = l * h;
  85. tileQuantity = size;
  86. board = new int[size]();
  87. // Add floor everywhere
  88. for (unsigned int k(0); k < size; k++)
  89. board[k] = FREE;
  90. // Add walls to the first and last columns
  91. for (unsigned int i(0); i < h; i++) {
  92. markOne(i * l, WALL);
  93. markOne(i * l + l - 1, WALL);
  94. }
  95. // Add walls to the first and last lines
  96. for (unsigned int j = 0; j < l; j++) {
  97. markOne(j, WALL);
  98. markOne((h - 1) * l + j, WALL);
  99. }
  100. // Randomly add walls
  101. for (unsigned int i = 0; i < h; i++)
  102. {
  103. for (unsigned int j = 0; j < l; j++)
  104. {
  105. // add a wall in this tile with probability p and provided that it is neither
  106. // the starting tile nor the goal tile
  107. if ((double) rand() / RAND_MAX < p && !(i == 1 && j == 1) && !(i == h - 2 && j == l - 2))
  108. markOne(i * l + j, WALL);
  109. }
  110. }
  111. }
  112. // Display the world
  113. void display()
  114. {
  115. for (unsigned int i = 0; i < h; i++)
  116. {
  117. for (unsigned int j = 0; j < l; j++)
  118. {
  119. switch (board[identifyTile(i, j, l)])
  120. {
  121. case FREE:
  122. cout << " ";
  123. break;
  124. case WALL:
  125. if (DEFAULT_COLOR_ENABLE) cout << "\033[0;43m";
  126. cout << "#";
  127. break;
  128. case ORIGIN:
  129. if (DEFAULT_COLOR_ENABLE) cout << "\033[1;33m";
  130. cout << "o";
  131. break;
  132. case TARGET:
  133. if (DEFAULT_COLOR_ENABLE) cout << "\033[1;33m";
  134. cout << "T";
  135. break;
  136. case DISCOVERED:
  137. if (DEFAULT_COLOR_ENABLE) cout << "\033[0;34m";
  138. cout << ":";
  139. break;
  140. case TRACE:
  141. if (DEFAULT_COLOR_ENABLE) {
  142. cout << "\033[0;32m";
  143. cout << "\033[1;42m";
  144. cout << ":";
  145. }
  146. else
  147. cout << "+";
  148. break;
  149. }
  150. if (DEFAULT_COLOR_ENABLE) cout << "\033[0m";
  151. }
  152. cout << endl;
  153. }
  154. }
  155. // compute the successors of tile number i in world w
  156. // we return the number n of valid successors
  157. // the actual list is in array r where only the first n
  158. // elements are significant
  159. unsigned int successors(unsigned int i, unsigned int r[4])
  160. {
  161. unsigned int n = 0;
  162. if (i >= 0 && i < size && board[i] != WALL)
  163. {
  164. // if i is a correct tile number (inside the array and not on a wall)
  165. // look in the four adjacent tiles and keep only those with no wall
  166. const unsigned int moves[] = { i - 1, i + 1, i - l, i + l};
  167. for (unsigned int k = 0; k < 4; k++)
  168. {
  169. if (board[moves[k]] != WALL)
  170. {
  171. r[n] = moves[k];
  172. n++;
  173. }
  174. }
  175. }
  176. return n;
  177. }
  178. // Mark a list of points in the world
  179. void markAll(const list<unsigned int>& path, int value = TRACE) {
  180. for (auto tile : path) {
  181. markOne(tile, value);
  182. }
  183. }
  184. // Mark a point in the world
  185. void markOne(unsigned int tile, int value = ORIGIN) {
  186. if (value == WALL && board[tile] != WALL)
  187. tileQuantity --;
  188. board[tile] = value;
  189. }
  190. // Depth-first search
  191. // starting from tile number origin, find a path to tile number t
  192. // return true if such a path exists, false otherwise
  193. // if it exists the path is given in variable path (hence the reference &)
  194. const bool dfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
  195. {
  196. bool targetIsReached = false;
  197. bool explored[size];
  198. unsigned int previous[size];
  199. for (unsigned int k(0); k < size; k ++) {
  200. explored[k] = false;
  201. previous[k] = k;
  202. }
  203. explored[origin] = true;
  204. stack<unsigned int> open;
  205. open.push(origin);
  206. int current;
  207. int neighbour;
  208. unsigned int succs[4];
  209. unsigned int nbSuccs;
  210. do {
  211. current = open.top();
  212. open.pop();
  213. nbSuccs = successors(current, succs);
  214. for (unsigned int i(0); i < nbSuccs; i++) {
  215. neighbour = succs[i];
  216. if (!explored[neighbour]) {
  217. open.push(neighbour);
  218. explored[neighbour] = true;
  219. previous[neighbour] = current;
  220. }
  221. }
  222. // Current tile is now processed
  223. discovered.push_back(current);
  224. // Stop if target found
  225. targetIsReached = (current == target);
  226. } while (!targetIsReached && !open.empty());
  227. // Remove origin and target
  228. if (!discovered.empty()) {
  229. discovered.remove(origin);
  230. discovered.remove(target);
  231. }
  232. // Build path
  233. if (targetIsReached) {
  234. do {
  235. path.push_back(current);
  236. current = previous[current];
  237. } while (current != origin);
  238. path.pop_front();
  239. }
  240. return targetIsReached;
  241. }
  242. // Breadth-first search
  243. // starting from tile number origin, find a path to tile number t
  244. // return true if such a path exists, false otherwise
  245. // if it exists the path is given in variable path (hence the reference &)
  246. const bool bfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
  247. {
  248. bool targetIsReached = false;
  249. bool explored[size];
  250. unsigned int previous[size];
  251. for (unsigned int k(0); k < size; k ++) {
  252. explored[k] = false;
  253. previous[k] = k;
  254. }
  255. explored[origin] = true;
  256. queue<unsigned int> open;
  257. open.push(origin);
  258. int current;
  259. int neighbour;
  260. unsigned int succs[4];
  261. unsigned int nbSuccs;
  262. do {
  263. current = open.front();
  264. open.pop();
  265. nbSuccs = successors(current, succs);
  266. for (unsigned int i(0); i < nbSuccs; i++) {
  267. neighbour = succs[i];
  268. if (!explored[neighbour]) {
  269. open.push(neighbour);
  270. explored[neighbour] = true;
  271. previous[neighbour] = current;
  272. }
  273. }
  274. // Current tile is now processed
  275. discovered.push_back(current);
  276. // Stop if target found
  277. targetIsReached = (current == target);
  278. } while (!targetIsReached && !open.empty());
  279. // Remove origin and target
  280. if (!discovered.empty()) {
  281. discovered.remove(origin);
  282. discovered.remove(target);
  283. }
  284. // Build path
  285. if (targetIsReached) {
  286. do {
  287. path.push_back(current);
  288. current = previous[current];
  289. } while (current != origin);
  290. path.pop_front();
  291. }
  292. return targetIsReached;
  293. }
  294. const bool aStar(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
  295. {
  296. bool targetIsReached = false;
  297. bool explored[size];
  298. unsigned int previous[size];
  299. unsigned int realCost[size]; // distance from origin
  300. for (unsigned int k(0); k < size; k ++) {
  301. explored[k] = false;
  302. previous[k] = k;
  303. realCost[k] = numeric_limits<unsigned int>::max();
  304. }
  305. explored[origin] = true;
  306. realCost[origin] = 0;
  307. priority_queue
  308. <
  309. pair<unsigned int, unsigned int>,
  310. vector<pair<unsigned int, unsigned int>>,
  311. greater<pair<unsigned int, unsigned int>>
  312. > open;
  313. open.push(make_pair(euristic(origin, target), origin));
  314. int current;
  315. int currentCost;
  316. int neighbour;
  317. unsigned int succs[4];
  318. unsigned int nbSuccs;
  319. do {
  320. current = open.top().second;
  321. currentCost = realCost[current];
  322. open.pop();
  323. nbSuccs = successors(current, succs);
  324. for (unsigned int i(0); i < nbSuccs; i++) {
  325. neighbour = succs[i];
  326. // Update path if better
  327. if (currentCost + 1 < realCost[neighbour]) {
  328. previous[neighbour] = current;
  329. realCost[neighbour] = currentCost + 1;
  330. }
  331. // Explore if unknown
  332. if (!explored[neighbour]) {
  333. explored[neighbour] = true;
  334. open.push(make_pair(realCost[neighbour] + euristic(neighbour, target), neighbour));
  335. }
  336. }
  337. // Current tile is now processed
  338. discovered.push_back(current);
  339. // Stop if target found
  340. targetIsReached = (current == target);
  341. } while (!targetIsReached && !open.empty());
  342. // Remove origin and target
  343. if (!discovered.empty()) {
  344. discovered.remove(origin);
  345. discovered.remove(target);
  346. }
  347. // Build path
  348. if (targetIsReached) {
  349. do {
  350. path.push_back(current);
  351. current = previous[current];
  352. } while (current != origin);
  353. path.pop_front();
  354. }
  355. return targetIsReached;
  356. }
  357. unsigned int euristic(unsigned int from, unsigned int to) {
  358. // Manhattan
  359. unsigned int x1, y1, x2, y2, dx, dy;
  360. locateTile(from, l, x1, y1);
  361. locateTile(to, l, x2, y2);
  362. dx = delta(x1, x2);
  363. dy = delta(y1, y2);
  364. return dx + dy;
  365. }
  366. void animate(bool exitFound, const list<unsigned int>& discovered, const list<unsigned int>& path) {
  367. for (unsigned int tile : discovered) {
  368. markOne(tile, DISCOVERED);
  369. display();
  370. usleep(DEFAULT_ANIMATION_DELAY);
  371. moveCursorUp(h);
  372. }
  373. if (exitFound)
  374. for (unsigned int tile : path) {
  375. markOne(tile, TRACE);
  376. display();
  377. usleep(DEFAULT_ANIMATION_DELAY);
  378. moveCursorUp(h);
  379. }
  380. }
  381. void showResults(bool exitFound, const list<unsigned int>& discovered, const list<unsigned int>& path) {
  382. markAll(discovered, DISCOVERED);
  383. if (!path.empty()) {
  384. markAll(path, TRACE);
  385. }
  386. display();
  387. // Display DFS results
  388. if (exitFound)
  389. cout << "SUCCESS !" << endl;
  390. else
  391. cout << "FAILURE ..." << endl;
  392. const unsigned int discoveryRate(100 * discovered.size() / tileQuantity);
  393. cout << discovered.size() << " tiles discovered (" << discoveryRate << "%);" << endl;
  394. cout << path.size() << " path length." << endl;
  395. }
  396. void showProperties() {
  397. cout << "Length : " << l << endl;
  398. cout << "Height : " << h << endl;
  399. cout << "Number of floor tiles : " << tileQuantity << endl;
  400. }
  401. };
  402. int main()
  403. {
  404. // Initialise the random number generator
  405. srand(time(0));
  406. // Create a world
  407. const unsigned int l(DEFAULT_LENGTH), h(DEFAULT_HEIGHT);
  408. const double wallProbability(DEFAULT_PROBABILITY);
  409. World w;
  410. w.generate(l, h, wallProbability);
  411. unsigned int start(identifyTile(1, 1, l));
  412. unsigned int end(identifyTile(h - 2, l - 2, l));
  413. // Display it
  414. cout << endl << "Generated world" << endl;
  415. w.showProperties();
  416. w.markOne(start, ORIGIN);
  417. w.markOne(end, TARGET);
  418. w.display();
  419. const bool animation(DEFAULT_ANIMATION);
  420. // 1
  421. cout << endl << "Depth-First Search" << endl;
  422. World dfsWorld(w);
  423. list<unsigned int> dfsPath;
  424. list<unsigned int> dfsDiscovered;
  425. bool dfsExitFound = dfsWorld.dfs(start, end, dfsPath, dfsDiscovered);
  426. if (animation)
  427. dfsWorld.animate(dfsExitFound, dfsDiscovered, dfsPath);
  428. dfsWorld.showResults(dfsExitFound, dfsDiscovered, dfsPath);
  429. // 2
  430. cout << endl << "Breadth-First Search" << endl;
  431. World bfsWorld(w);
  432. list<unsigned int> bfsPath;
  433. list<unsigned int> bfsDiscovered;
  434. bool bfsExitFound = bfsWorld.bfs(start, end, bfsPath, bfsDiscovered);
  435. if (animation)
  436. bfsWorld.animate(bfsExitFound, bfsDiscovered, bfsPath);
  437. bfsWorld.showResults(bfsExitFound, bfsDiscovered, bfsPath);
  438. // 3
  439. cout << endl << "A star" << endl;
  440. World aStarWorld(w);
  441. list<unsigned int> aStarPath;
  442. list<unsigned int> aStarDiscovered;
  443. bool aStarExitFound = aStarWorld.aStar(start, end, aStarPath, aStarDiscovered);
  444. if (animation)
  445. aStarWorld.animate(aStarExitFound, aStarDiscovered, aStarPath);
  446. aStarWorld.showResults(aStarExitFound, aStarDiscovered, aStarPath);
  447. // End
  448. return 0;
  449. }