123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- // Example of world building, display, and successor computation for the artificial
- // intelligence path-finding lab
- //
- // Author: Didier LIME
- // Adapted by : Jovian HERSEMEULE
- // Date: 2018-10-03
- #include <iostream>
- #include <list>
- #include <cstdlib>
- #include <ctime>
- #include <stack>
- #include <queue>
- #include <unistd.h>
- using namespace std;
- // Default parameters
- #define DEFAULT_LENGTH 50
- #define DEFAULT_HEIGHT 15
- #define DEFAULT_PROBABILITY 0.2
- #define DEFAULT_ANIMATION true
- #define DEFAULT_ANIMATION_DELAY 50000 // us
- #define DEFAULT_COLOR_ENABLE true
- // Tile codes
- #define FREE 0
- #define WALL 1
- #define ORIGIN 2
- #define TARGET 3
- #define DISCOVERED 4
- #define TRACE 5
- unsigned int identifyTile(unsigned int y, unsigned int x, unsigned int l) {
- return y * l + x;
- }
- void moveCursorUp(const unsigned int& h) {
- cout << "\033[" << h << 'A';
- }
- class World
- {
- private:
- // Number of columns
- unsigned int l;
- // Number of lines
- unsigned int h;
- // Size of the array
- const unsigned int size;
- // Unidimensional array for tiles
- int* board;
- // Number of empty tiles
- unsigned int tileQuantity;
- public:
- // Constructor
- World(unsigned int l_, unsigned int h_, double p)
- :l(l_), h(h_), size(l_ * h_), tileQuantity(l_ * h_)
- {
- board = new int[size]();
- // Add walls to the first and last columns
- for (unsigned int i = 0; i < h; i++)
- {
- board[i * l] = WALL;
- board[i * l + l - 1] = WALL;
- tileQuantity -= 2;
- }
- // Add walls to the first and last lines
- for (unsigned int j = 0; j < l; j++)
- {
- board[j] = WALL;
- board[(h - 1) * l + j] = WALL;
- tileQuantity -= 2;
- }
- for (unsigned int i = 0; i < h; i++)
- {
- for (unsigned int j = 0; j < l; j++)
- {
- // add a wall in this tile with probability p and provided that it is neither
- // the starting tile nor the goal tile
- if ((double) rand() / RAND_MAX < p && !(i == 1 && j == 1) && !(i == h - 2 && j == l - 2))
- {
- board[i * l + j] = WALL;
- tileQuantity --;
- }
- }
- }
- }
- // Copy constructor
- World(const World& other)
- :l(other.l), h(other.h), size(other.size), tileQuantity(other.tileQuantity)
- {
- board = new int[size]();
- for (int k(0); k < size; k++) {
- board[k] = other.board[k];
- }
- }
- // Display the world
- void display()
- {
- for (unsigned int i = 0; i < h; i++)
- {
- for (unsigned int j = 0; j < l; j++)
- {
- switch (board[identifyTile(i, j, l)])
- {
- case FREE:
- cout << " ";
- break;
- case WALL:
- if (DEFAULT_COLOR_ENABLE) cout << "\033[0;43m";
- cout << "#";
- break;
- case ORIGIN:
- if (DEFAULT_COLOR_ENABLE) cout << "\033[1;33m";
- cout << "o";
- break;
- case TARGET:
- if (DEFAULT_COLOR_ENABLE) cout << "\033[1;33m";
- cout << "T";
- break;
- case DISCOVERED:
- if (DEFAULT_COLOR_ENABLE) cout << "\033[0;34m";
- cout << ":";
- break;
- case TRACE:
- if (DEFAULT_COLOR_ENABLE) {
- cout << "\033[0;32m";
- cout << "\033[1;42m";
- cout << ":";
- }
- else
- cout << "+";
- break;
- }
- if (DEFAULT_COLOR_ENABLE) cout << "\033[0;30m";
- }
- cout << endl;
- }
- }
- // compute the successors of tile number i in world w
- // we return the number n of valid successors
- // the actual list is in array r where only the first n
- // elements are significant
- unsigned int successors(unsigned int i, unsigned int r[4])
- {
- unsigned int n = 0;
- if (i >= 0 && i < size && board[i] != WALL)
- {
- // if i is a correct tile number (inside the array and not on a wall)
- // look in the four adjacent tiles and keep only those with no wall
- const unsigned int moves[] = { i - 1, i + 1, i - l, i + l};
- for (unsigned int k = 0; k < 4; k++)
- {
- if (board[moves[k]] != WALL)
- {
- r[n] = moves[k];
- n++;
- }
- }
- }
- return n;
- }
- // Mark a list of points in the world
- void markAll(const list<unsigned int>& path, int value = TRACE) {
- for (auto tile : path) {
- markOne(tile, value);
- }
- }
- // Mark a point in the world
- void markOne(unsigned int tile, int value = ORIGIN) {
- board[tile] = value;
- }
- // Depth-first search
- // starting from tile number origin, find a path to tile number t
- // return true if such a path exists, false otherwise
- // if it exists the path is given in variable path (hence the reference &)
- const bool dfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
- {
- bool targetIsReached = false;
- bool explored[size];
- unsigned int previous[size];
- for (unsigned int k(0); k < size; k ++) {
- explored[k] = false;
- previous[k] = k;
- }
- explored[origin] = true;
- stack<unsigned int> open;
- open.push(origin);
- int current;
- int neighbour;
- unsigned int succs[4];
- unsigned int nbSuccs;
- do {
- current = open.top();
- open.pop();
- nbSuccs = successors(current, succs);
- for (unsigned int i(0); i < nbSuccs; i++) {
- neighbour = succs[i];
- if (!explored[neighbour]) {
- open.push(neighbour);
- explored[neighbour] = true;
- previous[neighbour] = current;
- }
- }
- // Current tile is now processed
- discovered.push_back(current);
- // Stop if target found
- targetIsReached = (current == target);
- } while (!targetIsReached && !open.empty());
- // Remove origin and target
- if (!discovered.empty()) {
- discovered.remove(origin);
- discovered.remove(target);
- }
- // Build path
- if (targetIsReached) {
- do {
- path.push_back(current);
- current = previous[current];
- } while (current != origin);
- path.pop_front();
- }
- return targetIsReached;
- }
- // Breadth-first search
- // starting from tile number origin, find a path to tile number t
- // return true if such a path exists, false otherwise
- // if it exists the path is given in variable path (hence the reference &)
- const bool bfs(unsigned int origin, unsigned int target, list<unsigned int>& path, list<unsigned int>& discovered)
- {
- bool targetIsReached = false;
- bool explored[size];
- unsigned int previous[size];
- for (unsigned int k(0); k < size; k ++) {
- explored[k] = false;
- previous[k] = k;
- }
- explored[origin] = true;
- queue<unsigned int> open;
- open.push(origin);
- int current;
- int neighbour;
- unsigned int succs[4];
- unsigned int nbSuccs;
- do {
- current = open.front();
- open.pop();
- nbSuccs = successors(current, succs);
- for (unsigned int i(0); i < nbSuccs; i++) {
- neighbour = succs[i];
- if (!explored[neighbour]) {
- open.push(neighbour);
- explored[neighbour] = true;
- previous[neighbour] = current;
- }
- }
- // Current tile is now processed
- discovered.push_back(current);
- // Stop if target found
- targetIsReached = (current == target);
- } while (!targetIsReached && !open.empty());
- // Remove origin and target
- if (!discovered.empty()) {
- discovered.remove(origin);
- discovered.remove(target);
- }
- // Build path
- if (targetIsReached) {
- do {
- path.push_back(current);
- current = previous[current];
- } while (current != origin);
- path.pop_front();
- }
- return targetIsReached;
- }
- void animate(bool exitFound, const list<unsigned int>& discovered, const list<unsigned int>& path) {
- for (unsigned int tile : discovered) {
- markOne(tile, DISCOVERED);
- display();
- usleep(DEFAULT_ANIMATION_DELAY);
- moveCursorUp(h);
- }
- if (exitFound)
- for (unsigned int tile : path) {
- markOne(tile, TRACE);
- display();
- usleep(DEFAULT_ANIMATION_DELAY);
- moveCursorUp(h);
- }
- }
- void showResults(bool exitFound, const list<unsigned int>& discovered, const list<unsigned int>& path) {
- markAll(discovered, DISCOVERED);
- if (!path.empty()) {
- markAll(path, TRACE);
- }
- display();
- // Display DFS results
- if (exitFound)
- cout << "SUCCESS !" << endl;
- else
- cout << "FAILURE ..." << endl;
- const unsigned int discoveryRate(100 * discovered.size() / tileQuantity);
- cout << discovered.size() << " tiles discovered (" << discoveryRate << "%);" << endl;
- cout << path.size() << " path length." << endl;
- }
- void showProperties() {
- cout << "Length : " << l << endl;
- cout << "Height : " << h << endl;
- cout << "Number of floor tiles : " << tileQuantity << endl;
- }
- };
- int main()
- {
- // Initialise the random number generator
- srand(time(0));
- // Create a world
- const unsigned int l(DEFAULT_LENGTH), h(DEFAULT_HEIGHT);
- const double wallProbability(DEFAULT_PROBABILITY);
- World w(l, h, wallProbability);
- unsigned int start(identifyTile(1, 1, l));
- unsigned int end(identifyTile(h - 2, l - 2, l));
- // Display it
- cout << endl << "Generated world" << endl;
- w.showProperties();
- w.markOne(start, ORIGIN);
- w.markOne(end, TARGET);
- w.display();
- const bool animation(DEFAULT_ANIMATION);
- // 1
- cout << endl << "Depth-First Search" << endl;
- World dfsWorld(w);
- list<unsigned int> dfsPath;
- list<unsigned int> dfsDiscovered;
- bool dfsExitFound = dfsWorld.dfs(start, end, dfsPath, dfsDiscovered);
- if (animation)
- dfsWorld.animate(dfsExitFound, dfsDiscovered, dfsPath);
- dfsWorld.showResults(dfsExitFound, dfsDiscovered, dfsPath);
- // 2
- cout << endl << "Breadth-First Search" << endl;
- World bfsWorld(w);
- list<unsigned int> bfsPath;
- list<unsigned int> bfsDiscovered;
- bool bfsExitFound = bfsWorld.bfs(start, end, bfsPath, bfsDiscovered);
- if (animation)
- bfsWorld.animate(bfsExitFound, bfsDiscovered, bfsPath);
- bfsWorld.showResults(bfsExitFound, bfsDiscovered, bfsPath);
- // End
- return 0;
- }
|