diff options
| -rw-r--r-- | compile_flags.txt | 1 | ||||
| -rw-r--r-- | makefile | 9 | ||||
| -rw-r--r-- | src/day04.cpp | 26 | ||||
| -rw-r--r-- | src/day05.cpp | 27 | ||||
| -rw-r--r-- | src/day09.cpp | 13 | ||||
| -rw-r--r-- | src/day10.cpp | 19 | ||||
| -rw-r--r-- | src/day12.cpp | 78 | ||||
| -rw-r--r-- | src/day14.cpp | 22 | ||||
| -rw-r--r-- | src/day16.cpp | 4 | ||||
| -rw-r--r-- | src/day22.cpp | 157 | ||||
| -rw-r--r-- | src/day23.cpp | 152 | ||||
| -rw-r--r-- | src/day24.cpp | 173 | ||||
| -rw-r--r-- | src/day25.cpp | 140 |
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 | ||
| @@ -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 | ||
| 25 | bin: | 29 | bin: |
| 26 | mkdir $@ | 30 | mkdir $@ |
| @@ -28,6 +32,9 @@ bin: | |||
| 28 | bin/%: src/%.cpp | bin | 32 | bin/%: src/%.cpp | bin |
| 29 | c++ $(CPPFLAGS) $^ -o $@ | 33 | c++ $(CPPFLAGS) $^ -o $@ |
| 30 | 34 | ||
| 35 | bin/day24: src/day24.cpp | bin | ||
| 36 | c++ $(CPPFLAGS) -Wno-sign-conversion -I/usr/local/include $^ -L/usr/local/lib -lz3 -o $@ | ||
| 37 | |||
| 31 | clean: | 38 | clean: |
| 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) | |||
| 25 | vector<string> | 24 | vector<string> |
| 26 | split(const string& line, char sep) | 25 | split(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 | ||
| 52 | vector<unsigned long> | 51 | auto |
| 53 | build_data_set() | 52 | build_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 | ||
| 77 | void | 75 | void |
| 78 | pass1() | 76 | pass1(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 | ||
| 90 | void | 88 | void |
| 91 | pass2() | 89 | pass2(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() | |||
| 111 | int | 108 | int |
| 112 | main() | 109 | main() |
| 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) | |||
| 49 | vector<string> | 49 | vector<string> |
| 50 | split(const string& line, char sep) | 50 | split(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 | ||
| 62 | void | 62 | void |
| 63 | part1() | 63 | part1(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 | ||
| 119 | void | 117 | void |
| 120 | part2() | 118 | part2(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() | |||
| 181 | int | 177 | int |
| 182 | main() | 178 | main() |
| 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 | ||
| 47 | void | 47 | void |
| 48 | part1() | 48 | part1(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 | ||
| 59 | void | 57 | void |
| 60 | part2() | 58 | part2(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() | |||
| 71 | int | 69 | int |
| 72 | main() | 70 | main() |
| 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) | |||
| 38 | bool | 38 | bool |
| 39 | predict_direction(const puzzle_t& puzzle, pos_t& current, char& direction) | 39 | predict_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 | ||
| 117 | void | 116 | void |
| 118 | part1() | 117 | part1(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 | ||
| 165 | void | 162 | void |
| 166 | part2() | 163 | part2(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() | |||
| 216 | int | 210 | int |
| 217 | main() | 211 | main() |
| 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 | ||
| 71 | long | 72 | long |
| 72 | brute_force(string_view puzzle, const vector<long>& nums) | 73 | brute_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 | ||
| 107 | long | ||
| 108 | count(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 | |||
| 106 | void | 147 | void |
| 107 | part1() | 148 | part1(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 | |||
| 162 | void | ||
| 163 | part2(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() | |||
| 122 | int | 176 | int |
| 123 | main() | 177 | main() |
| 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 | ||
| 23 | void | 23 | void |
| 24 | part1() | 24 | part1(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) | |||
| 182 | void | 180 | void |
| 183 | tilt(vector<string>& lines) | 181 | tilt(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 | ||
| 191 | size_t | 188 | size_t |
| @@ -201,16 +198,14 @@ calc(const vector<string>& lines) | |||
| 201 | } | 198 | } |
| 202 | 199 | ||
| 203 | void | 200 | void |
| 204 | part2() | 201 | part2(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() | |||
| 238 | int | 233 | int |
| 239 | main() | 234 | main() |
| 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> | ||
| 8 | using namespace std; | ||
| 9 | |||
| 10 | struct piece { | ||
| 11 | int x1, y1, z1; | ||
| 12 | int x2, y2, z2; | ||
| 13 | }; | ||
| 14 | |||
| 15 | vector<piece> | ||
| 16 | read_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 | |||
| 40 | void | ||
| 41 | part1(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 | |||
| 92 | void | ||
| 93 | part2(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 | |||
| 151 | int | ||
| 152 | main() | ||
| 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> | ||
| 12 | using namespace std; | ||
| 13 | |||
| 14 | using position_t = tuple<size_t, size_t>; | ||
| 15 | |||
| 16 | vector<string> | ||
| 17 | read_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 | |||
| 29 | set<position_t> | ||
| 30 | get_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 | |||
| 85 | set<position_t> | ||
| 86 | get_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 | |||
| 112 | void | ||
| 113 | solve(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 | |||
| 146 | int | ||
| 147 | main() | ||
| 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 | |||
| 11 | using namespace std; | ||
| 12 | |||
| 13 | using line_type = tuple<double, double, double, double, double, double>; | ||
| 14 | using point_type = tuple<double, double>; | ||
| 15 | using puzzle_type = vector<line_type>; | ||
| 16 | |||
| 17 | template<typename T> | ||
| 18 | int | ||
| 19 | sgn(T val) | ||
| 20 | { | ||
| 21 | return (T(0) < val) - (val < T(0)); | ||
| 22 | } | ||
| 23 | |||
| 24 | puzzle_type | ||
| 25 | read_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 | |||
| 45 | point_type | ||
| 46 | get_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 | |||
| 53 | point_type | ||
| 54 | get_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 | |||
| 62 | bool | ||
| 63 | lint(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 | |||
| 93 | void | ||
| 94 | part1(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 | |||
| 128 | void | ||
| 129 | part2(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 | |||
| 166 | int | ||
| 167 | main() | ||
| 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> | ||
| 13 | using namespace std; | ||
| 14 | |||
| 15 | using graph_type = map<string, set<string>>; | ||
| 16 | |||
| 17 | vector<string> | ||
| 18 | split(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 | |||
| 30 | graph_type | ||
| 31 | read_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 | |||
| 48 | bool | ||
| 49 | bfs(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 | |||
| 76 | auto | ||
| 77 | random_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 | |||
| 89 | void | ||
| 90 | part1(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 | |||
| 135 | int | ||
| 136 | main() | ||
| 137 | { | ||
| 138 | auto graph = read_file("data/day25.txt"); | ||
| 139 | part1(graph); | ||
| 140 | } | ||
