From bdcd0019ec239c52a75f21648376c188f9458d5b Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Sat, 26 Nov 2022 10:01:10 -0800 Subject: [PATCH] 2018-15 checkpoint --- 2017/19.cpp | 79 +++++++++++++++++ 2017/CMakeLists.txt | 3 + 2018/15.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++++ 2018/CMakeLists.txt | 2 + CMakeLists.txt | 1 + 5 files changed, 293 insertions(+) create mode 100644 2017/19.cpp create mode 100644 2018/15.cpp create mode 100644 2018/CMakeLists.txt diff --git a/2017/19.cpp b/2017/19.cpp new file mode 100644 index 0000000..1725a60 --- /dev/null +++ b/2017/19.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace aocpp; + +namespace { + +auto Drive(Grid const& grid) -> std::pair +{ + std::string path; + int steps = 0; + + Coord here {}; + Coord dir {0,1}; + + // Find starting column + for (std::size_t i = 0; i < grid.rows[0].size(); i++) { + if (grid.rows[0][i] != ' ') { + here.x = i; + break; + } + } + + for(;;) { + char c = grid[here]; + if (c == '+') { + if (grid[here + CW(dir)] != ' ') { + dir = CW(dir); + } else if (grid[here + CCW(dir)] != ' ') { + dir = CCW(dir); + } + } else if (c == ' ') { + break; + } else if (std::isalpha(c)) { + path += c; + } + steps++; + here += dir; + } + + return {path, steps}; +} + +} // namespace + +TEST_SUITE("2017-19 examples") { + TEST_CASE("example") { + std::istringstream in { + " | \n" + " | +--+ \n" + " A | C \n" + " F---|--|-E---+ \n" + " | | | D \n" + " +B-+ +--+ \n" + " \n" + }; + auto [p1,p2] = Drive(Grid::Parse(in)); + CHECK(p1 == "ABCDEF"); + CHECK(p2 == 38); + } +} + +auto main(int argc, char** argv) -> int { + auto input = Grid::Parse(*aocpp::Startup(argc, argv)); + auto [part1, part2] = Drive(input); + std::cout << "Part 1: " << part1 << std::endl; + std::cout << "Part 2: " << part2 << std::endl; +} diff --git a/2017/CMakeLists.txt b/2017/CMakeLists.txt index 46a94b4..6533e18 100644 --- a/2017/CMakeLists.txt +++ b/2017/CMakeLists.txt @@ -24,3 +24,6 @@ target_link_libraries(2017_17 aocpp) add_executable(2017_18 18.cpp) target_link_libraries(2017_18 aocpp) + +add_executable(2017_19 19.cpp) +target_link_libraries(2017_19 aocpp) diff --git a/2018/15.cpp b/2018/15.cpp new file mode 100644 index 0000000..c26c781 --- /dev/null +++ b/2018/15.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +using namespace aocpp; + +namespace { + +int const initial_hp = 200; + +auto IsUnit(char c) -> bool { + return c == 'G' || c == 'E'; +} + +auto IsOpposed(char x, char y) -> bool { + return x == 'G' && y == 'E' || x == 'E' && y == 'G'; +} + +auto PickTarget( + Grid const& grid, + std::map & hp, + Coord my_coord, char my_team +) -> std::optional +{ + std::optional best; + int best_hp; + for (auto const dir : {Coord{0,-1}, Coord{-1,0}, Coord{1,0}, Coord{0,1}}) { //reading order + auto here = dir + my_coord; + if (IsOpposed(my_team, grid[here]) && + (!best || best_hp > hp[here])) { + best = here; + best_hp = hp[here]; + } + } + return best; +} + +auto Before(Coord a, Coord b) { + return a.y < b.y || a.y == b.y && a.x < b.x; +} + +auto Move( + Grid const& grid, + std::map & hp, + Coord my_coord, char my_team +) -> std::optional +{ + + int best_distance; + std::optional best_start, best_end; + + std::map> seen; + std::deque> todo; + + for (auto const dir : {Coord{0,-1}, Coord{-1,0}, Coord{1,0}, Coord{0,1}}) { + auto start = my_coord + dir; + todo.push_back({start,start,1}); + } + + while (!todo.empty()) { + auto [start, cursor, steps] = todo.front(); + todo.pop_front(); + + if (grid[cursor] != '.') { + continue; + } + + if (best_start && best_distance < steps) break; + + auto [it,success] = seen.insert({cursor, {start, steps}}); + if (!success) { + if (Before(start, it->second.first)) continue; + it->second.first = start; + } + + for (auto const dir : {Coord{0,-1}, Coord{-1,0}, Coord{1,0}, Coord{0,1}}) { + auto next = cursor + dir; + if (seen.insert({next, {start, steps+1}}).second) { + todo.push_back({start, next, steps+1}); + } + } + + if (PickTarget(grid, hp, cursor, my_team) && + (!best_start || + steps < best_distance || + steps == best_distance && Before(cursor, *best_end) || + steps == best_distance && cursor == *best_end && Before(start, *best_start))) + { + best_end = cursor; + best_distance = steps; + best_start = start; + } + } + return best_start; +} + +auto Simulate(Grid & grid) { + + // hit points for unit at coordinate + std::map hp; + + // set each unit to its initial hit points + grid.each([&](auto k, auto v) { + if (IsUnit(v)) { + hp[k] = initial_hp; + } + }); + + for (int rounds = 0;; rounds++) { + std::cout << "round " << rounds << std::endl; + for (auto const& row : grid.rows) { + std::cout << row << std::endl; + } + + // determine order of unit updates + std::deque turn_order; + grid.each([&](auto k, auto v) { + if (IsUnit(v)) { + turn_order.push_back(k); + } + }); + + while (!turn_order.empty()) { + auto active_coord = turn_order.front(); + char active_team = grid[active_coord]; + turn_order.pop_front(); + + // Detect when no targets remain for this unit and end the game + bool game_over = true; + grid.each([&](auto k, auto v) { + if (IsOpposed(active_team, v)) game_over = false; + }); + if (game_over) { + int total_hp = 0; + for (auto const& [_,v] : hp) { total_hp += v; } + return rounds * total_hp; + } + + // Try to pick a target without moving + auto target = PickTarget(grid, hp, active_coord, active_team); + + // only move when necessary + if (!target) { + // move if we can + if (auto const destination = Move(grid, hp, active_coord, active_team)) { + std::swap(grid[*destination], grid[active_coord]); + + auto const it = hp.find(active_coord); + hp[*destination] = it->second; + hp.erase(it); + + active_coord = *destination; + } + + target = PickTarget(grid, hp, active_coord, active_team); + } + + // target in range, do the attack + if (target) { + + // apply damage + auto remaining = hp[*target] -= 3; + + // handle death + if (remaining <= 0) { + hp.erase(*target); + + // free up location on the map + grid[*target] = '.'; + + // possibly remove dead unit from turn queue + auto it = std::find(turn_order.begin(), turn_order.end(), *target); + if (it != turn_order.end()) { + turn_order.erase(it); + } + + + } + } + } + } +} + + + +} // namespace + +TEST_SUITE("2018-15 examples") { + TEST_CASE("example") { + } +} + +auto main(int argc, char** argv) -> int { + auto grid = Grid::Parse(*aocpp::Startup(argc, argv)); + auto part1 = Simulate(grid); + std::cout << "Part 1: " << part1 << std::endl; + //std::cout << "Part 2: " << Run2(std::move(input)) << std::endl; +} diff --git a/2018/CMakeLists.txt b/2018/CMakeLists.txt new file mode 100644 index 0000000..58d8f30 --- /dev/null +++ b/2018/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(2018_15 15.cpp) +target_link_libraries(2018_15 aocpp) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34a8a59..be8052a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,5 +25,6 @@ add_subdirectory(knothash) add_subdirectory(zmod) add_subdirectory(intcode) add_subdirectory(2017) +add_subdirectory(2018) add_subdirectory(2019) add_subdirectory(2020)