diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 704befe..69c855e 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -4,10 +4,16 @@ project(aoc2019) find_package(GTest REQUIRED) +option(ANIMATE_DAY13 "Animate the Arkanoid game in day 13." Off) + file(GLOB DAYS_FILES src/day*.cpp) add_library(AoCSolutions src/implementations.cpp "${DAYS_FILES}" src/point.hpp src/utils.cpp src/utils.hpp) target_compile_features(AoCSolutions PUBLIC cxx_std_17) +if (ANIMATE_DAY13) + target_compile_definitions(AoCSolutions ANIMATE_DAY13) +endif () + add_executable(runner src/runner.cpp) target_compile_features(runner PUBLIC cxx_std_17) target_link_libraries(runner AoCSolutions) diff --git a/2019/src/day13.cpp b/2019/src/day13.cpp index a611d36..aba4aeb 100644 --- a/2019/src/day13.cpp +++ b/2019/src/day13.cpp @@ -1,4 +1,8 @@ #include +#ifdef ANIMATE_DAY13 +#include +#include +#endif #include "days.hpp" #include "utils.hpp" #include "point.hpp" @@ -13,28 +17,128 @@ namespace { PADDLE, BALL, }; + + typedef std::unordered_map Screen; + + std::optional update_screen(std::deque &output_buffer, Screen &screen) { + std::optional score; + while (!output_buffer.empty()) { + auto x = output_buffer.front(); output_buffer.pop_front(); + auto y = output_buffer.front(); output_buffer.pop_front(); + auto type = output_buffer.front(); output_buffer.pop_front(); + + if (x == -1 && y == 0) { + score = type; + continue; + } + + screen[{x, y}] = static_cast(type); + } + return score; + } + + void draw_screen(const Screen &screen, std::ostream& output) { + // Determine bounding box + using limits = std::numeric_limits; + + std::int64_t left_edge = limits::max(), right_edge = limits::min(), top_edge = limits::max(), bottom_edge = limits::min(); + for (auto& entry : screen) { + left_edge = std::min(entry.first[0], left_edge); + right_edge = std::max(entry.first[0], right_edge); + top_edge = std::min(entry.first[1], top_edge); + bottom_edge = std::max(entry.first[1], bottom_edge); + } + + for (auto y = top_edge; y <= bottom_edge; ++y) { + for (auto x = left_edge; x <= right_edge; ++x) { + char c = ' '; + if (auto it = screen.find({x, y}); it != screen.end()) { + switch (it->second) { + case Tile::EMPTY: + c = ' '; + break; + + case Tile::BALL: + c = '*'; + break; + + case Tile::BLOCK: + c = '='; + break; + + case Tile::PADDLE: + c = '_'; + break; + + case Tile::WALL: + c = '#'; + break; + } + } + + output << c; + } + + output << '\n'; + } + } + + auto find_pos(const Screen &screen, Tile to_find) { + return std::find_if(screen.begin(), screen.end(), [to_find](const auto& x) { + return x.second == to_find; + }); + } } void aoc2019::day13_part1(std::istream &input, std::ostream &output) { + Screen screen; aoc2019::IntCodeComputer computer(aoc2019::IntCodeComputer::read_intcode(input)); std::deque output_buffer; - computer.connectOutput(output_buffer); computer.run(); + update_screen(output_buffer, screen); - std::unordered_map drawn; - - while (!output_buffer.empty()) { - auto x = output_buffer.front(); output_buffer.pop_front(); - auto y = output_buffer.front(); output_buffer.pop_front(); - auto type = output_buffer.front(); output_buffer.pop_front(); - - drawn[{x, y}] = static_cast(type); - } - - output << std::count_if(drawn.begin(), drawn.end(), [](const auto& x) { return x.second == Tile::BLOCK; })<< std::endl; + output << std::count_if(screen.begin(), screen.end(), [](const auto &x) { return x.second == Tile::BLOCK; }) + << std::endl; } void aoc2019::day13_part2(std::istream &input, std::ostream &output) { - output << "Not implemented\n"; + auto program = aoc2019::IntCodeComputer::read_intcode(input); + program[0] = 2; + + aoc2019::IntCodeComputer computer(std::move(program)); + std::deque output_buffer; + computer.connectOutput(output_buffer); + computer.run(); + + Screen screen; + + std::int64_t score = 0; + + while (!computer.isTerminated()) { + computer.run(); + auto new_score = update_screen(output_buffer, screen); + if (new_score) { + score = *new_score; + } + +#ifdef ANIMATE_DAY13 + output << "Score: " << score << std::endl; + draw_screen(screen, output); + std::this_thread::sleep_for(std::chrono::milliseconds(40)); +#endif + + auto ball_pos = find_pos(screen, Tile::BALL)->first; + auto paddle_pos = find_pos(screen, Tile::PADDLE)->first; + + if (ball_pos[0] < paddle_pos[0]) { + computer.sendInput(-1); + } else if (ball_pos[0] > paddle_pos[0]) { + computer.sendInput(1); + } else { + computer.sendInput(0); + } + } + + output << score << std::endl; }