Skip to content

Commit 70c5171

Browse files
committed
fix: pathfinding
1 parent b4f40d6 commit 70c5171

2 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Pathfinding on a 2D grid
2+
3+
4+
5+
```c++
6+
#include <iostream>
7+
#include <unordered_map>
8+
#include <unordered_set>
9+
#include <cmath>
10+
#include <vector>
11+
#include <queue>
12+
// Simple A-star pathfinding algorithm
13+
14+
// vector2 struct
15+
template <typename T>
16+
// requires T to be int32_t or float_t
17+
requires std::is_same<T, int32_t>::value || std::is_same<T, float_t>::value
18+
struct Vector2 {
19+
T x, y;
20+
Vector2() : x(0), y(0) {}
21+
Vector2(T x, T y) : x(x), y(y) {}
22+
Vector2(const Vector2& v) : x(v.x), y(v.y) {}
23+
Vector2& operator=(const Vector2& v) {
24+
x = v.x;
25+
y = v.y;
26+
return *this;
27+
}
28+
Vector2 operator+(const Vector2& v) const {
29+
return Vector2(x + v.x, y + v.y);
30+
}
31+
Vector2 operator-(const Vector2& v) const {
32+
return Vector2(x - v.x, y - v.y);
33+
}
34+
float distance(const Vector2& v) const {
35+
return sqrt((x - v.x) * (x - v.x) + (y - v.y) * (y - v.y));
36+
}
37+
float distanceSquared(const Vector2& v) const {
38+
return (x - v.x) * (x - v.x) + (y - v.y) * (y - v.y);
39+
}
40+
Vector2<int32_t> quantized(float scale=1) const {
41+
return {(int32_t)std::round(x / scale), (int32_t)std::round(y / scale)};
42+
}
43+
// operator < for std::map
44+
bool operator<(const Vector2& v) const {
45+
return x < v.x || (x == v.x && y < v.y);
46+
}
47+
// operator == for std::map
48+
bool operator==(const Vector2& v) const {
49+
return x == v.x && y == v.y;
50+
}
51+
};
52+
53+
using Vector2i = Vector2<int32_t>;
54+
using Vector2f = Vector2<float_t>;
55+
56+
struct uid_type {
57+
private:
58+
static inline size_t nextId = 0; // to be used as a counter
59+
size_t uid; // to be used as a unique identifier
60+
public:
61+
// not thread safe, but it is not a problem for this example
62+
uid_type(): uid(nextId++) {}
63+
inline size_t getUid() const { return uid; }
64+
};
65+
66+
67+
struct GameObject: public uid_type {
68+
Vector2f position;
69+
GameObject(const Vector2f& position) : position(position) {}
70+
GameObject() : position(Vector2f()) {}
71+
72+
// operator < for std::map
73+
bool operator<(const GameObject& g) const {
74+
return getUid() < g.getUid();
75+
}
76+
77+
// operator == for std::map
78+
bool operator==(const GameObject& g) const {
79+
return getUid() == g.getUid();
80+
}
81+
};
82+
83+
// hash function for std::unordered_map
84+
template <typename T>
85+
struct std::hash<Vector2<T>> {
86+
size_t operator()(const Vector2<T> &v) const {
87+
return hash<T>()(v.x) ^ hash<T>()(v.y);
88+
}
89+
};
90+
91+
// hash function for std::unordered_map
92+
template <>
93+
struct std::hash<GameObject> {
94+
size_t operator()(const GameObject &g) const {
95+
return std::hash<size_t>()(g.getUid());
96+
}
97+
};
98+
99+
std::unordered_map<Vector2i, std::unordered_set<GameObject>> quantizedMap;
100+
std::unordered_set<GameObject> gameObjects;
101+
std::unordered_map<Vector2i, bool> isWall;
102+
103+
std::vector<Vector2i> getVisitableNeighbors(const Vector2i& v) {
104+
std::vector<Vector2i> neighbors;
105+
auto candidates = {Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)};
106+
for (auto c : candidates) {
107+
Vector2i n = v + c;
108+
if (!isWall.contains(n)) {
109+
neighbors.push_back(n);
110+
}
111+
}
112+
return neighbors;
113+
}
114+
115+
// Pathfinding algorithm from position A to position B
116+
std::vector<Vector2i> findPath(const Vector2f& start, const Vector2f& end) {
117+
// quantize
118+
Vector2i startQuantized = start.quantized();
119+
Vector2i endQuantized = end.quantized();
120+
121+
// datastructures
122+
std::unordered_map<Vector2i, bool> isVisited;
123+
std::unordered_map<Vector2i, Vector2i> cameFrom;
124+
std::unordered_map<Vector2i, float> costSoFar;
125+
std::unordered_map<Vector2i, float> priority;
126+
std::priority_queue<std::pair<float, Vector2i>> frontier;
127+
128+
// initialize
129+
isVisited[startQuantized] = false;
130+
costSoFar[startQuantized] = 0;
131+
priority[startQuantized] = 0;
132+
frontier.push({0, startQuantized});
133+
134+
// main loop
135+
while (!frontier.empty()) {
136+
auto current = frontier.top().second;
137+
frontier.pop();
138+
139+
if (current == endQuantized) {
140+
break;
141+
}
142+
143+
for (auto next : getVisitableNeighbors(current)) {
144+
float newCost = costSoFar[current] + current.distance(next);
145+
if (!costSoFar.contains(next) || newCost < costSoFar[next]) {
146+
costSoFar[next] = newCost;
147+
float priority = newCost + next.distance(endQuantized);
148+
frontier.push({-priority, next});
149+
cameFrom[next] = current;
150+
}
151+
}
152+
}
153+
154+
// reconstruct path
155+
std::vector<Vector2i> path;
156+
Vector2i current = endQuantized;
157+
while (current != startQuantized) {
158+
path.push_back(current);
159+
current = cameFrom[current];
160+
}
161+
path.push_back(startQuantized);
162+
std::reverse(path.begin(), path.end());
163+
return path;
164+
}
165+
166+
167+
168+
int main() {
169+
// Create 2 Game Objects
170+
GameObject a(Vector2f(0, 0));
171+
GameObject b(Vector2f(10, 10));
172+
173+
// place walls
174+
isWall[Vector2i(1, 1)] = true;
175+
isWall[Vector2i(1, 2)] = true;
176+
isWall[Vector2i(1, 3)] = true;
177+
isWall[Vector2i(1, 4)] = true;
178+
isWall[Vector2i(1, 5)] = true;
179+
180+
// add game objects to the set
181+
gameObjects.insert(a);
182+
gameObjects.insert(b);
183+
184+
// add game objects to the quantized map
185+
for (auto& g : gameObjects)
186+
quantizedMap[g.position.quantized()].insert(g);
187+
188+
// find path
189+
auto path = findPath(a.position, b.position);
190+
191+
// print path
192+
for (auto& p : path) {
193+
std::cout << p.x << " " << p.y << std::endl;
194+
}
195+
196+
return 0;
197+
}
198+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ nav:
1515
- Spatial Quantization: artificialintelligence/readings/spatial-quantization.md
1616
- Spatial Hashing: artificialintelligence/04-spatialhashing/README.md
1717
- KD-Tree: artificialintelligence/05-kdtree/README.md
18+
- Pathfinding: artificialintelligence/06-pathfinding/README.md
1819
- Maze Data Structure: blog/posts/MazeDataStructure/MazeDataStructures.md
1920
- Assignments:
2021
- artificialintelligence/assignments/README.md

0 commit comments

Comments
 (0)