main.cpp (4909B)
1 #include <iostream> 2 #include <filesystem> 3 #include <fstream> 4 #include <vector> 5 #include <set> 6 #include <cstring> 7 #include <cstdio> 8 #include <tuple> 9 #include <algorithm> 10 #include <expected> 11 #include <ranges> 12 #include <thread> 13 14 enum class Dir { 15 up, 16 down, 17 left, 18 right 19 }; 20 21 typedef std::vector<std::string> Data; 22 typedef std::pair<int,int> Vec2; // x, y 23 typedef std::tuple<int,int,Dir> Vec3; 24 25 //@todo implement a generic map<width, height> class 26 27 template<typename T> 28 T read_file(const std::string &path) { 29 std::ifstream ifs(path, std::ios::binary); 30 if (!ifs.is_open()) { 31 throw std::runtime_error(path+":"+std::strerror(errno)); 32 } 33 T buf; 34 while (1) { 35 std::string str; 36 std::getline(ifs, str); 37 if (!str.empty()) buf.push_back(str); 38 if (!ifs) break; 39 } 40 return buf; 41 } 42 43 std::expected<Vec3, std::string> find_guard(const Data &m) { 44 for (int y=0; y<(int)m.size(); ++y) { 45 for (int x=0; x<(int)m[y].size(); ++x) { 46 if (m[y][x]=='^') return Vec3(x, y, Dir::up); 47 } 48 } 49 return std::unexpected("no guard found"); 50 } 51 52 [[ maybe_unused ]] 53 static const char * str(Dir d) { 54 switch (d) { 55 case Dir::up: return "up"; 56 case Dir::down: return "down"; 57 case Dir::left: return "left"; 58 case Dir::right: return "right"; 59 } 60 return "NULL"; 61 } 62 63 std::expected<Vec3, std::string> get_next(const Data &m, const Vec3 &p) { 64 Vec3 np = p; 65 auto [x,y,d] = np; 66 switch (d) { 67 case Dir::up: 68 if (y-1>=0) { 69 if (m[y-1][x]=='#') { 70 d=Dir::right; 71 } else { 72 y--; 73 } 74 } else { 75 return std::unexpected("out"); 76 } 77 break; 78 case Dir::down: 79 if (y+1<(int)m.size()) { 80 if (m[y+1][x]=='#') { 81 d=Dir::left; 82 } else { 83 y++; 84 } 85 } else { 86 return std::unexpected("out"); 87 } 88 break; 89 case Dir::left: 90 if (x-1>=0) { 91 if (m[y][x-1]=='#') { 92 d=Dir::up; 93 } else { 94 x--; 95 } 96 } else { 97 return std::unexpected("out"); 98 } 99 break; 100 case Dir::right: 101 if (x+1<(int)m.size()) { 102 if (m[y][x+1]=='#') { 103 d=Dir::down; 104 } else { 105 x++; 106 } 107 } else { 108 return std::unexpected("out"); 109 } 110 break; 111 } 112 return Vec3(x,y,d); 113 } 114 115 // works only on square input 116 void traverse(const Data &m, std::vector<Vec3> &visited, const Vec3 &p, bool &loop) { 117 visited.push_back(p); 118 auto np = get_next(m, p); 119 if (np.has_value()) { 120 if (std::count(visited.begin(), visited.end(), np.value())>0) { 121 loop = true; 122 } else { 123 traverse(m, visited, np.value(), loop); 124 } 125 } 126 } 127 128 [[maybe_unused]] void print_map(const Data &m) { 129 std::cout << "---------------------" << std::endl; 130 for (auto &s : m) { 131 std::cout << s << std::endl; 132 } 133 std::cout << "---------------------" << std::endl; 134 } 135 136 Vec2 solve(const Data &input [[ maybe_unused ]]) { 137 std::vector<Vec3> visited; 138 139 if (auto g=find_guard(input); g.has_value()) { 140 bool loop = false; 141 traverse(input, visited, g.value(), loop); 142 } 143 144 std::set<Vec2> p1; 145 int p2 = 0; 146 for (auto it=visited.begin(); it!=visited.end(); ++it) { 147 auto [x, y, d] = *it; 148 Vec2 p(x, y); 149 p1.insert(p); 150 // p2 - put obstacles on every walked tile and traverse everything all over again 151 std::vector<Vec3> visited1; 152 std::copy(visited.begin(), it+1, std::back_inserter(visited1)); 153 // obstacle will be placed at the next tile 154 Data input1; 155 std::copy(input.begin(), input.end(), std::back_inserter(input1)); 156 auto it1 = it+1; 157 if (it1!=visited.end()) { 158 auto [x1, y1, d1] = *it1; 159 bool on_path = std::count_if(visited1.begin(), visited1.end(), [&x1, &y1](const Vec3 &v1){ 160 auto [x2,y2,d2] = v1; 161 return x1==x2 && y1==y2; 162 })>0; 163 if (input[y1][x1]=='#' || (x==x1&&y==y1) || on_path) { 164 continue; 165 } 166 input1[y1][x1] = '#'; 167 bool loop1 = false; 168 traverse(input1, visited1, *it, loop1); 169 if (loop1) p2++; 170 } 171 } 172 173 return {(int)p1.size(), p2}; 174 } 175 176 int main(int argc, char **argv) { 177 const std::string fname = argc>1 ? argv[1] : "test1.txt"; 178 std::cout << "AoC 2024 day 06 " << fname << std::endl; 179 Data input = read_file<Data>(fname); 180 auto [p1, p2] = solve(input); 181 std::cout << "part1: " << p1 << std::endl; 182 std::cout << "part2: " << p2 << std::endl; 183 184 std::cout << std::jthread::hardware_concurrency() << " threads supported" << std::endl; 185 186 return 0; 187 }