aoc

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

day06b.cpp (3934B)


      1 /*
      2 This is quite inefficient, but it still gives the right answer in less
      3 than a minute (25 seconds on my old laptop).
      4 */
      5 
      6 #include <iostream>
      7 #include <algorithm>
      8 #include <string>
      9 #include <string_view>
     10 #include <vector>
     11 using namespace std;
     12 
     13 enum Direction { U = 0, D, R, L };
     14 Direction all_directions[] = {
     15 	Direction::U, Direction::D, Direction::R, Direction::L,
     16 };
     17 
     18 pair<int, int> step(pair<int, int> p, Direction d) {
     19 	auto [i, j] = p;
     20 
     21 	switch (d) {
     22 	case Direction::U:
     23 		return make_pair(i-1, j);
     24 	case Direction::D:
     25 		return make_pair(i+1, j);
     26 	case Direction::R:
     27 		return make_pair(i, j+1);
     28 	case Direction::L:
     29 		return make_pair(i, j-1);
     30 	}
     31 
     32 	return make_pair(-999,-999);
     33 }
     34 
     35 Direction turn(Direction d) {
     36 	switch (d) {
     37 	case Direction::U:
     38 		return Direction::R;
     39 	case Direction::D:
     40 		return Direction::L;
     41 	case Direction::R:
     42 		return Direction::D;
     43 	case Direction::L:
     44 		return Direction::U;
     45 	}
     46 
     47 	return Direction::U;
     48 }
     49 
     50 class Board {
     51 public:
     52 	const char out_of_bound = '$';
     53 	int M, N;
     54 
     55 	Board(vector<string> &lines) {
     56 		N = lines.size();
     57 		M = 0;
     58 		for (string l : lines)
     59 			M = max(M, (int)l.size());
     60 
     61 		cells = new char[M * N];
     62 		visited = new int[M * N];
     63 		for (int i = 0; i < N; i++) {
     64 			for (int j = 0; j < M; j++) {
     65 				cells[M*i + j] = j < (int)lines[i].size() ?
     66 				    lines[i][j] : out_of_bound;
     67 				visited[M*i + j] = 0;
     68 			}
     69 		}
     70 	}
     71 
     72 	~Board() {
     73 		delete []cells;
     74 		delete []visited;
     75 	}
     76 
     77 	char operator[](pair<int, int> p) {
     78 		int c = coord(p);
     79 		return c == -1 ? out_of_bound : cells[c];
     80 	}
     81 
     82 	bool is_visited(pair<int, int> p, Direction d) {
     83 		return get_vmask(p) & (1 << d);
     84 	}
     85 
     86 	void set_visited(pair<int, int> p, Direction d) {
     87 		toggle_vmask(p, d);
     88 	}
     89 
     90 	void clear_visited() {
     91 		for (int i = 0; i < N; i++)
     92 			for (int j = 0; j < M; j++)
     93 				set_vmask(make_pair(i, j), 0);
     94 	}
     95 
     96 	bool is_obstruction(pair<int, int> p) {
     97 		return (*this)[p] == '#';
     98 	}
     99 
    100 	void set_obstruction(pair<int, int> p) {
    101 		set_cell(p, '#');
    102 	}
    103 
    104 	void set_clean(pair<int, int> p) {
    105 		set_cell(p, '.');
    106 	}
    107 
    108 private:
    109 	char *cells;
    110 	int *visited;
    111 
    112 	int coord(pair<int, int> p) {
    113 		auto [i, j] = p;
    114 		return i >= N || i < 0 || j >= M || j < 0 ? -1 : M*i + j;
    115 	}
    116 
    117 	void set_cell(pair<int, int> p, char c) {
    118 		int i = coord(p);
    119 		cells[i] = c;
    120 	}
    121 
    122 	int get_vmask(pair<int, int> p) {
    123 		int i = coord(p);
    124 		return visited[i];
    125 	}
    126 
    127 	void set_vmask(pair<int, int> p, int x) {
    128 		int i = coord(p);
    129 		visited[i] = x;
    130 	}
    131 
    132 	void toggle_vmask(pair<int, int> p, Direction d) {
    133 		int i = coord(p);
    134 		visited[i] ^= 1 << d;
    135 	}
    136 };
    137 
    138 Direction guard_direction(char c) {
    139 	switch (c) {
    140 	case '^':
    141 		return Direction::U;
    142 	case 'v':
    143 		return Direction::D;
    144 	case '>':
    145 		return Direction::R;
    146 	case '<':
    147 		return Direction::L;
    148 	}
    149 
    150 	return Direction::U;
    151 }
    152 
    153 pair<pair<int, int>, Direction> find_guard(Board &board) {
    154 	pair<int, int> p(0, 0);
    155 	for (p.first = 0; p.first < board.N; p.first++) {
    156 		for (p.second = 0; p.second < board.M; p.second++) {
    157 			if (board[p] != '.' && board[p] != '#') {
    158 				Direction d = guard_direction(board[p]);
    159 				return make_pair(p, d);
    160 			}
    161 		}
    162 	}
    163 
    164 	return make_pair(make_pair(-999, -999), Direction::U);
    165 }
    166 
    167 bool isloop(pair<int, int> i, pair<int, int> p, Direction d, Board &board) {
    168 	bool ret = false;
    169 
    170 	board.set_obstruction(i);
    171 
    172 	while (true) {
    173 		auto q = step(p, d);
    174 		if (board.is_visited(p, d)) {
    175 			ret = true;
    176 			break;
    177 		}
    178 		board.set_visited(p, d);
    179 
    180 		if (board[q] == board.out_of_bound)
    181 			break;
    182 
    183 		if (board.is_obstruction(q)) {
    184 			d = turn(d);
    185 		} else {
    186 			p = q;
    187 		}
    188 	}
    189 
    190 	board.clear_visited();
    191 	board.set_clean(i);
    192 
    193 	return ret;
    194 }
    195 
    196 int main() {
    197 	string line;
    198 	vector<string> lines;
    199 	while (getline(cin, line))
    200 		lines.push_back(line);
    201 
    202 	Board board(lines);
    203 
    204 	int tot = 0;
    205 	auto [p, d] = find_guard(board);
    206 
    207 	pair i(0, 0);
    208 	for (i.first = 0; i.first < board.N; i.first++)
    209 		for (i.second = 0; i.second < board.M; i.second++)
    210 			if (board[i] == '.')
    211 				tot += isloop(i, p, d, board);
    212 
    213 	cout << tot << endl;
    214 
    215 	return 0;
    216 }