advent2024

Advent of Code 2024
git clone git://bsandro.tech/advent2024
Log | Files | Refs

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 }