summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Source/Puzzle.h6
-rw-r--r--Source/PuzzleSerializer.cpp65
2 files changed, 59 insertions, 12 deletions
diff --git a/Source/Puzzle.h b/Source/Puzzle.h index 8afb9ca..b7dedd5 100644 --- a/Source/Puzzle.h +++ b/Source/Puzzle.h
@@ -62,10 +62,12 @@ struct Cell {
62 62
63struct Negation {}; 63struct Negation {};
64struct Pos { 64struct Pos {
65 Pos() = default; // Required for use in std::maps
65 Pos(int x_, int y_) : x(x_), y(y_) {} 66 Pos(int x_, int y_) : x(x_), y(y_) {}
66 Pos(const std::tuple<int, int>& xy) : x(std::get<0>(xy)), y(std::get<1>(xy)) {} 67 Pos(const std::tuple<int, int>& xy) : x(std::get<0>(xy)), y(std::get<1>(xy)) {}
67 int x; 68 bool operator==(Pos other) {return x == other.x && y == other.y;}
68 int y; 69 int x = 0;
70 int y = 0;
69}; 71};
70 72
71class Puzzle { 73class Puzzle {
diff --git a/Source/PuzzleSerializer.cpp b/Source/PuzzleSerializer.cpp index f0ddf0b..a131376 100644 --- a/Source/PuzzleSerializer.cpp +++ b/Source/PuzzleSerializer.cpp
@@ -44,7 +44,7 @@ void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
44 WIDTH_INTERVAL = (MAX - MIN) / (p.width/2); 44 WIDTH_INTERVAL = (MAX - MIN) / (p.width/2);
45 HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2); 45 HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2);
46 GAP_SIZE = min(WIDTH_INTERVAL, HEIGHT_INTERVAL) / 2; 46 GAP_SIZE = min(WIDTH_INTERVAL, HEIGHT_INTERVAL) / 2;
47 // @Improvement: This will make grid cells square... but how do I keep the puzzle centered? 47 // @Improvement: This will make grid cells square... but how do I keep the puzzle centered? Maybe save extra metadata?
48 // INTERVAL = (MAX - MIN) / (max(p.width, p.height) / 2); 48 // INTERVAL = (MAX - MIN) / (max(p.width, p.height) / 2);
49 // GAP_SIZE = INTERVAL / 2; 49 // GAP_SIZE = INTERVAL / 2;
50 50
@@ -78,7 +78,8 @@ void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) {
78} 78}
79 79
80void PuzzleSerializer::ReadIntersections(Puzzle& p) { 80void PuzzleSerializer::ReadIntersections(Puzzle& p) {
81 // @Cleanup: Change defaults? 81 // @Cleanup: Just change the defaults, instead of this?
82 // Mark every edge as a full gap
82 for (int x=0; x<p.width; x++) { 83 for (int x=0; x<p.width; x++) {
83 for (int y=0; y<p.height; y++) { 84 for (int y=0; y<p.height; y++) {
84 if (x%2 == y%2) continue; 85 if (x%2 == y%2) continue;
@@ -86,7 +87,8 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p) {
86 } 87 }
87 } 88 }
88 89
89 for (int i=0; i<_numIntersections; i++) { 90 // Iterate all connections (that are in the grid) to see which edges are connected.
91 for (int i=0; i<_connectionsA.size(); i++) {
90 int locationA = _connectionsA[i]; 92 int locationA = _connectionsA[i];
91 int locationB = _connectionsB[i]; 93 int locationB = _connectionsB[i];
92 if (locationA > locationB) std::swap(locationA, locationB); // A < B 94 if (locationA > locationB) std::swap(locationA, locationB); // A < B
@@ -121,6 +123,9 @@ void PuzzleSerializer::ReadExtras(Puzzle& p) {
121 } 123 }
122 } 124 }
123 125
126 // Maps "extra gap intersection location" -> grid location. Note that there should be two locations for each position.
127 std::unordered_map<int, Pos> gapLocations;
128
124 // Iterate the remaining intersections (endpoints, dots, gaps) 129 // Iterate the remaining intersections (endpoints, dots, gaps)
125 for (; i < _numIntersections; i++) { 130 for (; i < _numIntersections; i++) {
126 int location = FindConnection(i); 131 int location = FindConnection(i);
@@ -152,6 +157,21 @@ void PuzzleSerializer::ReadExtras(Puzzle& p) {
152 else if (y1 < y2) y++; 157 else if (y1 < y2) y++;
153 else if (y1 > y2) y--; 158 else if (y1 > y2) y--;
154 p.grid[x][y].gap = Cell::Gap::BREAK; 159 p.grid[x][y].gap = Cell::Gap::BREAK;
160 gapLocations[i] = Pos{x, y};
161 }
162 }
163
164 // Fixups for asymmetrical gaps
165 for (int i=0; i<_connectionsA.size(); i++) {
166 // Only consider connections to non-grid locations
167 int locationA = _connectionsA[i];
168 if (locationA < _numGridLocations) continue;
169 int locationB = _connectionsB[i];
170 if (locationB < _numGridLocations) continue;
171
172 Pos pos = gapLocations[locationA];
173 if (pos == gapLocations[locationB]) {
174 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
155 } 175 }
156 } 176 }
157} 177}
@@ -343,7 +363,18 @@ void PuzzleSerializer::WriteGaps(const Puzzle& p) {
343 for (int x=0; x<p.width; x++) { 363 for (int x=0; x<p.width; x++) {
344 for (int y=0; y<p.height; y++) { 364 for (int y=0; y<p.height; y++) {
345 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled. 365 if (x%2 == y%2) continue; // Cells are invalid, intersections are already handled.
346 if (p.grid[x][y].gap != Cell::Gap::BREAK) continue; 366
367 bool shouldWriteGap = false;
368 if (p.grid[x][y].gap == Cell::Gap::BREAK) {
369 shouldWriteGap = true;
370 } else if (p.symmetry != Puzzle::Symmetry::NONE) {
371 Pos sym = p.GetSymmetricalPos(x, y);
372 // Write symmetrical gaps, but also add an extra connection so they don't look like a gap.
373 if (p.grid[sym.x][sym.y].gap == Cell::Gap::BREAK) {
374 shouldWriteGap = true;
375 }
376 }
377 if (!shouldWriteGap) continue;
347 378
348 // We need to introduce a new segment which contains this dot. Break the existing segment, and add one. 379 // We need to introduce a new segment which contains this dot. Break the existing segment, and add one.
349 int connectionLocation = -1; 380 int connectionLocation = -1;
@@ -358,26 +389,38 @@ void PuzzleSerializer::WriteGaps(const Puzzle& p) {
358 } 389 }
359 if (connectionLocation == -1) continue; // @Error 390 if (connectionLocation == -1) continue; // @Error
360 391
392 int gap1Location, gap2Location;
361 auto [xPos, yPos] = xy_to_pos(p, x, y); 393 auto [xPos, yPos] = xy_to_pos(p, x, y);
362 // TODO: Use AddIntersection here?
363 // Reminder: Y goes from 0.0 (bottom) to 1.0 (top) 394 // Reminder: Y goes from 0.0 (bottom) to 1.0 (top)
364 if (x%2 == 0) { // Vertical gap 395 if (x%2 == 0) { // Vertical gap
396 gap1Location = static_cast<int>(_intersectionFlags.size());
365 _connectionsA[connectionLocation] = xy_to_loc(p, x, y-1); 397 _connectionsA[connectionLocation] = xy_to_loc(p, x, y-1);
366 _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); 398 _connectionsB[connectionLocation] = gap1Location;
367 AddIntersection(p, x, y, xPos, yPos + GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); 399 AddIntersection(p, x, y, xPos, yPos + GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN);
368 400
401 gap2Location = static_cast<int>(_intersectionFlags.size());
369 _connectionsA.push_back(xy_to_loc(p, x, y+1)); 402 _connectionsA.push_back(xy_to_loc(p, x, y+1));
370 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); 403 _connectionsB.push_back(gap2Location);
371 AddIntersection(p, x, y, xPos, yPos - GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); 404 AddIntersection(p, x, y, xPos, yPos - GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN);
372 } else if (y%2 == 0) { // Horizontal gap 405 } else if (y%2 == 0) { // Horizontal gap
406 gap1Location = static_cast<int>(_intersectionFlags.size());
373 _connectionsA[connectionLocation] = xy_to_loc(p, x-1, y); 407 _connectionsA[connectionLocation] = xy_to_loc(p, x-1, y);
374 _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); 408 _connectionsB[connectionLocation] = gap1Location;
375 AddIntersection(p, x, y, xPos - GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); 409 AddIntersection(p, x, y, xPos - GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN);
376 410
411 gap2Location = static_cast<int>(_intersectionFlags.size());
377 _connectionsA.push_back(xy_to_loc(p, x+1, y)); 412 _connectionsA.push_back(xy_to_loc(p, x+1, y));
378 _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); 413 _connectionsB.push_back(gap2Location);
379 AddIntersection(p, x, y, xPos + GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); 414 AddIntersection(p, x, y, xPos + GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN);
380 } 415 }
416 if (p.symmetry != Puzzle::Symmetry::NONE) {
417 if (p.grid[x][y].gap == Cell::Gap::NONE) {
418 // A gap was asked to be introduced strictly for interaction reasons, but it shouldn't look like a gap.
419 // Add a connection between two halves of the gap to cover it graphically.
420 _connectionsA.push_back(gap1Location);
421 _connectionsB.push_back(gap2Location);
422 }
423 }
381 } 424 }
382 } 425 }
383} 426}
@@ -446,7 +489,7 @@ void PuzzleSerializer::WriteSymmetry(const Puzzle& p, int id) {
446 location = extra_xy_to_loc(p, x, y); 489 location = extra_xy_to_loc(p, x, y);
447 Pos sym = p.GetSymmetricalPos(x, y); 490 Pos sym = p.GetSymmetricalPos(x, y);
448 symLocation = extra_xy_to_loc(p, sym.x, sym.y); 491 symLocation = extra_xy_to_loc(p, sym.x, sym.y);
449 reflectionData[location] = symLocation; 492 reflectionData[location] = symLocation; // @Assume the symmetrical endpoint will write the other pair
450 } 493 }
451 } 494 }
452 } 495 }
@@ -463,6 +506,8 @@ void PuzzleSerializer::WriteSymmetry(const Puzzle& p, int id) {
463 // Rely on symmetry to set the other pairs 506 // Rely on symmetry to set the other pairs
464 reflectionData[location] = symLocation; 507 reflectionData[location] = symLocation;
465 reflectionData[location-1] = symLocation-1; 508 reflectionData[location-1] = symLocation-1;
509 reflectionData[symLocation] = location;
510 reflectionData[symLocation-1] = location-1;
466 } 511 }
467 } 512 }
468 513