|
| 1 | +#include <algorithm> |
| 2 | +#include <fstream> |
| 3 | +#include <iostream> |
| 4 | +#include <string> |
| 5 | +#include <unordered_set> |
| 6 | +#include <unordered_map> |
| 7 | +#include <vector> |
| 8 | +#include <queue> |
| 9 | +#include <stack> |
| 10 | + |
| 11 | +constexpr long long pow(const int base, const int raised_to) { |
| 12 | + long long ans = 1; |
| 13 | + for (int i = 1; i <= raised_to; i++) { |
| 14 | + ans *= base; |
| 15 | + } |
| 16 | + return ans; |
| 17 | +} |
| 18 | + |
| 19 | +struct hash_pair { |
| 20 | + template <class T1, class T2> |
| 21 | + size_t operator()(const std::pair<T1, T2>& p) const { |
| 22 | + auto hash1 = std::hash<T1>{}(p.first); |
| 23 | + auto hash2 = std::hash<T2>{}(p.second); |
| 24 | + return hash1 ^ hash2; |
| 25 | + } |
| 26 | +}; |
| 27 | + |
| 28 | +class Program { |
| 29 | + public: |
| 30 | + Program(const std::vector<long long>& program) : init_program_state(program) { |
| 31 | + for (long long i = 0; i < program.size(); i++) { |
| 32 | + memory[i] = program[i]; |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + void InitIfNotInited(const long long key) { |
| 37 | + if (memory.find(key) == memory.end()) { |
| 38 | + memory[key] = 0; |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + void reset() { |
| 43 | + for (long long i = 0; i < init_program_state.size(); i++) { |
| 44 | + memory[i] = init_program_state[i]; |
| 45 | + } |
| 46 | + complete = false; |
| 47 | + inst_ptr = 0; |
| 48 | + relative_base = 0; |
| 49 | + } |
| 50 | + |
| 51 | + std::vector<long long> getNOps(const size_t n, const size_t inst_ptr, |
| 52 | + long long modes) { |
| 53 | + std::vector<long long> ops; |
| 54 | + for (size_t i = 1; i <= n; ++i) { |
| 55 | + size_t rem = modes % 10; |
| 56 | + modes = modes / 10; |
| 57 | + if (rem == 0) { |
| 58 | + InitIfNotInited(inst_ptr + i); |
| 59 | + InitIfNotInited(memory[inst_ptr + i]); |
| 60 | + ops.push_back(memory[memory[inst_ptr + i]]); |
| 61 | + } else if (rem == 1) { |
| 62 | + InitIfNotInited(inst_ptr + i); |
| 63 | + InitIfNotInited(memory[inst_ptr + i]); |
| 64 | + ops.push_back(memory[inst_ptr + i]); |
| 65 | + } else if (rem == 2) { |
| 66 | + InitIfNotInited(inst_ptr + i); |
| 67 | + InitIfNotInited(relative_base + memory[inst_ptr + i]); |
| 68 | + InitIfNotInited(memory[relative_base + memory[inst_ptr + i]]); |
| 69 | + ops.push_back(memory[relative_base + memory[inst_ptr + i]]); |
| 70 | + } else { |
| 71 | + std::cout << "Error" << '\n'; |
| 72 | + exit(0); |
| 73 | + } |
| 74 | + } |
| 75 | + return ops; |
| 76 | + } |
| 77 | + |
| 78 | + long long getAddress(const size_t increment, long long modes) { |
| 79 | + long long address; |
| 80 | + const size_t rem = modes; |
| 81 | + if (rem == 0) { |
| 82 | + InitIfNotInited(inst_ptr + increment); |
| 83 | + address = memory[inst_ptr + increment]; |
| 84 | + } else if (rem == 2) { |
| 85 | + InitIfNotInited(inst_ptr + increment); |
| 86 | + InitIfNotInited(relative_base + memory[inst_ptr + increment]); |
| 87 | + address = relative_base + memory[inst_ptr + increment]; |
| 88 | + } else { |
| 89 | + std::cout << "Error" << '\n'; |
| 90 | + exit(0); |
| 91 | + } |
| 92 | + return address; |
| 93 | + } |
| 94 | + |
| 95 | + long long compute(const std::vector<long long>& input) { |
| 96 | + long long output = -1; |
| 97 | + size_t input_ptr = 0; |
| 98 | + while (memory[inst_ptr] != 99) { |
| 99 | + const long long instr = memory[inst_ptr]; |
| 100 | + const long long opcode = instr % 100; |
| 101 | + const long long modes = instr / 100; |
| 102 | + if (opcode == 1) { |
| 103 | + constexpr int n_ops = 2; |
| 104 | + const auto ops = getNOps(n_ops, inst_ptr, modes); |
| 105 | + memory[getAddress(3, modes / pow(10, n_ops))] = ops[0] + ops[1]; |
| 106 | + inst_ptr += 4; |
| 107 | + } else if (opcode == 2) { |
| 108 | + constexpr int n_ops = 2; |
| 109 | + const auto ops = getNOps(n_ops, inst_ptr, modes); |
| 110 | + memory[getAddress(3, modes / pow(10, n_ops))] = ops[0] * ops[1]; |
| 111 | + inst_ptr += 4; |
| 112 | + } else if (opcode == 3) { |
| 113 | + memory[getAddress(1, modes)] = input[input_ptr]; |
| 114 | + inst_ptr += 2; |
| 115 | + input_ptr += 1; |
| 116 | + } else if (opcode == 4) { |
| 117 | + const auto ops = getNOps(1, inst_ptr, modes); |
| 118 | + output = ops[0]; |
| 119 | + inst_ptr += 2; |
| 120 | + // std::cout << output << '\n'; |
| 121 | + return output; |
| 122 | + } else if (opcode == 5) { |
| 123 | + const auto ops = getNOps(2, inst_ptr, modes); |
| 124 | + if (ops[0] != 0) { |
| 125 | + inst_ptr = ops[1]; |
| 126 | + } else { |
| 127 | + inst_ptr += 3; |
| 128 | + } |
| 129 | + } else if (opcode == 6) { |
| 130 | + const auto ops = getNOps(2, inst_ptr, modes); |
| 131 | + if (ops[0] == 0) { |
| 132 | + inst_ptr = ops[1]; |
| 133 | + } else { |
| 134 | + inst_ptr += 3; |
| 135 | + } |
| 136 | + } else if (opcode == 7) { |
| 137 | + constexpr int n_ops = 2; |
| 138 | + const auto ops = getNOps(n_ops, inst_ptr, modes); |
| 139 | + if (ops[0] < ops[1]) { |
| 140 | + memory[getAddress(3, modes / pow(10, n_ops))] = 1; |
| 141 | + } else { |
| 142 | + memory[getAddress(3, modes / pow(10, n_ops))] = 0; |
| 143 | + } |
| 144 | + inst_ptr += 4; |
| 145 | + } else if (opcode == 8) { |
| 146 | + constexpr int n_ops = 2; |
| 147 | + const auto ops = getNOps(n_ops, inst_ptr, modes); |
| 148 | + if (ops[0] == ops[1]) { |
| 149 | + memory[getAddress(3, modes / pow(10, n_ops))] = 1; |
| 150 | + } else { |
| 151 | + memory[getAddress(3, modes / pow(10, n_ops))] = 0; |
| 152 | + } |
| 153 | + inst_ptr += 4; |
| 154 | + } else if (opcode == 9) { |
| 155 | + const auto ops = getNOps(1, inst_ptr, modes); |
| 156 | + relative_base += ops[0]; |
| 157 | + inst_ptr += 2; |
| 158 | + } |
| 159 | + } |
| 160 | + complete = true; |
| 161 | + return output; |
| 162 | + } |
| 163 | + |
| 164 | + bool isComplete() { return complete; } |
| 165 | + |
| 166 | + private: |
| 167 | + const std::vector<long long> init_program_state; |
| 168 | + std::unordered_map<long long, long long> memory; |
| 169 | + bool complete = false; |
| 170 | + size_t inst_ptr = 0; |
| 171 | + size_t relative_base = 0; |
| 172 | +}; |
| 173 | + |
| 174 | +class IntcodeComputer { |
| 175 | + public: |
| 176 | + void addProgram(const std::vector<long long>& program) { |
| 177 | + programs.emplace_back(program); |
| 178 | + } |
| 179 | + |
| 180 | + long long runProgram(const size_t index, |
| 181 | + const std::vector<long long>& input) { |
| 182 | + return programs[index].compute(input); |
| 183 | + } |
| 184 | + |
| 185 | + void resetProgram(const size_t index) { programs[index].reset(); } |
| 186 | + |
| 187 | + bool isLastComplete() { return programs.back().isComplete(); } |
| 188 | + |
| 189 | + bool isComplete(size_t index) { return programs[index].isComplete(); } |
| 190 | + |
| 191 | + void reset() { programs.clear(); } |
| 192 | + |
| 193 | + private: |
| 194 | + std::vector<Program> programs; |
| 195 | +}; |
| 196 | + |
| 197 | +// TODO(vss): Refactor to remoe extra functions |
| 198 | +struct Point2D { |
| 199 | + int x, y; |
| 200 | + Point2D(const int x = 0, const int y = 0) : x(x), y(y){} |
| 201 | + Point2D operator+ (const Point2D& p) { |
| 202 | + return Point2D(this->x + p.x, this->y + p.y); |
| 203 | + } |
| 204 | + Point2D operator- (const Point2D& p) { |
| 205 | + return Point2D(this->x - p.x, this->y - p.y); |
| 206 | + } |
| 207 | + bool operator== (const Point2D& p) const { |
| 208 | + return (this->x == p.x && this->y == p.y); |
| 209 | + } |
| 210 | +}; |
| 211 | + |
| 212 | +bool ComparePts(const Point2D& p1, const Point2D& p2) { |
| 213 | + return (p1.x == p2.x && p1.y == p2.y); |
| 214 | +} |
| 215 | + |
| 216 | +Point2D AddPoints(const Point2D& p1, const Point2D& p2) { |
| 217 | + return Point2D(p1.x + p2.x, p1.y + p2.y); |
| 218 | +} |
| 219 | +Point2D SubtractPoints(const Point2D& p1, const Point2D& p2) { |
| 220 | + return Point2D(p1.x - p2.x, p1.y - p2.y); |
| 221 | +} |
| 222 | + |
| 223 | +struct hashp2d { |
| 224 | + size_t operator()(const Point2D& p) const { |
| 225 | + return std::hash<int>{}(p.x) ^ std::hash<int>{}(p.y); |
| 226 | + } |
| 227 | +}; |
| 228 | + |
| 229 | +std::vector<Point2D> getNeighbours(const Point2D& p) { |
| 230 | + std::vector<Point2D> deltas; |
| 231 | + deltas.emplace_back(AddPoints(p, Point2D(0, 1))); |
| 232 | + deltas.emplace_back(AddPoints(p, Point2D(0, -1))); |
| 233 | + deltas.emplace_back(AddPoints(p, Point2D(1, 0))); |
| 234 | + deltas.emplace_back(AddPoints(p, Point2D(-1, 0))); |
| 235 | + return deltas; |
| 236 | +} |
| 237 | + |
| 238 | +int convertMoveToInstruction (const Point2D& p) { |
| 239 | + if( ComparePts(p, Point2D(0, 1))) { |
| 240 | + return 1; |
| 241 | + } |
| 242 | + if (ComparePts(p, Point2D(1, 0))) { |
| 243 | + return 4; |
| 244 | + } |
| 245 | + if (ComparePts(p, Point2D(-1, 0))) { |
| 246 | + return 3; |
| 247 | + } |
| 248 | + if (ComparePts(p, Point2D(0, -1))) { |
| 249 | + return 2; |
| 250 | + } |
| 251 | + return 0; |
| 252 | +} |
| 253 | + |
| 254 | +void print(const std::vector<Point2D>& v) { |
| 255 | + for(const auto& ele : v) { |
| 256 | + std::cout << "(" << ele.x << ", " << ele.y << ")" << ' '; |
| 257 | + } |
| 258 | + std::cout << '\n'; |
| 259 | +} |
| 260 | + |
| 261 | +void print(const Point2D& ele) { |
| 262 | + std::cout << "(" << ele.x << ", " << ele.y << ")" << ' '; |
| 263 | + std::cout << '\n'; |
| 264 | +} |
| 265 | + |
| 266 | +void CallError(const std::string& err) { |
| 267 | + std::cout << err << '\n'; |
| 268 | + exit(0); |
| 269 | +} |
| 270 | + |
| 271 | +std::vector<Point2D> dijkstra(std::unordered_map<Point2D, int, hashp2d>& grid, |
| 272 | + const Point2D& start, |
| 273 | + const Point2D& dest) { |
| 274 | + std::vector<Point2D> moves; |
| 275 | + std::queue<Point2D> points; |
| 276 | + std::unordered_set <Point2D, hashp2d> visited; |
| 277 | + std::unordered_map <Point2D, Point2D, hashp2d> parent; |
| 278 | + points.push(start); |
| 279 | + // if (grid.find(start) != grid.end() && grid[start] == 0) CallError("Start is obstacle"); // Obstacle |
| 280 | + while (!points.empty() && !ComparePts(points.front(), dest)) { |
| 281 | + const auto current = points.front(); |
| 282 | + points.pop(); |
| 283 | + visited.insert(current); |
| 284 | + bool found = false; // Early break condition |
| 285 | + for (auto& neighbour : getNeighbours(current)) { |
| 286 | + if (visited.find(neighbour) != visited.end()) continue; // Already popped |
| 287 | + if (grid.find(neighbour) != grid.end() && grid[neighbour] == 0) continue; // Obstacle |
| 288 | + if (grid.find(neighbour) == grid.end() && !ComparePts(neighbour, dest)) continue; // Unknown point. Search shoudl be restricted to known points |
| 289 | + parent[neighbour] = current; |
| 290 | + if (ComparePts(neighbour, dest)) { |
| 291 | + found = true; |
| 292 | + points.push(neighbour); |
| 293 | + break; |
| 294 | + } |
| 295 | + points.push(neighbour); |
| 296 | + } |
| 297 | + if (found) break; |
| 298 | + } |
| 299 | + if (points.empty()) CallError("Failed to find a path"); |
| 300 | + auto current = dest; |
| 301 | + while(!ComparePts(current, start)) { |
| 302 | + moves.push_back(SubtractPoints(current, parent[current])); |
| 303 | + current = parent[current]; |
| 304 | + } |
| 305 | + std::reverse(std::begin(moves), std::end(moves)); |
| 306 | + return moves; |
| 307 | +} |
| 308 | + |
| 309 | +template<typename T> |
| 310 | +void print(const std::vector<T>& v) { |
| 311 | + for(const auto& ele : v) { |
| 312 | + std::cout << ele << ' '; |
| 313 | + } |
| 314 | + std::cout << '\n'; |
| 315 | +} |
| 316 | + |
| 317 | +void explore(IntcodeComputer& computer) { |
| 318 | + int output = 1; |
| 319 | + Point2D current(0, 0); |
| 320 | + std::stack<Point2D> unknown; |
| 321 | + std::unordered_set <Point2D, hashp2d> visited; |
| 322 | + |
| 323 | + for (const auto& neighbour : getNeighbours(current)) { |
| 324 | + unknown.push(neighbour); |
| 325 | + } |
| 326 | + std::unordered_map<Point2D, int, hashp2d> grid; |
| 327 | + grid[current] = 1; |
| 328 | + |
| 329 | + while (output != 2) { |
| 330 | + const auto nearest_unknown_pt = unknown.top(); |
| 331 | + unknown.pop(); |
| 332 | + if(grid.find(nearest_unknown_pt) != grid.end() && grid[nearest_unknown_pt] == 0) continue; |
| 333 | + if(current == nearest_unknown_pt) continue; |
| 334 | + if (grid.find(current) != grid.end() && grid[current] == 0) continue; |
| 335 | + // Search will never go through an obstacle, only the dest pount can be an obstacle, but is at yet unknow |
| 336 | + const auto moves = dijkstra(grid, current, nearest_unknown_pt); |
| 337 | + for (const auto& move : moves) { |
| 338 | + current = AddPoints(current, move); |
| 339 | + output = computer.runProgram(0, {convertMoveToInstruction(move)}); |
| 340 | + } |
| 341 | + grid[nearest_unknown_pt] = output; |
| 342 | + if (output == 0) { |
| 343 | + current = SubtractPoints(current, moves.back()); |
| 344 | + } else if (output == 1) { |
| 345 | + for (const auto& neighbour : getNeighbours(current)) { |
| 346 | + if(grid.find(neighbour) == grid.end()) unknown.push(neighbour); |
| 347 | + } |
| 348 | + } |
| 349 | + } |
| 350 | + std::cout << dijkstra(grid, Point2D(0,0), current).size() << '\n'; |
| 351 | +} |
| 352 | + |
| 353 | +int main(int argc, char* argv[]) { |
| 354 | + // Get input |
| 355 | + std::string input = "../input/day_15_input"; |
| 356 | + if (argc > 1) { |
| 357 | + input = argv[1]; |
| 358 | + } |
| 359 | + std::ifstream file(input); |
| 360 | + std::string input_str; |
| 361 | + std::getline(file, input_str); |
| 362 | + const std::string delimiter = ","; |
| 363 | + |
| 364 | + std::size_t start = 0; |
| 365 | + std::size_t end = input_str.find(delimiter); |
| 366 | + std::vector<long long> program; |
| 367 | + while (end != std::string::npos) { |
| 368 | + program.emplace_back(std::stoll(input_str.substr(start, end - start))); |
| 369 | + start = end + delimiter.size(); |
| 370 | + end = input_str.find(delimiter, start); |
| 371 | + } |
| 372 | + program.emplace_back(std::stoll(input_str.substr(start, end - start))); |
| 373 | + |
| 374 | + // Solve |
| 375 | + IntcodeComputer computer; |
| 376 | + computer.addProgram(program); |
| 377 | + explore(computer); |
| 378 | +} |
0 commit comments