diff options
-rw-r--r-- | Source/Puzzle.h | 6 | ||||
-rw-r--r-- | Source/PuzzleSerializer.cpp | 65 |
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 | ||
63 | struct Negation {}; | 63 | struct Negation {}; |
64 | struct Pos { | 64 | struct 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 | ||
71 | class Puzzle { | 73 | class 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 | ||
80 | void PuzzleSerializer::ReadIntersections(Puzzle& p) { | 80 | void 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 | ||