Advent-of-Code/2024/6/main.cpp

278 lines
6.1 KiB
C++

#include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <unistd.h>
#include <vector>
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<Direction, bool> 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<int, int> 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<std::pair<int, int>, 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<int, int> 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<Direction>(((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<Direction, bool> 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<int,int> 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<int,int> 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<int,int> 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<int,int> 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;
}