Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ignore the following
*.o
pathfinder
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# gros_MidtermProject
Midterm Project of lecture "Advanced Introduction to C++, scientific computing and machine learning".
gros_MidtermProject
Midterm Project of lecture "Advanced Introduction to C++, scientific computing and machine learning".
Wintersemester 2018/19

# Project Description
### 2.20 Path finding
Supervisor: [Emanuele Varriale](mailto:varriale@itp.uni-frankfurt.de)
Path finding algorithms have to fi nd the optimal path between two points on a map (1). A map can
be represented as a graph, the easiest way being "discretizing" the map into a grid: cells are nodes that
are connected, for example, with nearest neighbours.
In an unweighted graph one can use a Breadth fi rst search algorithm to explore the graph, enumerate
all possible paths and then fi nd the shortest paths to the targets (2).
The graph can also be weighted, representing, for example, the cost of walking over different terrains.
The Uniform Cost Search algorithm (4) (similar to Dijkstra's algorithm) takes the weights into account
by enumerating all possible paths, but prioritizing the ones with the least cost.
Another possibility is the greedy best fi rst search (3), that has a heuristic measure of distance to the
target and fi rst explore the paths with the least distance. One such heuristic on a square grid can be, for
example, the Manhattan distance `d = |x1 - x2| + |y1 - y2| `. It is faster than the other approaches but it
is not guaranteed to fi nd the optimal solution.
The A* algorithm (5) also prioritizes the search but it combines the calculated distance and the
heuristic distance. With the right heuristic it finds the optimal solution, but exploring less paths than
Uniform cost algorithm.
Your task is to implement these algorithms, compare the time performance, optimality of solution
on different kind of maps. You should consider maps with normal cells with weight 1, walls, i.e. cells
that cannot be visited, and, "forest" cells with weight 5. Use these elements to create interesting geometries
showing different behaviours of the algorithms. As an implementation detail, `std::queue` and
`std::priority_queue` can be used to store the nodes to visit next.
(1) [Pathfi nding](https://en.wikipedia.org/wiki/Pathfinding)
(2) [Breadth first search](https://en.wikipedia.org/wiki/Breadth-first_search)
(3) [Greedy best fi rst](https://en.wikipedia.org/wiki/Best-first_search)
(4) [Uniform cost search](https://algorithmicthoughts.wordpress.com/2012/12/15/artificial-intelligence-uniform-cost-searchucs/)
(5) [A* algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm)


# How to compile
After cloning the repository, or making changes in the sourcecode, just do
` /gros_MidtermProject$ make `
if this succeeds without errors, there will be an executable "pathfinder".
47 changes: 47 additions & 0 deletions class_map.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "class_map.h"

#include <iostream>
#include <chrono>
#include <random>


pfMap::pfMap(int w, int h){
width = w;
height = h;
// fill the nodes-vector with random nodes
// nodes on the edge of the map should be walls

// obtain a seed, initialize a random number generator and initialize a uniform distribution
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::mt19937_64 gen(seed);
std::uniform_int_distribution<int> random(1,4);

for(int i=0 ; i<width ; i++){
for(int j=0 ; j<height ; j++){
if( i==0 || i==(width-1) || j==0 || j==(height-1) ){
nodes.insert( std::make_pair( std::array<int,2>{i,j}, pfNode(1) ) );
} else {
int rnd_type = random(gen) ;
if(rnd_type == 4 ){ rnd_type = 2; }
nodes.insert( std::make_pair( std::array<int,2>{i,j}, pfNode(rnd_type) ) );
}
}
}

}

int pfMap::PrintMap(){
std::array<int,2> pos;
for(int j=height ; j>0 ; j--){
for(int i=0 ; i<width ; i++){
pos[0] = i;
pos[1] = j-1;
auto it = nodes.find(pos);
if( it != nodes.end() ){
it->second.Print();
}
}
std::cout << std::endl ;
}
std::cout << std::endl ;
}
35 changes: 35 additions & 0 deletions class_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef pfMap_h
#define pfMap_h

#include "class_node.h"
#include <map>
#include <array>

class pfMap {
private:
int width; // number of nodes in horizontal direction
int height; // number of nodes in vertical direction


std::map< std::array<int,2>, pfNode > nodes ;

public:
// constructor to initialise the map with random nodes
pfMap(int w, int h);

void SetWidth(int w) ;
void SetHeight(int h) ;

int GetWidth(){ return width; }
int GetHeigth(){ return height; }
int GetNnodes(){ return width * height ; }

// prints the map to std::cout
int PrintMap();

};




#endif
55 changes: 55 additions & 0 deletions class_node.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "class_node.h"

#include <iostream>
#include <array>

pfNode::pfNode(int t){
// TODO: catch wrong values of t .
type = t;
switch (type) {
case 1 : // Wall
weight = -1 ;
break;
case 2 : // Grass
weight = 1 ;
break;
case 3 : // Forest
weight = 5 ;
break;
default :
weight = 1 ;
}

}

std::string pfNode::GetTypeName(){
switch (type) {
case 1 : // Wall
return "Wall" ;
case 2 : // Grass
return "Grass" ;
case 3 : // Forest
return "Forest" ;
default :
return "Undefined" ;
}

}

void pfNode::Print(){
std::array<int,3> color = {0,0,0};
switch (type) {
case 1 : // Wall
color = {115,115,115};
break;
case 2 : // Grass
color = {51, 204, 51};
break;
case 3 : // Forest
color = {0, 153, 51};
break;
}
char buffer1[50];
sprintf(buffer1, "\033[48;2;%u;%u;%um \033[0m", color[0],color[1],color[2]);
std::cout << buffer1 ;
}
30 changes: 30 additions & 0 deletions class_node.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef pfNode_h
#define pfNode_h

#include <string>

class pfNode {
private:
int type ; // type of node. 1 wall, 2 grass, 3 forest
int weight; // depends on the type. grass 1, forest 5, wall -1


public:
// constructor
pfNode(int t);


int GetType(){ return type; }
int GetWeight(){ return weight; }

// returns "Wall", "Grass", etc..
std::string GetTypeName() ;

// prints the node to std::cout with an rgb colored whitespace
void Print();

};



#endif
24 changes: 24 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// MA: TEST

#include <iostream>

#include "class_node.h"
#include "class_map.h"

int main(int argc, char** argv){

/*
pfNode n1(1) ;
pfNode n2(2);
pfNode n3(3);

std::cout << n1.GetTypeName() << std::endl ;
std::cout << n2.GetTypeName() << std::endl ;
std::cout << n3.GetTypeName() << std::endl ;
*/
pfMap map1(50,20);

map1.PrintMap();

return 0;
}
17 changes: 17 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CFLAGS = -Wall -W -O -std=c++11

all: main.o class_map.o class_node.o
g++ $(CFLAGS) -o pathfinder main.o class_map.o class_node.o

main: main.cpp
g++ $(CFLAGS) -c main.cpp

class_map: class_map.cpp class_map.h
g++ $(CFLAGS) -c class_map.cpp

class_node: class_node.cpp class_node.h
g++ $(CFLAGS) -c class_node.cpp

clean:
rm -f core *.o
rm *~