#include #include #include #include #include #include #include #include #include enum Direction { UP = 0, RIGHT, DOWN, LEFT }; struct Position { // NOTE(dev): Could do enum empty/reached/blocked, but I'm not interested. bool blocked = false; std::map reached; bool Reached() const { return reached.size(); } char ReachedChar() const { bool up_or_down = false; bool left_or_right = false; if(reached.contains(Direction::UP) || reached.contains(Direction::DOWN)) up_or_down = true; if(reached.contains(Direction::LEFT) || reached.contains(Direction::RIGHT)) left_or_right = true; if(up_or_down != left_or_right) { if(up_or_down) return '|'; else return '-'; } else if(up_or_down) return '+'; else return ' '; }; }; struct Player { std::pair location; Direction direction = Direction::UP; }; int main() { const std::string filename = "data_test.txt"; std::ifstream ifs(filename); if(!ifs.is_open()) { std::cerr << "Missing " << filename << "." << std::endl; return -1; } unsigned long total = 0; unsigned long total_pt2 = 0; std::map, Position> level; Player player; int max_x = 0; int max_y = 0; bool first_row = true; int y = 0; auto DebugPrint = [&]() { std::cout << std::endl; for(auto y = 0; y < max_y; ++y) { for(auto x = 0; x < max_x; ++x) { if(player.location.first == x && player.location.second == y) { switch(player.direction) { case Direction::UP: std::cout << "^"; break; case Direction::RIGHT: std::cout << ">"; break; case Direction::DOWN: std::cout << "v"; break; case Direction::LEFT: std::cout << "<"; break; }; } else { const auto &pos = level[{x, y}]; if(pos.Reached()) std::cout << pos.ReachedChar(); else if(pos.blocked) std::cout << "#"; else std::cout << "."; } } std::cout << std::endl; } std::cout << std::endl; std::cout << "PT2 Total: " << total_pt2 << std::endl; }; // Parse Input for(std::string line; std::getline(ifs, line); ) { if(line == "") continue; int x = 0; for(const char &value : line) { Position pos; if(value == '#') pos.blocked = true; if(value == '^') // always up? { pos.reached[Direction::UP] = true; player.location = {x, y}; } level[{x, y}] = pos; ++x; } if(first_row) { max_x = x; first_row = false; } ++y; } max_y = y; // DEBUG: DebugPrint(); std::cout << std::endl; while(true) { std::pair next_location = player.location; switch(player.direction) { case Direction::UP: --next_location.second; break; case Direction::RIGHT: ++next_location.first; break; case Direction::DOWN: ++next_location.second; break; case Direction::LEFT: --next_location.first; break; }; if(!level.contains(next_location)) { player.location = next_location; break; } if(level[next_location].blocked) { // NOTE(dev): Also check if the next potential position is the last space before a turn, // as then the up/down or left/right flag wouldn't be set, but the // left/right or up/down flag would be. level[player.location].reached[player.direction] = true; player.direction = static_cast(((int(player.direction)) + 1) % 4); level[player.location].reached[player.direction] = true; } else { // TODO(dev): Need to handle the scenario where you've yet to explore a section but it // would result in a loop. // e.g.: Placing a stopping point at the O // // # # // +->O # // #| # // S # # // # # // # # // // IDEA: Follow turns when exploring a direction like that. Snake your way until // you fall off the map or you line up with a square you've been in before std::map could_loop; // Part 2: If you could loop in this next position, COUNT IT if(player.direction == Direction::DOWN) { // search left of new position to see if you've been in this horizontal bit: for(int x = next_location.first; x >= 0; --x) { std::pair potential_position = { x, next_location.second }; if(level[potential_position].reached.contains(Direction::LEFT)) { could_loop[Direction::LEFT] = true; break; } } } else if(player.direction == Direction::UP) { // search right of new position to see if you've been in this horizontal bit: for(int x = next_location.first; x < max_x; ++x) { std::pair potential_position = { x, next_location.second }; if(level[potential_position].reached.contains(Direction::RIGHT)) { could_loop[Direction::RIGHT] = true; break; } } } else if(player.direction == Direction::LEFT) { // search left of new position to see if you've been in this horizontal bit: for(int y = next_location.second; y >= 0; --y) { std::pair potential_position = { next_location.first, y }; if(level[potential_position].reached.contains(Direction::UP)) { could_loop[Direction::UP] = true; break; } } } else { // search left of new position to see if you've been in this horizontal bit: for(int y = next_location.second; y < max_y; ++y) { std::pair potential_position = { next_location.first, y }; if(level[potential_position].reached.contains(Direction::DOWN)) { could_loop[Direction::DOWN] = true; break; } } } total_pt2 += could_loop.size(); level[next_location].reached[player.direction] = true; player.location = next_location; DebugPrint(); usleep(500000); } } DebugPrint(); for(auto y = 0; y < max_y; ++y) for(auto x = 0; x < max_x; ++x) if(level[{x,y}].Reached()) ++total; std::cout << std::endl; std::cout << " Total: " << total << std::endl; std::cout << "PT2 Total: " << total_pt2 << std::endl; return 0; }