From 40562a67c86d21a8ad411c0711889c519697e150 Mon Sep 17 00:00:00 2001 From: Maykeye Date: Mon, 27 Aug 2018 22:58:22 +0600 Subject: [PATCH] Changed A* exit condition, added 2 tests for it A* now exits when next node from open set with least cost happens to be end_point, not when node with least cost has end_point as a neigbour. Added two tests for astar: * ABC tests case where start and end node are neigbours * ABCX tests case with intermediate nodes --- core/math/a_star.cpp | 22 ++------ main/tests/test_astar.cpp | 116 ++++++++++++++++++++++++++++++++++++++ main/tests/test_astar.h | 41 ++++++++++++++ main/tests/test_main.cpp | 7 +++ 4 files changed, 169 insertions(+), 17 deletions(-) create mode 100644 main/tests/test_astar.cpp create mode 100644 main/tests/test_astar.h diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 021391da832..09472059b12 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -249,14 +249,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; n->last_pass = pass; open_list.add(&n->list); - - if (end_point == n) { - found_route = true; - break; - } } - while (!found_route) { + while (true) { if (open_list.first() == NULL) { // No path found @@ -276,13 +271,16 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { cost += _estimate_cost(p->id, end_point->id); if (cost < least_cost) { - least_cost_point = E; least_cost = cost; } } Point *p = least_cost_point->self(); + if (p == end_point) { + found_route = true; + break; + } for (Set::Element *E = p->neighbours.front(); E; E = E->next()) { @@ -294,7 +292,6 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { // Already visited, is this cheaper? if (e->distance > distance) { - e->prev_point = p; e->distance = distance; } @@ -305,18 +302,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { e->distance = distance; e->last_pass = pass; // Mark as used open_list.add(&e->list); - - if (e == end_point) { - // End reached; stop algorithm - found_route = true; - break; - } } } - if (found_route) - break; - open_list.remove(least_cost_point); } diff --git a/main/tests/test_astar.cpp b/main/tests/test_astar.cpp new file mode 100644 index 00000000000..0f034c78f78 --- /dev/null +++ b/main/tests/test_astar.cpp @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* test_astar.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "a_star.h" +#include "os/os.h" +#include + +#include "test_astar.h" + +namespace TestAStar { + +class ABCX : public AStar { +public: + enum { A, + B, + C, + X }; + + ABCX() { + add_point(A, Vector3(0, 0, 0)); + add_point(B, Vector3(1, 0, 0)); + add_point(C, Vector3(0, 1, 0)); + add_point(X, Vector3(0, 0, 1)); + connect_points(A, B); + connect_points(A, C); + connect_points(B, C); + connect_points(X, A); + } + + // Disable heuristic completely + float _compute_cost(int p_from, int p_to) { + if (p_from == A && p_to == C) { + return 1000; + } + return 100; + } +}; + +bool test_abc() { + ABCX abcx; + PoolVector path = abcx.get_id_path(ABCX::A, ABCX::C); + bool ok = path.size() == 3; + int i = 0; + ok = ok && path[i++] == ABCX::A; + ok = ok && path[i++] == ABCX::B; + ok = ok && path[i++] == ABCX::C; + return ok; +} + +bool test_abcx() { + ABCX abcx; + PoolVector path = abcx.get_id_path(ABCX::X, ABCX::C); + bool ok = path.size() == 4; + int i = 0; + ok = ok && path[i++] == ABCX::X; + ok = ok && path[i++] == ABCX::A; + ok = ok && path[i++] == ABCX::B; + ok = ok && path[i++] == ABCX::C; + return ok; +} + +typedef bool (*TestFunc)(void); + +TestFunc test_funcs[] = { + test_abc, + test_abcx, + NULL +}; + +MainLoop *test() { + int count = 0; + int passed = 0; + + while (true) { + if (!test_funcs[count]) + break; + bool pass = test_funcs[count](); + if (pass) + passed++; + OS::get_singleton()->print("\t%s\n", pass ? "PASS" : "FAILED"); + + count++; + } + OS::get_singleton()->print("\n"); + OS::get_singleton()->print("Passed %i of %i tests\n", passed, count); + return NULL; +} + +} // namespace TestAStar diff --git a/main/tests/test_astar.h b/main/tests/test_astar.h new file mode 100644 index 00000000000..60297420171 --- /dev/null +++ b/main/tests/test_astar.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* test_astar.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_ASTAR_H +#define TEST_ASTAR_H + +#include "os/main_loop.h" + +namespace TestAStar { + +MainLoop *test(); +} + +#endif diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index cbc1107acb8..e82d7997095 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -33,6 +33,7 @@ #ifdef DEBUG_ENABLED +#include "test_astar.h" #include "test_gdscript.h" #include "test_gui.h" #include "test_image.h" @@ -64,6 +65,7 @@ const char **tests_get_names() { "gd_bytecode", "image", "ordered_hash_map", + "astar", NULL }; @@ -149,6 +151,11 @@ MainLoop *test_main(String p_test, const List &p_args) { return TestOrderedHashMap::test(); } + if (p_test == "astar") { + + return TestAStar::test(); + } + return NULL; }