optimal play

master
Thomas Lindner 2022-05-20 21:00:58 +02:00
parent 374402299d
commit 7e64cf85e0
1 changed files with 98 additions and 13 deletions

View File

@ -11,7 +11,9 @@
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/write.hpp>
#include <iostream>
#include <map>
#include <random>
#include <set>
#include <sstream>
#include <string>
#include <string_view>
@ -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<std::pair<int, int>, unsigned> known_map;
asio::awaitable<void> Protocol();
asio::awaitable<void> Join();
asio::awaitable<bool> Move(Direction dir);
unsigned ShortestPath(int x, int y);
asio::awaitable<void> 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<void> 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<bool> Bot::Move(Direction direction) {
co_return true;
}
asio::awaitable<void> Bot::ChooseMove() {
// follow right wall
while (!co_await Move(heading)) {
heading = Direction{(static_cast<int>(heading) + 3) % 4};
unsigned Bot::ShortestPath(int x, int y) {
// Djikstra
std::set<std::pair<int, int>> visited;
std::multimap<unsigned, std::pair<int, int>> 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<unsigned>(Direction::LEFT))) {
queue.emplace(distance + 1,
std::make_pair(position.first - 1, position.second));
}
if (!(known_map[position] & static_cast<unsigned>(Direction::RIGHT))) {
queue.emplace(distance + 1,
std::make_pair(position.first + 1, position.second));
}
if (!(known_map[position] & static_cast<unsigned>(Direction::UP))) {
queue.emplace(distance + 1,
std::make_pair(position.first, position.second - 1));
}
if (!(known_map[position] & static_cast<unsigned>(Direction::DOWN))) {
queue.emplace(distance + 1,
std::make_pair(position.first, position.second + 1));
}
visited.emplace(position);
}
heading = Direction{(static_cast<int>(heading) + 1) % 4};
// should never happen
return -1;
}
asio::awaitable<void> Bot::ChooseMove() {
// update map
known_map[std::make_pair(x, y)] =
(up ? static_cast<unsigned>(Direction::UP) : 0) |
(right ? static_cast<unsigned>(Direction::RIGHT) : 0) |
(down ? static_cast<unsigned>(Direction::DOWN) : 0) |
(left ? static_cast<unsigned>(Direction::LEFT) : 0);
known_map[std::make_pair(x - 1, y)] |=
(left ? static_cast<unsigned>(Direction::RIGHT) : 0);
known_map[std::make_pair(x + 1, y)] |=
(right ? static_cast<unsigned>(Direction::LEFT) : 0);
known_map[std::make_pair(x, y - 1)] |=
(up ? static_cast<unsigned>(Direction::DOWN) : 0);
known_map[std::make_pair(x, y + 1)] |=
(down ? static_cast<unsigned>(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;
}