diff options
-rw-r--r-- | Source/PuzzleSerializer.cpp | 116 | ||||
-rw-r--r-- | Source/PuzzleSerializer.h | 11 |
2 files changed, 65 insertions, 62 deletions
diff --git a/Source/PuzzleSerializer.cpp b/Source/PuzzleSerializer.cpp index e7381d8..f0ddf0b 100644 --- a/Source/PuzzleSerializer.cpp +++ b/Source/PuzzleSerializer.cpp | |||
@@ -8,12 +8,13 @@ | |||
8 | PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {} | 8 | PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {} |
9 | 9 | ||
10 | Puzzle PuzzleSerializer::ReadPuzzle(int id) { | 10 | Puzzle PuzzleSerializer::ReadPuzzle(int id) { |
11 | int width = _memory->ReadEntityData<int>(id, GRID_SIZE_X, 1)[0] - 1; | 11 | int width = _memory->ReadEntityData<int>(id, GRID_SIZE_X, 1)[0]; |
12 | int height = _memory->ReadEntityData<int>(id, GRID_SIZE_Y, 1)[0] - 1; | 12 | int height = _memory->ReadEntityData<int>(id, GRID_SIZE_Y, 1)[0]; |
13 | if (width == 0) width = height; | 13 | if (width == 0) width = height; |
14 | if (height == 0) height = width; | 14 | if (height == 0) height = width; |
15 | if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. | 15 | if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. |
16 | 16 | ||
17 | _numGridLocations = width * height; // Highest location which represents a gridded intersection | ||
17 | _numIntersections = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0]; | 18 | _numIntersections = _memory->ReadEntityData<int>(id, NUM_DOTS, 1)[0]; |
18 | _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, _numIntersections); | 19 | _intersectionFlags = _memory->ReadArray<int>(id, DOT_FLAGS, _numIntersections); |
19 | int numConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0]; | 20 | int numConnections = _memory->ReadEntityData<int>(id, NUM_CONNECTIONS, 1)[0]; |
@@ -22,7 +23,7 @@ Puzzle PuzzleSerializer::ReadPuzzle(int id) { | |||
22 | _intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, _numIntersections*2); | 23 | _intersectionLocations = _memory->ReadArray<float>(id, DOT_POSITIONS, _numIntersections*2); |
23 | 24 | ||
24 | Puzzle p; | 25 | Puzzle p; |
25 | p.NewGrid(width, height); | 26 | p.NewGrid(width - 1, height - 1); |
26 | ReadIntersections(p); | 27 | ReadIntersections(p); |
27 | ReadExtras(p); | 28 | ReadExtras(p); |
28 | ReadDecorations(p, id); | 29 | ReadDecorations(p, id); |
@@ -36,14 +37,17 @@ void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { | |||
36 | _connectionsA.clear(); | 37 | _connectionsA.clear(); |
37 | _connectionsB.clear(); | 38 | _connectionsB.clear(); |
38 | _intersectionLocations.clear(); | 39 | _intersectionLocations.clear(); |
40 | _extraLocations.clear(); | ||
39 | 41 | ||
40 | MIN = 0.1f; | 42 | MIN = 0.1f; |
41 | MAX = 0.9f; | 43 | MAX = 0.9f; |
42 | WIDTH_INTERVAL = (MAX - MIN) / (p.width/2); | 44 | WIDTH_INTERVAL = (MAX - MIN) / (p.width/2); |
43 | HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2); | 45 | HEIGHT_INTERVAL = (MAX - MIN) / (p.height/2); |
44 | HORIZ_GAP_SIZE = WIDTH_INTERVAL / 2; | 46 | GAP_SIZE = min(WIDTH_INTERVAL, HEIGHT_INTERVAL) / 2; |
45 | VERTI_GAP_SIZE = HEIGHT_INTERVAL / 2; | 47 | // @Improvement: This will make grid cells square... but how do I keep the puzzle centered? |
46 | 48 | // INTERVAL = (MAX - MIN) / (max(p.width, p.height) / 2); | |
49 | // GAP_SIZE = INTERVAL / 2; | ||
50 | |||
47 | WriteIntersections(p); | 51 | WriteIntersections(p); |
48 | WriteEndpoints(p); | 52 | WriteEndpoints(p); |
49 | WriteDots(p); | 53 | WriteDots(p); |
@@ -82,14 +86,17 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p) { | |||
82 | } | 86 | } |
83 | } | 87 | } |
84 | 88 | ||
85 | for (int j=0; j<_numIntersections; j++) { | 89 | for (int i=0; i<_numIntersections; i++) { |
86 | if (_intersectionFlags[_connectionsA[j]] & Flags::IS_ENDPOINT) break; | 90 | int locationA = _connectionsA[i]; |
87 | if (_intersectionFlags[_connectionsB[j]] & Flags::IS_ENDPOINT) break; | 91 | int locationB = _connectionsB[i]; |
88 | float x1 = _intersectionLocations[2*_connectionsA[j]]; | 92 | if (locationA > locationB) std::swap(locationA, locationB); // A < B |
89 | float y1 = _intersectionLocations[2*_connectionsA[j]+1]; | 93 | if (locationB >= _numGridLocations) continue; // Connection goes to a non-grid location |
90 | float x2 = _intersectionLocations[2*_connectionsB[j]]; | 94 | |
91 | float y2 = _intersectionLocations[2*_connectionsB[j]+1]; | 95 | float x1 = _intersectionLocations[2*locationA]; |
92 | auto [x, y] = loc_to_xy(p, _connectionsA[j]); | 96 | float y1 = _intersectionLocations[2*locationA+1]; |
97 | float x2 = _intersectionLocations[2*locationB]; | ||
98 | float y2 = _intersectionLocations[2*locationB+1]; | ||
99 | auto [x, y] = loc_to_xy(p, locationA); | ||
93 | 100 | ||
94 | if (x1 < x2) x++; | 101 | if (x1 < x2) x++; |
95 | else if (x1 > x2) x--; | 102 | else if (x1 > x2) x--; |
@@ -100,12 +107,11 @@ void PuzzleSerializer::ReadIntersections(Puzzle& p) { | |||
100 | } | 107 | } |
101 | 108 | ||
102 | void PuzzleSerializer::ReadExtras(Puzzle& p) { | 109 | void PuzzleSerializer::ReadExtras(Puzzle& p) { |
103 | // This iterates bottom-top, left-right | 110 | // This iterates left-right, bottom-top |
104 | int i = 0; | 111 | int i = 0; |
105 | for (; i < _numIntersections; i++) { | 112 | for (; i < _numGridLocations; i++) { |
106 | int flags = _intersectionFlags[i]; | 113 | int flags = _intersectionFlags[i]; |
107 | auto [x, y] = loc_to_xy(p, i); | 114 | auto [x, y] = loc_to_xy(p, i); |
108 | if (y < 0) break; // This is the expected exit point | ||
109 | if (flags & Flags::IS_STARTPOINT) { | 115 | if (flags & Flags::IS_STARTPOINT) { |
110 | p.grid[x][y].start = true; | 116 | p.grid[x][y].start = true; |
111 | } | 117 | } |
@@ -201,7 +207,7 @@ void PuzzleSerializer::ReadSymmetry(Puzzle& p, int id) { | |||
201 | 207 | ||
202 | void PuzzleSerializer::WriteIntersections(const Puzzle& p) { | 208 | void PuzzleSerializer::WriteIntersections(const Puzzle& p) { |
203 | // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator. | 209 | // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator. |
204 | // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1); | 210 | // Use _numGridIntersections computation: = (p.width / 2 + 1) * (p.height / 2 + 1); |
205 | // Grided intersections | 211 | // Grided intersections |
206 | for (int y=p.height-1; y>=0; y-=2) { | 212 | for (int y=p.height-1; y>=0; y-=2) { |
207 | for (int x=0; x<p.width; x+=2) { | 213 | for (int x=0; x<p.width; x+=2) { |
@@ -255,18 +261,12 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p) { | |||
255 | if (numConnections == 1) flags |= HAS_ONE_CONN; | 261 | if (numConnections == 1) flags |= HAS_ONE_CONN; |
256 | 262 | ||
257 | auto [xPos, yPos] = xy_to_pos(p, x, y); | 263 | auto [xPos, yPos] = xy_to_pos(p, x, y); |
258 | AddIntersection(x, y, xPos, yPos, flags); | 264 | AddIntersection(p, x, y, xPos, yPos, flags); |
259 | } | 265 | } |
260 | } | 266 | } |
261 | } | 267 | } |
262 | 268 | ||
263 | void PuzzleSerializer::WriteEndpoints(const Puzzle& p) { | 269 | void PuzzleSerializer::WriteEndpoints(const Puzzle& p) { |
264 | // int xMin, xMax, yMin, yMax; | ||
265 | // | ||
266 | // if (p.symmetry == Puzzle::Symmetry::NONE) { | ||
267 | // xMin = | ||
268 | // } | ||
269 | |||
270 | for (int x=0; x<p.width; x++) { | 270 | for (int x=0; x<p.width; x++) { |
271 | for (int y=0; y<p.height; y++) { | 271 | for (int y=0; y<p.height; y++) { |
272 | if (p.grid[x][y].end == Cell::Dir::NONE) continue; | 272 | if (p.grid[x][y].end == Cell::Dir::NONE) continue; |
@@ -288,7 +288,7 @@ void PuzzleSerializer::WriteEndpoints(const Puzzle& p) { | |||
288 | yPos -= .05f; | 288 | yPos -= .05f; |
289 | break; | 289 | break; |
290 | } | 290 | } |
291 | AddIntersection(x, y, xPos, yPos, Flags::IS_ENDPOINT); | 291 | AddIntersection(p, x, y, xPos, yPos, Flags::IS_ENDPOINT); |
292 | } | 292 | } |
293 | } | 293 | } |
294 | } | 294 | } |
@@ -334,7 +334,7 @@ void PuzzleSerializer::WriteDots(const Puzzle& p) { | |||
334 | } | 334 | } |
335 | 335 | ||
336 | auto [xPos, yPos] = xy_to_pos(p, x, y); | 336 | auto [xPos, yPos] = xy_to_pos(p, x, y); |
337 | AddIntersection(x, y, xPos, yPos, flags); | 337 | AddIntersection(p, x, y, xPos, yPos, flags); |
338 | } | 338 | } |
339 | } | 339 | } |
340 | } | 340 | } |
@@ -364,27 +364,19 @@ void PuzzleSerializer::WriteGaps(const Puzzle& p) { | |||
364 | if (x%2 == 0) { // Vertical gap | 364 | if (x%2 == 0) { // Vertical gap |
365 | _connectionsA[connectionLocation] = xy_to_loc(p, x, y-1); | 365 | _connectionsA[connectionLocation] = xy_to_loc(p, x, y-1); |
366 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); | 366 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); |
367 | _intersectionLocations.push_back(xPos); | 367 | AddIntersection(p, x, y, xPos, yPos + GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); |
368 | _intersectionLocations.push_back(yPos + VERTI_GAP_SIZE / 2); | ||
369 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); | ||
370 | 368 | ||
371 | _connectionsA.push_back(xy_to_loc(p, x, y+1)); | 369 | _connectionsA.push_back(xy_to_loc(p, x, y+1)); |
372 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | 370 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); |
373 | _intersectionLocations.push_back(xPos); | 371 | AddIntersection(p, x, y, xPos, yPos - GAP_SIZE / 2, Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); |
374 | _intersectionLocations.push_back(yPos - VERTI_GAP_SIZE / 2); | ||
375 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); | ||
376 | } else if (y%2 == 0) { // Horizontal gap | 372 | } else if (y%2 == 0) { // Horizontal gap |
377 | _connectionsA[connectionLocation] = xy_to_loc(p, x-1, y); | 373 | _connectionsA[connectionLocation] = xy_to_loc(p, x-1, y); |
378 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); | 374 | _connectionsB[connectionLocation] = static_cast<int>(_intersectionFlags.size()); |
379 | _intersectionLocations.push_back(xPos - HORIZ_GAP_SIZE / 2); | 375 | AddIntersection(p, x, y, xPos - GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); |
380 | _intersectionLocations.push_back(yPos); | ||
381 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); | ||
382 | 376 | ||
383 | _connectionsA.push_back(xy_to_loc(p, x+1, y)); | 377 | _connectionsA.push_back(xy_to_loc(p, x+1, y)); |
384 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); | 378 | _connectionsB.push_back(static_cast<int>(_intersectionFlags.size())); |
385 | _intersectionLocations.push_back(xPos + HORIZ_GAP_SIZE / 2); | 379 | AddIntersection(p, x, y, xPos + GAP_SIZE / 2, yPos, Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); |
386 | _intersectionLocations.push_back(yPos); | ||
387 | _intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); | ||
388 | } | 380 | } |
389 | } | 381 | } |
390 | } | 382 | } |
@@ -424,8 +416,9 @@ void PuzzleSerializer::WriteSequence(const Puzzle& p, int id) { | |||
424 | } | 416 | } |
425 | } | 417 | } |
426 | 418 | ||
427 | Pos endpoint = p.sequence[p.sequence.size() - 1]; | 419 | // TODO: Orphaned code? |
428 | int location = extra_xy_to_loc(endpoint); | 420 | // Pos endpoint = p.sequence[p.sequence.size() - 1]; |
421 | // int location = extra_xy_to_loc(p, endpoint.x, endpoint.y); | ||
429 | 422 | ||
430 | _memory->WriteEntityData<int>(id, SEQUENCE_LEN, {static_cast<int>(sequence.size())}); | 423 | _memory->WriteEntityData<int>(id, SEQUENCE_LEN, {static_cast<int>(sequence.size())}); |
431 | _memory->WriteNewArray<int>(id, SEQUENCE, sequence); | 424 | _memory->WriteNewArray<int>(id, SEQUENCE, sequence); |
@@ -449,20 +442,31 @@ void PuzzleSerializer::WriteSymmetry(const Puzzle& p, int id) { | |||
449 | reflectionData[location] = symLocation; | 442 | reflectionData[location] = symLocation; |
450 | reflectionData[symLocation] = location; | 443 | reflectionData[symLocation] = location; |
451 | if (p.grid[x][y].end != Cell::Dir::NONE) { | 444 | if (p.grid[x][y].end != Cell::Dir::NONE) { |
452 | location = extra_xy_to_loc(Pos{x, y}); | 445 | // Rely on symmetry to set the other pair |
453 | symLocation = extra_xy_to_loc(p.GetSymmetricalPos(x, y)); | 446 | location = extra_xy_to_loc(p, x, y); |
447 | Pos sym = p.GetSymmetricalPos(x, y); | ||
448 | symLocation = extra_xy_to_loc(p, sym.x, sym.y); | ||
454 | reflectionData[location] = symLocation; | 449 | reflectionData[location] = symLocation; |
455 | reflectionData[symLocation] = location; | ||
456 | } | 450 | } |
457 | } | 451 | } |
458 | } | 452 | } |
459 | 453 | ||
460 | auto [x, y] = loc_to_xy(p, 0); | 454 | for (int x=0; x<p.width; x++) { |
461 | Pos sym = p.GetSymmetricalPos(x, y); | 455 | for (int y=0; y<p.height; y++) { |
462 | int i = xy_to_loc(p, sym.x, sym.y); | 456 | if (x%2 == y%2) continue; |
457 | if (p.grid[x][y].gap != Cell::Gap::BREAK) continue; | ||
463 | 458 | ||
464 | int k = 1; | 459 | Pos sym = p.GetSymmetricalPos(x, y); |
465 | // TODO: Done? No, still need gaps (if they're reflected). No idea how to do this, though. Maybe I can safely assume that they're at consecutive locations? | 460 | int location = extra_xy_to_loc(p, x, y); |
461 | int symLocation = extra_xy_to_loc(p, sym.x, sym.y); | ||
462 | // Each gap results in two intersections, @Assume they're written consecutively | ||
463 | // Rely on symmetry to set the other pairs | ||
464 | reflectionData[location] = symLocation; | ||
465 | reflectionData[location-1] = symLocation-1; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | _memory->WriteArray<int>(id, REFLECTION_DATA, reflectionData); | ||
466 | } | 470 | } |
467 | 471 | ||
468 | std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { | 472 | std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { |
@@ -484,12 +488,10 @@ int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) const { | |||
484 | return rowsFromBottom * width2 + x/2; | 488 | return rowsFromBottom * width2 + x/2; |
485 | } | 489 | } |
486 | 490 | ||
487 | int PuzzleSerializer::extra_xy_to_loc(Pos pos) const { | 491 | int PuzzleSerializer::extra_xy_to_loc(const Puzzle& p, int x, int y) const { |
488 | for (auto [x, y, location] : _extraLocations) { | 492 | auto search = _extraLocations.find(x * p.height + y); |
489 | if (pos.x == x && pos.y == y) return location; | 493 | if (search == _extraLocations.end()) return -1; // @Error |
490 | } | 494 | return search->second; |
491 | |||
492 | return -1; // @Error | ||
493 | } | 495 | } |
494 | 496 | ||
495 | std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { | 497 | std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { |
@@ -532,8 +534,8 @@ int PuzzleSerializer::FindConnection(int location) const { | |||
532 | return -1; | 534 | return -1; |
533 | } | 535 | } |
534 | 536 | ||
535 | void PuzzleSerializer::AddIntersection(int x, int y, float xPos, float yPos, int flags) { | 537 | void PuzzleSerializer::AddIntersection(const Puzzle& p, int x, int y, float xPos, float yPos, int flags) { |
536 | _extraLocations.emplace_back(x, y, static_cast<int>(_intersectionFlags.size())); | 538 | _extraLocations[x * p.height + y] = static_cast<int>(_intersectionFlags.size()); |
537 | _intersectionLocations.push_back(xPos); | 539 | _intersectionLocations.push_back(xPos); |
538 | _intersectionLocations.push_back(yPos); | 540 | _intersectionLocations.push_back(yPos); |
539 | _intersectionFlags.push_back(flags); | 541 | _intersectionFlags.push_back(flags); |
diff --git a/Source/PuzzleSerializer.h b/Source/PuzzleSerializer.h index 3c8f480..4d254d8 100644 --- a/Source/PuzzleSerializer.h +++ b/Source/PuzzleSerializer.h | |||
@@ -43,7 +43,7 @@ private: | |||
43 | 43 | ||
44 | std::tuple<int, int> loc_to_xy(const Puzzle& p, int location) const; | 44 | std::tuple<int, int> loc_to_xy(const Puzzle& p, int location) const; |
45 | int xy_to_loc(const Puzzle& p, int x, int y) const; | 45 | int xy_to_loc(const Puzzle& p, int x, int y) const; |
46 | int extra_xy_to_loc(Pos pos) const; | 46 | int extra_xy_to_loc(const Puzzle& p, int x, int y) const; |
47 | // Decoration location | 47 | // Decoration location |
48 | std::tuple<int, int> dloc_to_xy(const Puzzle& p, int location) const; | 48 | std::tuple<int, int> dloc_to_xy(const Puzzle& p, int location) const; |
49 | int xy_to_dloc(const Puzzle& p, int x, int y) const; | 49 | int xy_to_dloc(const Puzzle& p, int x, int y) const; |
@@ -52,17 +52,18 @@ private: | |||
52 | Cell::Dot FlagsToDot(int flags) const; | 52 | Cell::Dot FlagsToDot(int flags) const; |
53 | // Iterate connection lists for another location which is connected to us; return that other location. | 53 | // Iterate connection lists for another location which is connected to us; return that other location. |
54 | int FindConnection(int location) const; | 54 | int FindConnection(int location) const; |
55 | void AddIntersection(int x, int y, float xPos, float yPos, int flags); | 55 | void AddIntersection(const Puzzle& p, int x, int y, float xPos, float yPos, int flags); |
56 | 56 | ||
57 | std::shared_ptr<Memory> _memory; | 57 | std::shared_ptr<Memory> _memory; |
58 | 58 | ||
59 | std::vector<float> _intersectionLocations; | 59 | int _numGridLocations; |
60 | int _numIntersections; | 60 | int _numIntersections; |
61 | std::vector<int> _intersectionFlags; | 61 | std::vector<int> _intersectionFlags; |
62 | std::vector<int> _connectionsA; | 62 | std::vector<int> _connectionsA; |
63 | std::vector<int> _connectionsB; | 63 | std::vector<int> _connectionsB; |
64 | std::vector<float> _intersectionLocations; | ||
64 | // Locations of non-grid points, i.e. dots, gaps, and endpoints | 65 | // Locations of non-grid points, i.e. dots, gaps, and endpoints |
65 | std::vector<std::tuple<int, int, int>> _extraLocations; | 66 | std::unordered_map<int, int> _extraLocations; |
66 | 67 | ||
67 | float MIN, MAX, WIDTH_INTERVAL, HEIGHT_INTERVAL, HORIZ_GAP_SIZE, VERTI_GAP_SIZE; | 68 | float MIN, MAX, WIDTH_INTERVAL, HEIGHT_INTERVAL, INTERVAL, GAP_SIZE; |
68 | }; | 69 | }; |