aoc

My solutions for the Advent of Code
git clone https://git.tronto.net/aoc
Download | Log | Files | Refs | README

day16b.cpp (4754B)


      1 /*
      2 This solution is bad, it runs in 45 minutes or so. Baiscally I find the
      3 distances between all pairs of valid (position, direction) pairs; a position p
      4 is on a valid shortest path if and only if for any direction d in {>, <, v, ^}
      5 the distance from (S, >) to (p, d) plus the distance from (p, d) to E is
      6 equal to the shortest path distance.
      7 
      8 I tried to do smarter things but I kept making mistakes. This one is
      9 inefficient, but it has the advantage of requiring minimal changes from part 1.
     10 */
     11 
     12 #include <algorithm>
     13 #include <cstdint>
     14 #include <iostream>
     15 #include <queue>
     16 #include <string>
     17 #include <string_view>
     18 #include <vector>
     19 #include <set>
     20 using namespace std;
     21 
     22 #define INF 999999999
     23 
     24 class Direction {
     25 public:
     26 	int U, R;
     27 
     28 	Direction(char c) :
     29 	    U{c == 'v' ? 1 : (c == '^' ? -1 : 0)},
     30 	    R{c == '>' ? 1 : (c == '<' ? -1 : 0)} {}
     31 
     32 	Direction(const int i, const int j) : U{i}, R{j} {}
     33 
     34 	Direction turnright() const {
     35 		return turn(-1, 0);
     36 	}
     37 
     38 	Direction turnleft() const {
     39 		return turn(1, 0);
     40 	}
     41 
     42 	bool operator<(const Direction& d) const { // For set<Direction>
     43 		return this->U < d.U || (this->U == d.U && this->R < d.R);
     44 	}
     45 
     46 	int index() const {
     47 		if (U == 1 && R == 0) return 0;
     48 		if (U == -1 && R == 0) return 1;
     49 		if (U == 0 && R == 1) return 2;
     50 		if (U == 0 && R == -1) return 3;
     51 		return -1;
     52 	}
     53 private:
     54 	Direction turn(int64_t sin, int64_t cos) const {
     55 		return Direction(cos * U - sin * R, sin * U + cos * R);
     56 	}
     57 };
     58 
     59 class Position {
     60 public:
     61 	int64_t i, j;
     62 
     63 	Position() : Position(0, 0) {}
     64 
     65 	Position(int64_t a, int64_t b) : i{a}, j{b} {}
     66 
     67 	Position step(const Direction d) const {
     68 		return Position(i+d.U, j+d.R);
     69 	}
     70 };
     71 
     72 const Direction all_directions[] = {
     73 	Direction(1, 0), Direction(-1, 0), Direction(0, 1), Direction(0, -1)
     74 };
     75 
     76 class Qelem {
     77 public:
     78 	int64_t distance;
     79 	Position p;
     80 	Direction d;
     81 
     82 	Qelem(int dis, Position pp, Direction dd) :
     83 	    distance{dis}, p{pp}, d{dd} {}
     84 
     85 	bool operator<(const Qelem& other) const {
     86 		return distance > other.distance;
     87 	}
     88 };
     89 
     90 class Board {
     91 public:
     92 	int64_t N, M;
     93 
     94 	Board(const vector<string>& lines) :
     95 	    N{static_cast<int64_t>(lines.size())},
     96 	    M{static_cast<int64_t>(lines[0].size())},
     97 	    cells(M*N), visited(4*M*N)
     98 	{
     99 		for (int64_t i = 0; i < N; i++)
    100 			for (int64_t j = 0; j < M; j++)
    101 				cells[M*i+j] = lines[i][j];
    102 	}
    103 
    104 	char& operator[](const Position p) {
    105 		if (const auto c = coord(p); c == -1)
    106 			return out_of_bound;
    107 		else
    108 			return cells[c];
    109 	}
    110 
    111 	Position find_S() {
    112 		for (Position p(0, 0); p.i < N; p.i++)
    113 			for (p.j = 0; p.j < M; p.j++)
    114 				if ((*this)[p] == 'S')
    115 					return p;
    116 		return Position(-1, -1);
    117 	}
    118 
    119 	int shortest_path(Position p, Direction d, vector<int>& shortest, bool save) {
    120 		clear_visited();
    121 		if (save)
    122 			for (int i = 0; i < 4*M*N; i++)
    123 				shortest[i] = INF;
    124 
    125 		priority_queue<Qelem> q;
    126 
    127 		q.push(Qelem(0, p, d));
    128 
    129 		while (!q.empty()) {
    130 			auto e = q.top();
    131 			q.pop();
    132 
    133 			if ((*this)[e.p] == 'E')
    134 				return e.distance;
    135 
    136 			if (is_visited(e.p, e.d) || (*this)[e.p] == '#')
    137 				continue;
    138 			set_visited(e.p, e.d);
    139 
    140 			if (save)
    141 				shortest[4*coord(e.p) + e.d.index()] = e.distance;
    142 
    143 			q.push(Qelem(e.distance+1000, e.p, e.d.turnright()));
    144 			q.push(Qelem(e.distance+1000, e.p, e.d.turnleft()));
    145 			q.push(Qelem(e.distance+1, e.p.step(e.d), e.d));
    146 		}
    147 		return 999999999;
    148 	}
    149 
    150 	void print() {
    151 		for (Position p(0, 0); p.i < N; p.i++) {
    152 			for (p.j = 0; p.j < M; p.j++)
    153 				cout << (*this)[p];
    154 			cout << endl;
    155 		}
    156 	}
    157 
    158 	int64_t coord(const Position p) const {
    159 		auto [i, j] = p;
    160 		return i >= N || i < 0 || j >= M || j < 0 ? -1 : M * i + j;
    161 	}
    162 private:
    163 	char out_of_bound = '$';
    164 
    165 	vector<char> cells;
    166 	vector<bool> visited;
    167 
    168 	void clear_visited() {
    169 		for (int64_t i = 0; i < 4*M*N; i++)
    170 			visited[i] = false;
    171 	}
    172 
    173 	bool is_visited(const Position p, const Direction d) {
    174 		if (const auto c = coord(p); c == -1)
    175 			return false;
    176 		else
    177 			return visited[4*c + d.index()];
    178 	}
    179 
    180 	void set_visited(const Position p, const Direction d) {
    181 		if (const auto c = coord(p); c != -1)
    182 			visited[4*c + d.index()] = true;
    183 	}
    184 };
    185 
    186 int main() {
    187 	string line;
    188 	vector<string> lines;
    189 
    190 	while (getline(cin, line))
    191 		lines.push_back(line);
    192 
    193 	Board board(lines);
    194 
    195 	vector<int> v(4*board.N*board.M);
    196 	int sh = board.shortest_path(board.find_S(), Direction('>'), v, true);
    197 
    198 	int count = 0;
    199 	for (Position p(0, 0); p.i < board.N; p.i++) {
    200 		for (p.j = 0; p.j < board.M; p.j++) {
    201 			cout << "Checking intermediate position "
    202 			    << p.i << ", " << p.j << endl;
    203 			if (board[p] == '#') continue;
    204 			for (auto d : all_directions) {
    205 				int i = 4*board.coord(p) + d.index();
    206 				if (v[i] == INF) continue;
    207 				int ssh = board.shortest_path(p, d, v, false);
    208 				if (ssh + v[i] == sh) {
    209 					count++;
    210 					break;
    211 				}
    212 			}
    213 		}
    214 	}
    215 
    216 	cout << count+1 << endl;
    217 
    218 	return 0;
    219 }