#include #include "dbc.hpp" #include #include #include "levelmanager.hpp" #include "matrix.hpp" #include "components.hpp" #include using namespace dbc; using namespace components; using AStarPath = std::deque; void update_map(Matrix& map, std::deque& total_path) { for(auto &point : total_path) { map[point.y][point.x] = 10; } } AStarPath reconstruct_path(std::unordered_map& came_from, Point current) { std::deque total_path{current}; while(came_from.contains(current)) { current = came_from[current]; total_path.push_front(current); } return total_path; } inline h(Point from, Point to) { return std::hypot(float(from.x) - float(to.x), float(from.y) - float(to.y)); } inline d(Point current, Point neighbor) { return std::hypot(float(current.x) - float(neighbor.x), float(current.y) - float(neighbor.y)); } inline Point find_lowest(std::unordered_map& open_set) { dbc::check(!open_set.empty(), "open set can't be empty in find_lowest"); Point result; float lowest_score = 10000; for(auto [point, score] : open_set) { if(score < lowest_score) { lowest_score = score; result = point; } } return result; } std::optional path_to_player(Matrix& map, Point start, Point goal) { std::unordered_map open_set; std::unordered_map came_from; std::unordered_map g_score; g_score[start] = 0; open_set[start] = g_score[start] + h(start, goal); while(!open_set.empty()) { auto current = find_lowest(open_set); if(current == goal) { return std::make_optional(reconstruct_path(came_from, current)); } open_set.erase(current); for(matrix::compass it{map, current.x, current.y}; it.next();) { Point neighbor{it.x, it.y}; float d_score = d(current, neighbor) + map[it.y][it.x] * 1000; float tentative_g_score = g_score[current] + d_score; float neighbor_g_score = g_score.contains(neighbor) ? g_score[neighbor] : 10000.0f; if(tentative_g_score < neighbor_g_score) { came_from[neighbor] = current; g_score[neighbor] = tentative_g_score; // open_set gets the fScore open_set[neighbor] = tentative_g_score + h(neighbor, goal); } } } return std::nullopt; } TEST_CASE("basic feature tests", "[goap]") { for(int i = 0; i < 10; i++) { LevelManager levels; GameLevel level = levels.current(); auto &map = *level.map; auto& player_at = level.world->get(level.player); // matrix::dump("A* PLAYER", map.walls(), player_at.location.x, player_at.location.y); level.world->query([&](const auto ent, auto& enemy_at, auto&) { if(ent != level.player) { auto result = path_to_player(map.walls(), enemy_at.location, player_at.location); REQUIRE(result != std::nullopt); } }); } }