Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/tests/core/math/test_astar.cpp
23450 views
1
/**************************************************************************/
2
/* test_astar.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "tests/test_macros.h"
32
33
TEST_FORCE_LINK(test_astar)
34
35
#include "core/math/a_star.h"
36
37
namespace TestAStar {
38
39
class ABCX : public AStar3D {
40
public:
41
enum {
42
A,
43
B,
44
C,
45
X,
46
};
47
48
ABCX() {
49
add_point(A, Vector3(0, 0, 0));
50
add_point(B, Vector3(1, 0, 0));
51
add_point(C, Vector3(0, 1, 0));
52
add_point(X, Vector3(0, 0, 1));
53
connect_points(A, B);
54
connect_points(A, C);
55
connect_points(B, C);
56
connect_points(X, A);
57
}
58
59
// Disable heuristic completely.
60
real_t _compute_cost(int64_t p_from, int64_t p_to) {
61
if (p_from == A && p_to == C) {
62
return 1000;
63
}
64
return 100;
65
}
66
};
67
68
TEST_CASE("[AStar3D] ABC path") {
69
ABCX abcx;
70
Vector<int64_t> path = abcx.get_id_path(ABCX::A, ABCX::C);
71
REQUIRE(path.size() == 3);
72
CHECK(path[0] == ABCX::A);
73
CHECK(path[1] == ABCX::B);
74
CHECK(path[2] == ABCX::C);
75
}
76
77
TEST_CASE("[AStar3D] ABCX path") {
78
ABCX abcx;
79
Vector<int64_t> path = abcx.get_id_path(ABCX::X, ABCX::C);
80
REQUIRE(path.size() == 4);
81
CHECK(path[0] == ABCX::X);
82
CHECK(path[1] == ABCX::A);
83
CHECK(path[2] == ABCX::B);
84
CHECK(path[3] == ABCX::C);
85
}
86
87
TEST_CASE("[AStar3D] Add/Remove") {
88
AStar3D a;
89
90
// Manual tests.
91
a.add_point(1, Vector3(0, 0, 0));
92
a.add_point(2, Vector3(0, 1, 0));
93
a.add_point(3, Vector3(1, 1, 0));
94
a.add_point(4, Vector3(2, 0, 0));
95
a.connect_points(1, 2, true);
96
a.connect_points(1, 3, true);
97
a.connect_points(1, 4, false);
98
99
CHECK(a.are_points_connected(2, 1));
100
CHECK(a.are_points_connected(4, 1));
101
CHECK(a.are_points_connected(2, 1, false));
102
CHECK_FALSE(a.are_points_connected(4, 1, false));
103
104
a.disconnect_points(1, 2, true);
105
CHECK(a.get_point_connections(1).size() == 2); // 3, 4
106
CHECK(a.get_point_connections(2).size() == 0);
107
108
a.disconnect_points(4, 1, false);
109
CHECK(a.get_point_connections(1).size() == 2); // 3, 4
110
CHECK(a.get_point_connections(4).size() == 0);
111
112
a.disconnect_points(4, 1, true);
113
CHECK(a.get_point_connections(1).size() == 1); // 3
114
CHECK(a.get_point_connections(4).size() == 0);
115
116
a.connect_points(2, 3, false);
117
CHECK(a.get_point_connections(2).size() == 1); // 3
118
CHECK(a.get_point_connections(3).size() == 1); // 1
119
120
a.connect_points(2, 3, true);
121
CHECK(a.get_point_connections(2).size() == 1); // 3
122
CHECK(a.get_point_connections(3).size() == 2); // 1, 2
123
124
a.disconnect_points(2, 3, false);
125
CHECK(a.get_point_connections(2).size() == 0);
126
CHECK(a.get_point_connections(3).size() == 2); // 1, 2
127
128
a.connect_points(4, 3, true);
129
CHECK(a.get_point_connections(3).size() == 3); // 1, 2, 4
130
CHECK(a.get_point_connections(4).size() == 1); // 3
131
132
a.disconnect_points(3, 4, false);
133
CHECK(a.get_point_connections(3).size() == 2); // 1, 2
134
CHECK(a.get_point_connections(4).size() == 1); // 3
135
136
a.remove_point(3);
137
CHECK(a.get_point_connections(1).size() == 0);
138
CHECK(a.get_point_connections(2).size() == 0);
139
CHECK(a.get_point_connections(4).size() == 0);
140
141
a.add_point(0, Vector3(0, -1, 0));
142
a.add_point(3, Vector3(2, 1, 0));
143
// 0: (0, -1)
144
// 1: (0, 0)
145
// 2: (0, 1)
146
// 3: (2, 1)
147
// 4: (2, 0)
148
149
// Tests for get_closest_position_in_segment.
150
a.connect_points(2, 3);
151
CHECK(a.get_closest_position_in_segment(Vector3(0.5, 0.5, 0)) == Vector3(0.5, 1, 0));
152
153
a.connect_points(3, 4);
154
a.connect_points(0, 3);
155
a.connect_points(1, 4);
156
a.disconnect_points(1, 4, false);
157
a.disconnect_points(4, 3, false);
158
a.disconnect_points(3, 4, false);
159
// Remaining edges: <2, 3>, <0, 3>, <1, 4> (directed).
160
CHECK(a.get_closest_position_in_segment(Vector3(2, 0.5, 0)) == Vector3(1.75, 0.75, 0));
161
CHECK(a.get_closest_position_in_segment(Vector3(-1, 0.2, 0)) == Vector3(0, 0, 0));
162
CHECK(a.get_closest_position_in_segment(Vector3(3, 2, 0)) == Vector3(2, 1, 0));
163
164
Math::seed(0);
165
166
// Random tests for connectivity checks
167
for (int i = 0; i < 20000; i++) {
168
int u = Math::rand() % 5;
169
int v = Math::rand() % 4;
170
if (u == v) {
171
v = 4;
172
}
173
if (Math::rand() % 2 == 1) {
174
// Add a (possibly existing) directed edge and confirm connectivity.
175
a.connect_points(u, v, false);
176
CHECK(a.are_points_connected(u, v, false));
177
} else {
178
// Remove a (possibly nonexistent) directed edge and confirm disconnectivity.
179
a.disconnect_points(u, v, false);
180
CHECK_FALSE(a.are_points_connected(u, v, false));
181
}
182
}
183
184
// Random tests for point removal.
185
for (int i = 0; i < 20000; i++) {
186
a.clear();
187
for (int j = 0; j < 5; j++) {
188
a.add_point(j, Vector3(0, 0, 0));
189
}
190
191
// Add or remove random edges.
192
for (int j = 0; j < 10; j++) {
193
int u = Math::rand() % 5;
194
int v = Math::rand() % 4;
195
if (u == v) {
196
v = 4;
197
}
198
if (Math::rand() % 2 == 1) {
199
a.connect_points(u, v, false);
200
} else {
201
a.disconnect_points(u, v, false);
202
}
203
}
204
205
// Remove point 0.
206
a.remove_point(0);
207
// White box: this will check all edges remaining in the segments set.
208
for (int j = 1; j < 5; j++) {
209
CHECK_FALSE(a.are_points_connected(0, j, true));
210
}
211
}
212
}
213
214
TEST_CASE("[AStar3D] Path from disabled point is empty") {
215
AStar3D a;
216
Vector3 p1(0, 0, 0);
217
Vector3 p2(0, 1, 0);
218
a.add_point(1, p1);
219
a.add_point(2, p2);
220
a.connect_points(1, 2);
221
222
CHECK_EQ(a.get_id_path(1, 1), Vector<int64_t>{ 1 });
223
CHECK_EQ(a.get_id_path(1, 2), Vector<int64_t>{ 1, 2 });
224
225
CHECK_EQ(a.get_point_path(1, 1), Vector<Vector3>{ p1 });
226
CHECK_EQ(a.get_point_path(1, 2), Vector<Vector3>{ p1, p2 });
227
228
a.set_point_disabled(1, true);
229
230
CHECK(a.get_id_path(1, 1).is_empty());
231
CHECK(a.get_id_path(1, 2).is_empty());
232
233
CHECK(a.get_point_path(1, 1).is_empty());
234
CHECK(a.get_point_path(1, 2).is_empty());
235
}
236
237
} // namespace TestAStar
238
239