11#pragma once
2+ #include < algorithm>
23#include < fstream>
34#include < iostream>
45
@@ -25,19 +26,108 @@ bool areOverlapping(GameObject& guard, GameObject& b) {
2526 guard.y < b.y + b.height && guard.y + guard.height > b.y ;
2627}
2728
29+ bool isCornerPosition (GameObject& distinctPosition) {
30+ if (distinctPosition.color [0 ] == 255 && distinctPosition.color [1 ] == 0 &&
31+ distinctPosition.color [2 ] == 255 ) {
32+ return true ;
33+ }
34+ return false ;
35+ }
36+
37+ std::optional<GameObject> doLast4CornerDistinctPositionsFormRectangle (
38+ std::vector<GameObject>& distinctPositions
39+ ) {
40+ // find last 4 corner distinct positions
41+ std::vector<GameObject> corners;
42+ for (int i = distinctPositions.size () - 1 ; i >= 0 ; i--) {
43+ if (isCornerPosition (distinctPositions[i])) {
44+ corners.push_back (distinctPositions[i]);
45+ }
46+ if (corners.size () == 4 ) {
47+ break ;
48+ }
49+ }
50+
51+ if (corners.size () != 4 ) {
52+ return std::nullopt ;
53+ }
54+
55+ // Initialize with first corner
56+ GameObject topLeft = corners[0 ];
57+ GameObject topRight = corners[0 ];
58+ GameObject bottomLeft = corners[0 ];
59+ GameObject bottomRight = corners[0 ];
60+
61+ // Find the extreme corners
62+ for (const auto & corner : corners) {
63+ // Top-left has minimum x and y
64+ if (corner.x + corner.y < topLeft.x + topLeft.y ) {
65+ topLeft = corner;
66+ }
67+ // Top-right has maximum x and minimum y
68+ if (corner.x - corner.y > topRight.x - topRight.y ) {
69+ topRight = corner;
70+ }
71+ // Bottom-left has minimum x and maximum y
72+ if (corner.x - corner.y < bottomLeft.x - bottomLeft.y ) {
73+ bottomLeft = corner;
74+ }
75+ // Bottom-right has maximum x and y
76+ if (corner.x + corner.y > bottomRight.x + bottomRight.y ) {
77+ bottomRight = corner;
78+ }
79+ }
80+
81+ // Verify we have a rectangle by checking:
82+ // 1. Width equality (top and bottom)
83+ // 2. Height equality (left and right)
84+ // 3. Diagonal equality
85+ double topWidth = std::abs (topRight.x - topLeft.x );
86+ double bottomWidth = std::abs (bottomRight.x - bottomLeft.x );
87+ double leftHeight = std::abs (bottomLeft.y - topLeft.y );
88+ double rightHeight = std::abs (bottomRight.y - topRight.y );
89+
90+ // Allow for small floating-point differences
91+ const double epsilon = 0.001 ;
92+
93+ bool widthsEqual = std::abs (topWidth - bottomWidth) < epsilon;
94+ bool heightsEqual = std::abs (leftHeight - rightHeight) < epsilon;
95+
96+ if (widthsEqual && heightsEqual) {
97+ return GameObject (
98+ topWidth,
99+ leftHeight,
100+ topLeft.x ,
101+ topLeft.y ,
102+ 0 ,
103+ 0 ,
104+ {255 , 0 , 255 }
105+ );
106+ }
107+
108+ return std::nullopt ;
109+ }
110+
111+ struct Cycle {
112+ GameObject rectangle;
113+ GameObject corner;
114+ };
115+
28116class MinimalLoopStrategy : public Core ::IStrategy {
29117 public:
30118 std::vector<GameObject> obstructions;
31119 GameObject guard;
32120 std::vector<GameObject> path;
33121 std::vector<GameObject> distinctPositions;
34122 std::vector<std::string> recentObstructionCoords;
123+ GameObject distinctPathPieceNext;
124+ std::vector<Cycle> cycles;
35125
36126 int boundaryHeight;
37127 int boundaryWidth;
38128
39- int scale = 10 ;
40- int defaultVelocity = 100 ;
129+ int scale = 30 ;
130+ int defaultVelocity = 30 ;
41131
42132 double totalTime = 0 ;
43133
@@ -46,7 +136,7 @@ class MinimalLoopStrategy : public Core::IStrategy {
46136
47137 auto size = 1 * scale;
48138
49- const std::string filePath = " assets/input.txt" ;
139+ const std::string filePath = " assets/input-example-1 .txt" ;
50140 auto input = ParseInput (filePath);
51141
52142 boundaryWidth = input[0 ].size () * size;
@@ -83,6 +173,9 @@ class MinimalLoopStrategy : public Core::IStrategy {
83173 vel_y,
84174 {255 , 0 , 0 }
85175 );
176+
177+ distinctPathPieceNext =
178+ GameObject (size, size, guard.x , guard.y , 0 , 0 , {0 , 0 , 255 });
86179 }
87180 }
88181 }
@@ -109,6 +202,8 @@ class MinimalLoopStrategy : public Core::IStrategy {
109202 guard.x += guard.velX * deltaTime;
110203 guard.y += guard.velY * deltaTime;
111204
205+ bool hasChangedDirection = false ;
206+
112207 for (auto & obstruction : obstructions) {
113208 auto obstructionCoords =
114209 std::to_string (obstruction.x ) + " -" + std::to_string (obstruction.y );
@@ -149,6 +244,7 @@ class MinimalLoopStrategy : public Core::IStrategy {
149244 if (recentObstructionCoords.size () > 2 ) {
150245 recentObstructionCoords.erase (recentObstructionCoords.begin ());
151246 }
247+ hasChangedDirection = true ;
152248 }
153249 }
154250
@@ -199,14 +295,56 @@ class MinimalLoopStrategy : public Core::IStrategy {
199295 }
200296 }
201297
298+ // move distinct piece next in the direction of the guard
299+ distinctPathPieceNext.x = guard.velX > 0 ? distinctPathPiece.x + scale
300+ : guard.velX < 0 ? distinctPathPiece.x - scale
301+ : distinctPathPiece.x ;
302+ distinctPathPieceNext.y = guard.velY > 0 ? distinctPathPiece.y + scale
303+ : guard.velY < 0 ? distinctPathPiece.y - scale
304+ : distinctPathPiece.y ;
305+
202306 if (!isOverlapping) {
203307 distinctPositions.push_back (distinctPathPiece);
308+ } else {
309+ if (hasChangedDirection) {
310+ distinctPositions.back ().color = {255 , 0 , 255 };
311+ }
312+ }
313+
314+ // if distinctPathPieceNext overlapping with distinct path
315+ for (auto & distinctPiece : distinctPositions) {
316+ if (areOverlapping (distinctPiece, distinctPathPieceNext)) {
317+ distinctPiece.color = {255 , 0 , 255 };
318+
319+ auto rectangle =
320+ doLast4CornerDistinctPositionsFormRectangle (distinctPositions);
321+
322+ if (rectangle.has_value ()) {
323+ // if rectangle doesn't exist in cycles
324+ auto isRectangleInCycles = false ;
325+ for (auto & cycle : cycles) {
326+ if (cycle.rectangle .x == rectangle.value ().x &&
327+ cycle.rectangle .y == rectangle.value ().y &&
328+ cycle.rectangle .width == rectangle.value ().width &&
329+ cycle.rectangle .height == rectangle.value ().height
330+
331+ ) {
332+ isRectangleInCycles = true ;
333+ break ;
334+ }
335+ }
336+
337+ if (!isRectangleInCycles) {
338+ std::cout << " Rectangle found" << std::endl;
339+ cycles.push_back (Cycle{rectangle.value (), distinctPathPieceNext});
340+ }
341+ }
342+ }
204343 }
205344 }
206345 }
207346
208347 void OnRender (Core::Window& window, Core::Renderer& renderer) override {
209- guard.Render (renderer);
210348 for (auto & obstruction : obstructions) {
211349 obstruction.Render (renderer);
212350 }
@@ -217,6 +355,13 @@ class MinimalLoopStrategy : public Core::IStrategy {
217355 distinctPiece.Render (renderer);
218356 }
219357
358+ for (auto & cycle : cycles) {
359+ cycle.rectangle .RenderHollow (renderer);
360+ }
361+
362+ distinctPathPieceNext.Render (renderer);
363+ guard.Render (renderer);
364+
220365 SDL_Rect rect = {0 , 0 , boundaryWidth, boundaryHeight};
221366 SDL_SetRenderDrawColor (renderer.Get ().get (), 255 , 255 , 255 , 255 );
222367 SDL_RenderDrawRect (renderer.Get ().get (), &rect);
0 commit comments