From d33dc72129a56b0d8b0f763bfb7637797dd3f64b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Sun, 22 Dec 2024 13:35:06 -0600 Subject: [PATCH] didn't get day 21... :( --- 2024/src/CMakeLists.txt | 2 + 2024/src/day21.cpp | 326 ++++++++++++++++++++++++++++++++++++++++ 2024/src/day22.cpp | 94 ++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 2024/src/day21.cpp create mode 100644 2024/src/day22.cpp diff --git a/2024/src/CMakeLists.txt b/2024/src/CMakeLists.txt index d6dc874..8a76a01 100644 --- a/2024/src/CMakeLists.txt +++ b/2024/src/CMakeLists.txt @@ -31,6 +31,8 @@ create_standard_test(NAME 2024_day17 SOURCES day17.cpp LIBRARIES aoc2024) create_standard_test(NAME 2024_day18 SOURCES day18.cpp LIBRARIES aoc2024) create_standard_test(NAME 2024_day19 SOURCES day19.cpp LIBRARIES aoc2024) create_standard_test(NAME 2024_day20 SOURCES day20.cpp LIBRARIES aoc2024) +create_standard_test(NAME 2024_day21 SOURCES day21.cpp LIBRARIES aoc2024) +create_standard_test(NAME 2024_day22 SOURCES day22.cpp LIBRARIES aoc2024) ################################################################################ # Copy input data diff --git a/2024/src/day21.cpp b/2024/src/day21.cpp new file mode 100644 index 0000000..f94a294 --- /dev/null +++ b/2024/src/day21.cpp @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct Data +{ + std::vector codes; +}; + +std::vector numeric_keypad = { + "789", + "456", + "123", + " 0A" +}; +std::map numeric_keyapd_char_to_pos = { + {'7', Pos{.x = 0, .y = 0}}, {'8', Pos{.x = 1, .y = 0}}, {'9', Pos{.x = 2, .y = 0}}, + {'4', Pos{.x = 0, .y = 1}}, {'5', Pos{.x = 1, .y = 1}}, {'6', Pos{.x = 2, .y = 1}}, + {'1', Pos{.x = 0, .y = 2}}, {'2', Pos{.x = 1, .y = 2}}, {'3', Pos{.x = 2, .y = 2}}, + {'0', Pos{.x = 1, .y = 3}}, {'A', Pos{.x = 2, .y = 3}} +}; + +std::vector directional_keypad = { + " ^A", + "" +}; +std::map directional_keyapd_char_to_pos = { + {'^', Pos{.x = 1, .y = 0}}, {'A', Pos{.x = 2, .y = 0}}, + {'<', Pos{.x = 0, .y = 1}}, {'v', Pos{.x = 1, .y = 1}}, {'>', Pos{.x = 2, .y = 1}} +}; + +bool in_bounds(int64_t x, int64_t y, const std::vector &keypad) +{ + return x >= 0 && x < keypad[0].size() && y >= 0 && y < keypad.size(); +} + +using Vertices = std::vector; +using Edges = std::map>; + +std::pair build_graph(const std::vector &keypad) +{ + std::vector vertices; + std::map> edges; + + for (int64_t j = 0; j < keypad.size(); ++j) + { + for (int64_t i = 0; i < keypad[j].size(); ++i) + { + if (keypad[j][i] != ' ') + { + vertices.push_back(Pos{.x = i, .y = j}); + } + } + } + + for (const auto &v : vertices) + { + for (const auto &dir : directions) + { + Pos next = v + dir; + if (in_bounds(next.x, next.y, keypad) && keypad[next.y][next.x] != ' ') + { + edges[v].push_back(next); + } + } + } + return {vertices, edges}; +} + +// after writing ths, I realized that I could have just computed the manhattan distance +// between each pair of vertices and stored that in a map +using Distances = std::map>; +using Paths = std::map>; + +std::pair floyd_warshall(const std::pair& graph) +{ + const auto& [vertices, edges] = graph; + Distances dist; + Paths prev; + + for (const auto &u : vertices) + { + for (const auto &v : vertices) + { + dist[u][v] = INT_MAX; + prev[u][v] = Pos{.x = -1, .y = -1}; + } + } + + for (const auto &u : vertices) + { + for (const auto &v : edges.at(u)) + { + dist[u][v] = 1; + prev[u][v] = u; + } + dist[u][u] = 0; + prev[u][u] = u; + } + + for (const auto &k : vertices) + { + for (const auto &i : vertices) + { + for (const auto &j : vertices) + { + auto &dik = dist[i][k]; + auto &dkj = dist[k][j]; + auto &dij = dist[i][j]; + if (dik != INT_MAX && dkj != INT_MAX && dij > dik + dkj) + { + dij = dik + dkj; + prev[i][j] = prev[k][j]; + } + } + } + } + + return {dist, prev}; +} + +std::vector shortest_path1(const Pos &start, const Pos &end, const Paths &paths, const Distances &distances, std::vector keypad) +{ + std::vector path; + Pos current = end; + while (current != start) + { + path.push_back(current); + current = paths.at(start).at(current); + } + path.push_back(start); + std::reverse(path.begin(), path.end()); + return path; +} + +std::vector shortest_path2(const Pos &start, const Pos &end, const Paths &paths, const Distances &distances, std::vector keypad) +{ + std::vector path; + Pos current = end; + + Pos last_direction = {0, 0}; + + while (current != start) + { + path.push_back(current); + + Pos next = paths.at(start).at(current); + if (next != start) + { + Pos candidate_direction = current - next; + + // Check if the same-distance alternative exists with the same direction + Pos alternative = current; + bool found_alternative = false; + + // Iterate through potential neighbors of `current` + for(auto& dir : directions) + { + if (dir == -1 * last_direction) + { + continue; + } + Pos candidate_pos = current + dir; + if (in_bounds(candidate_pos.x, candidate_pos.y, keypad) && keypad.at(candidate_pos.y).at(candidate_pos.x) != ' ' && + distances.at(start).at(candidate_pos) == distances.at(start).at(current)) + { + alternative = candidate_pos; + found_alternative = true; + break; + } + } + + if (found_alternative) + { + next = alternative; + } + + // Update the last direction + last_direction = candidate_direction; + } + + current = next; + } + + path.push_back(start); + std::reverse(path.begin(), path.end()); + return path; +} + +std::string get_path(std::string path, const Paths& shortest_paths, const Distances& distances, std::vector keypad, std::map char_to_pos) { + Pos start = char_to_pos['A']; + std::string numeric_path; + Pos current = start; + for (char c : path) { + Pos next = char_to_pos[c]; + auto path = shortest_path1(current, next, shortest_paths, distances, keypad); + for(size_t i = 0; i < path.size() - 1; ++i) { + Pos dir = path[i + 1] - path[i]; + if (dir == up) { + numeric_path += '^'; + } else if (dir == right) { + numeric_path += '>'; + } else if (dir == down) { + numeric_path += 'v'; + } else { + numeric_path += '<'; + } + } + numeric_path.push_back('A'); + current = next; + } + return numeric_path; +} + +int part1(const Data &data) +{ + auto [numeric_distances, numeric_paths] = floyd_warshall(build_graph(numeric_keypad)); + auto [directional_distances, directional_paths] = floyd_warshall(build_graph(directional_keypad)); + int n_robots = 2; + + for(const auto& code : data.codes) { + std::cout << code << std::endl; + auto numeric_path = get_path(code, numeric_paths, numeric_distances, numeric_keypad, numeric_keyapd_char_to_pos); + for(auto c : numeric_path) { + std::cout << c; + } + std::cout << " " << numeric_path.size() << std::endl; + std::string directional_path = numeric_path; + for(int i = 0; i < n_robots; ++i) { + directional_path = get_path(directional_path, directional_paths, directional_distances, directional_keypad, directional_keyapd_char_to_pos); + for(auto c : directional_path) { + std::cout << c; + } + std::cout << " " << directional_path.size() << std::endl; + } + std::cout << std::endl; + } + + return 0; +} + +int part2(const Data &data) +{ + return 0; +} + +Data parse() +{ + std::ifstream file(std::filesystem::path("inputs/day21.txt")); + if (!file.is_open()) + { + throw std::runtime_error("file not found"); + } + std::string line; + Data data; + + while (std::getline(file, line)) + { + data.codes.push_back(line); + } + + return data; +} + +class BenchmarkFixture : public benchmark::Fixture +{ +public: + static Data data; +}; + +Data BenchmarkFixture::data = parse(); + +BENCHMARK_DEFINE_F(BenchmarkFixture, Part1Benchmark) +(benchmark::State &state) +{ + for (auto _ : state) + { + auto s = part1(data); + benchmark::DoNotOptimize(s); + } +} + +BENCHMARK_DEFINE_F(BenchmarkFixture, Part2Benchmark) +(benchmark::State &state) +{ + for (auto _ : state) + { + auto s = part2(data); + benchmark::DoNotOptimize(s); + } +} + +BENCHMARK_REGISTER_F(BenchmarkFixture, Part1Benchmark)->Unit(benchmark::kMillisecond); +BENCHMARK_REGISTER_F(BenchmarkFixture, Part2Benchmark)->Unit(benchmark::kMillisecond); + +int main(int argc, char **argv) +{ + Data data = parse(); + + int answer1 = 0; + int answer2 = 0; + + auto first = part1(data); + std::cout << "Part 1: " << first << std::endl; + + auto second = part2(data); + std::cout << "Part 2: " << second << std::endl; + + first != answer1 ? throw std::runtime_error("Part 1 incorrect") : nullptr; + second != answer2 ? throw std::runtime_error("Part 2 incorrect") : nullptr; + + for (int i = 1; i < argc; ++i) + { + if (std::string(argv[i]) == "--benchmark") + { + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + return 0; + } + } +} diff --git a/2024/src/day22.cpp b/2024/src/day22.cpp new file mode 100644 index 0000000..9596f2b --- /dev/null +++ b/2024/src/day22.cpp @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Data { + +}; + +int part1(const Data &data) +{ + return 0; +} + +int part2(const Data &data) +{ + return 0; +} + +Data parse() +{ + std::ifstream file(std::filesystem::path("inputs/dayX.txt")); + if (!file.is_open()) + { + throw std::runtime_error("file not found"); + } + std::string line; + Data data; + + while (std::getline(file, line)) + { + } + + return data; +} + +class BenchmarkFixture : public benchmark::Fixture +{ +public: + static Data data; +}; + +Data BenchmarkFixture::data = parse(); + +BENCHMARK_DEFINE_F(BenchmarkFixture, Part1Benchmark) +(benchmark::State &state) +{ + for (auto _ : state) + { + auto s = part1(data); + benchmark::DoNotOptimize(s); + } +} + +BENCHMARK_DEFINE_F(BenchmarkFixture, Part2Benchmark) +(benchmark::State &state) +{ + for (auto _ : state) + { + auto s = part2(data); + benchmark::DoNotOptimize(s); + } +} + +BENCHMARK_REGISTER_F(BenchmarkFixture, Part1Benchmark)->Unit(benchmark::kMillisecond); +BENCHMARK_REGISTER_F(BenchmarkFixture, Part2Benchmark)->Unit(benchmark::kMillisecond); + +int main(int argc, char **argv) +{ + Data data = parse(); + + int answer1 = 0; + int answer2 = 0; + + auto first = part1(data); + std::cout << "Part 1: " << first << std::endl; + + auto second = part2(data); + std::cout << "Part 2: " << second << std::endl; + + first != answer1 ? throw std::runtime_error("Part 1 incorrect") : nullptr; + second != answer2 ? throw std::runtime_error("Part 2 incorrect") : nullptr; + + for (int i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "--benchmark") { + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + return 0; + } + } +} \ No newline at end of file