#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 using namespace olc; enum Run{ FILE1, FILE2 }; // Override base class with your custom functionality class AoC2023 : public olc::PixelGameEngine { std::vectorlines; 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>tiles; std::vectorpipeEdges; vi2d startingPipe; int tileCounts=0; std::optionalGetPipe(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&upper,std::vector&middle,std::vector&lower){ auto ParseCharacter=[&](char c,std::vector&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::vectorupperRow; std::vectormiddleRow; std::vectorlowerRow; for(int i=0;iup)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 "<row; for(int i=0;itraversed=true; GetPipe(coords).value()->distance=distance; if(distance>maxDistance)maxDistance=distance; pipesRemaining.push_back(coords); }; while(pipesRemaining.size()>0){ std::optionalpipe=GetPipe(pipesRemaining.front()); if(pipe){ pipe.value()->traversed=true; int distance=pipe.value()->distance; vi2d checkCoord=pipesRemaining.front()+vi2d{0,1}; std::optionaldownPipe=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::optionalupPipe=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::optionalleftPipe=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::optionalrightPipe=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<&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