2018-15 checkpoint
This commit is contained in:
parent
f4d5612483
commit
bdcd0019ec
79
2017/19.cpp
Normal file
79
2017/19.cpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cctype>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include <aocpp/Startup.hpp>
|
||||||
|
#include <aocpp/Grid.hpp>
|
||||||
|
#include <aocpp/Coord.hpp>
|
||||||
|
|
||||||
|
using namespace aocpp;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
auto Drive(Grid const& grid) -> std::pair<std::string, int>
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
|
@ -24,3 +24,6 @@ target_link_libraries(2017_17 aocpp)
|
||||||
|
|
||||||
add_executable(2017_18 18.cpp)
|
add_executable(2017_18 18.cpp)
|
||||||
target_link_libraries(2017_18 aocpp)
|
target_link_libraries(2017_18 aocpp)
|
||||||
|
|
||||||
|
add_executable(2017_19 19.cpp)
|
||||||
|
target_link_libraries(2017_19 aocpp)
|
||||||
|
|
208
2018/15.cpp
Normal file
208
2018/15.cpp
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include <aocpp/Startup.hpp>
|
||||||
|
#include <aocpp/Grid.hpp>
|
||||||
|
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<Coord, int> & hp,
|
||||||
|
Coord my_coord, char my_team
|
||||||
|
) -> std::optional<Coord>
|
||||||
|
{
|
||||||
|
std::optional<Coord> 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<Coord, int> & hp,
|
||||||
|
Coord my_coord, char my_team
|
||||||
|
) -> std::optional<Coord>
|
||||||
|
{
|
||||||
|
|
||||||
|
int best_distance;
|
||||||
|
std::optional<Coord> best_start, best_end;
|
||||||
|
|
||||||
|
std::map<Coord, std::pair<Coord, int>> seen;
|
||||||
|
std::deque<std::tuple<Coord, Coord, int>> 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<Coord, int> 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<Coord> 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;
|
||||||
|
}
|
2
2018/CMakeLists.txt
Normal file
2
2018/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
add_executable(2018_15 15.cpp)
|
||||||
|
target_link_libraries(2018_15 aocpp)
|
|
@ -25,5 +25,6 @@ add_subdirectory(knothash)
|
||||||
add_subdirectory(zmod)
|
add_subdirectory(zmod)
|
||||||
add_subdirectory(intcode)
|
add_subdirectory(intcode)
|
||||||
add_subdirectory(2017)
|
add_subdirectory(2017)
|
||||||
|
add_subdirectory(2018)
|
||||||
add_subdirectory(2019)
|
add_subdirectory(2019)
|
||||||
add_subdirectory(2020)
|
add_subdirectory(2020)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user