#pragma once #include #include #include #include #include #include #include #include #include using namespace std; const string OUTPATH = "../output/"; const string MSPATH = "makespan/"; const string DISTPATH = "distance/"; const string INPATH = "../input/"; const string EXT = ".in"; const string OUTEXT = ".out"; string remove_ext(string s) { size_t last_slash = s.find_last_of("/"); if (last_slash != string::npos) s = s.substr(last_slash + 1); size_t last_dot = s.find_last_of("."); if (last_dot == string::npos) return s; return s.substr(0, last_dot); } string out_file_full(string name, bool makespan) { return OUTPATH + (makespan ? MSPATH : DISTPATH) + name + OUTEXT; } const int INF = 0x3f3f3f3f; struct pt { pt() : x(0), y(0) {} pt(int x, int y) : x(x), y(y) {} int x, y; pt operator+(const pt& oth) const { return pt(x + oth.x, y + oth.y); } pt operator-(const pt& oth) const { return pt(x - oth.x, y - oth.y); } bool operator==(const pt& oth) const { return tie(x, y) == tie(oth.x, oth.y); } bool operator!=(const pt& oth) const { return tie(x, y) != tie(oth.x, oth.y); } bool operator<(const pt& oth) const { return tie(x, y) < tie(oth.x, oth.y); } }; int abs(const pt& p) { return abs(p.x) + abs(p.y); } int dist(const pt& a, const pt& b) { return abs(a - b); } long long dot(const pt& a, const pt& b) { return a.x * b.x + a.y * b.y; } double norm(const pt& a) { return sqrt(dot(a, a)); } ostream& operator<<(ostream& stream, const pt& p) { stream << "(" << p.x << ", " << p.y << ")"; return stream; } // Move encoding // 0: Stand still // 1: North // 2: East // 3: South // 4: West const int dx[] = {0, 0, 1, 0, -1, 1, -1, 1, -1}; const int dy[] = {0, 1, 0, -1, 0, 1, -1, -1, 1}; const pt dxy[] = {pt(0, 0), pt(0, 1), pt(1, 0), pt(0, -1), pt(-1, 0), pt(1, 1), pt(-1, -1), pt(1, -1), pt(-1, 1)}; const vector dirnames = {"", "N", "E", "S", "W"}; const int opposite_dir[] = {0, 3, 4, 1, 2}; namespace std { template <> struct hash { size_t operator()(const pt& p) const { return (p.x << 9) + p.y; } }; template <> struct hash> { size_t operator()(const pair& p) const { return hash()(p.first) ^ (hash()(p.second) << 1); } }; } // namespace std struct instance; bool verify(instance& ins); bool improvement(instance& ins, bool makespan); bool improvement_custom(instance& ins, string full_filename, bool makespan); struct instance { string name; int n, m; // n is the number of robots, m obstacles int time; vector start; vector target; vector obstacle; vector> moves; // list of simultaneous moves (not sparse) // big score is bad // TODO we don't actually use this long double score; bool operator<(const instance& other) const { return score > other.score; } bool done(int i) { return abs(start[i] - target[i]) == 0; } bool done() { for (int i = 0; i < n; i++) if (!done(i)) return false; return true; } void clear() { name = ""; n = m = 0; time = 0; score = 0; start.clear(); target.clear(); obstacle.clear(); moves.clear(); } instance sub_instance(const vector& sub_robots) { instance ret; ret.clear(); ret.m = m; ret.obstacle = obstacle; ret.time = time; ret.n = sub_robots.size(); for (int i = 0; i < ret.n; ++i) { ret.start.push_back(start[sub_robots[i]]); ret.target.push_back(target[sub_robots[i]]); } ret.moves.resize(ret.time); for (int t = 0; t < ret.time; ++t) for (size_t i = 0; i < sub_robots.size(); ++i) ret.moves[t].push_back(moves[t][sub_robots[i]]); return ret; } void merge_sub_instance(const instance& sub, const vector& sub_robots) { time = max(time, sub.time); while (moves.size() < size_t(time)) moves.emplace_back(n); for (int t = 0; t < sub.time; ++t) for (size_t i = 0; i < sub_robots.size(); ++i) moves[t][sub_robots[i]] = sub.moves[t][i]; for (int t = sub.time; t < time; ++t) for (size_t i = 0; i < sub_robots.size(); ++i) moves[t][sub_robots[i]] = 0; } void read(string filename) { // clear instance and read new instance clear(); filename = remove_ext(filename); name = filename; string filepath = INPATH + filename + EXT; // cerr << "READING FILE " << filepath << endl; ifstream in(filepath); in >> n >> m; for (int i = 0; i < n; i++) { int x, y; in >> x >> y; start.emplace_back(x, y); } for (int i = 0; i < n; i++) { int x, y; in >> x >> y; target.emplace_back(x, y); } for (int i = 0; i < m; i++) { int x, y; in >> x >> y; obstacle.emplace_back(x, y); } } void read_custom(string fullpath) { ifstream in(fullpath); string in_name; in >> in_name; read(in_name); int idump; in >> idump; in >> time; moves = vector>(time, vector(n)); for (auto& vi : moves) for (auto& i : vi) in >> i; } string out_file(bool makespan) { return out_file_full(name, makespan); } void read_out(bool makespan) { // read moves and time ifstream in(out_file(makespan)); // dump extra info, and get time string dump; in >> dump; int idump; in >> idump; in >> time; moves = vector>(time, vector(n)); for (auto& vi : moves) for (auto& i : vi) in >> i; } void write_input(string filename) { // Re-outputs the input string // Only really used for converting json ofstream out(INPATH + filename + EXT); // cout << "writing to " << INPATH << filename << EXT << endl; out << n << ' ' << m << '\n'; for (const auto& p : start) out << p.x << ' ' << p.y << '\n'; for (const auto& p : target) out << p.x << ' ' << p.y << '\n'; for (const auto& p : obstacle) out << p.x << ' ' << p.y << '\n'; } void write_mapf(string filename) { const string MAPF_OUTPATH = "../mapf_input/"; const string MAPF_MAP_EXT = ".map"; const string MAPF_AGENT_EXT = ".agent"; const int GAP = 2; string map_filepath = MAPF_OUTPATH + filename + MAPF_MAP_EXT; string agents_filepath = MAPF_OUTPATH + filename + MAPF_AGENT_EXT; ofstream mapout(map_filepath); ofstream agentout(agents_filepath); cerr << "writing to " << map_filepath << " and " << agents_filepath << endl; // compute map for mapf pt mx(-1, -1); string blank(120, '.'); vector g(120, blank); for (auto& p : obstacle) { g[p.x + GAP][p.y + GAP] = '@'; mx.x = max(mx.x, p.x); mx.y = max(mx.y, p.y); } for (auto& p : start) { mx.x = max(mx.x, p.x); mx.y = max(mx.y, p.y); } for (auto& p : target) { mx.x = max(mx.x, p.x); mx.y = max(mx.y, p.y); } mx.x += 2 * GAP; mx.y += 2 * GAP; mapout << mx.x << ',' << mx.y << '\n'; while ((int)g.size() > mx.y) { g.pop_back(); } for (auto& s : g) { s.resize(mx.x); mapout << s << '\n'; } agentout << n << '\n'; for (int i = 0; i < n; i++) { agentout << start[i].x + GAP << ',' << start[i].y + GAP << ','; agentout << target[i].x + GAP << ',' << target[i].y + GAP << '\n'; } } void complete_write(bool makespan) { string out_f = out_file(makespan); // cout << "writing to " << out_f << endl; ofstream out(out_f); out << name << '\n'; // also include the instance name for convenience out << n << '\n'; // also include the value of n for convenience out << time << '\n'; for (const auto& v : moves) { for (int i = 0; i < n; i++) { out << v[i] << " "; } out << '\n'; } } void write() { // TODO: add code that overwrites only new solution is better // (and have two output directories) if (!verify(*this)) { cerr << "Verification failed! Not writing file." << endl; return; } vector vals = {false, true}; for (bool makespan : vals) if (improvement(*this, makespan)) { /* cout << "writing improvement in " << (makespan ? "makespan" : "distance") << "!" << endl; */ complete_write(makespan); } } void debug_write(string full_filename) { ofstream out(full_filename); out << name << '\n'; out << n << '\n'; out << time << '\n'; for (const auto& v : moves) { for (int i = 0; i < n; i++) { out << v[i] << " "; } out << '\n'; } } void improve_only_debug_write(string full_filename, bool makespan) { if (!verify(*this)) { cerr << "Verification failed! Not writing file." << endl; return; } if (improvement_custom(*this, full_filename, makespan)) { cerr << "It's a (debug) improvement (" << full_filename << ")!" << endl; debug_write(full_filename); } } }; void slowdown(instance& ins, int slow) { vector> m2; vector zeroes(ins.n); for (auto v : ins.moves) { m2.push_back(v); for (int i = 0; i < slow; i++) m2.push_back(zeroes); } ins.time = m2.size(); ins.moves = m2; } // the reverse problem (and solution) void reverse_instance(instance& ins) { ins.start.swap(ins.target); reverse(ins.moves.begin(), ins.moves.end()); for (auto& v : ins.moves) { for (int& d : v) { d = opposite_dir[d]; } } } vector> combine_movesets(const instance& ins1, const instance& ins2) { assert(ins1.n == ins2.n); assert(ins1.m == ins2.m); assert(ins1.target == ins2.start); vector> moves = ins1.moves; for (auto& vi : ins2.moves) moves.push_back(vi); return moves; } bool verify(instance& ins) { if (ins.time != int(ins.moves.size())) return false; vector locations = ins.start; set obstacle_s; for (pt& p : ins.obstacle) { obstacle_s.insert(p); } int t = 0; for (auto& step : ins.moves) { map dir_from_loc; for (int i = 0; i < ins.n; ++i) { dir_from_loc[locations[i]] = step[i]; } for (int i = 0; i < ins.n; ++i) { pt p = locations[i] + dxy[step[i]]; // determine if move is a collision if (obstacle_s.count(p) > 0 || (dir_from_loc.count(p) > 0 && dir_from_loc[p] != step[i])) { cout << "failed on timestep t=" << t << endl; return false; } // move if it's not (doesn't affect later loop checks) locations[i] = p; } ++t; } return locations == ins.target; } // lower score is better int score(instance& ins, bool makespan) { if (!verify(ins)) return INF; assert(ins.time == int(ins.moves.size())); if (makespan) return ins.time; int distance = 0; for (auto& step : ins.moves) { for (int i = 0; i < ins.n; ++i) { if (step[i] != 0) ++distance; } } return distance; } double average_makespan(instance& ins) { vector counter(ins.n); double ms = 0; for (auto& step : ins.moves) { for (int i = 0; i < ins.n; i++) { if (step[i]) { ms += 1 + counter[i]; counter[i] = 0; } else { counter[i]++; } } } return ms / ins.n; } double squared_makespan_sum(instance& ins) { double ms = 0; for (int i = 0; i < ins.n; i++) { double cur = 0; int counter = 0; for (size_t s = 0; s < ins.moves.size(); ++s) { if (ins.moves[s][i]) { cur += 1 + counter; counter = 0; } else { counter++; } } ms += cur * cur; } return ms; } bool improvement(instance& ins, bool makespan) { instance dupe = ins; // copy most info from ins // dupe.read(ins.name); // cout << "out_file_full=" << out_file_full(ins.name, makespan) << endl; dupe.read_out(makespan); int old_score = score(dupe, makespan); int new_score = score(ins, makespan); /* cout << "makespan=" << (makespan ? "true" : "false") << ", old score=" << old_score << ", new score=" << new_score << endl;*/ if (old_score == new_score) { // Break ties by average makespan if (squared_makespan_sum(ins) < squared_makespan_sum(dupe)) { return true; } } return new_score < old_score; } bool improvement_custom(instance& ins, string full_filename, bool makespan) { instance dupe; // copy most info from ins // dupe.read(ins.name); // cout << "out_file_full=" << out_file_full(ins.name, makespan) << endl; dupe.read_custom(full_filename); if (ins.n != dupe.n) return false; int old_score = score(dupe, makespan); int new_score = score(ins, makespan); /* cout << "makespan=" << (makespan ? "true" : "false") << ", old score=" << old_score << ", new score=" << new_score << endl;*/ if (old_score == new_score) { // Break ties by average makespan if (squared_makespan_sum(ins) < squared_makespan_sum(dupe)) { return true; } } return new_score < old_score; }