From b845304656ea2bb054bc830538bfe28a6ccf03f9 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Sat, 29 Dec 2018 23:47:21 +0000 Subject: [PATCH 01/11] Equality operators for TileCoordsXYZ Introduce operator == and operator != for TileCoordsXYZ, so that we can more easily operate on them in tests. --- src/openrct2/world/Location.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/openrct2/world/Location.hpp b/src/openrct2/world/Location.hpp index cc057d0c25..950a2937c6 100644 --- a/src/openrct2/world/Location.hpp +++ b/src/openrct2/world/Location.hpp @@ -116,6 +116,16 @@ struct TileCoordsXYZ y += rhs.y; return *this; } + + bool operator==(const TileCoordsXYZ& other) const + { + return x == other.x && y == other.y && z == other.z; + } + bool operator!=(const TileCoordsXYZ& other) const + { + return !(*this == other); + } + int32_t x = 0, y = 0, z = 0; }; From 8fb81a2d89e7747e74c15333487f735a70116bc1 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Sun, 30 Dec 2018 00:13:44 +0000 Subject: [PATCH 02/11] Initial data-driven tests for pathfinder behaviour Introduce some basic scenario-style tests for the pathfinding AI. There are two tests: * Test that a peep can get from a given start position to a given end position, and that it takes them an expected number of ticks to do so. Also test that they did not walk on any 'forbidden' tiles in the process, e.g. tiles that are completely the wrong direction from the goal etc. * Test that a peep can *not* get from a given start position to a given end position after a given number of ticks. Each test is parametric, and instantiated for multiple different start/end positions within the provided test park. If we find a new situation that needs a test, it should just be a matter of building that situation in the saved game and then adding a line to the code to set it up. Indicating 'forbidden' tiles is done using terrain surface type IDs: tiles that the pathfinder should never send the peep into should be painted with the red neon surface type (index 8). This means we have no way to forbid some path elements on a tile while allowing others, but we don't need that right now. Similarly, to help ensure that the test data and code are kept in sync, the tests also require that peep start tiles are painted with the green neon surface type (index 11) and that goal tiles are painted with the yellow neon surface type (index 9). --- test/tests/Pathfinding.cpp | 249 ++++++++++++++++++ .../testdata/parks/pathfinding-tests.sv6 | Bin 0 -> 286574 bytes test/tests/tests.vcxproj | 3 +- 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 test/tests/Pathfinding.cpp create mode 100644 test/tests/testdata/parks/pathfinding-tests.sv6 diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp new file mode 100644 index 0000000000..16d4bfe4f7 --- /dev/null +++ b/test/tests/Pathfinding.cpp @@ -0,0 +1,249 @@ +#include "TestData.h" +#include "openrct2/core/StringReader.hpp" +#include "openrct2/peep/Peep.h" +#include "openrct2/scenario/Scenario.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace OpenRCT2; + +static std::ostream& operator<<(std::ostream& os, const TileCoordsXYZ& coords) +{ + return os << "(" << coords.x << ", " << coords.y << ", " << coords.z << ")"; +} + +class PathfindingTestBase : public testing::Test +{ +public: + static void SetUpTestCase() + { + gOpenRCT2Headless = true; + gOpenRCT2NoGraphics = true; + _context = CreateContext(); + const bool initialised = _context->Initialise(); + ASSERT_TRUE(initialised); + + std::string parkPath = TestData::GetParkPath("pathfinding-tests.sv6"); + load_from_sv6(parkPath.c_str()); + game_load_init(); + + bitcount_init(); + } + + void SetUp() override + { + // Use a consistent random seed in every test + gScenarioSrand0 = 0x12345678; + gScenarioSrand1 = 0x87654321; + } + + static void TearDownTestCase() + { + _context = nullptr; + } + +protected: + + static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int maxSteps) + { + // Our start position is in tile coordinates, but we need to give the peep spawn + // position in actual world coords (32 units per tile X/Y, 8 per Z level). + // Add 16 so the peep spawns in the center of the tile. + rct_peep* peep = peep_generate(pos->x * 32 + 16, pos->y * 32 + 16, pos->z * 8); + + // Peeps that are outside of the park use specialized pathfinding which we don't want to + // use here + peep->outside_of_park = 0; + + // Pick the direction the peep should initially move in, given the goal position. + // This will also store the goal position and initialize pathfinding data for the peep. + gPeepPathFindGoalPosition = goal; + const int32_t moveDir = peep_pathfind_choose_direction(*pos, peep); + if (moveDir < 0) + { + // Couldn't determine a direction to move off in + return false; + } + + // We have already set up the peep's overall pathfinding goal, but we also have to set their initial + // 'destination' which is a close position that they will walk towards in a straight line - in this case, one + // tile away. Stepping the peep will move them towards their destination, and once they reach it, a new + // destination will be picked, to try and get the peep towards the overall pathfinding goal. + peep->direction = moveDir; + peep->destination_x = peep->x + CoordsDirectionDelta[moveDir].x; + peep->destination_y = peep->y + CoordsDirectionDelta[moveDir].y; + peep->destination_tolerance = 2; + + // Repeatedly step the peep, until they reach the target position or until the expected number of steps have + // elapsed. Each step, check that the tile they are standing on is not marked as forbidden in the test data + // (red neon ground type). + int step = 0; + while (!(*pos == goal) && step < maxSteps) + { + uint8_t pathingResult = 0; + peep->PerformNextAction(pathingResult); + ++step; + + pos->x = peep->x / 32; + pos->y = peep->y / 32; + pos->z = peep->z / 8; + + EXPECT_PRED_FORMAT1(AssertIsNotForbiddenPosition, *pos); + } + + // Clean up the peep, because we're reusing this loaded context for all tests. + peep_sprite_remove(peep); + + // Require that the number of steps taken is exactly what we expected. The pathfinder is supposed to be + // deterministic, and we reset the RNG seed for each test, everything should be entirely repeatable; as + // such a change in the number of steps taken on one of these paths needs to be reviewed. + EXPECT_EQ(step, maxSteps); + + return *pos == goal; + } + + static ::testing::AssertionResult AssertIsStartPosition(const char*, const TileCoordsXYZ& location) + { + return AssertPositionIsSetUp("Start", 11u, location); + } + + static ::testing::AssertionResult AssertIsGoalPosition(const char*, const TileCoordsXYZ& location) + { + return AssertPositionIsSetUp("Goal", 9u, location); + } + + static ::testing::AssertionResult AssertIsNotForbiddenPosition(const char*, const TileCoordsXYZ& location) + { + const uint32_t forbiddenSurfaceStyle = 8u; + + const uint32_t style = GetSurfaceStyleAtLocation(TileCoordsXY(location.x, location.y)); + + if (style == forbiddenSurfaceStyle) + return ::testing::AssertionFailure() + << "Path traversed location " << location << ", but it is marked as a forbidden location (surface style " + << forbiddenSurfaceStyle << "). Either the map is set up incorrectly, or the pathfinder went the wrong way."; + + return ::testing::AssertionSuccess(); + } + +private: + + static uint32_t GetSurfaceStyleAtLocation(const TileCoordsXY& location) + { + TileElement* element = map_get_first_element_at(location.x, location.y); + + // Every tile *should* have a surface sprite, so we should be guaranteed to find + // something before we go off the end of the data. + while (element->GetType() != TILE_ELEMENT_TYPE_SURFACE) + element++; + + return element->AsSurface()->GetSurfaceStyle(); + } + + static ::testing::AssertionResult AssertPositionIsSetUp(const char* positionKind, uint32_t expectedSurfaceStyle, const TileCoordsXYZ& location) + { + const uint32_t style = GetSurfaceStyleAtLocation(TileCoordsXY(location.x, location.y)); + + if (style != expectedSurfaceStyle) + return ::testing::AssertionFailure() + << positionKind << " location " << location << " should have surface style " << expectedSurfaceStyle + << " but actually has style " << style + << ". Either the test map is not set up correctly, or you got the coordinates wrong."; + + return ::testing::AssertionSuccess(); + } + + static std::shared_ptr _context; +}; + +std::shared_ptr PathfindingTestBase::_context; + +struct SimplePathfindingScenario +{ + const char* name; + TileCoordsXYZ start; + TileCoordsXYZ goal; + uint32_t steps; + + SimplePathfindingScenario(const char* _name, const TileCoordsXYZ& _start, const TileCoordsXYZ& _goal, int _steps = 10000) + : name(_name) + , start(_start) + , goal(_goal) + , steps(_steps) + { + } + + friend std::ostream& operator<<(std::ostream& os, const SimplePathfindingScenario& scenario) + { + return os << scenario.start << " => " << scenario.goal; + } + + static std::string ToName(const ::testing::TestParamInfo& param_info) + { + return param_info.param.name; + } +}; + +class SimplePathfindingTest : public PathfindingTestBase, public ::testing::WithParamInterface +{ +}; + +TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) +{ + const SimplePathfindingScenario& scenario = GetParam(); + + ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); + ASSERT_PRED_FORMAT1(AssertIsGoalPosition, scenario.goal); + + TileCoordsXYZ pos = scenario.start; + + const auto succeeded = FindPath(&pos, scenario.goal, scenario.steps) ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() + << "Failed to find path from " << scenario.start << " to " << scenario.goal << " in " << scenario.steps + << " steps; reached " << pos << " before giving up."; + + EXPECT_TRUE(succeeded); +} + +INSTANTIATE_TEST_CASE_P( + ForScenario, SimplePathfindingTest, + ::testing::Values( + SimplePathfindingScenario("StraightFlat", { 2, 19, 14 }, { 4, 19, 14 }, 24), + SimplePathfindingScenario("SBend", { 2, 17, 14 }, { 4, 16, 14 }, 39), + SimplePathfindingScenario("UBend", { 2, 14, 14 }, { 2, 12, 14 }, 88), + SimplePathfindingScenario("CBend", { 2, 10, 14 }, { 2, 7, 14 }, 133), + SimplePathfindingScenario("TwoEqualRoutes", { 6, 18, 14 }, { 10, 18, 14 }, 819), + SimplePathfindingScenario("TwoUnequalRoutes", { 6, 14, 14 }, { 10, 14, 14 }, 15643), + SimplePathfindingScenario("StraightUpBridge", { 2, 4, 14 }, { 4, 4, 16 }, 24), + SimplePathfindingScenario("StraightUpSlope", { 4, 1, 14 }, { 6, 1, 16 }, 24), + SimplePathfindingScenario("SelfCrossingPath", { 6, 5, 14 }, { 8, 5, 14 }, 213)), + SimplePathfindingScenario::ToName); + +class ImpossiblePathfindingTest : public PathfindingTestBase, public ::testing::WithParamInterface +{ +}; + +TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) +{ + const SimplePathfindingScenario& scenario = GetParam(); + TileCoordsXYZ pos = scenario.start; + + ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); + ASSERT_PRED_FORMAT1(AssertIsGoalPosition, scenario.goal); + + EXPECT_FALSE(FindPath(&pos, scenario.goal, 10000)); +} + +INSTANTIATE_TEST_CASE_P( + ForScenario, ImpossiblePathfindingTest, + ::testing::Values( + SimplePathfindingScenario("PathWithGap", { 6, 9, 14 }, { 10, 9, 14 }), + SimplePathfindingScenario("PathWithFences", { 6, 7, 14 }, { 10, 7, 14 }), + SimplePathfindingScenario("PathWithCliff", { 10, 5, 14 }, { 12, 5, 14 })), + SimplePathfindingScenario::ToName); diff --git a/test/tests/testdata/parks/pathfinding-tests.sv6 b/test/tests/testdata/parks/pathfinding-tests.sv6 new file mode 100644 index 0000000000000000000000000000000000000000..41c0ab0345b313169b02cfed49083c16bf473635 GIT binary patch literal 286574 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≻*(3EWb+ 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 z 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≻*(3EWb+1Ka{`E8GQc19uee0e67A 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 + ..\..\ @@ -64,6 +64,7 @@ + From f4685f7b9421131e5dd505ceb688ef3b1d7a6a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Sun, 30 Dec 2018 11:15:03 +0100 Subject: [PATCH 03/11] Add Pathfinding test to CMake --- test/tests/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/tests/CMakeLists.txt b/test/tests/CMakeLists.txt index 098924659c..25d946547b 100644 --- a/test/tests/CMakeLists.txt +++ b/test/tests/CMakeLists.txt @@ -197,3 +197,10 @@ if (NOT DISABLE_NETWORK) target_link_libraries(test_replays ${GTEST_LIBRARIES} libopenrct2 ${LDL} z) add_test(NAME replay_tests COMMAND test_replays) endif () + +# Pathfinding test +set(PATHFINDING_TEST_SOURCES "${CMAKE_CURRENT_LIST_DIR}/Pathfinding.cpp" + "${CMAKE_CURRENT_LIST_DIR}/TestData.cpp") +add_executable(test_pathfinding ${PATHFINDING_TEST_SOURCES}) +target_link_libraries(test_pathfinding ${GTEST_LIBRARIES} libopenrct2 ${LDL} z) +add_test(NAME pathfinding COMMAND test_pathfinding) From 49e46f8940d24926abf3fc396ea1d43bd3c3ef76 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Sun, 30 Dec 2018 11:47:30 +0000 Subject: [PATCH 04/11] Replace GetSurfaceStyleAtLocation with map_get_surface_element_at There exists a helper function map_get_surface_element_at which already searches a tile's element list for the surface element, so we can just use that. --- test/tests/Pathfinding.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 16d4bfe4f7..d1abec1e0f 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -122,7 +122,7 @@ protected: { const uint32_t forbiddenSurfaceStyle = 8u; - const uint32_t style = GetSurfaceStyleAtLocation(TileCoordsXY(location.x, location.y)); + const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); if (style == forbiddenSurfaceStyle) return ::testing::AssertionFailure() @@ -134,21 +134,9 @@ protected: private: - static uint32_t GetSurfaceStyleAtLocation(const TileCoordsXY& location) - { - TileElement* element = map_get_first_element_at(location.x, location.y); - - // Every tile *should* have a surface sprite, so we should be guaranteed to find - // something before we go off the end of the data. - while (element->GetType() != TILE_ELEMENT_TYPE_SURFACE) - element++; - - return element->AsSurface()->GetSurfaceStyle(); - } - static ::testing::AssertionResult AssertPositionIsSetUp(const char* positionKind, uint32_t expectedSurfaceStyle, const TileCoordsXYZ& location) { - const uint32_t style = GetSurfaceStyleAtLocation(TileCoordsXY(location.x, location.y)); + const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); if (style != expectedSurfaceStyle) return ::testing::AssertionFailure() From 357ee293f3e9456d5165960abb79a6588de11242 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Mon, 31 Dec 2018 12:06:19 +0000 Subject: [PATCH 05/11] Remove default value for SimplePathfindingScenario steps Remove the default value as it's more consistent to just always have the test case specify it explicitly. --- test/tests/Pathfinding.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index d1abec1e0f..1065c0f5bb 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -159,7 +159,7 @@ struct SimplePathfindingScenario TileCoordsXYZ goal; uint32_t steps; - SimplePathfindingScenario(const char* _name, const TileCoordsXYZ& _start, const TileCoordsXYZ& _goal, int _steps = 10000) + SimplePathfindingScenario(const char* _name, const TileCoordsXYZ& _start, const TileCoordsXYZ& _goal, int _steps) : name(_name) , start(_start) , goal(_goal) @@ -231,7 +231,7 @@ TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) INSTANTIATE_TEST_CASE_P( ForScenario, ImpossiblePathfindingTest, ::testing::Values( - SimplePathfindingScenario("PathWithGap", { 6, 9, 14 }, { 10, 9, 14 }), - SimplePathfindingScenario("PathWithFences", { 6, 7, 14 }, { 10, 7, 14 }), - SimplePathfindingScenario("PathWithCliff", { 10, 5, 14 }, { 12, 5, 14 })), + SimplePathfindingScenario("PathWithGap", { 6, 9, 14 }, { 10, 9, 14 }, 10000), + SimplePathfindingScenario("PathWithFences", { 6, 7, 14 }, { 10, 7, 14 }, 10000), + SimplePathfindingScenario("PathWithCliff", { 10, 5, 14 }, { 12, 5, 14 }, 10000)), SimplePathfindingScenario::ToName); From 280a0c61e40276bd9067dbdcd049390e2da8804a Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Mon, 31 Dec 2018 12:08:55 +0000 Subject: [PATCH 06/11] Rename maxSteps to expectedSteps To make it a little clearer what the parameter is for, rename FindPath::maxSteps to FindPath::expectedSteps, and extend the comment to describe what happens in negative test scenarios. --- test/tests/Pathfinding.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 1065c0f5bb..3a7dd9d785 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -50,7 +50,7 @@ public: protected: - static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int maxSteps) + static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int expectedSteps) { // Our start position is in tile coordinates, but we need to give the peep spawn // position in actual world coords (32 units per tile X/Y, 8 per Z level). @@ -84,7 +84,7 @@ protected: // elapsed. Each step, check that the tile they are standing on is not marked as forbidden in the test data // (red neon ground type). int step = 0; - while (!(*pos == goal) && step < maxSteps) + while (!(*pos == goal) && step < expectedSteps) { uint8_t pathingResult = 0; peep->PerformNextAction(pathingResult); @@ -102,8 +102,10 @@ protected: // Require that the number of steps taken is exactly what we expected. The pathfinder is supposed to be // deterministic, and we reset the RNG seed for each test, everything should be entirely repeatable; as - // such a change in the number of steps taken on one of these paths needs to be reviewed. - EXPECT_EQ(step, maxSteps); + // such a change in the number of steps taken on one of these paths needs to be reviewed. For the negative + // tests, we will not have reached the goal but we still expect the loop to have run for the total number + // of steps requested before giving up. + EXPECT_EQ(step, expectedSteps); return *pos == goal; } From a20c8d61bea51313f714c4f5d08a4c21f4841cfb Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Mon, 31 Dec 2018 12:10:52 +0000 Subject: [PATCH 07/11] Call core_init instead of bitcount_init --- test/tests/Pathfinding.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 3a7dd9d785..ffba1c650d 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -4,6 +4,7 @@ #include "openrct2/scenario/Scenario.h" #include +#include #include #include #include @@ -23,6 +24,8 @@ class PathfindingTestBase : public testing::Test public: static void SetUpTestCase() { + core_init(); + gOpenRCT2Headless = true; gOpenRCT2NoGraphics = true; _context = CreateContext(); @@ -32,8 +35,6 @@ public: std::string parkPath = TestData::GetParkPath("pathfinding-tests.sv6"); load_from_sv6(parkPath.c_str()); game_load_init(); - - bitcount_init(); } void SetUp() override From c8c1abd10ab4708dcfc642b896902c82eb1239f0 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Mon, 31 Dec 2018 12:28:49 +0000 Subject: [PATCH 08/11] Reformat --- test/tests/Pathfinding.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index ffba1c650d..1f11a9d9cb 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -4,11 +4,11 @@ #include "openrct2/scenario/Scenario.h" #include -#include #include #include #include #include +#include #include #include @@ -37,7 +37,7 @@ public: game_load_init(); } - void SetUp() override + void SetUp() override { // Use a consistent random seed in every test gScenarioSrand0 = 0x12345678; @@ -50,7 +50,6 @@ public: } protected: - static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int expectedSteps) { // Our start position is in tile coordinates, but we need to give the peep spawn @@ -97,7 +96,7 @@ protected: EXPECT_PRED_FORMAT1(AssertIsNotForbiddenPosition, *pos); } - + // Clean up the peep, because we're reusing this loaded context for all tests. peep_sprite_remove(peep); @@ -136,8 +135,8 @@ protected: } private: - - static ::testing::AssertionResult AssertPositionIsSetUp(const char* positionKind, uint32_t expectedSurfaceStyle, const TileCoordsXYZ& location) + static ::testing::AssertionResult AssertPositionIsSetUp( + const char* positionKind, uint32_t expectedSurfaceStyle, const TileCoordsXYZ& location) { const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); From bb4e79ddda09251ebbc779f0cb483630d13101eb Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Tue, 1 Jan 2019 14:38:29 +0000 Subject: [PATCH 09/11] Rework tests to use ride entrances as path targets It turns out that trying to just give a peep a pathfinding goal and then let them loose doesn't work, because every time they reach a junction, the pathfinder has them walk 'aimlessly' instead of pursuing their target. That's why we were seeing some very large step counts in previous tests - they were (eventually) walking onto the target square, but only after lots of wandering around in circles. This commit reworks the test data to contain an actual ride for each test scenario, where the peep will path to the tile in front of the ride entrance. A nice side benefit of this is that the ride names must match the test names, so you can now tell from looking at the rides in the test data which one is used for which test instance. The 'yellow marker' tiles for goal positions are also removed here, as we're deriving goal positions from the ride entrances instead. --- test/tests/Pathfinding.cpp | 117 +++++++++++------- .../testdata/parks/pathfinding-tests.sv6 | Bin 286574 -> 294792 bytes 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 1f11a9d9cb..40be195863 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "openrct2/ride/Station.h" using namespace OpenRCT2; @@ -50,7 +51,22 @@ public: } protected: - static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int expectedSteps) + + static Ride* FindRideByName(const char* name, int32_t* outRideIndex) + { + Ride* ride; + FOR_ALL_RIDES ((*outRideIndex), ride) + { + char thisName[256]; + format_string(thisName, sizeof(thisName), ride->name, &ride->name_arguments); + if (!_strnicmp(thisName, name, sizeof(thisName))) + return ride; + } + + return nullptr; + } + + static bool FindPath(TileCoordsXYZ* pos, const TileCoordsXYZ& goal, int expectedSteps, int targetRideID) { // Our start position is in tile coordinates, but we need to give the peep spawn // position in actual world coords (32 units per tile X/Y, 8 per Z level). @@ -61,6 +77,12 @@ protected: // use here peep->outside_of_park = 0; + // An earlier iteration of this code just gave peeps a target position to walk to, but it turns out + // that with no actual ride to head towards, when a peep reaches a junction they use the 'aimless' + // pathfinder instead of pursuing their original pathfinding target. So, we always need to give them + // an actual ride to walk to the entrance of. + peep->guest_heading_to_ride_id = targetRideID; + // Pick the direction the peep should initially move in, given the goal position. // This will also store the goal position and initialize pathfinding data for the peep. gPeepPathFindGoalPosition = goal; @@ -112,12 +134,16 @@ protected: static ::testing::AssertionResult AssertIsStartPosition(const char*, const TileCoordsXYZ& location) { - return AssertPositionIsSetUp("Start", 11u, location); - } + const uint32_t expectedSurfaceStyle = 11u; + const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); - static ::testing::AssertionResult AssertIsGoalPosition(const char*, const TileCoordsXYZ& location) - { - return AssertPositionIsSetUp("Goal", 9u, location); + if (style != expectedSurfaceStyle) + return ::testing::AssertionFailure() + << "Start location " << location << " should have surface style " << expectedSurfaceStyle + << " but actually has style " << style + << ". Either the test map is not set up correctly, or you got the coordinates wrong."; + + return ::testing::AssertionSuccess(); } static ::testing::AssertionResult AssertIsNotForbiddenPosition(const char*, const TileCoordsXYZ& location) @@ -135,20 +161,6 @@ protected: } private: - static ::testing::AssertionResult AssertPositionIsSetUp( - const char* positionKind, uint32_t expectedSurfaceStyle, const TileCoordsXYZ& location) - { - const uint32_t style = map_get_surface_element_at(location.x, location.y)->AsSurface()->GetSurfaceStyle(); - - if (style != expectedSurfaceStyle) - return ::testing::AssertionFailure() - << positionKind << " location " << location << " should have surface style " << expectedSurfaceStyle - << " but actually has style " << style - << ". Either the test map is not set up correctly, or you got the coordinates wrong."; - - return ::testing::AssertionSuccess(); - } - static std::shared_ptr _context; }; @@ -158,22 +170,15 @@ struct SimplePathfindingScenario { const char* name; TileCoordsXYZ start; - TileCoordsXYZ goal; uint32_t steps; - SimplePathfindingScenario(const char* _name, const TileCoordsXYZ& _start, const TileCoordsXYZ& _goal, int _steps) + SimplePathfindingScenario(const char* _name, const TileCoordsXYZ& _start, int _steps) : name(_name) , start(_start) - , goal(_goal) , steps(_steps) { } - friend std::ostream& operator<<(std::ostream& os, const SimplePathfindingScenario& scenario) - { - return os << scenario.start << " => " << scenario.goal; - } - static std::string ToName(const ::testing::TestParamInfo& param_info) { return param_info.param.name; @@ -189,13 +194,21 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) const SimplePathfindingScenario& scenario = GetParam(); ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); - ASSERT_PRED_FORMAT1(AssertIsGoalPosition, scenario.goal); - TileCoordsXYZ pos = scenario.start; - const auto succeeded = FindPath(&pos, scenario.goal, scenario.steps) ? ::testing::AssertionSuccess() - : ::testing::AssertionFailure() - << "Failed to find path from " << scenario.start << " to " << scenario.goal << " in " << scenario.steps + int32_t rideIndex; + Ride* ride = FindRideByName(scenario.name, &rideIndex); + ASSERT_NE(ride, nullptr); + + auto entrancePos = ride_get_entrance_location(ride, 0); + TileCoordsXYZ goal = TileCoordsXYZ( + entrancePos.x - TileDirectionDelta[entrancePos.direction].x, + entrancePos.y - TileDirectionDelta[entrancePos.direction].y, + entrancePos.z); + + const auto succeeded = FindPath(&pos, goal, scenario.steps, rideIndex) ? ::testing::AssertionSuccess() + : ::testing::AssertionFailure() + << "Failed to find path from " << scenario.start << " to " << goal << " in " << scenario.steps << " steps; reached " << pos << " before giving up."; EXPECT_TRUE(succeeded); @@ -204,15 +217,15 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) INSTANTIATE_TEST_CASE_P( ForScenario, SimplePathfindingTest, ::testing::Values( - SimplePathfindingScenario("StraightFlat", { 2, 19, 14 }, { 4, 19, 14 }, 24), - SimplePathfindingScenario("SBend", { 2, 17, 14 }, { 4, 16, 14 }, 39), - SimplePathfindingScenario("UBend", { 2, 14, 14 }, { 2, 12, 14 }, 88), - SimplePathfindingScenario("CBend", { 2, 10, 14 }, { 2, 7, 14 }, 133), - SimplePathfindingScenario("TwoEqualRoutes", { 6, 18, 14 }, { 10, 18, 14 }, 819), - SimplePathfindingScenario("TwoUnequalRoutes", { 6, 14, 14 }, { 10, 14, 14 }, 15643), - SimplePathfindingScenario("StraightUpBridge", { 2, 4, 14 }, { 4, 4, 16 }, 24), - SimplePathfindingScenario("StraightUpSlope", { 4, 1, 14 }, { 6, 1, 16 }, 24), - SimplePathfindingScenario("SelfCrossingPath", { 6, 5, 14 }, { 8, 5, 14 }, 213)), + SimplePathfindingScenario("StraightFlat", { 19, 15, 14 }, 24), + SimplePathfindingScenario("SBend", { 15, 12, 14 }, 88), + SimplePathfindingScenario("UBend", { 17, 9, 14 }, 86), + SimplePathfindingScenario("CBend", { 14, 5, 14 }, 164), + SimplePathfindingScenario("TwoEqualRoutes", { 9, 13, 14 }, 87), + SimplePathfindingScenario("TwoUnequalRoutes", { 3, 13, 14 }, 87), + SimplePathfindingScenario("StraightUpBridge", { 12, 15, 14 }, 24), + SimplePathfindingScenario("StraightUpSlope", { 14, 15, 14 }, 24), + SimplePathfindingScenario("SelfCrossingPath", { 6, 5, 14 }, 213)), SimplePathfindingScenario::ToName); class ImpossiblePathfindingTest : public PathfindingTestBase, public ::testing::WithParamInterface @@ -223,17 +236,25 @@ TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) { const SimplePathfindingScenario& scenario = GetParam(); TileCoordsXYZ pos = scenario.start; - ASSERT_PRED_FORMAT1(AssertIsStartPosition, scenario.start); - ASSERT_PRED_FORMAT1(AssertIsGoalPosition, scenario.goal); - EXPECT_FALSE(FindPath(&pos, scenario.goal, 10000)); + int32_t rideIndex; + Ride* ride = FindRideByName(scenario.name, &rideIndex); + ASSERT_NE(ride, nullptr); + + auto entrancePos = ride_get_entrance_location(ride, 0); + TileCoordsXYZ goal = TileCoordsXYZ( + entrancePos.x + TileDirectionDelta[entrancePos.direction].x, + entrancePos.y + TileDirectionDelta[entrancePos.direction].y, + entrancePos.z); + + EXPECT_FALSE(FindPath(&pos, goal, 10000, rideIndex)); } INSTANTIATE_TEST_CASE_P( ForScenario, ImpossiblePathfindingTest, ::testing::Values( - SimplePathfindingScenario("PathWithGap", { 6, 9, 14 }, { 10, 9, 14 }, 10000), - SimplePathfindingScenario("PathWithFences", { 6, 7, 14 }, { 10, 7, 14 }, 10000), - SimplePathfindingScenario("PathWithCliff", { 10, 5, 14 }, { 12, 5, 14 }, 10000)), + SimplePathfindingScenario("PathWithGap", { 1, 6, 14 }, 10000), + SimplePathfindingScenario("PathWithFences", { 11, 6, 14 }, 10000), + SimplePathfindingScenario("PathWithCliff", { 7, 17, 14 }, 10000)), SimplePathfindingScenario::ToName); diff --git a/test/tests/testdata/parks/pathfinding-tests.sv6 b/test/tests/testdata/parks/pathfinding-tests.sv6 index 41c0ab0345b313169b02cfed49083c16bf473635..2fbb02269206d081926c92f95071d01da12d7f0b 100644 GIT binary patch delta 13327 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 b`=j2i7knS{J`tMJj#BBCerhd#^tkcAk}{+l delta 4758 zcmZ`-TTEQn6+Lt2&OJN~+`$I(GLK*zzpw|}u_un3%wRKPQmKVRs!P;1B#L6wyuh*A z@ZsLEffBA`+xFQ62)dX{L!itLxk$ zFmp+TjAzz8cki>;UVHCzI1T?NulX;8n;X0kIF1jXup3)0<71R!7H^{i|G_q-WMkR6 zhnlyAk4GR|B)ZXq`U42~Lj&+)bP#K>N5FRqR!Ufyk&`ixZlcG;go&BN3isR0bFm%U?XUaL-e#dQ8o@}L-a;f8u4klSOq`m;njvYSUoqNK z0>k1Gx~4D@M-La9I=c4g3cKr;!y0B-Z@`}w6ilEpF$7=2gqK#mHRz$${;34UqTD7D zCC+{|Ysx%#Hc)A2(WLFYemU1*9`INjdfG%(^Dw4EXoy(hfl$JVqeYOskbnih!2MQR zFoK3q3?%~{F@Z_n!H!luci;?>*ePbAtM?KnrZ5ucUYN0dhdi56cC83<{c@1Z-6f1o zq5ovX0r*2YfkNjZhyozsLzi+)4o^E;nBjk24s~Oc0i)#S71pyTY`1mHA}8j0;2Uk| zS`2w2JLHpy4PZh<+sHsi!s1u2L-6o)Jc|AV6GoDq9Zan7TDKfi|Dqn5I(tcwSOwG! zh7wNl@^$hYMf6Y+iHK%v2C7CA*Tooh0?FS>d2C(w(zVE3&vZoZYs6FG)p=SbkYZ`E zhtLp*FRn6rVJ@5`4ad<>H$72ZTVrZPFj*9Sg=$z^OOZG-#0<@jT{O^OqJU-*-4wwD zOR3+U!dN_-peHA546}<4SNO{kM7s`)b<*=yg7EpAltT$K9c@BSSWO>97kxQ=k+tDO zZhGvcezC@EVY~345A~X($uGOaJ%iM|KMpm7(d^`~M=Tcu3LR<8P#X2i($G<7Dtb7g z_cey7#XL@Dg=ep5R%01O_GOnftFV?;NW$8ubOB9--MOTAb}bfuMmrN3-~H1OrL#Xw z)^#QseU|q`)Yh%CVOccv&OKkO7=S;fX$Uzq=)SUV&{|adu$UgwZE7Z>bx5QA zzL6+1F`=A7{l6SMxTG;VGm47Hfrz7o8jI5fd;v>Yo^E!RzCU85#T$d)#41l>FsnOs zEr#Ci$eT6g*KucyYlXIkS7_^Y0g5ZYz(=N7@)4?2mwcE0#Wd=vv6$d&-6SvUFj`;f z4aU8%)b8JtXa7XMn1P=nK1)y-@eaKhg`OS~nh5x~ajA-G5IROupClURp5(Z#$ zk=KCCf_#JwE|wmi43<~s6ZQ#sB@>d1ngWJ|?Q_A3=Lt#CE-%ibi97*QBE1Z1yZHi2 zl7n2dfV4>DyU;>`d`Y3?u3aT;6i5`h;39!S$)=vWxL9D7M3Dx>le} zqI?mxbviYf6$0fFYh8;g1=dPbxX>zr3W-V=S}jm1QRPBw1ga#eUFbT2YKa;sdr4Dz z6SV?066;)x>jc(G)Vk1mfm(?=7uq0DCt0GoOxz|=FVWya8wDC{iQ8Q8dV$*{8eQlH zfkuh-E_9>7dWj7#v`Jut#6}moNnoQyQ!Z*_vp|!?rd$|X1U5-*cA;AZHcM=Aq1yzu zNNjbX%>r8`&+xfSY!}!j(Y%DRiEH9_2sBGxZQ>B6}Vkuj|&Y7?2*{(LhlgR zD-m>|`vig#;X7RLodS1A>~o>}1@=kY=|aCIaHkyT>`rucd-zX|cXoR}o7%wf=3yt? zm|B>-$ZBt5lRE9=DLM|%T#$912eVF&|Deh6$UwgCx^vSpH66R9@?gVwRI%}%=I1Up zG1~cinyHz;%+d19tJsMD(UA#zAh0&Abs0k^ApL72$6CZqATq{2U+QqFx5|LtgjT z?_cAq<-&*OFDvi(BU_An4exIBCi|L=sw%ViEY03`2oK^|;P6^T9mSIZkCh;~aKxxP z*Be})|2yUL(8uSV@e4uYXnt}wX#B)JVIs*6`WJz>kmgnJO*#pQ1yWaV1>K`pX0P>O zlJlpucb0=;bp8;E*h3MUzdAaJ@_JS~CxH*ey%nT(w)?8M7KRL+w7SDqUjU`QxZ=O+ zKOZ^|v#%547)CA8ikOAawMmOUJk=*&4)FC=7B9Z}=)PvX=&U+^r4O~q7g~*ns}Ixs z`Q9^_3LColaF)+5{(6HEL2=$BVMj8N$sJ|7Nf+aAQYu{Xyebk(X47fzR=8{7dhCAj6m6 zgD>zIfvK4J&5~$w^6=WUia6`I-A|o3gTCvd&Px~Z-)QpsLF4J_axW#~l5)YP6$+=&3l9Ak80BH!yv7UfH-1!9lE!L96~6>d{*wzb`LiQN z$7 z&+;iSKOzn+fB#Sknf1N!L&d6NbABF0<8w!iYER{phR!J2f04{c9w|_*crMx9oNRg2 z*w=LUXt$>m_Xc8TajXIdDmLLM9KZPGc0N{zDG63&^1&yKyT?1fZ}`0CpVSj%*+KT) z*L^e(PxS}!a1iY{ej1Ozj7RctEDw>{8>8sWJB_2EFRgu|zUNp|8EbV<7~k<1bt7Ll Ln7^v#&@uTx=1>hP From 315ea057fef52d6261924b4aa28b75116e57b043 Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Tue, 1 Jan 2019 14:52:56 +0000 Subject: [PATCH 10/11] Verify that peeps stay on paths As per PR recommendation, confirm that a peep is still on the footpath for every step of the pathfinding tests. --- test/tests/Pathfinding.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 40be195863..17e6ffffb4 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -117,6 +117,10 @@ protected: pos->z = peep->z / 8; EXPECT_PRED_FORMAT1(AssertIsNotForbiddenPosition, *pos); + + // Check that the peep is still on a footpath. Use next_z instead of pos->z here because pos->z will change + // when the peep is halfway up a slope, but next_z will not change until they move to the next tile. + EXPECT_NE(map_get_footpath_element(pos->x, pos->y, peep->next_z), nullptr); } // Clean up the peep, because we're reusing this loaded context for all tests. From cec457fbf18d51a1a809c1e82b20e621afc4451a Mon Sep 17 00:00:00 2001 From: Richard Fine Date: Tue, 1 Jan 2019 14:54:04 +0000 Subject: [PATCH 11/11] Reformat --- test/tests/Pathfinding.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/test/tests/Pathfinding.cpp b/test/tests/Pathfinding.cpp index 17e6ffffb4..940c1a8c80 100644 --- a/test/tests/Pathfinding.cpp +++ b/test/tests/Pathfinding.cpp @@ -1,6 +1,7 @@ #include "TestData.h" #include "openrct2/core/StringReader.hpp" #include "openrct2/peep/Peep.h" +#include "openrct2/ride/Station.h" #include "openrct2/scenario/Scenario.h" #include @@ -11,7 +12,6 @@ #include #include #include -#include "openrct2/ride/Station.h" using namespace OpenRCT2; @@ -51,7 +51,6 @@ public: } protected: - static Ride* FindRideByName(const char* name, int32_t* outRideIndex) { Ride* ride; @@ -207,13 +206,12 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) auto entrancePos = ride_get_entrance_location(ride, 0); TileCoordsXYZ goal = TileCoordsXYZ( entrancePos.x - TileDirectionDelta[entrancePos.direction].x, - entrancePos.y - TileDirectionDelta[entrancePos.direction].y, - entrancePos.z); + entrancePos.y - TileDirectionDelta[entrancePos.direction].y, entrancePos.z); const auto succeeded = FindPath(&pos, goal, scenario.steps, rideIndex) ? ::testing::AssertionSuccess() - : ::testing::AssertionFailure() - << "Failed to find path from " << scenario.start << " to " << goal << " in " << scenario.steps - << " steps; reached " << pos << " before giving up."; + : ::testing::AssertionFailure() + << "Failed to find path from " << scenario.start << " to " << goal << " in " << scenario.steps << " steps; reached " + << pos << " before giving up."; EXPECT_TRUE(succeeded); } @@ -221,10 +219,8 @@ TEST_P(SimplePathfindingTest, CanFindPathFromStartToGoal) INSTANTIATE_TEST_CASE_P( ForScenario, SimplePathfindingTest, ::testing::Values( - SimplePathfindingScenario("StraightFlat", { 19, 15, 14 }, 24), - SimplePathfindingScenario("SBend", { 15, 12, 14 }, 88), - SimplePathfindingScenario("UBend", { 17, 9, 14 }, 86), - SimplePathfindingScenario("CBend", { 14, 5, 14 }, 164), + SimplePathfindingScenario("StraightFlat", { 19, 15, 14 }, 24), SimplePathfindingScenario("SBend", { 15, 12, 14 }, 88), + SimplePathfindingScenario("UBend", { 17, 9, 14 }, 86), SimplePathfindingScenario("CBend", { 14, 5, 14 }, 164), SimplePathfindingScenario("TwoEqualRoutes", { 9, 13, 14 }, 87), SimplePathfindingScenario("TwoUnequalRoutes", { 3, 13, 14 }, 87), SimplePathfindingScenario("StraightUpBridge", { 12, 15, 14 }, 24), @@ -249,8 +245,7 @@ TEST_P(ImpossiblePathfindingTest, CannotFindPathFromStartToGoal) auto entrancePos = ride_get_entrance_location(ride, 0); TileCoordsXYZ goal = TileCoordsXYZ( entrancePos.x + TileDirectionDelta[entrancePos.direction].x, - entrancePos.y + TileDirectionDelta[entrancePos.direction].y, - entrancePos.z); + entrancePos.y + TileDirectionDelta[entrancePos.direction].y, entrancePos.z); EXPECT_FALSE(FindPath(&pos, goal, 10000, rideIndex)); }