Advertisement
itai-nelken

AOC-2022/day-9

Dec 9th, 2022
1,099
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.81 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h> // abs, abort
  3. #include <assert.h>
  4. #include <string>
  5. #include <vector>
  6. #include <array>
  7. #include <unordered_map>
  8. #include <unordered_set>
  9.  
  10. enum class State {
  11.     Head, Tail, None
  12. };
  13.  
  14. struct Point {
  15.     Point() = default;
  16.     ~Point() = default;
  17.     Point(int x, int y) : x(x), y(y) {}
  18.  
  19.     bool operator==(const Point &other) const {
  20.         return this->x == other.x && this->y == other.y;
  21.     }
  22.     bool operator!=(const Point &other) const {
  23.         return !(*this == other);
  24.     }
  25.  
  26.     int x, y;
  27. };
  28.  
  29. namespace std {
  30.     template<>
  31.     struct hash<Point> {
  32.         std::size_t operator()(const Point &p) const {
  33.             return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1);
  34.         }
  35.     };
  36. };
  37.  
  38. enum class Direction {
  39.     Up, Down,
  40.     Left, Right,
  41.     DiagonalUpLeft, DiagonalUpRight,
  42.     DiagonalDownLeft, DiagonalDownRight
  43. };
  44.  
  45. std::string direction_as_str(Direction d) {
  46.     switch(d) {
  47.             case Direction::Up: return "Up";
  48.             case Direction::Down: return "Down";
  49.             case Direction::Right: return "Right";
  50.             case Direction::Left: return "Left";
  51.             case Direction::DiagonalUpRight: return "DiagonalUpRight";
  52.             case Direction::DiagonalUpLeft: return "DiagonalUpLeft";
  53.             case Direction::DiagonalDownRight: return "DiagonalDownRight";
  54.             case Direction::DiagonalDownLeft: return "DiagonalDownLeft";
  55.             default: assert(0);
  56.         }
  57. }
  58.  
  59. struct Op {
  60.     Op() = default;
  61.     ~Op() = default;
  62.     Direction direction;
  63.     int amount;
  64.  
  65.     std::string as_str() {
  66.         std::string s = "Op{";
  67.         switch(direction) {
  68.             case Direction::Up: s += "U"; break;
  69.             case Direction::Down: s += "D"; break;
  70.             case Direction::Left: s += "L"; break;
  71.             case Direction::Right: s += "R"; break;
  72.             default: assert(0);
  73.         }
  74.         s += ", ";
  75.         s += std::to_string(amount);
  76.         s += "}";
  77.         return s;
  78.     }
  79. };
  80.  
  81. template<int KNOT_COUNT>
  82. struct Simulation {
  83.     ~Simulation() {}
  84.     static Simulation parse(const char *path);
  85.     int simulate();
  86.  
  87. private:
  88.     Simulation();
  89.     void update(Point new_head_pos);
  90.     Direction tail_move(Point diff) const { return tail_moves.at(diff); }
  91.  
  92.     std::unordered_map<Point, Direction> tail_moves;
  93.     std::vector<Op> ops;
  94.     //static constexpr const int GRID_X = 6, GRID_Y = 5; // 6x5 for example, disable for input
  95.     //std::array<std::array<State, GRID_X>, GRID_Y> grid;
  96.     std::array<Point, KNOT_COUNT> knots;
  97.     static constexpr int HEAD = 0, TAIL = KNOT_COUNT - 1;
  98. };
  99.  
  100. template<int KNOT_COUNT>
  101. Simulation<KNOT_COUNT>::Simulation() {
  102.     //for(int y = 0; y < GRID_Y; ++y) {
  103.     //    for(int x = 0; x < GRID_X; ++x) {
  104.     //        grid[y][x] = State::None;
  105.     //    }
  106.     //}
  107.     for(auto &k : knots) {
  108.         k = Point(0, 0);
  109.     }
  110.     tail_moves.insert({Point(0, 2), Direction::Up});
  111.     tail_moves.insert({Point(0, -2), Direction::Down});
  112.     tail_moves.insert({Point(2, 0), Direction::Right});
  113.     tail_moves.insert({Point(-2, 0), Direction::Left});
  114.     tail_moves.insert({Point(1, 2), Direction::DiagonalUpRight});
  115.     tail_moves.insert({Point(-1, 2), Direction::DiagonalUpLeft});
  116.     tail_moves.insert({Point(1, -2), Direction::DiagonalDownRight});
  117.     tail_moves.insert({Point(-1, -2), Direction::DiagonalDownLeft});
  118.     tail_moves.insert({Point(2, 1), Direction::DiagonalUpRight});
  119.     tail_moves.insert({Point(-2, 1), Direction::DiagonalUpLeft});
  120.     tail_moves.insert({Point(2, -1), Direction::DiagonalDownRight});
  121.     tail_moves.insert({Point(-2, -1), Direction::DiagonalDownLeft});
  122.  
  123.     // moves for more than 2 knots.
  124.     tail_moves.insert({Point(2, 2), Direction::DiagonalUpRight});
  125.     tail_moves.insert({Point(-2, 2), Direction::DiagonalUpLeft});
  126.     tail_moves.insert({Point(-2, -2), Direction::DiagonalDownLeft});
  127.     tail_moves.insert({Point{2, -2}, Direction::DiagonalDownRight});
  128. }
  129.  
  130. template<int KNOT_COUNT>
  131. Simulation<KNOT_COUNT> Simulation<KNOT_COUNT>::parse(const char *path) {
  132.     FILE *f = fopen(path, "r");
  133.     if(!f) assert(0);
  134.     Simulation s;
  135.  
  136.     auto parse_direction = [](char d) -> Direction {
  137.         switch(d) {
  138.             case 'U': return Direction::Up;
  139.             case 'D': return Direction::Down;
  140.             case 'L': return Direction::Left;
  141.             case 'R': return Direction::Right;
  142.             default: assert(0);
  143.         }
  144.     };
  145.  
  146.     char buffer[6] = {0}; // <direction> <space> <amount (up to 2 characters)> <newline> <nul character>
  147.     while(fgets(buffer, 6, f)) {
  148.         char direction = buffer[0];
  149.         char *amount = &buffer[2]; // buffer[2]..end
  150.         s.ops.push_back(Op{parse_direction(direction), (int)strtol(amount, NULL, 10)});
  151.     }
  152.  
  153.     fclose(f);
  154.     return s;
  155. }
  156.  
  157. template<int KNOT_COUNT>
  158. void Simulation<KNOT_COUNT>::update(Point new_head_pos) {
  159.     // utilities
  160.     auto touching = [](Point a, Point b) -> bool {
  161.         return std::abs(a.x - b.x) <= 1 && std::abs(a.y - b.y) <= 1;
  162.     };
  163.  
  164.     auto diff = [](Point a, Point b) -> Point {
  165.         auto dx = a.x - b.x;
  166.         auto dy = a.y - b.y;
  167.         //printf("diff: (%d, %d) - (%d, %d) = (%d, %d)\n", a.x, a.y, b.x, b.y, dx, dy);
  168.         return Point(dx, dy);
  169.     };
  170.  
  171.  
  172.     // Update head.
  173.     //printf("head: (%d, %d) += (%d, %d)\n", head_pos.x, head_pos.y, new_head_pos.x, new_head_pos.y);
  174.     //grid[knots.at(HEAD).y][knots.at(HEAD).x] = State::None;
  175.     //grid[new_head_pos.y][new_head_pos.x] = State::Head;
  176.     knots.at(HEAD) = new_head_pos;
  177.  
  178.     auto current_head = knots.at(HEAD);
  179.     for(int i = 1; i < KNOT_COUNT; ++i) {
  180.         //printf(">> knot %d\n", i);
  181.         auto &k = knots.at(i);
  182.         // update tail
  183.         if(!touching(current_head, k)) {
  184.             //printf("head: (%d, %d), tail: (%d, %d)\n", current_head.x, current_head.y, k.x, k.y);
  185.             auto move = tail_moves.at(diff(current_head, k));
  186.             //printf("tail move: %s\n", direction_as_str(move).c_str());
  187.             //grid[k.y][k.x] = State::None;
  188.             switch(move) {
  189.                 case Direction::Up: k.y += 1; break;
  190.                 case Direction::Down: k.y -= 1; break;
  191.                 case Direction::Right: k.x += 1; break;
  192.                 case Direction::Left: k.x -= 1; break;
  193.                 case Direction::DiagonalUpRight: k.y += 1; k.x += 1; break;
  194.                 case Direction::DiagonalUpLeft: k.y += 1; k.x -= 1; break;
  195.                 case Direction::DiagonalDownRight: k.y -= 1; k.x += 1; break;
  196.                 case Direction::DiagonalDownLeft: k.y -= 1; k.x -= 1; break;
  197.                 default: assert(0);
  198.             }
  199.             //grid[k.y][k.x] = State::Tail;
  200.         }
  201.         current_head = k;
  202.     }
  203.     //printf("tail: (%d, %d)\n", tail_pos.x, tail_pos.y);
  204. }
  205.  
  206. template<int KNOT_COUNT>
  207. int Simulation<KNOT_COUNT>::simulate() {
  208.     //auto print_grid = [this]() {
  209.     //    for(int y = GRID_Y - 1; y >= 0; --y) {
  210.     //        for(int x = 0; x < GRID_X; ++x) {
  211.     //            auto state = this->grid[y][x];
  212.     //            switch(state) {
  213.     //                case State::Head: printf("H"); break;
  214.     //                case State::Tail: printf("T"); break;
  215.     //                case State::None: printf("."); break;
  216.     //                default: assert(0);
  217.     //            }
  218.     //        }
  219.     //        printf("\n");
  220.     //    }
  221.     //};
  222.  
  223.     std::unordered_set<Point> tail_visited;
  224.     for(auto &op : ops) {
  225.         //printf("== %s ==\n", op.as_str().c_str());
  226.         for(int i = 0; i < op.amount; ++i) {
  227.             auto &head_pos = knots.at(HEAD);
  228.             Point new_head_pos(head_pos.x, head_pos.y);
  229.             switch(op.direction) {
  230.                 case Direction::Up: new_head_pos.y += 1; break;
  231.                 case Direction::Down: new_head_pos.y -= 1; break;
  232.                 case Direction::Left: new_head_pos.x -= 1; break;
  233.                 case Direction::Right: new_head_pos.x += 1; break;
  234.                 default: assert(0);
  235.             }
  236.             //auto old_tail_pos = tail_pos;
  237.             update(new_head_pos);
  238.             tail_visited.insert(knots.at(TAIL));
  239.             // check if tail moved.
  240.             //if(old_tail_pos != tail_pos) {
  241.                 //printf("tail moved (%d, %d) -> (%d, %d).\n", old_tail_pos.x, old_tail_pos.y, tail_pos.x, tail_pos.y);
  242.             //}
  243.             //print_grid(); puts("\n");
  244.         }
  245.     }
  246.     //auto &head = knots.at(HEAD);
  247.     //auto &tail = knots.at(TAIL);
  248.     //printf("H(%d, %d), T(%d, %d)\n", head.x, head.y, tail.x, tail.y);
  249.     return tail_visited.size();
  250. }
  251.  
  252. int main(void) {
  253.     auto s1 = Simulation<2>::parse("input.txt");
  254.     printf("part 1 (2 knots): %d\n", s1.simulate());
  255.     auto s2 = Simulation<10>::parse("input.txt");
  256.     printf("part 2 (10 knots): %d\n", s2.simulate());
  257.     return 0;
  258. }
  259.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement