You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
AoC2023/Day 10/main.cpp

448 lines
13 KiB

#pragma region Hidden Setup Stuff
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define OLC_IMAGE_STB
#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
#include <optional>
using namespace olc;
enum Run{
FILE1,
FILE2
};
// Override base class with your custom functionality
class AoC2023 : public olc::PixelGameEngine
{
std::vector<std::string>lines;
bool waitForRender=false;
void wait(int pauseMs=0){
waitForRender=true;
while(waitForRender);
std::this_thread::sleep_for(std::chrono::milliseconds(pauseMs));
stbi_write_png("file.png", GetDrawTarget()->width, GetDrawTarget()->height, 4, GetDrawTarget()->GetData(), GetDrawTarget()->width * 4);
}
#pragma endregion
const int DAY = 10;
Run runInput=FILE1;
enum Direction{
UP,
RIGHT,
DOWN,
LEFT,
};
struct Pipe{
bool down=false;
bool up=false;
bool left=false;
bool right=false;
char originalChar;
bool traversed=false;
bool partOfLoop=false;
};
std::vector<std::vector<Pipe>>tiles;
std::vector<vi2d>pipeEdges;
vi2d startingPipe;
int tileCounts=0;
std::optional<Pipe*>GetPipe(vi2d coords){
if(coords.x<0||coords.y<0||coords.y>=tiles.size()||coords.x>=tiles[coords.y].size())return {};
return &tiles[coords.y][coords.x];
}
void MarkAndMove(vi2d&pos,Direction dir){
tiles[pos.y][pos.x].partOfLoop=true;
tiles[pos.y][pos.x].traversed=true;
pipeEdges.push_back(pos);
switch(dir){
case UP:{
pos.y-=1;
}break;
case RIGHT:{
pos.x+=1;
}break;
case DOWN:{
pos.y+=1;
}break;
case LEFT:{
pos.x-=1;
}break;
default:throw;
}
}
void AddPipe(std::string row1,std::string row2,std::string row3,std::vector<Pipe>&upper,std::vector<Pipe>&middle,std::vector<Pipe>&lower){
auto ParseCharacter=[&](char c,std::vector<Pipe>&row){
switch(c){
case '.':{
row.push_back(Pipe{.originalChar=c});
}break;
case '-':{
row.push_back(Pipe{.left=true,.right=true,.originalChar=c});
}break;
case '|':{
row.push_back(Pipe{.down=true,.up=true,.originalChar=c});
}break;
case '7':{
row.push_back(Pipe{.down=true,.left=true,.originalChar=c});
}break;
case 'F':{
row.push_back(Pipe{.down=true,.right=true,.originalChar=c});
}break;
case 'L':{
row.push_back(Pipe{.up=true,.right=true,.originalChar=c});
}break;
case 'J':{
row.push_back(Pipe{.up=true,.left=true,.originalChar=c});
}break;
}
};
for(char&c:row1){
ParseCharacter(c,upper);
}
for(char&c:row2){
ParseCharacter(c,middle);
}
for(char&c:row3){
ParseCharacter(c,lower);
}
}
bool HitEdge(vi2d coord,Direction dir){
while(true){
switch(dir){
case DOWN:coord+={0,1};break;
case UP:coord+={0,-1};break;
case RIGHT:coord+={1,0};break;
case LEFT:coord+={-1,0};break;
}
auto pipe=GetPipe(coord);
if(pipe){
if(pipe.value()->partOfLoop){
return false; //Hit one of our edge pipes.
}
}else{
return true;
}
}
}
void FloodFill(vi2d coord){
auto pipe=GetPipe(coord);
if(pipe&&!pipe.value()->partOfLoop&&!pipe.value()->originalChar!=' '){
pipe.value()->originalChar=' ';
auto downPipe=GetPipe(coord+vi2d{0,1});
if(downPipe&&!downPipe.value()->partOfLoop&&downPipe.value()->originalChar!=' ')FloodFill(coord+vi2d{0,1});
auto upPipe=GetPipe(coord+vi2d{0,-1});
if(upPipe&&!upPipe.value()->partOfLoop&&upPipe.value()->originalChar!=' ')FloodFill(coord+vi2d{0,-1});
auto leftPipe=GetPipe(coord+vi2d{-1,0});
if(leftPipe&&!leftPipe.value()->partOfLoop&&leftPipe.value()->originalChar!=' ')FloodFill(coord+vi2d{-1,0});
auto rightPipe=GetPipe(coord+vi2d{1,0});
if(rightPipe&&!rightPipe.value()->partOfLoop&&rightPipe.value()->originalChar!=' ')FloodFill(coord+vi2d{1,0});
}
}
void doStuff2(){
while(true){ //lines is accessible as a global.
for(std::string&line:lines){
std::vector<Pipe>upperRow;
std::vector<Pipe>middleRow;
std::vector<Pipe>lowerRow;
for(int i=0;i<line.length();i++){
switch(line[i]){
case '|':{
AddPipe(
".|.",
".|.",
".|.",upperRow,middleRow,lowerRow);
}break;
case '-':{
AddPipe(
"...",
"---",
"...",upperRow,middleRow,lowerRow);
}break;
case 'L':{
AddPipe(
".L.",
".LL",
"...",upperRow,middleRow,lowerRow);
}break;
case 'J':{
AddPipe(
".J.",
"JJ.",
"...",upperRow,middleRow,lowerRow);
}break;
case '7':{
AddPipe(
"...",
"77.",
".7.",upperRow,middleRow,lowerRow);
}break;
case 'F':{
AddPipe(
"...",
".FF",
".F.",upperRow,middleRow,lowerRow);
}break;
case '.':{
for(int j=0;j<3;j++)upperRow.push_back({.originalChar=line[i]});
for(int j=0;j<3;j++)middleRow.push_back({.originalChar=line[i]});
for(int j=0;j<3;j++)lowerRow.push_back({.originalChar=line[i]});
}break;
case 'S':{
startingPipe={int(upperRow.size()),int(tiles.size())};
for(int j=0;j<3;j++)upperRow.push_back({.originalChar=line[i]});
for(int j=0;j<3;j++)middleRow.push_back({.originalChar=line[i]});
for(int j=0;j<3;j++)lowerRow.push_back({.originalChar=line[i]});
}break;
default:{
throw;
}
}
tileCounts++;
}
tiles.push_back(upperRow);
tiles.push_back(middleRow);
tiles.push_back(lowerRow);
}
std::cout<<tileCounts<<std::endl;
//tilesRemaining.push_back(startingPipe);
auto initialPipe=GetPipe(startingPipe);
auto bottomPipe=GetPipe(startingPipe+vi2d{0,3}+vi2d{1,1});
if(bottomPipe&&bottomPipe.value()->up)initialPipe.value()->down=true;
auto topPipe=GetPipe(startingPipe+vi2d{0,-3}+vi2d{1,1});
if(topPipe&&topPipe.value()->down)initialPipe.value()->up=true;
auto leftPipe=GetPipe(startingPipe+vi2d{-3,0}+vi2d{1,1});
if(leftPipe&&leftPipe.value()->right)initialPipe.value()->left=true;
auto rightPipe=GetPipe(startingPipe+vi2d{3,0}+vi2d{1,1});
if(rightPipe&&rightPipe.value()->left)initialPipe.value()->right=true;
for(int y=0;y<3;y++){
for(int x=0;x<3;x++){
vi2d coords=startingPipe+vi2d{x,y};
tiles[coords.y][coords.x].originalChar='.';
}
}
char symbol='\0';
if(initialPipe.value()->down&&initialPipe.value()->up)symbol='|';
if(initialPipe.value()->left&&initialPipe.value()->right)symbol='-';
if(initialPipe.value()->down&&initialPipe.value()->left)symbol='7';
if(initialPipe.value()->down&&initialPipe.value()->right)symbol='F';
if(initialPipe.value()->up&&initialPipe.value()->left)symbol='J';
if(initialPipe.value()->up&&initialPipe.value()->right)symbol='L';
if(symbol=='\0')throw;
if(initialPipe.value()->down){tiles[startingPipe.y+2][startingPipe.x+1].originalChar=symbol;tiles[startingPipe.y+2][startingPipe.x+1].partOfLoop=true;}
if(initialPipe.value()->right){tiles[startingPipe.y+1][startingPipe.x+2].originalChar=symbol;tiles[startingPipe.y+1][startingPipe.x+2].partOfLoop=true;}
if(initialPipe.value()->up){tiles[startingPipe.y][startingPipe.x+1].originalChar=symbol;tiles[startingPipe.y][startingPipe.x+1].partOfLoop=true;}
if(initialPipe.value()->left){tiles[startingPipe.y+1][startingPipe.x].originalChar=symbol;tiles[startingPipe.y+1][startingPipe.x].partOfLoop=true;}
tiles[startingPipe.y+1][startingPipe.x+1].originalChar=symbol;
vi2d currentPos=startingPipe+vi2d{1,1};
if(initialPipe.value()->down)MarkAndMove(currentPos,DOWN);
else if(initialPipe.value()->right)MarkAndMove(currentPos,RIGHT);
else if(initialPipe.value()->up)MarkAndMove(currentPos,UP);
else if(initialPipe.value()->left)MarkAndMove(currentPos,LEFT);
else throw;
while(true){
auto pipe=GetPipe(currentPos);
if(pipe){
int moveCount=0;
auto upPipe=GetPipe(currentPos+vi2d{0,-1});
if(upPipe&&!upPipe.value()->traversed&&upPipe.value()->originalChar!='.'){moveCount++;MarkAndMove(currentPos,UP);continue;}
auto leftPipe=GetPipe(currentPos+vi2d{-1,0});
if(leftPipe&&!leftPipe.value()->traversed&&leftPipe.value()->originalChar!='.'){moveCount++;MarkAndMove(currentPos,LEFT);continue;}
auto rightPipe=GetPipe(currentPos+vi2d{1,0});
if(rightPipe&&!rightPipe.value()->traversed&&rightPipe.value()->originalChar!='.'){moveCount++;MarkAndMove(currentPos,RIGHT);continue;}
auto downPipe=GetPipe(currentPos+vi2d{0,1});
if(downPipe&&!downPipe.value()->traversed&&downPipe.value()->originalChar!='.'){moveCount++;MarkAndMove(currentPos,DOWN);continue;}
if(moveCount>1)throw;
if(moveCount==0)break;
}else{
throw;
}
}
//Test runs in all directions of looped pipes, see if they hit outer walls.
//If they do, starting from the wall, flood fill all adjacent tiles.
//Start at any other edge, traverse in all directions and try to find a way out.
for(vi2d&coord:pipeEdges){
//Try to draw a line going in all directions and see if we hit an edge.
vi2d tempCoord=coord;
if(HitEdge(coord,DOWN)){std::cout<<"Flood Fill from "<<coord+vi2d{0,1}<<std::endl;FloodFill(coord+vi2d{0,1});break;}
if(HitEdge(coord,UP)){std::cout<<"Flood Fill from "<<coord+vi2d{0,-1}<<std::endl;FloodFill(coord+vi2d{0,-1});break;}
if(HitEdge(coord,RIGHT)){std::cout<<"Flood Fill from "<<coord+vi2d{1,0}<<std::endl;FloodFill(coord+vi2d{1,0});break;}
if(HitEdge(coord,LEFT)){std::cout<<"Flood Fill from "<<coord+vi2d{-1,0}<<std::endl;FloodFill(coord+vi2d{-1,0});break;}
}
for(int y=0;y<tiles.size()/3;y++){
for(int x=0;x<tiles[y].size()/3;x++){
vi2d coords=vi2d{x,y}*3+vi2d{1,1};
if(tiles[coords.y][coords.x].originalChar==' '||tiles[coords.y][coords.x].partOfLoop)tileCounts--;
}
}
std::cout<<tileCounts<<std::endl;
wait(0);
break;
//Wait for 0ms and render the screen (calls draw())
}
}
/*
void doStuff(){
while(true){ //lines is accessible as a global.
for(std::string&line:lines){
std::vector<Pipe>row;
for(int i=0;i<line.length();i++){
switch(line[i]){
case '|':{
row.push_back(Pipe{.down=true,.up=true,.originalChar=line[i]});
}break;
case '-':{
row.push_back(Pipe{.left=true,.right=true,.originalChar=line[i]});
}break;
case 'L':{
row.push_back(Pipe{.up=true,.right=true,.originalChar=line[i]});
}break;
case 'J':{
row.push_back(Pipe{.up=true,.left=true,.originalChar=line[i]});
}break;
case '7':{
row.push_back(Pipe{.down=true,.left=true,.originalChar=line[i]});
}break;
case 'F':{
row.push_back(Pipe{.down=true,.right=true,.originalChar=line[i]});
}break;
case 'S':{
startingPipe={int(row.size()),int(pipes.size())};
}
default:{
row.push_back({.originalChar=line[i]});
}
}
}
pipes.push_back(row);
}
int maxDistance=0;
auto AddPipe=[&](vi2d coords,int distance){
GetPipe(coords).value()->traversed=true;
GetPipe(coords).value()->distance=distance;
if(distance>maxDistance)maxDistance=distance;
pipesRemaining.push_back(coords);
};
while(pipesRemaining.size()>0){
std::optional<Pipe*>pipe=GetPipe(pipesRemaining.front());
if(pipe){
pipe.value()->traversed=true;
int distance=pipe.value()->distance;
vi2d checkCoord=pipesRemaining.front()+vi2d{0,1};
std::optional<Pipe*>downPipe=GetPipe(checkCoord);
if((pipesRemaining.front()==startingPipe||pipe.value()->down)&&downPipe&&downPipe.value()->up&&!downPipe.value()->traversed)AddPipe(checkCoord,distance+1);
checkCoord=pipesRemaining.front()+vi2d{0,-1};
std::optional<Pipe*>upPipe=GetPipe(checkCoord);
if((pipesRemaining.front()==startingPipe||pipe.value()->up)&&upPipe&&upPipe.value()->down&&!upPipe.value()->traversed)AddPipe(checkCoord,distance+1);
checkCoord=pipesRemaining.front()+vi2d{-1,0};
std::optional<Pipe*>leftPipe=GetPipe(checkCoord);
if((pipesRemaining.front()==startingPipe||pipe.value()->left)&&leftPipe&&leftPipe.value()->right&&!leftPipe.value()->traversed)AddPipe(checkCoord,distance+1);
checkCoord=pipesRemaining.front()+vi2d{1,0};
std::optional<Pipe*>rightPipe=GetPipe(checkCoord);
if((pipesRemaining.front()==startingPipe||pipe.value()->right)&&rightPipe&&rightPipe.value()->left&&!rightPipe.value()->traversed)AddPipe(checkCoord,distance+1);
//wait(10);
}else{
throw;
}
pipesRemaining.erase(pipesRemaining.begin());
}
std::cout<<maxDistance<<std::endl;
break;
//wait(0); //Wait for 0ms and render the screen (calls draw())
}
}
*/
void draw(){ //Only use Sprites! If using decals, you must reference global variables!
SetScreenSize(tiles[0].size()*8,tiles.size()*8);
Clear(BLACK);
int count=0;
for(int y=0;std::vector<Pipe>&row:tiles){
for(int x=0;Pipe&pipe:row){
vi2d coords={x,y};
DrawString(coords*8,std::string(1,pipe.originalChar),pipe.partOfLoop?BLUE:(((x/3)+(y/3))%2==0)?YELLOW:WHITE);
x++;
}
y++;
}
}
#pragma region Hidden Engine Stuff
public:
AoC2023()
{
// Name your application
std::string fileName="day"+std::to_string(DAY)+"_1.txt";
if(runInput==FILE2){fileName="day"+std::to_string(DAY)+"_2.txt";}
std::ifstream file(fileName);
while(file.good()){
std::string line;
std::getline(file,line);
lines.push_back(line);
}
sAppName = "Advent of Code 2023 - Day "+std::to_string(DAY);
}
public:
bool OnUserCreate() override
{
return true;
}
bool OnUserUpdate(float fElapsedTime) override
{
static std::thread aocSolver(&AoC2023::doStuff2,this);
if(waitForRender){
draw();
waitForRender=false;
}
return true;
}
};
int main()
{
AoC2023 game;
if (game.Construct(480,480, 2, 2))
game.Start();
return 0;
}
#pragma endregion