Advertisement
Archon

Test case: pathfinding

Dec 18th, 2020
770
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.73 KB | None | 0 0
  1. TEST_CASE_METHOD(ServerAndClientWithData, "Pathfinding") {
  2.   GIVEN("a colliding square wall object type, and a wolf NPC") {
  3.     auto data = ""s;
  4.     data = R"(
  5.      <npcType id="wolf" maxHealth="10000" attack="1" speed="100"
  6.        pursuesEndlessly="1" >
  7.        <collisionRect x="-5" y="-5" w="10" h="10" />
  8.      </npcType>
  9.      <objectType id="wall">
  10.        <collisionRect x="-5" y="-5" w="10" h="10" />
  11.      </objectType>
  12.    )";
  13.  
  14.     SECTION("Single static obstacle") {
  15.       GIVEN("a wall between a wolf and a player") {
  16.         useData(data.c_str());
  17.         server->addObject("wall", {60, 10});
  18.         auto &wolf = server->addNPC("wolf", {90, 10});
  19.  
  20.         WHEN("the wolf starts chasing the user") {
  21.           wolf.makeAwareOf(*user);
  22.  
  23.           THEN("it can reach him") {
  24.             WAIT_UNTIL_TIMEOUT(distance(wolf, *user) <= wolf.attackRange(),
  25.                                10000);
  26.           }
  27.         }
  28.       }
  29.     }
  30.  
  31.     SECTION("Random obstacles") {
  32.       GIVEN("three walls randomly between a wolf and the user") {
  33.         data += R"(
  34.            <spawnPoint y="55" x="55" type="wall" quantity="3" radius="50" />
  35.          )";
  36.         useData(data.c_str());
  37.  
  38.         auto &wolf = server->addNPC("wolf", {100, 100});
  39.  
  40.         WHEN("the wolf starts chasing the user") {
  41.           wolf.makeAwareOf(*user);
  42.  
  43.           THEN("it can reach him") {
  44.             WAIT_UNTIL_TIMEOUT(distance(wolf, *user) <= wolf.attackRange(),
  45.                                10000);
  46.           }
  47.         }
  48.       }
  49.     }
  50.  
  51.     SECTION("Reacting to a path becoming blocked") {
  52.       GIVEN("a clear path between wolf and user") {
  53.         useData(data.c_str());
  54.         auto &wolf = server->addNPC("wolf", {280, 10});
  55.  
  56.         WHEN("the wolf starts following a path to the user") {
  57.           const auto wolfStartLocation = wolf.location();
  58.           wolf.makeAwareOf(*user);
  59.           WAIT_UNTIL(wolf.location() != wolfStartLocation);
  60.  
  61.           AND_WHEN("a wall appears that blocks the wolf") {
  62.             server->addObject("wall", {200, 10});
  63.  
  64.             THEN("the wolf can reach the user without getting stuck") {
  65.               WAIT_UNTIL_TIMEOUT(distance(wolf, *user) <= wolf.attackRange(),
  66.                                  3000);
  67.             }
  68.           }
  69.         }
  70.       }
  71.     }
  72.  
  73.     SECTION("Reacting to the target moving") {
  74.       GIVEN("a clear path between wolf and user") {
  75.         useData(data.c_str());
  76.         auto &wolf = server->addNPC("wolf", {280, 10});
  77.  
  78.         WHEN("the wolf starts following a path to the user") {
  79.           const auto wolfStartLocation = wolf.location();
  80.           wolf.makeAwareOf(*user);
  81.           WAIT_UNTIL(wolf.location() != wolfStartLocation);
  82.  
  83.           AND_WHEN("the user teleports away") {
  84.             user->teleportTo({200, 200});
  85.  
  86.             THEN("the wolf can reach the user") {
  87.               WAIT_UNTIL_TIMEOUT(distance(wolf, *user) <= wolf.attackRange(),
  88.                                  10000);
  89.             }
  90.           }
  91.         }
  92.       }
  93.     }
  94.  
  95.     SECTION("Performance on a large, empty map") {
  96.       GIVEN("a very large map") {
  97.         data += LARGE_MAP;
  98.         useData(data.c_str());
  99.  
  100.         AND_GIVEN("a wolf that's very far from the player") {
  101.           auto &wolf = server->addNPC("wolf", {2000, 2000});
  102.  
  103.           WHEN("the wolf starts chasing the user") {
  104.             wolf.makeAwareOf(*user);
  105.  
  106.             THEN("it begins moving in a reasonable amount of time") {
  107.               const auto originalLocation = wolf.location();
  108.               REPEAT_FOR_MS(500);
  109.               CHECK(wolf.isAwareOf(*user));
  110.               CHECK(wolf.location() != originalLocation);
  111.             }
  112.           }
  113.         }
  114.       }
  115.     }
  116.  
  117.     SECTION("Obstacles are properly identified across collision chunks") {
  118.       GIVEN("a map spanning a number of collision chunks") {
  119.         data += R"(
  120.          <newPlayerSpawn x="10" y="10" range="0" />
  121.          <terrain index="." id="grass" />
  122.          <list id="default" default="1" >
  123.            <allow id="grass" />
  124.          </list>
  125.          <size x="30" y="2" />
  126.          <row y= "0" terrain = ".............................." />
  127.          <row y= "1" terrain = ".............................." />
  128.        )";
  129.         useData(data.c_str());
  130.  
  131.         AND_GIVEN("the user is blocked by walls in the leftmost chunk") {
  132.           /* ...W....|.......|.......|...
  133.              .U.W....|.......|.......|.N.
  134.              ...W....|.......|.......|... */
  135.           server->addObject("wall", {150, 5});
  136.           server->addObject("wall", {150, 15});
  137.           server->addObject("wall", {150, 25});
  138.           server->addObject("wall", {150, 35});
  139.           server->addObject("wall", {150, 45});
  140.           server->addObject("wall", {150, 55});
  141.           server->addObject("wall", {150, 65});
  142.  
  143.           AND_GIVEN("a wolf on the opposite side of the map") {
  144.             auto &wolf = server->addNPC("wolf", {900, 30});
  145.  
  146.             WHEN("the wolf tries to chase the user") {
  147.               const auto wolfStartingPosition = wolf.location();
  148.               wolf.makeAwareOf(*user);
  149.  
  150.               THEN("the wolf never moves (i.e., it knows there's no path") {
  151.                 REPEAT_FOR_MS(1000);
  152.                 CHECK(wolf.location() == wolfStartingPosition);
  153.               }
  154.             }
  155.           }
  156.         }
  157.       }
  158.     }
  159.  
  160.     SECTION("More efficient than breadth-first") {
  161.       GIVEN("a very large map") {
  162.         data += LARGE_MAP;
  163.         useData(data.c_str());
  164.  
  165.         AND_GIVEN("the user is walled off to block the direct-path shortcut") {
  166.           /* ...W.
  167.              .U.W.
  168.              ...W.
  169.              ...W.
  170.              .....
  171.              WWWW.
  172.              ..... */
  173.           server->addObject("wall", {90, 0});
  174.           server->addObject("wall", {90, 10});
  175.           server->addObject("wall", {90, 20});
  176.           server->addObject("wall", {90, 30});
  177.           server->addObject("wall", {0, 90});
  178.           server->addObject("wall", {10, 90});
  179.           server->addObject("wall", {20, 90});
  180.           server->addObject("wall", {30, 90});
  181.           server->addObject("wall", {40, 90});
  182.           server->addObject("wall", {50, 90});
  183.           server->addObject("wall", {60, 90});
  184.           server->addObject("wall", {70, 90});
  185.           server->addObject("wall", {80, 90});
  186.           server->addObject("wall", {90, 90});
  187.  
  188.           AND_GIVEN("a wolf on the opposite side of the map from the user") {
  189.             auto &wolf = server->addNPC("wolf", {2000, 2000});
  190.  
  191.             WHEN("the wolf starts chasing the user") {
  192.               const auto originalLocation = wolf.location();
  193.               wolf.makeAwareOf(*user);
  194.  
  195.               THEN("it begins moving in a reasonable amount of time") {
  196.                 WAIT_UNTIL_TIMEOUT(wolf.location() != originalLocation, 5000);
  197.               }
  198.             }
  199.           }
  200.         }
  201.       }
  202.     }
  203.  
  204.     SECTION("a large target entity doesn't block pathfinding to it") {
  205.       GIVEN("dinosaurs have a wide collision rect") {
  206.         data += R"(
  207.          <npcType id="dinosaur">
  208.            <collisionRect x="-100" y="-100" w="200" h="200" />
  209.          </npcType>
  210.        )";
  211.         useData(data.c_str());
  212.  
  213.         AND_GIVEN("a dinosaur and a wolf hostile to it") {
  214.           auto &dinosaur = server->addNPC("dinosaur", {100, 150});
  215.           dinosaur.permissions.setPlayerOwner(user->name());
  216.           dinosaur.ai.giveOrder(AI::PetOrder::ORDER_TO_STAY);
  217.  
  218.           auto &wolf = server->addNPC("wolf", {275, 150});
  219.  
  220.           WHEN("the wolf starts chasing the dinosaur") {
  221.             const auto originalLocation = wolf.location();
  222.             wolf.makeAwareOf(dinosaur);
  223.  
  224.             THEN("it begins moving (i.e., a path is successfully found)") {
  225.               WAIT_UNTIL_TIMEOUT(wolf.location() != originalLocation, 5000);
  226.             }
  227.           }
  228.         }
  229.       }
  230.     }
  231.   }
  232.  
  233.   GIVEN("walls, and ranged longbowmen") {
  234.     auto data = R"(
  235.      <npcType id="longbowman" maxHealth="10000" attack="1" isRanged="1"
  236.        pursuesEndlessly="1" >
  237.        <collisionRect x="-5" y="-5" w="10" h="10" />
  238.      </npcType>
  239.      <objectType id="wall">
  240.        <collisionRect x="-5" y="-5" w="10" h="10" />
  241.      </objectType>
  242.    )";
  243.     useData(data);
  244.  
  245.     AND_GIVEN("the user is blocked off from a longbowman") {
  246.       auto &longbowman = server->addNPC("longbowman", {280, 280});
  247.       server->addObject("wall", {90, 0});
  248.       server->addObject("wall", {90, 10});
  249.       server->addObject("wall", {90, 20});
  250.       server->addObject("wall", {90, 30});
  251.       server->addObject("wall", {90, 40});
  252.       server->addObject("wall", {90, 50});
  253.       server->addObject("wall", {90, 60});
  254.       server->addObject("wall", {90, 70});
  255.       server->addObject("wall", {90, 80});
  256.       server->addObject("wall", {0, 90});
  257.       server->addObject("wall", {10, 90});
  258.       server->addObject("wall", {20, 90});
  259.       server->addObject("wall", {30, 90});
  260.       server->addObject("wall", {40, 90});
  261.       server->addObject("wall", {50, 90});
  262.       server->addObject("wall", {60, 90});
  263.       server->addObject("wall", {70, 90});
  264.       server->addObject("wall", {80, 90});
  265.       server->addObject("wall", {90, 90});
  266.  
  267.       SECTION("longer-ranged NPCs don't need to path as close") {
  268.         WHEN("the longbowman starts chasing the player") {
  269.           longbowman.makeAwareOf(*user);
  270.  
  271.           THEN("the user takes damage)") {
  272.             WAIT_UNTIL_TIMEOUT(user->health() < user->stats().maxHealth, 10000);
  273.           }
  274.         }
  275.       }
  276.  
  277.       SECTION("ranged pets follow close") {
  278.         WHEN("the longbowman becomes the user's pet [and tries to follow]") {
  279.           longbowman.permissions.setPlayerOwner(user->name());
  280.  
  281.           THEN("it doesn't move (i.e., correctly concludes there's no path)") {
  282.             const auto oldLocation = longbowman.location();
  283.             REPEAT_FOR_MS(1000);
  284.             CHECK(longbowman.location() == oldLocation);
  285.           }
  286.         }
  287.       }
  288.     }
  289.   }
  290.  
  291.   SECTION("Path lengths are limited") {
  292.     GIVEN("kobold NPCs (without pursuesEndlessly)") {
  293.       auto data = ""s;
  294.       data = R"(
  295.        <npcType id="kobold" attack="1" />
  296.      )";
  297.  
  298.       AND_GIVEN("a very large map") {
  299.         data += LARGE_MAP;
  300.         useData(data.c_str());
  301.  
  302.         AND_GIVEN("a kobold on the other side of the map from the user") {
  303.           auto &kobold = server->addNPC("kobold", {2000, 2000});
  304.  
  305.           WHEN("the kobold starts chasing the user") {
  306.             const auto originalLocation = kobold.location();
  307.             kobold.makeAwareOf(*user);
  308.  
  309.             THEN("it never moves") {
  310.               REPEAT_FOR_MS(5000);
  311.               CHECK(kobold.location() == originalLocation);
  312.             }
  313.           }
  314.         }
  315.       }
  316.     }
  317.   }
  318. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement