diff --git a/src/main.cc b/src/main.cc index c361864..8ab9925 100644 --- a/src/main.cc +++ b/src/main.cc @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -19,11 +21,11 @@ namespace asio = boost::asio; -enum class Direction : int { - UP, - RIGHT, - DOWN, - LEFT, +enum class Direction : unsigned { + UP = 1, + RIGHT = 2, + DOWN = 4, + LEFT = 8, }; std::string_view DirectionToString(Direction direction) { @@ -50,11 +52,12 @@ class Bot { int goal_x, goal_y; int x, y; bool up, right, down, left; - Direction heading; + std::map, unsigned> known_map; asio::awaitable Protocol(); asio::awaitable Join(); asio::awaitable Move(Direction dir); + unsigned ShortestPath(int x, int y); asio::awaitable ChooseMove(); public: @@ -68,8 +71,7 @@ Bot::Bot(asio::any_io_executor executor, std::string_view name, timer{executor}, socket{executor}, name{name}, - pass{pass}, - heading{Direction::LEFT} { + pass{pass} { asio::co_spawn(executor, std::bind(&Bot::Protocol, this), asio::detached); } @@ -112,6 +114,7 @@ asio::awaitable Bot::Protocol() { iss >> goal_x >> dummy >> goal_y; std::cout << "-> Goal is at (" << goal_x << ", " << goal_y << ")" << std::endl; + known_map.clear(); } else if (field == "pos") { iss >> x >> dummy >> y >> dummy >> up >> dummy >> right >> dummy >> down >> dummy >> left; @@ -171,12 +174,94 @@ asio::awaitable Bot::Move(Direction direction) { co_return true; } -asio::awaitable Bot::ChooseMove() { - // follow right wall - while (!co_await Move(heading)) { - heading = Direction{(static_cast(heading) + 3) % 4}; +unsigned Bot::ShortestPath(int x, int y) { + // Djikstra + std::set> visited; + std::multimap> queue; + auto goal = std::make_pair(goal_x, goal_y); + + queue.emplace(0, std::make_pair(x, y)); + while (queue.begin() != queue.end()) { + auto it = queue.begin(); + auto distance = it->first; + auto position = it->second; + queue.erase(it); + if (position == goal) { + return distance; + } + if (visited.count(position)) { + continue; + } + if (!(known_map[position] & static_cast(Direction::LEFT))) { + queue.emplace(distance + 1, + std::make_pair(position.first - 1, position.second)); + } + if (!(known_map[position] & static_cast(Direction::RIGHT))) { + queue.emplace(distance + 1, + std::make_pair(position.first + 1, position.second)); + } + if (!(known_map[position] & static_cast(Direction::UP))) { + queue.emplace(distance + 1, + std::make_pair(position.first, position.second - 1)); + } + if (!(known_map[position] & static_cast(Direction::DOWN))) { + queue.emplace(distance + 1, + std::make_pair(position.first, position.second + 1)); + } + visited.emplace(position); } - heading = Direction{(static_cast(heading) + 1) % 4}; + // should never happen + return -1; +} + +asio::awaitable Bot::ChooseMove() { + // update map + known_map[std::make_pair(x, y)] = + (up ? static_cast(Direction::UP) : 0) | + (right ? static_cast(Direction::RIGHT) : 0) | + (down ? static_cast(Direction::DOWN) : 0) | + (left ? static_cast(Direction::LEFT) : 0); + known_map[std::make_pair(x - 1, y)] |= + (left ? static_cast(Direction::RIGHT) : 0); + known_map[std::make_pair(x + 1, y)] |= + (right ? static_cast(Direction::LEFT) : 0); + known_map[std::make_pair(x, y - 1)] |= + (up ? static_cast(Direction::DOWN) : 0); + known_map[std::make_pair(x, y + 1)] |= + (down ? static_cast(Direction::UP) : 0); + + // find best move + Direction best; + unsigned best_estimate = -1; + if (!left) { + unsigned estimate = ShortestPath(x - 1, y); + if (estimate < best_estimate) { + best = Direction::LEFT; + best_estimate = estimate; + } + } + if (!right) { + unsigned estimate = ShortestPath(x + 1, y); + if (estimate < best_estimate) { + best = Direction::RIGHT; + best_estimate = estimate; + } + } + if (!up) { + unsigned estimate = ShortestPath(x, y - 1); + if (estimate < best_estimate) { + best = Direction::UP; + best_estimate = estimate; + } + } + if (!down) { + unsigned estimate = ShortestPath(x, y + 1); + if (estimate < best_estimate) { + best = Direction::DOWN; + best_estimate = estimate; + } + } + co_await Move(best); co_return; }