aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile_flags.txt1
-rw-r--r--makefile9
-rw-r--r--src/day04.cpp26
-rw-r--r--src/day05.cpp27
-rw-r--r--src/day09.cpp13
-rw-r--r--src/day10.cpp19
-rw-r--r--src/day12.cpp78
-rw-r--r--src/day14.cpp22
-rw-r--r--src/day16.cpp4
-rw-r--r--src/day22.cpp157
-rw-r--r--src/day23.cpp152
-rw-r--r--src/day24.cpp173
-rw-r--r--src/day25.cpp140
13 files changed, 745 insertions, 76 deletions
diff --git a/compile_flags.txt b/compile_flags.txt
index 966e06b..0000d86 100644
--- a/compile_flags.txt
+++ b/compile_flags.txt
@@ -5,3 +5,4 @@
5-Wdouble-promotion 5-Wdouble-promotion
6-pedantic 6-pedantic
7-std=c++20 7-std=c++20
8-I/usr/local/include
diff --git a/makefile b/makefile
index a54e3aa..1849790 100644
--- a/makefile
+++ b/makefile
@@ -20,7 +20,11 @@ all: bin/day01 \
20 bin/day18 \ 20 bin/day18 \
21 bin/day19 \ 21 bin/day19 \
22 bin/day20 \ 22 bin/day20 \
23 bin/day21 23 bin/day21 \
24 bin/day22 \
25 bin/day23 \
26 bin/day24 \
27 bin/day25
24 28
25bin: 29bin:
26 mkdir $@ 30 mkdir $@
@@ -28,6 +32,9 @@ bin:
28bin/%: src/%.cpp | bin 32bin/%: src/%.cpp | bin
29 c++ $(CPPFLAGS) $^ -o $@ 33 c++ $(CPPFLAGS) $^ -o $@
30 34
35bin/day24: src/day24.cpp | bin
36 c++ $(CPPFLAGS) -Wno-sign-conversion -I/usr/local/include $^ -L/usr/local/lib -lz3 -o $@
37
31clean: 38clean:
32 rm -rf bin 39 rm -rf bin
33 40
diff --git a/src/day04.cpp b/src/day04.cpp
index 8afcb2c..0d3e552 100644
--- a/src/day04.cpp
+++ b/src/day04.cpp
@@ -1,4 +1,3 @@
1#include <cctype>
2#include <fstream> 1#include <fstream>
3#include <iostream> 2#include <iostream>
4#include <set> 3#include <set>
@@ -25,8 +24,8 @@ read_file(string_view filename)
25vector<string> 24vector<string>
26split(const string& line, char sep) 25split(const string& line, char sep)
27{ 26{
28 vector<string> parts{};
29 stringstream input{ line }; 27 stringstream input{ line };
28 vector<string> parts;
30 29
31 for ( string part; getline(input, part, sep); ) { 30 for ( string part; getline(input, part, sep); ) {
32 parts.emplace_back(part); 31 parts.emplace_back(part);
@@ -49,13 +48,12 @@ read_ints(const string& line)
49 return container; 48 return container;
50} 49}
51 50
52vector<unsigned long> 51auto
53build_data_set() 52build_data_set(string_view filename)
54{ 53{
55 const auto lines = read_file("data/day04.txt"); 54 vector<unsigned long> values;
56 vector<unsigned long> values{};
57 55
58 for ( const auto& line: lines ) { 56 for ( const auto& line: read_file(filename) ) {
59 const auto cards = split(line, ':'); 57 const auto cards = split(line, ':');
60 const auto numbers = split(cards[1], '|'); 58 const auto numbers = split(cards[1], '|');
61 59
@@ -75,10 +73,10 @@ build_data_set()
75} 73}
76 74
77void 75void
78pass1() 76pass1(const vector<unsigned long>& values)
79{ 77{
80 auto sum = 0U; 78 auto sum = 0U;
81 for ( auto value: build_data_set() ) { 79 for ( auto value: values ) {
82 if ( value != 0 ) { 80 if ( value != 0 ) {
83 sum += (1U << (value - 1)); 81 sum += (1U << (value - 1));
84 } 82 }
@@ -88,10 +86,9 @@ pass1()
88} 86}
89 87
90void 88void
91pass2() 89pass2(const vector<unsigned long>& values)
92{ 90{
93 const auto values = build_data_set(); 91 auto count = 0U;
94 auto count = 0U;
95 92
96 function<void(size_t, size_t)> process = [&](size_t start, size_t end) -> void { 93 function<void(size_t, size_t)> process = [&](size_t start, size_t end) -> void {
97 for ( ; start != end; ++start ) { 94 for ( ; start != end; ++start ) {
@@ -111,6 +108,7 @@ pass2()
111int 108int
112main() 109main()
113{ 110{
114 pass1(); 111 const auto values = build_data_set("data/day04.txt");
115 pass2(); 112 pass1(values);
113 pass2(values);
116} 114}
diff --git a/src/day05.cpp b/src/day05.cpp
index 92e39fc..42fda29 100644
--- a/src/day05.cpp
+++ b/src/day05.cpp
@@ -49,7 +49,7 @@ read_file(string_view filename)
49vector<string> 49vector<string>
50split(const string& line, char sep) 50split(const string& line, char sep)
51{ 51{
52 vector<string> parts{}; 52 vector<string> parts;
53 stringstream input{ line }; 53 stringstream input{ line };
54 54
55 for ( string part; getline(input, part, sep); ) { 55 for ( string part; getline(input, part, sep); ) {
@@ -60,10 +60,8 @@ split(const string& line, char sep)
60} 60}
61 61
62void 62void
63part1() 63part1(const vector<string>& lines)
64{ 64{
65 auto lines = read_file("data/day05.txt");
66
67 auto read_ints = [](const string& line) { 65 auto read_ints = [](const string& line) {
68 stringstream iss{ line }; 66 stringstream iss{ line };
69 return vector<long>{ istream_iterator<long>{ iss }, istream_iterator<long>{} }; 67 return vector<long>{ istream_iterator<long>{ iss }, istream_iterator<long>{} };
@@ -71,8 +69,8 @@ part1()
71 69
72 auto seeds = read_ints(lines[0].substr(6)); 70 auto seeds = read_ints(lines[0].substr(6));
73 71
74 map<string, string> category_mapping{}; 72 map<string, string> category_mapping;
75 map<string, vector<Entry>> range_mapping{}; 73 map<string, vector<Entry>> range_mapping;
76 74
77 // read blocks 75 // read blocks
78 for ( size_t idx = 1; idx < lines.size(); ) { 76 for ( size_t idx = 1; idx < lines.size(); ) {
@@ -84,7 +82,7 @@ part1()
84 82
85 category_mapping[parts[0]] = parts[2]; 83 category_mapping[parts[0]] = parts[2];
86 84
87 vector<Entry> mapping{}; 85 vector<Entry> mapping;
88 86
89 for ( ; idx < lines.size() && !lines[idx].empty(); ++idx ) { 87 for ( ; idx < lines.size() && !lines[idx].empty(); ++idx ) {
90 auto values = read_ints(lines[idx]); 88 auto values = read_ints(lines[idx]);
@@ -117,10 +115,8 @@ part1()
117} 115}
118 116
119void 117void
120part2() 118part2(const vector<string>& lines)
121{ 119{
122 auto lines = read_file("data/day05-sample1.txt");
123
124 auto read_ints = [](const string& line) { 120 auto read_ints = [](const string& line) {
125 stringstream iss{ line }; 121 stringstream iss{ line };
126 return vector<long>{ istream_iterator<long>{ iss }, istream_iterator<long>{} }; 122 return vector<long>{ istream_iterator<long>{ iss }, istream_iterator<long>{} };
@@ -128,8 +124,8 @@ part2()
128 124
129 auto seeds = read_ints(lines[0].substr(6)); 125 auto seeds = read_ints(lines[0].substr(6));
130 126
131 map<string, string> category_mapping{}; 127 map<string, string> category_mapping;
132 map<string, vector<Entry>> range_mapping{}; 128 map<string, vector<Entry>> range_mapping;
133 129
134 // read blocks 130 // read blocks
135 for ( size_t idx = 1; idx < lines.size(); ) { 131 for ( size_t idx = 1; idx < lines.size(); ) {
@@ -141,7 +137,7 @@ part2()
141 137
142 category_mapping[parts[0]] = parts[2]; 138 category_mapping[parts[0]] = parts[2];
143 139
144 vector<Entry> mapping{}; 140 vector<Entry> mapping;
145 141
146 for ( ; idx < lines.size() && !lines[idx].empty(); ++idx ) { 142 for ( ; idx < lines.size() && !lines[idx].empty(); ++idx ) {
147 auto values = read_ints(lines[idx]); 143 auto values = read_ints(lines[idx]);
@@ -181,6 +177,7 @@ part2()
181int 177int
182main() 178main()
183{ 179{
184 part1(); 180 auto lines = read_file("data/day05.txt");
185 part2(); 181 part1(lines);
182 part2(lines);
186} 183}
diff --git a/src/day09.cpp b/src/day09.cpp
index 0019d05..4b30e6e 100644
--- a/src/day09.cpp
+++ b/src/day09.cpp
@@ -45,10 +45,8 @@ solve_rec(const vector<T>& values)
45} 45}
46 46
47void 47void
48part1() 48part1(const vector<string>& lines)
49{ 49{
50 auto lines = read_file("data/day09.txt");
51
52 long sum = 0; 50 long sum = 0;
53 for ( const auto& line: lines ) { 51 for ( const auto& line: lines ) {
54 sum += solve_rec(read_ints(line)); 52 sum += solve_rec(read_ints(line));
@@ -57,10 +55,10 @@ part1()
57} 55}
58 56
59void 57void
60part2() 58part2(const vector<string>& lines)
61{ 59{
62 long sum = 0; 60 long sum = 0;
63 for ( const auto& line: read_file("data/day09.txt") ) { 61 for ( const auto& line: lines ) {
64 auto values = read_ints(line); 62 auto values = read_ints(line);
65 reverse(values.begin(), values.end()); 63 reverse(values.begin(), values.end());
66 sum += solve_rec(values); 64 sum += solve_rec(values);
@@ -71,6 +69,7 @@ part2()
71int 69int
72main() 70main()
73{ 71{
74 part1(); 72 auto lines = read_file("data/day09.txt");
75 part2(); 73 part1(lines);
74 part2(lines);
76} 75}
diff --git a/src/day10.cpp b/src/day10.cpp
index 5591290..264e111 100644
--- a/src/day10.cpp
+++ b/src/day10.cpp
@@ -38,8 +38,7 @@ find_start_pos(const puzzle_t& puzzle)
38bool 38bool
39predict_direction(const puzzle_t& puzzle, pos_t& current, char& direction) 39predict_direction(const puzzle_t& puzzle, pos_t& current, char& direction)
40{ 40{
41 auto x = get<0>(current); // NOLINT 41 auto [x, y] = current;
42 auto y = get<1>(current); // NOLINT
43 42
44 if ( direction == 'S' ) { 43 if ( direction == 'S' ) {
45 ++y; 44 ++y;
@@ -115,10 +114,8 @@ predict_direction(const puzzle_t& puzzle, pos_t& current, char& direction)
115}; 114};
116 115
117void 116void
118part1() 117part1(const puzzle_t& puzzle)
119{ 118{
120 const auto puzzle = read_file("data/day10.txt");
121
122 const auto start_pos = find_start_pos(puzzle); 119 const auto start_pos = find_start_pos(puzzle);
123 120
124 auto max_steps = 0; 121 auto max_steps = 0;
@@ -163,10 +160,8 @@ flood_fill(puzzle_t& puzzle, size_t x, size_t y)
163}; 160};
164 161
165void 162void
166part2() 163part2(const puzzle_t& puzzle_original)
167{ 164{
168 const auto puzzle_original = read_file("data/day10.txt");
169
170 puzzle_t puzzle{ puzzle_original.size(), string(puzzle_original[0].size(), ' ') }; 165 puzzle_t puzzle{ puzzle_original.size(), string(puzzle_original[0].size(), ' ') };
171 166
172 const auto start_pos = find_start_pos(puzzle_original); 167 const auto start_pos = find_start_pos(puzzle_original);
@@ -176,8 +171,7 @@ part2()
176 auto current_pos = start_pos; 171 auto current_pos = start_pos;
177 172
178 for ( ;; ) { 173 for ( ;; ) {
179 auto x = get<0>(current_pos); // NOLINT 174 auto [x, y] = current_pos;
180 auto y = get<1>(current_pos); // NOLINT
181 175
182 puzzle[y][x] = puzzle_original[y][x]; 176 puzzle[y][x] = puzzle_original[y][x];
183 177
@@ -216,6 +210,7 @@ part2()
216int 210int
217main() 211main()
218{ 212{
219 part1(); 213 const auto puzzle = read_file("data/day10.txt");
220 part2(); 214 part1(puzzle);
215 part2(puzzle);
221} 216}
diff --git a/src/day12.cpp b/src/day12.cpp
index 31a2739..11bd80b 100644
--- a/src/day12.cpp
+++ b/src/day12.cpp
@@ -1,5 +1,6 @@
1#include <fstream> 1#include <fstream>
2#include <iostream> 2#include <iostream>
3#include <map>
3#include <sstream> 4#include <sstream>
4#include <string> 5#include <string>
5#include <vector> 6#include <vector>
@@ -69,9 +70,9 @@ count_groups(string_view str)
69} 70}
70 71
71long 72long
72brute_force(string_view puzzle, const vector<long>& nums) 73brute_force(string_view springs, const vector<long>& groups)
73{ 74{
74 long counts = count_if(puzzle.begin(), puzzle.end(), [](char chr) { return chr == '?'; }); 75 long counts = count_if(springs.begin(), springs.end(), [](char chr) { return chr == '?'; });
75 76
76 string test_pattern; 77 string test_pattern;
77 78
@@ -79,7 +80,7 @@ brute_force(string_view puzzle, const vector<long>& nums)
79 for ( size_t counter = 0; counter != (1U << size_t(counts)); ++counter ) { 80 for ( size_t counter = 0; counter != (1U << size_t(counts)); ++counter ) {
80 auto bit_pattern = counter; 81 auto bit_pattern = counter;
81 82
82 for ( char chr: puzzle ) { 83 for ( char chr: springs ) {
83 if ( chr == '?' ) { 84 if ( chr == '?' ) {
84 if ( (bit_pattern & 1U) != 0U ) { 85 if ( (bit_pattern & 1U) != 0U ) {
85 test_pattern += '.'; 86 test_pattern += '.';
@@ -93,7 +94,7 @@ brute_force(string_view puzzle, const vector<long>& nums)
93 test_pattern += chr; 94 test_pattern += chr;
94 } 95 }
95 } 96 }
96 if ( nums == count_groups(test_pattern) ) { 97 if ( groups == count_groups(test_pattern) ) {
97 arrangements++; 98 arrangements++;
98 } 99 }
99 100
@@ -103,18 +104,71 @@ brute_force(string_view puzzle, const vector<long>& nums)
103 return arrangements; 104 return arrangements;
104} 105}
105 106
107long
108count(const string& springs, const vector<long>& groups)
109{
110 map<tuple<string, vector<long>>, long> cache;
111
112 function<long(string, const vector<long>&)> count_rec = [&](string springs, const vector<long>& groups) -> long {
113 auto cached_value = cache.find(make_tuple(springs, groups));
114 if ( cached_value != cache.end() ) {
115 return cached_value->second;
116 }
117
118 springs.erase(0, springs.find_first_not_of('.'));
119
120 if ( springs.empty() ) {
121 return groups.empty() ? 1 : 0;
122 }
123
124 if ( groups.empty() ) {
125 return springs.find('#') == string_view::npos ? 1 : 0;
126 }
127
128 if ( springs[0] == '#' ) {
129 auto gidx = size_t(groups[0]);
130 if ( springs.length() < gidx || springs.substr(0, gidx).find('.') != string_view::npos || springs[gidx] == '#' ) {
131 return 0;
132 }
133
134 return count_rec(springs.substr(gidx + 1), { groups.begin() + 1, groups.end() });
135 }
136
137 auto value = count_rec(string("#") + springs.substr(1), groups) + count_rec(springs.substr(1), groups);
138
139 cache[make_tuple(springs, groups)] = value;
140
141 return value;
142 };
143
144 return count_rec(springs + ".", groups);
145}
146
106void 147void
107part1() 148part1(const vector<string>& input)
108{ 149{
109 const auto input = read_file("data/day12.txt"); 150 auto sum = 0L;
151 for ( const auto& line: input ) {
152 auto parts = split(line, ' ');
153 auto springs = parts[0];
154 auto groups = read_ints(parts[1]);
110 155
156 sum += brute_force(springs, groups);
157 // sum += count(springs, groups);
158 }
159 cout << sum << endl;
160}
161
162void
163part2(const vector<string>& input)
164{
111 auto sum = 0L; 165 auto sum = 0L;
112 for ( const auto& line: input ) { 166 for ( const auto& line: input ) {
113 auto parts = split(line, ' '); 167 auto parts = split(line, ' ');
114 auto puzzle = parts[0]; 168 auto springs = parts[0] + "?" + parts[0] + "?" + parts[0] + "?" + parts[0] + "?" + parts[0];
115 auto nums = read_ints(parts[1]); 169 auto groups = read_ints(parts[1] + "," + parts[1] + "," + parts[1] + "," + parts[1] + "," + parts[1]);
116 170
117 sum += brute_force(puzzle, nums); 171 sum += count(springs, groups);
118 } 172 }
119 cout << sum << endl; 173 cout << sum << endl;
120} 174}
@@ -122,5 +176,7 @@ part1()
122int 176int
123main() 177main()
124{ 178{
125 part1(); 179 const auto input = read_file("data/day12.txt");
180 part1(input);
181 part2(input);
126} 182}
diff --git a/src/day14.cpp b/src/day14.cpp
index 2b7a5b8..8816ce1 100644
--- a/src/day14.cpp
+++ b/src/day14.cpp
@@ -21,10 +21,8 @@ read_file(string_view filename)
21} 21}
22 22
23void 23void
24part1() 24part1(const vector<string>& lines)
25{ 25{
26 const auto lines = read_file("data/day14.txt");
27
28 auto start = chrono::steady_clock::now(); 26 auto start = chrono::steady_clock::now();
29 27
30 size_t sum = 0; 28 size_t sum = 0;
@@ -182,10 +180,9 @@ tilt_east(vector<string>& lines)
182void 180void
183tilt(vector<string>& lines) 181tilt(vector<string>& lines)
184{ 182{
185 tilt_north(lines); 183 for ( auto tilt: { tilt_north, tilt_west, tilt_south, tilt_east } ) {
186 tilt_west(lines); 184 tilt(lines);
187 tilt_south(lines); 185 }
188 tilt_east(lines);
189} 186}
190 187
191size_t 188size_t
@@ -201,16 +198,14 @@ calc(const vector<string>& lines)
201} 198}
202 199
203void 200void
204part2() 201part2(vector<string> lines)
205{ 202{
206 auto lines = read_file("data/day14.txt");
207
208 auto start = chrono::steady_clock::now(); 203 auto start = chrono::steady_clock::now();
209 map<vector<string>, long> cache; 204 map<vector<string>, long> cache;
210 205
211 const long dest = 1'000'000'000; 206 const long dest = 1'000'000'000;
212 207
213 for ( long index = 0; true; ++index ) { 208 for ( long index = 0;; ++index ) {
214 auto iter = cache.find(lines); 209 auto iter = cache.find(lines);
215 if ( iter != cache.end() ) { 210 if ( iter != cache.end() ) {
216 auto offset = iter->second; 211 auto offset = iter->second;
@@ -238,6 +233,7 @@ part2()
238int 233int
239main() 234main()
240{ 235{
241 part1(); 236 const auto lines = read_file("data/day14.txt");
242 part2(); 237 part1(lines);
238 part2(lines);
243} 239}
diff --git a/src/day16.cpp b/src/day16.cpp
index c2cd365..93c7479 100644
--- a/src/day16.cpp
+++ b/src/day16.cpp
@@ -44,9 +44,7 @@ solve(const vector<string>& lines, tuple<size_t, size_t, unsigned int> start)
44 positions.emplace(start); 44 positions.emplace(start);
45 45
46 while ( !positions.empty() ) { 46 while ( !positions.empty() ) {
47 auto row = get<0>(positions.front()); 47 auto [row, col, dir] = positions.front();
48 auto col = get<1>(positions.front());
49 auto dir = get<2>(positions.front());
50 positions.pop(); 48 positions.pop();
51 49
52 while ( true ) { 50 while ( true ) {
diff --git a/src/day22.cpp b/src/day22.cpp
new file mode 100644
index 0000000..1afb994
--- /dev/null
+++ b/src/day22.cpp
@@ -0,0 +1,157 @@
1#include <fstream>
2#include <iostream>
3#include <map>
4#include <regex>
5#include <set>
6#include <string>
7#include <vector>
8using namespace std;
9
10struct piece {
11 int x1, y1, z1;
12 int x2, y2, z2;
13};
14
15vector<piece>
16read_file(string_view filename)
17{
18 static const regex pattern{ R"((\d+),(\d+),(\d+)~(\d+),(\d+),(\d+))" };
19
20 fstream input{ filename };
21 vector<piece> data;
22
23 for ( string line; getline(input, line); ) {
24 smatch matches;
25 if ( regex_search(line, matches, pattern) ) {
26 data.emplace_back(piece{
27 stoi(matches[1]),
28 stoi(matches[2]),
29 stoi(matches[3]),
30 stoi(matches[4]),
31 stoi(matches[5]),
32 stoi(matches[6]) });
33 }
34 }
35 sort(data.begin(), data.end(), [](const auto& lhs, const auto& rhs) { return lhs.z1 < rhs.z1; });
36
37 return data;
38}
39
40void
41part1(vector<piece> puzzle)
42{
43 map<int, map<int, int>> grid;
44
45 map<size_t, vector<size_t>> supported_by;
46 map<size_t, vector<size_t>> supports;
47
48 for ( size_t idx = 0; idx != puzzle.size(); ++idx ) {
49 auto& piece = puzzle[idx];
50
51 int max_z = 0;
52 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
53 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
54 max_z = max(max_z, grid[x][y]);
55 }
56 }
57
58 auto height = piece.z2 - piece.z1 + 1;
59
60 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
61 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
62 grid[x][y] = max_z + height;
63 }
64 }
65
66 piece.z1 = max_z + 1;
67 piece.z2 = max_z + height;
68
69 for ( size_t idx2 = idx; idx2-- > 0; ) {
70 if ( piece.x1 > puzzle[idx2].x2 || puzzle[idx2].x1 > piece.x2 ) {
71 continue;
72 }
73 if ( piece.y1 > puzzle[idx2].y2 || puzzle[idx2].y1 > piece.y2 ) {
74 continue;
75 }
76 if ( puzzle[idx].z1 == puzzle[idx2].z2 + 1 ) {
77 supports[idx2].emplace_back(idx);
78 supported_by[idx].emplace_back(idx2);
79 }
80 }
81 }
82
83 long number = 0;
84 for ( size_t idx = 0; idx != puzzle.size(); ++idx ) {
85 if ( all_of(supports[idx].begin(), supports[idx].end(), [&](const auto& idx2) { return supported_by[idx2].size() > 1; }) ) {
86 ++number;
87 }
88 }
89 cout << number << endl;
90}
91
92void
93part2(vector<piece> puzzle)
94{
95 map<int, map<int, int>> grid;
96
97 for ( auto& piece: puzzle ) {
98 int max_z = 0;
99 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
100 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
101 max_z = max(max_z, grid[x][y]);
102 }
103 }
104
105 auto height = piece.z2 - piece.z1 + 1;
106
107 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
108 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
109 grid[x][y] = max_z + height;
110 }
111 }
112
113 piece.z1 = max_z + 1;
114 piece.z2 = max_z + height;
115 }
116
117 int num = 0;
118 for ( size_t idx = 0; idx != puzzle.size(); ++idx ) {
119 map<int, map<int, int>> grid;
120
121 for ( size_t idx2 = 0; idx2 != puzzle.size(); ++idx2 ) {
122 if ( idx == idx2 ) {
123 continue;
124 }
125
126 const auto& piece = puzzle[idx2];
127
128 int max_z = 0;
129 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
130 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
131 max_z = max(max_z, grid[x][y]);
132 }
133 }
134
135 auto height = piece.z2 - piece.z1 + 1;
136
137 for ( auto x = piece.x1; x <= piece.x2; ++x ) {
138 for ( auto y = piece.y1; y <= piece.y2; ++y ) {
139 grid[x][y] = max_z + height;
140 }
141 }
142
143 if ( piece.z1 != max_z + 1 ) {
144 ++num;
145 }
146 }
147 }
148 cout << num << endl;
149}
150
151int
152main()
153{
154 const auto puzzle = read_file("data/day22.txt");
155 part1(puzzle);
156 part2(puzzle);
157}
diff --git a/src/day23.cpp b/src/day23.cpp
new file mode 100644
index 0000000..d8c20d3
--- /dev/null
+++ b/src/day23.cpp
@@ -0,0 +1,152 @@
1#include <algorithm>
2#include <array>
3#include <fstream>
4#include <functional>
5#include <iostream>
6#include <iterator>
7#include <map>
8#include <queue>
9#include <set>
10#include <string>
11#include <vector>
12using namespace std;
13
14using position_t = tuple<size_t, size_t>;
15
16vector<string>
17read_file(string_view filename)
18{
19 fstream input{ filename };
20 vector<string> data;
21
22 for ( string line; getline(input, line); ) {
23 data.emplace_back(line);
24 }
25
26 return data;
27}
28
29set<position_t>
30get_neighbours(const vector<string>& maze, position_t possition)
31{
32 static const position_t deltas[] = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; // NOLINT
33
34 static const auto SYM_UP = '^';
35 static const auto SYM_RIGHT = '>';
36 static const auto SYM_DOWN = 'v';
37 static const auto SYM_LEFT = '<';
38
39 const auto [row, col] = possition;
40
41 switch ( maze[row][col] ) {
42 case SYM_UP:
43 return { make_tuple(row - 1, col) };
44 case SYM_RIGHT:
45 return { make_tuple(row, col + 1) };
46 case SYM_DOWN:
47 return { make_tuple(row + 1, col) };
48 case SYM_LEFT:
49 return { make_tuple(row, col - 1) };
50 }
51
52 set<position_t> positions;
53
54 for ( const auto& delta: deltas ) {
55 const auto new_row = row + get<0>(delta);
56 const auto new_col = col + get<1>(delta);
57
58 if ( new_row >= maze.size() || new_col >= maze[0].size() ) {
59 continue;
60 }
61
62 if ( maze[new_row][new_col] == '#' ) {
63 continue;
64 }
65
66 if ( new_row < row && maze[new_row][new_col] == SYM_DOWN ) {
67 continue;
68 }
69 if ( new_row > row && maze[new_row][new_col] == SYM_UP ) {
70 continue;
71 }
72 if ( new_col < col && maze[new_row][new_col] == SYM_RIGHT ) {
73 continue;
74 }
75 if ( new_col > col && maze[new_row][new_col] == SYM_LEFT ) {
76 continue;
77 }
78
79 positions.emplace(new_row, new_col);
80 }
81
82 return positions;
83}
84
85set<position_t>
86get_neighbours2(const vector<string>& maze, position_t possition)
87{
88 static const position_t deltas[] = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } }; // NOLINT
89
90 const auto [row, col] = possition;
91
92 set<position_t> positions;
93
94 for ( const auto& delta: deltas ) {
95 const auto new_row = row + get<0>(delta);
96 const auto new_col = col + get<1>(delta);
97
98 if ( new_row >= maze.size() || new_col >= maze[0].size() ) {
99 continue;
100 }
101
102 if ( maze[new_row][new_col] == '#' ) {
103 continue;
104 }
105
106 positions.emplace(new_row, new_col);
107 }
108
109 return positions;
110}
111
112void
113solve(const vector<string>& maze, function<set<position_t>(const vector<string>&, position_t)> neighbours)
114{
115 const position_t start = { 0, maze[0].find('.') };
116 const position_t end = { maze.size() - 1, maze[maze.size() - 1].find('.') };
117
118 vector<vector<bool>> visited(maze.size(), vector<bool>(maze[0].size()));
119
120 function<long(position_t, long)> find_longest_path = [&](position_t position, long current) -> long {
121 const auto [row, col] = position;
122
123 if ( visited[row][col] ) {
124 return 0;
125 }
126
127 if ( position == end ) {
128 return current;
129 }
130
131 long value = 0;
132
133 visited[row][col] = true;
134 for ( const auto& neighbour: neighbours(maze, position) ) {
135 value = max(value, find_longest_path(neighbour, current + 1));
136 }
137 visited[row][col] = false;
138
139 return value;
140 };
141
142 auto value = find_longest_path(start, 0);
143 cout << value << endl;
144}
145
146int
147main()
148{
149 const auto maze = read_file("data/day23.txt");
150 solve(maze, get_neighbours);
151 solve(maze, get_neighbours2);
152}
diff --git a/src/day24.cpp b/src/day24.cpp
new file mode 100644
index 0000000..76f459b
--- /dev/null
+++ b/src/day24.cpp
@@ -0,0 +1,173 @@
1#include <cstdlib>
2#include <fstream>
3#include <iostream>
4#include <limits>
5#include <map>
6#include <string>
7#include <tuple>
8#include <vector>
9#include <z3++.h>
10
11using namespace std;
12
13using line_type = tuple<double, double, double, double, double, double>;
14using point_type = tuple<double, double>;
15using puzzle_type = vector<line_type>;
16
17template<typename T>
18int
19sgn(T val)
20{
21 return (T(0) < val) - (val < T(0));
22}
23
24puzzle_type
25read_file(string_view filename)
26{
27 fstream input{ filename };
28 puzzle_type result;
29
30 for ( string line; getline(input, line); ) {
31 const auto NFIELDS = 6;
32
33 double x, y, z, dx, dy, dz; // NOLINT
34
35 if ( sscanf(line.data(), "%lf, %lf, %lf @ %lf, %lf, %lf", &x, &y, &z, &dx, &dy, &dz) != NFIELDS ) {
36 continue;
37 }
38
39 result.emplace_back(make_tuple(x, y, z, dx, dy, dz));
40 }
41
42 return result;
43}
44
45point_type
46get_direction(line_type line)
47{
48 const auto [x, y, z, dx, dy, dz] = line;
49
50 return make_tuple(sgn(dx), sgn(dy));
51}
52
53point_type
54get_direction(line_type from, point_type to)
55{
56 const auto [x, y, z, dx, dy, dz] = from;
57 const auto [to_x, to_y] = to;
58
59 return make_tuple(sgn(to_x - x), sgn(to_y - y));
60}
61
62bool
63lint(line_type stone_a, line_type stone_b, point_type& cross_point)
64{
65 const auto p_1 = make_tuple(get<0>(stone_a), get<1>(stone_a));
66 const auto p_2 = make_tuple(get<0>(stone_a) + get<3>(stone_a), get<1>(stone_a) + get<4>(stone_a));
67
68 const auto p_3 = make_tuple(get<0>(stone_b), get<1>(stone_b));
69 const auto p_4 = make_tuple(get<0>(stone_b) + get<3>(stone_b), get<1>(stone_b) + get<4>(stone_b));
70
71 const auto a_1 = get<1>(p_2) - get<1>(p_1);
72 const auto b_1 = get<0>(p_1) - get<0>(p_2);
73 const auto c_1 = a_1 * (get<0>(p_1)) + b_1 * (get<1>(p_1));
74
75 const auto a_2 = get<1>(p_4) - get<1>(p_3);
76 const auto b_2 = get<0>(p_3) - get<0>(p_4);
77 const auto c_2 = a_2 * (get<0>(p_3)) + b_2 * (get<1>(p_3));
78
79 const auto determinant = a_1 * b_2 - a_2 * b_1;
80
81 if ( determinant == 0 ) {
82 return false;
83 }
84
85 auto p_x = (b_2 * c_1 - b_1 * c_2) / determinant;
86 auto p_y = (a_1 * c_2 - a_2 * c_1) / determinant;
87
88 cross_point = make_tuple(p_x, p_y);
89
90 return true;
91}
92
93void
94part1(const puzzle_type& input)
95{
96 const auto in_range = [](point_type point) -> bool {
97 static const auto lower_limit = 200000000000000.0;
98 static const auto upper_limit = 400000000000000.0;
99
100 const auto [x, y] = point;
101
102 return x > lower_limit && x < upper_limit && y > lower_limit && y < upper_limit;
103 };
104
105 const auto in_future = [](line_type stone, point_type point) -> bool {
106 const auto sgn_stone = get_direction(stone);
107 const auto sgn_point = get_direction(stone, point);
108
109 return sgn_stone == sgn_point;
110 };
111
112 long sum = 0;
113 for ( size_t idx = 0; idx != input.size(); ++idx ) {
114 const auto& stone_a = input[idx];
115
116 for ( size_t idx2 = idx + 1; idx2 < input.size(); ++idx2 ) {
117 const auto& stone_b = input[idx2];
118
119 point_type cross_point;
120 if ( lint(stone_a, stone_b, cross_point) && in_range(cross_point) && in_future(stone_a, cross_point) && in_future(stone_b, cross_point) ) {
121 ++sum;
122 }
123 }
124 }
125 cout << sum << endl;
126}
127
128void
129part2(const puzzle_type& input)
130{
131 auto config = z3::config();
132 auto context = z3::context(config);
133 auto solver = z3::solver(context);
134
135 const auto px = context.int_const("px");
136 const auto py = context.int_const("py");
137 const auto pz = context.int_const("pz");
138 const auto vx = context.int_const("vx");
139 const auto vy = context.int_const("vy");
140 const auto vz = context.int_const("vz");
141
142 for ( size_t i = 0; i != input.size(); ++i ) {
143 const auto& stone = input[i];
144
145 const auto pxn = context.int_val(int64_t(get<0>(stone)));
146 const auto pyn = context.int_val(int64_t(get<1>(stone)));
147 const auto pzn = context.int_val(int64_t(get<2>(stone)));
148 const auto vxn = context.int_val(int64_t(get<3>(stone)));
149 const auto vyn = context.int_val(int64_t(get<4>(stone)));
150 const auto vzn = context.int_val(int64_t(get<5>(stone)));
151 const auto tn = context.int_const(to_string(i+1).c_str());
152
153 solver.add(tn >= 0);
154 solver.add(pxn + vxn * tn == px + vx * tn);
155 solver.add(pyn + vyn * tn == py + vy * tn);
156 solver.add(pzn + vzn * tn == pz + vz * tn);
157 }
158
159 solver.check();
160
161 auto model = solver.get_model();
162
163 cout << model.eval(px).as_int64() + model.eval(py).as_int64() + model.eval(pz).as_int64() << endl;
164}
165
166int
167main()
168{
169 const auto input = read_file("data/day24.txt");
170
171 part1(input);
172 part2(input);
173}
diff --git a/src/day25.cpp b/src/day25.cpp
new file mode 100644
index 0000000..1d5e19e
--- /dev/null
+++ b/src/day25.cpp
@@ -0,0 +1,140 @@
1#include <algorithm>
2#include <array>
3#include <fstream>
4#include <iostream>
5#include <map>
6#include <queue>
7#include <random>
8#include <set>
9#include <sstream>
10#include <string>
11#include <string_view>
12#include <vector>
13using namespace std;
14
15using graph_type = map<string, set<string>>;
16
17vector<string>
18split(const string& line, char sep)
19{
20 vector<string> parts;
21 stringstream input{ line };
22
23 for ( string part; getline(input, part, sep); ) {
24 parts.emplace_back(part);
25 }
26
27 return parts;
28}
29
30graph_type
31read_file(string_view filename)
32{
33 fstream input{ filename };
34 graph_type graph;
35
36 for ( string line; getline(input, line); ) {
37 const auto components = split(line.erase(line.find(':'), 1), ' ');
38
39 for ( size_t i = 1; i < components.size(); ++i ) {
40 graph[components[0]].insert(components[i]);
41 graph[components[i]].insert(components[0]);
42 }
43 }
44
45 return graph;
46}
47
48bool
49bfs(graph_type graph, const string& source, const string& dest, function<void(const string&, const string&)> visit) // NOLINT
50{
51 set<string> visited;
52
53 queue<string> queue;
54 queue.emplace(source);
55
56 while ( !queue.empty() ) {
57 auto candidate = queue.front();
58 queue.pop();
59
60 if ( candidate == dest ) {
61 return true;
62 }
63
64 for ( const auto& neighbour: graph[candidate] ) {
65 if ( !visited.contains(neighbour) ) {
66 queue.emplace(neighbour);
67
68 visit(candidate, neighbour);
69 visited.emplace(neighbour);
70 }
71 }
72 }
73 return false;
74}
75
76auto
77random_node(const graph_type& graph)
78{
79 static random_device dev;
80 static mt19937 generator(dev());
81
82 uniform_int_distribution<graph_type::size_type> distribute(0, graph.size()-1);
83
84 auto iter = graph.begin();
85 advance(iter, distribute(generator));
86 return iter->first;
87}
88
89void
90part1(graph_type graph)
91{
92 map<tuple<string, string>, int> frequencies;
93 for ( size_t idx = 0; idx != 2; ++idx ) {
94 for ( const auto& second: graph ) {
95 bfs(graph, random_node(graph), second.first, [&](const string& candidate, const string& neighbour) {
96 if ( candidate < neighbour ) {
97 frequencies[make_tuple(candidate, neighbour)]++;
98 }
99 else {
100 frequencies[make_tuple(neighbour, candidate)]++;
101 }
102 });
103 }
104 }
105
106 vector<tuple<tuple<string, string>, int>> sorted_frequencies{ frequencies.begin(), frequencies.end() };
107
108 sort(sorted_frequencies.begin(), sorted_frequencies.end(), [](const auto& lhs, const auto& rhs) {
109 return get<1>(lhs) > get<1>(rhs);
110 });
111
112 sorted_frequencies.erase(sorted_frequencies.begin() + 3, sorted_frequencies.end());
113
114 for ( const auto& link: sorted_frequencies ) {
115 const auto [left, right] = get<0>(link);
116
117 graph[left].erase(right);
118 graph[right].erase(left);
119 }
120
121 auto reachable = [&graph](const string& start_node) {
122 set<string> nodes;
123 bfs(graph, start_node, "", [&nodes](const string& candidate, const string& neighbour) {
124 nodes.insert(candidate);
125 nodes.insert(neighbour);
126 });
127 return nodes.size();
128 };
129
130 const auto [left, right] = get<0>(sorted_frequencies[0]);
131
132 cout << reachable(left) * reachable(right) << endl;
133}
134
135int
136main()
137{
138 auto graph = read_file("data/day25.txt");
139 part1(graph);
140}