#include #include #include #include "algos/spatialmap.hpp" #include "algos/dinkyecs.hpp" #include "algos/rand.hpp" #include #include using DinkyECS::Entity; using namespace fmt; using namespace fuc2; namespace spatialmap_tests { void test_SpatialMap_insert() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto item = world.entity(); auto potion = world.entity(); auto enemy = world.entity(); Point at{10,10}; Point enemy_at{11,11}; map.insert(at, item, false); map.insert(at, potion, false); CHECK(!map.occupied(at)); map.insert(at, player, true); CHECK(map.occupied_by(at) == player); BLOWS_UP([&]() { map.insert(at, enemy, true); }); map.insert(enemy_at, enemy, true); CHECK(map.occupied_by(enemy_at) == enemy); CHECK(map.occupied(enemy_at)); } void test_SpatialMap_remove() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto item = world.entity(); Point at{120, 120}; // confirm that things can be in any order map.insert(at, player, true); map.insert(at, item, false); CHECK(map.occupied(at)); CHECK(map.occupied_by(at) == player); auto data = map.remove(at, player); CHECK(!map.occupied(at)); CHECK(data.entity == player); CHECK(data.collision == true); BLOWS_UP([&]() { map.remove(at, player); }); } void test_SpatialMap_move() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto item = world.entity(); Point at{10, 320}; map.insert(at, player, true); map.insert(at, item, false); CHECK(map.occupied(at)); auto enemy = world.entity(); auto potion = world.entity(); Point enemy_at{11, 320}; map.insert(enemy_at, enemy, true); map.insert(enemy_at, potion, false); CHECK(map.occupied(enemy_at)); CHECK(map.occupied_by(enemy_at) == enemy); Point target{at.x + 1, at.y}; // try bad move with a slot that's empty BLOWS_UP([&]() { map.move({0,0}, target, player); }); // try move into an occupied spot also fails BLOWS_UP([&]() { map.move(at, target, player); }); // now move to a new spot, need to add them back map.insert(at, player, true); target.x++; // just move farther map.move(at, target, player); CHECK(map.occupied(target)); CHECK(map.occupied_by(target) == player); auto data = map.remove(target, player); CHECK(data.entity == player); CHECK(data.collision == true); } void test_SpatialMap_occupied() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto item = world.entity(); Point at{1000, 20}; // first test empty locations CHECK(!map.something_there(at)); CHECK(!map.occupied(at)); // then when there's something without collision map.insert(at, item, false); CHECK(map.something_there(at)); CHECK(!map.occupied(at)); // finally with collision and an item there map.insert(at, player, true); CHECK(map.something_there(at)); CHECK(map.occupied(at)); CHECK(map.occupied_by(at) == player); // then remove the item and still have collision map.remove(at, item); CHECK(map.something_there(at)); CHECK(map.occupied(at)); CHECK(map.occupied_by(at) == player); // remove player and back to no collision map.remove(at, player); CHECK(!map.something_there(at)); CHECK(!map.occupied(at)); // last thing, put just the player in at a new spot Point target{at.x+1, at.y+10}; map.insert(target, player, true); CHECK(map.something_there(target)); CHECK(map.occupied(target)); CHECK(map.occupied_by(target) == player); } void test_SpatialMap_get() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto item = world.entity(); Point at{101, 31}; // finally with collision and an item there map.insert(at, player, true); CHECK(map.occupied(at)); CHECK(map.occupied_by(at) == player); auto entity = map.get(at); CHECK(player == entity); // This probably doesn't work so need to // rethink how get works. map.insert(at, item, false); entity = map.get(at); CHECK(entity == item); } void test_SpatialMap_find() { DinkyECS::World world; SpatialMap map; Point at{101, 31}; DinkyECS::Entity should_collide = DinkyECS::NONE; for(int i = 0; i < 10; i++) { auto ent = world.entity(); map.insert(at, ent, i == 8); if(i == 8) { should_collide = ent; } } auto collision = map.find(at, [&](auto data) -> bool { return data.collision; }); CHECK(collision == should_collide); auto no_collide = map.find(at, [&](auto data) -> bool { return !data.collision; }); CHECK(no_collide != should_collide); } void test_SpatialMap_neighbors() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto enemy1 = world.entity(); auto enemy2 = world.entity(); //auto item1 = world.entity(); //auto item2 = world.entity(); Point at{101, 31}; map.insert(at, player, true); map.insert({at.x+1, at.y}, enemy1, true); map.insert({at.x-1, at.y+1}, enemy2, true); auto result = map.neighbors(at, true); CHECK(result.found); CHECK(result.nearby.size() == 2); bool maybe = result.nearby[0] == enemy1 || result.nearby[1] == enemy1; CHECK(maybe); maybe = result.nearby[0] == enemy2 || result.nearby[1] == enemy2; CHECK(maybe); result = map.neighbors(at, false); CHECK(result.found); CHECK(result.nearby.size() == 1); CHECK(result.nearby[0] == enemy1); } void test_SpatialMap_distance_sorted() { DinkyECS::World world; SpatialMap map; auto player = world.entity(); auto enemy1 = world.entity(); auto item = world.entity(); map.insert({1,1}, player, true); map.insert({4,4}, enemy1, true); map.insert({3, 3}, item, false); SortedEntities result; map.distance_sorted(result, {1, 1}, 100); CHECK(result.size() == 3); CHECK(result[0].entity == enemy1); CHECK(result[1].entity == item); CHECK(result[2].entity == player); int prev_dist = std::numeric_limits::max(); for(auto rec : result) { CHECK(rec.dist_square < prev_dist); prev_dist = rec.dist_square; } } fuc2::Set TESTS{ .name="spatialmap", .tests={ TEST(test_SpatialMap_distance_sorted), TEST(test_SpatialMap_neighbors), TEST(test_SpatialMap_find), TEST(test_SpatialMap_get), TEST(test_SpatialMap_occupied), TEST(test_SpatialMap_move), TEST(test_SpatialMap_remove), TEST(test_SpatialMap_insert), } }; }