about summary refs log tree commit diff stats
path: root/Source
diff options
context:
space:
mode:
authorjbzdarkid <jbzdarkid@gmail.com>2019-11-21 09:45:06 -0800
committerjbzdarkid <jbzdarkid@gmail.com>2019-11-21 09:45:06 -0800
commit4816728c4a5bcf8fd9992f32464a1707f94c3b13 (patch)
tree511335b92d2e16f1e057deda9a4c21d41ab43b78 /Source
parentd5bce3bba23a5ba4c296f1783ba277bcc206736c (diff)
downloadwitness-tutorializer-4816728c4a5bcf8fd9992f32464a1707f94c3b13.tar.gz
witness-tutorializer-4816728c4a5bcf8fd9992f32464a1707f94c3b13.tar.bz2
witness-tutorializer-4816728c4a5bcf8fd9992f32464a1707f94c3b13.zip
Symmetrical gaps are working, as are non-square puzzles
Diffstat (limited to 'Source')
-rw-r--r--Source/PuzzleSerializer.cpp116
-rw-r--r--Source/PuzzleSerializer.h11
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 @@
8PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {} 8PuzzleSerializer::PuzzleSerializer(const std::shared_ptr<Memory>& memory) : _memory(memory) {}
9 9
10Puzzle PuzzleSerializer::ReadPuzzle(int id) { 10Puzzle 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
102void PuzzleSerializer::ReadExtras(Puzzle& p) { 109void 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
202void PuzzleSerializer::WriteIntersections(const Puzzle& p) { 208void 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
263void PuzzleSerializer::WriteEndpoints(const Puzzle& p) { 269void 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
468std::tuple<int, int> PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { 472std::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
487int PuzzleSerializer::extra_xy_to_loc(Pos pos) const { 491int 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
495std::tuple<int, int> PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { 497std::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
535void PuzzleSerializer::AddIntersection(int x, int y, float xPos, float yPos, int flags) { 537void 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};