diff options
Diffstat (limited to 'Source')
| -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 | ||
