about summary refs log tree commit diff stats
path: root/Source
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2021-08-21 17:12:29 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2021-08-21 17:12:29 -0400
commitc60df0e75e63f488d94fd744ad70df8124dc7724 (patch)
tree92940cd8c0da9102c7f1f9ef37ed4d4b5d341985 /Source
parent02327bf40bb9f6ef8d5e17fa982da70a3fe93eb4 (diff)
downloadwitness-tutorializer-c60df0e75e63f488d94fd744ad70df8124dc7724.tar.gz
witness-tutorializer-c60df0e75e63f488d94fd744ad70df8124dc7724.tar.bz2
witness-tutorializer-c60df0e75e63f488d94fd744ad70df8124dc7724.zip
Souped up the UI
Diffstat (limited to 'Source')
-rw-r--r--Source/Randomizer2.cpp644
-rw-r--r--Source/Randomizer2.h19
-rw-r--r--Source/Randomizer2Core.cpp203
-rw-r--r--Source/Randomizer2Core.h17
-rw-r--r--Source/Source.vcxproj4
5 files changed, 0 insertions, 887 deletions
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp deleted file mode 100644 index 421ce69..0000000 --- a/Source/Randomizer2.cpp +++ /dev/null
@@ -1,644 +0,0 @@
1#include "pch.h"
2#include "Randomizer2.h"
3#include "PuzzleSerializer.h"
4#include "Randomizer2Core.h"
5#include "Random.h"
6#include "Solver.h"
7#include "Windows.h"
8
9Randomizer2::Randomizer2(const PuzzleSerializer& serializer) : _serializer(serializer) {
10}
11
12void Randomizer2::Randomize() {
13 // RandomizeTutorial();
14 // RandomizeGlassFactory();
15 RandomizeSymmetryIsland();
16 // RandomizeKeep();
17}
18
19void Randomizer2::RandomizeTutorial() {
20 { // Far center
21 Puzzle p;
22 p.NewGrid(4, 4);
23 p.grid[0][8].start = true;
24 p.grid[8][0].end = Cell::Dir::UP;
25
26 for (Pos pos : Randomizer2Core::CutEdges(p, 14)) {
27 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
28 }
29 _serializer.WritePuzzle(p, 0x293);
30 }
31
32 { // Center left
33 Puzzle p;
34 p.NewGrid(6, 6);
35
36 int x = Random::RandInt(0, (p.width-1)/2)*2;
37 int y = Random::RandInt(0, (p.height-1)/2)*2;
38 int rng = Random::RandInt(1, 4);
39 if (rng == 1) p.grid[x][0].end = Cell::Dir::UP;
40 else if (rng == 2) p.grid[x][p.height-1].end = Cell::Dir::DOWN;
41 else if (rng == 3) p.grid[0][y].end = Cell::Dir::LEFT;
42 else if (rng == 4) p.grid[p.width-1][y].end = Cell::Dir::RIGHT;
43
44 // [4/6/8][4/6/8]
45 p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true;
46
47 for (Pos pos : Randomizer2Core::CutEdges(p, 35)) {
48 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
49 }
50
51 _serializer.WritePuzzle(p, 0x295);
52 }
53
54 { // Far left
55 Puzzle p;
56 p.NewGrid(10, 10);
57
58 p.grid[0][20].start = true;
59 p.grid[20][0].end = Cell::Dir::RIGHT;
60
61 for (Pos pos : Randomizer2Core::CutEdges(p, 96)) {
62 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
63 }
64 _serializer.WritePuzzle(p, 0x2C2);
65 }
66
67 { // Back left
68 Puzzle p;
69 p.NewGrid(6, 6);
70
71 p.grid[0][12].start = true;
72 p.grid[12][0].end = Cell::Dir::RIGHT;
73 p.grid[12][12].end = Cell::Dir::RIGHT;
74
75 for (Pos pos : Randomizer2Core::CutEdges(p, 27)) {
76 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
77 }
78 _serializer.WritePuzzle(p, 0xA3B5);
79 }
80
81 { // Back right
82 Puzzle p;
83 p.NewGrid(6, 6);
84
85 p.grid[0][12].start = true;
86 p.grid[12][12].start = true;
87 p.grid[6][0].end = Cell::Dir::UP;
88
89 // @Cleanup
90 std::vector<Pos> cuts;
91 bool toTheRight;
92 // Start by generating a cut line, to ensure one of the two startpoints is inaccessible
93 int x, y;
94 switch (Random::RandInt(1, 4)) {
95 case 1:
96 x = 1; y = 1;
97 toTheRight = true;
98 cuts.emplace_back(0, 1);
99 break;
100 case 2:
101 x = 1; y = 1;
102 toTheRight = true;
103 cuts.emplace_back(1, 0);
104 break;
105 case 3:
106 x = 11; y = 1;
107 toTheRight = false;
108 cuts.emplace_back(12, 1);
109 break;
110 case 4:
111 x = 11; y = 1;
112 toTheRight = false;
113 cuts.emplace_back(11, 0);
114 break;
115 }
116 while (y < p.height) { // The final cut will push y below the bottom of the puzzle, which means we're done.
117 switch (Random::RandInt(1, 4)) {
118 case 1: // Go right
119 if (x < p.width-2) {
120 cuts.emplace_back(x+1, y);
121 x += 2;
122 }
123 break;
124 case 2: // Go left
125 if (x > 1) {
126 cuts.emplace_back(x-1, y);
127 x -= 2;
128 }
129 break;
130 case 3:
131 case 4: // Go down (biased x2)
132 cuts.emplace_back(x, y+1);
133 y += 2;
134 break;
135 }
136 }
137
138 for (Pos pos : cuts) {
139 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
140 }
141
142 for (Pos pos : Randomizer2Core::CutEdges(p, 30 - cuts.size())) {
143 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
144 }
145 _serializer.WritePuzzle(p, 0xA3B2);
146 }
147}
148
149void Randomizer2::RandomizeGlassFactory() {
150 { // Back wall 1
151 Puzzle p;
152 p.NewGrid(3, 3);
153 p.symmetry = Puzzle::Symmetry::X;
154 p.grid[0][6].start = true;
155 p.grid[6][6].start = true;
156 p.grid[2][0].end = Cell::Dir::UP;
157 p.grid[4][0].end = Cell::Dir::UP;
158
159 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 2);
160 for (Pos pos : cutEdges) {
161 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
162 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
163 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
164 }
165 _serializer.WritePuzzle(p, 0x86);
166 }
167 { // Back wall 2
168 Puzzle p;
169 p.NewGrid(4, 4);
170 p.symmetry = Puzzle::Symmetry::X;
171 p.grid[0][8].start = true;
172 p.grid[8][8].start = true;
173 p.grid[2][0].end = Cell::Dir::UP;
174 p.grid[6][0].end = Cell::Dir::UP;
175 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 4);
176 for (int i=0; i<cutEdges.size(); i++) {
177 Pos pos = cutEdges[i];
178 if (i%2 == 0) {
179 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
180 } else {
181 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
182 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
183 }
184 }
185
186 _serializer.WritePuzzle(p, 0x87);
187 }
188 { // Back wall 3
189 Puzzle p;
190 p.NewGrid(5, 6);
191 p.symmetry = Puzzle::Symmetry::X;
192 p.grid[2][10].start = true;
193 p.grid[8][10].start = true;
194 p.grid[4][0].end = Cell::Dir::UP;
195 p.grid[6][0].end = Cell::Dir::UP;
196 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 10);
197 for (int i=0; i<cutEdges.size(); i++) {
198 Pos pos = cutEdges[i];
199 if (i%2 == 0) {
200 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
201 } else {
202 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
203 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
204 }
205 }
206
207 _serializer.WritePuzzle(p, 0x59);
208 }
209 { // Back wall 4
210 Puzzle p;
211 p.NewGrid(5, 8);
212 p.symmetry = Puzzle::Symmetry::X;
213 p.grid[2][16].start = true;
214 p.grid[8][16].start = true;
215 p.grid[4][0].end = Cell::Dir::UP;
216 p.grid[6][0].end = Cell::Dir::UP;
217 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15);
218 for (int i=0; i<cutEdges.size(); i++) {
219 Pos pos = cutEdges[i];
220 if (i%2 == 0) {
221 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
222 } else {
223 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
224 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
225 }
226 }
227
228 _serializer.WritePuzzle(p, 0x62);
229 }
230 // TODO: Positioning is off, slightly -- which means you can start from the bottom left, if you peek around.
231 { // Back wall 5
232 Puzzle p;
233 p.NewGrid(11, 8);
234 p.symmetry = Puzzle::Symmetry::X;
235 p.grid[0][16].start = true;
236 p.grid[10][16].start = true;
237 p.grid[12][16].start = true;
238 p.grid[22][16].start = true;
239 p.grid[2][0].end = Cell::Dir::UP;
240 p.grid[8][0].end = Cell::Dir::UP;
241 p.grid[14][0].end = Cell::Dir::UP;
242 p.grid[20][0].end = Cell::Dir::UP;
243
244 Puzzle q;
245 q.NewGrid(5, 8);
246 q.symmetry = Puzzle::Symmetry::X;
247
248 for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(q, 16)) {
249 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
250 }
251 for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(q, 16)) {
252 p.grid[pos.x + 12][pos.y].gap = Cell::Gap::BREAK;
253 }
254
255 for (int y=0; y<p.height; y+=2) {
256 p.grid[5][y].gap = Cell::Gap::BREAK;
257 }
258
259 _serializer.WritePuzzle(p, 0x5C);
260 }
261
262 { // Rotational 1
263 Puzzle p;
264 p.NewGrid(3, 3);
265 p.symmetry = Puzzle::Symmetry::XY;
266 p.grid[6][0].start = true;
267 p.grid[0][6].start = true;
268 p.grid[4][0].end = Cell::Dir::UP;
269 p.grid[2][6].end = Cell::Dir::DOWN;
270
271 p.grid[5][0].gap = Cell::Gap::BREAK;
272 p.grid[1][6].gap = Cell::Gap::BREAK;
273
274 for (Pos pos : Randomizer2Core::CutSymmetricalEdgePairs(p, 1)) {
275 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
276 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
277 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
278 }
279 _serializer.WritePuzzle(p, 0x8D);
280 }
281 { // Rotational 2
282 Puzzle p;
283 p.NewGrid(3, 3);
284 p.symmetry = Puzzle::Symmetry::XY;
285 p.grid[6][0].start = true;
286 p.grid[0][6].start = true;
287 p.grid[4][0].end = Cell::Dir::UP;
288 p.grid[2][6].end = Cell::Dir::DOWN;
289
290 p.grid[5][0].gap = Cell::Gap::BREAK;
291 p.grid[1][6].gap = Cell::Gap::BREAK;
292
293 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 3);
294 for (int i=0; i<cutEdges.size(); i++) {
295 Pos pos = cutEdges[i];
296 if (i%2 == 0) {
297 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
298 } else {
299 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
300 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
301 }
302 }
303
304 p.grid[1][6].gap = Cell::Gap::NONE;
305
306 _serializer.WritePuzzle(p, 0x81);
307 }
308 { // Rotational 3
309 Puzzle p;
310 p.NewGrid(4, 4);
311 p.symmetry = Puzzle::Symmetry::XY;
312 p.grid[8][0].start = true;
313 p.grid[0][8].start = true;
314 p.grid[0][0].end = Cell::Dir::LEFT;
315 p.grid[8][8].end = Cell::Dir::RIGHT;
316
317 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 7);
318 for (int i=0; i<cutEdges.size(); i++) {
319 Pos pos = cutEdges[i];
320 if (i%2 == 0) {
321 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
322 } else {
323 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
324 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
325 }
326 }
327
328 _serializer.WritePuzzle(p, 0x83);
329 }
330 { // Melting
331 Puzzle p;
332 p.NewGrid(6, 6);
333 p.symmetry = Puzzle::Symmetry::XY;
334 p.grid[12][0].start = true;
335 p.grid[0][12].start = true;
336 p.grid[0][0].end = Cell::Dir::LEFT;
337 p.grid[12][12].end = Cell::Dir::RIGHT;
338 Puzzle q = p;
339
340 std::vector<Pos> cutEdges = Randomizer2Core::CutSymmetricalEdgePairs(p, 15);
341 for (int i=0; i<cutEdges.size(); i++) {
342 Pos pos = cutEdges[i];
343 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
344
345 if (i%2 == 0) {
346 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
347 } else {
348 p.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
349 }
350
351 if (pos.x < sym.x) {
352 q.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
353 } else {
354 q.grid[sym.x][sym.y].gap = Cell::Gap::BREAK;
355 }
356 }
357
358 _serializer.WritePuzzle(p, 0x84); // Melting 1
359 _serializer.WritePuzzle(q, 0x82); // Melting 2
360 _serializer.WritePuzzle(q, 0x343A); // Melting 3
361 }
362}
363
364void Randomizer2::RandomizeSymmetryIsland() {
365 { // Entry door
366 Puzzle p;
367 p.NewGrid(4, 3);
368 p.grid[0][6].start = true;
369 p.grid[8][0].end = Cell::Dir::RIGHT;
370 p.grid[4][3].gap = Cell::Gap::FULL;
371
372 std::vector<Pos> corners;
373 std::vector<Pos> cells;
374 std::vector<Pos> edges;
375 for (int x=0; x<p.width; x++) {
376 for (int y=0; y<p.height; y++) {
377 if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y});
378 else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y});
379 else edges.emplace_back(Pos{x, y});
380 }
381 }
382
383 for (int j=0;; j++) {
384 std::vector<Pos> dots = Random::SelectFromSet(edges, 4);
385 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK;
386
387 auto solutions = Solver::Solve(p);
388 if (solutions.size() > 0 && solutions.size() < 10) break;
389
390 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE;
391 }
392
393 _serializer.WritePuzzle(p, 0xB0);
394 }
395
396 { // Dots 1
397 Puzzle p;
398 p.NewGrid(3, 3);
399 p.symmetry = Puzzle::Symmetry::Y;
400 p.grid[0][2].start = true;
401 p.grid[0][4].start = true;
402 p.grid[6][2].end = Cell::Dir::RIGHT;
403 p.grid[6][4].end = Cell::Dir::RIGHT;
404
405 std::vector<Pos> corners;
406 std::vector<Pos> cells;
407 std::vector<Pos> edges;
408 for (int x=0; x<p.width; x++) {
409 for (int y=0; y<p.height/2; y++) {
410 if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y});
411 else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y});
412 else edges.emplace_back(Pos{x, y});
413 }
414 }
415 edges.insert(edges.end(), corners.begin(), corners.end());
416
417 std::vector<Pos> dots;
418 for (int j=0;; j++) {
419 dots = Random::SelectFromSet(edges, 3);
420 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK;
421
422 auto solutions = Solver::Solve(p);
423 if (solutions.size() == 2) break;
424
425 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE;
426 }
427
428 for (Pos pos : dots) {
429 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
430 p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK;
431 }
432
433 _serializer.WritePuzzle(p, 0x22);
434 }
435 { // Dots 2
436 Puzzle p;
437 p.NewGrid(3, 3);
438 p.symmetry = Puzzle::Symmetry::Y;
439 p.grid[0][2].start = true;
440 p.grid[0][4].start = true;
441 p.grid[6][2].end = Cell::Dir::RIGHT;
442 p.grid[6][4].end = Cell::Dir::RIGHT;
443
444 std::vector<Pos> corners;
445 std::vector<Pos> cells;
446 std::vector<Pos> edges;
447 for (int x=0; x<p.width; x++) {
448 for (int y=0; y<p.height/2; y++) {
449 if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y});
450 else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y});
451 else edges.emplace_back(Pos{x, y});
452 }
453 }
454 edges.insert(edges.end(), corners.begin(), corners.end());
455
456 std::vector<Pos> dots;
457 for (int j=0;; j++) {
458 dots = Random::SelectFromSet(edges, 3);
459 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::BLACK;
460
461 auto solutions = Solver::Solve(p);
462 if (solutions.size() == 2) break;
463
464 for (Pos pos : dots) p.grid[pos.x][pos.y].dot = Cell::Dot::NONE;
465 }
466
467 Pos pos = dots[1];
468 Pos sym = p.GetSymmetricalPos(pos.x, pos.y);
469 p.grid[pos.x][pos.y].dot = Cell::Dot::NONE;
470 p.grid[sym.x][sym.y].dot = Cell::Dot::BLACK;
471
472 _serializer.WritePuzzle(p, 0x23);
473 }
474}
475
476void Randomizer2::RandomizeKeep() {
477 { // Hedges 1
478 Puzzle p;
479 p.NewGrid(4, 4);
480
481 p.grid[2][1].gap = Cell::Gap::FULL;
482 p.grid[4][1].gap = Cell::Gap::FULL;
483 p.grid[6][1].gap = Cell::Gap::FULL;
484 p.grid[3][2].gap = Cell::Gap::FULL;
485 p.grid[5][2].gap = Cell::Gap::FULL;
486 p.grid[8][3].gap = Cell::Gap::FULL;
487 p.grid[2][5].gap = Cell::Gap::FULL;
488 p.grid[6][5].gap = Cell::Gap::FULL;
489 p.grid[7][6].gap = Cell::Gap::FULL;
490 p.grid[2][7].gap = Cell::Gap::FULL;
491 p.grid[4][7].gap = Cell::Gap::FULL;
492
493 p.grid[4][8].start = true;
494 p.grid[6][0].end = Cell::Dir::UP;
495
496 std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 5);
497 Puzzle copy = p;
498 std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496};
499 for (int i=0; i<cutEdges.size(); i++) {
500 Pos pos = cutEdges[i];
501 copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
502 SetGate(gates[i], pos.x, pos.y);
503 }
504 auto solution = GetUniqueSolution(copy);
505 p.sequence = solution.sequence;
506 _serializer.WritePuzzle(p, 0x139);
507 }
508
509 { // Hedges 2
510 Puzzle p;
511 p.NewGrid(4, 4);
512
513 p.grid[2][1].gap = Cell::Gap::FULL;
514 p.grid[1][2].gap = Cell::Gap::FULL;
515 p.grid[5][2].gap = Cell::Gap::FULL;
516 p.grid[7][4].gap = Cell::Gap::FULL;
517 p.grid[4][5].gap = Cell::Gap::FULL;
518 p.grid[6][5].gap = Cell::Gap::FULL;
519 p.grid[1][6].gap = Cell::Gap::FULL;
520 p.grid[2][7].gap = Cell::Gap::FULL;
521 p.grid[5][8].gap = Cell::Gap::FULL;
522
523 p.grid[0][8].start = true;
524 p.grid[8][0].end = Cell::Dir::RIGHT;
525
526 std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 7);
527 for (Pos pos : cutEdges) {
528 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
529 }
530 auto solution = GetUniqueSolution(p);
531
532 Puzzle q;
533 q.NewGrid(4, 4);
534 q.grid[0][8].start = true;
535 q.grid[8][0].end = Cell::Dir::RIGHT;
536 q.sequence = solution.sequence;
537 for (Pos pos : cutEdges) {
538 q.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
539 }
540 // Cut to 6 of 9 additional edges
541 for (Pos pos : Randomizer2Core::CutInsideEdges(q, 6)) {
542 q.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
543 }
544 _serializer.WritePuzzle(q, 0x19DC);
545 }
546
547 { // Hedges 3 [WIP]
548 Puzzle p;
549 p.NewGrid(4, 4);
550
551 p.grid[2][1].gap = Cell::Gap::FULL;
552 p.grid[5][2].gap = Cell::Gap::FULL;
553 p.grid[7][2].gap = Cell::Gap::FULL;
554 p.grid[4][3].gap = Cell::Gap::FULL;
555 p.grid[1][4].gap = Cell::Gap::FULL;
556 p.grid[6][5].gap = Cell::Gap::FULL;
557 p.grid[1][6].gap = Cell::Gap::FULL;
558 p.grid[3][6].gap = Cell::Gap::FULL;
559 p.grid[6][7].gap = Cell::Gap::FULL;
560
561 p.grid[0][8].start = true;
562 p.grid[8][2].end = Cell::Dir::RIGHT;
563
564 std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 7);
565 for (Pos pos : cutEdges) {
566 p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
567 }
568
569 std::vector<int> pebbleMarkers = {0x034a9, 0x034b1, 0x034be, 0x034c4};
570
571 // _serializer.WritePuzzle(p, 0x19E7);
572 }
573
574 { // Hedges 4
575 Puzzle p;
576 p.NewGrid(4, 4);
577
578 p.grid[3][0].gap = Cell::Gap::FULL;
579 p.grid[4][1].gap = Cell::Gap::FULL;
580 p.grid[8][1].gap = Cell::Gap::FULL;
581 p.grid[1][2].gap = Cell::Gap::FULL;
582 p.grid[4][3].gap = Cell::Gap::FULL;
583 p.grid[8][3].gap = Cell::Gap::FULL;
584 p.grid[1][4].gap = Cell::Gap::FULL;
585 p.grid[5][4].gap = Cell::Gap::FULL;
586 p.grid[2][5].gap = Cell::Gap::FULL;
587 p.grid[6][5].gap = Cell::Gap::FULL;
588 p.grid[3][6].gap = Cell::Gap::FULL;
589 p.grid[0][7].gap = Cell::Gap::FULL;
590 p.grid[8][7].gap = Cell::Gap::FULL;
591 p.grid[5][8].gap = Cell::Gap::FULL;
592
593 p.grid[0][8].start = true;
594 p.grid[4][0].end = Cell::Dir::UP;
595
596 std::vector<Pos> cutEdges = Randomizer2Core::CutInsideEdges(p, 2);
597 for (Pos pos : cutEdges) {
598 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
599 }
600 auto solution = GetUniqueSolution(p);
601
602 Puzzle q;
603 q.NewGrid(4, 4);
604 q.grid[0][8].start = true;
605 q.grid[4][0].end = Cell::Dir::UP;
606 q.sequence = solution.sequence;
607 for (Pos pos : cutEdges) {
608 q.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
609 }
610 for (Pos pos : Randomizer2Core::CutInsideEdges(q, 7)) {
611 q.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
612 }
613 _serializer.WritePuzzle(q, 0x1A0F);
614 }
615}
616
617Puzzle Randomizer2::GetUniqueSolution(Puzzle& p) {
618 auto solutions = Solver::Solve(p);
619 assert(solutions.size() == 1);
620 return solutions[0];
621}
622
623void Randomizer2::SetGate(int panel, int X, int Y) {
624 float x, y, z, w;
625 if (X%2 == 0 && Y%2 == 1) { // Horizontal
626 x = -1.49f * X + 0.22f * Y + 66.58f;
627 y = 0.275f * X + 1.6f * Y + 108.4f;
628 z = -.77f;
629 w = .63f;
630 } else { // Vertical
631 assert(X%2 == 1 && Y%2 == 0);
632 x = -1.6f * X + 0.35f * Y + 66.5f;
633 y = 0.25f * X + 1.6f * Y + 108.55f;
634 z = -0.1f;
635 w = 1.0f;
636 }
637
638 SetPos(panel, x, y, 19.2f);
639 // _memory->WriteEntityData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
640}
641
642void Randomizer2::SetPos(int panel, float x, float y, float z) {
643 // _memory->WriteEntityData<float>(panel, POSITION, {x, y, z});
644} \ No newline at end of file
diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h deleted file mode 100644 index a2b5ebd..0000000 --- a/Source/Randomizer2.h +++ /dev/null
@@ -1,19 +0,0 @@
1#pragma once
2#include "PuzzleSerializer.h"
3
4class Randomizer2 {
5public:
6 Randomizer2(const PuzzleSerializer& serializer);
7 void Randomize();
8 void RandomizeTutorial();
9 void RandomizeGlassFactory();
10 void RandomizeSymmetryIsland();
11 void RandomizeKeep();
12
13private:
14 Puzzle GetUniqueSolution(Puzzle& p);
15 void SetGate(int panel, int X, int Y);
16 void SetPos(int panel, float x, float y, float z);
17
18 PuzzleSerializer _serializer;
19};
diff --git a/Source/Randomizer2Core.cpp b/Source/Randomizer2Core.cpp deleted file mode 100644 index 867fa5a..0000000 --- a/Source/Randomizer2Core.cpp +++ /dev/null
@@ -1,203 +0,0 @@
1#include "pch.h"
2#include "Randomizer2Core.h"
3#include "Random.h"
4
5std::vector<Pos> Randomizer2Core::CutEdges(const Puzzle& p, size_t numEdges) {
6 return CutEdgesInternal(p, 0, p.width, 0, p.height, numEdges);
7}
8
9std::vector<Pos> Randomizer2Core::CutInsideEdges(const Puzzle& p, size_t numEdges) {
10 return CutEdgesInternal(p, 1, p.width-1, 1, p.height-1, numEdges);
11}
12
13std::vector<Pos> Randomizer2Core::CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges) {
14 Puzzle copy = p;
15 // Prevent cuts from landing on the midline
16 if (p.symmetry == Puzzle::Symmetry::X) {
17 for (int y=0; y<p.height; y++) {
18 copy.grid[p.width/2][y].gap = Cell::Gap::FULL;
19 }
20 return CutEdgesInternal(copy, 0, (p.width-1)/2, 0, p.height, numEdges);
21 } else if (p.symmetry == Puzzle::Symmetry::Y) {
22 for (int x=0; x<p.width; x++) {
23 copy.grid[x][p.height/2].gap = Cell::Gap::FULL;
24 }
25 return CutEdgesInternal(copy, 0, p.width, 0, (p.height-1)/2, numEdges);
26 } else {
27 assert(p.symmetry == Puzzle::Symmetry::XY);
28 int midX = p.width/2;
29 int midY = p.height/2;
30 if (p.width%4 == 1 && p.height%4 == 1) { // For double-even grids, cut around the center
31 copy.grid[midX-1][midY].gap = Cell::Gap::FULL;
32 copy.grid[midX][midY-1].gap = Cell::Gap::FULL;
33 copy.grid[midX][midY+1].gap = Cell::Gap::FULL;
34 copy.grid[midX+1][midY].gap = Cell::Gap::FULL;
35 } else if (p.width%4 == 1 && p.height%4 == 3) { // For half-even grids, there's only one line to cut
36 copy.grid[midX][midY].gap = Cell::Gap::FULL;
37 } else if (p.width%4 == 3 && p.height%4 == 1) { // For half-even grids, there's only one line to cut
38 copy.grid[midX][midY].gap = Cell::Gap::FULL;
39 }
40 return CutEdgesInternal(copy, 0, p.width, 0, p.height, numEdges);
41 }
42}
43
44std::vector<Pos> Randomizer2Core::CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, size_t numEdges) {
45 std::vector<Pos> edges;
46 for (int x=xMin; x<xMax; x++) {
47 for (int y=yMin; y<yMax; y++) {
48 if (x%2 == y%2) continue;
49 if (p.grid[x][y].gap != Cell::Gap::NONE) continue;
50 if (p.grid[x][y].start) continue;
51 if (p.grid[x][y].end != Cell::Dir::NONE) continue;
52
53 if (p.symmetry == Puzzle::Symmetry::XY) {
54 assert(p.width == p.height); // TODO: This solution only supports square rotational symmetry.
55 if (x > y) continue; // Only allow cuts bottom-left of the diagonal
56 }
57
58 // If the puzzle already has a sequence, don't cut along it.
59 bool inSequence = false;
60 for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y);
61 if (inSequence) continue;
62 edges.emplace_back(x, y);
63 }
64 }
65 assert(numEdges <= edges.size());
66
67 auto [colorGrid, numColors] = CreateColorGrid(p);
68 assert(numEdges <= numColors);
69
70 // @Hack... sort of. I couldn't think of a better way to do this.
71 if (p.symmetry == Puzzle::Symmetry::XY) {
72 // Recolor the diagonal so that opposite cells share a color. This is because we're only cutting along half their edges,
73 // so they are in fact two sides of the same cell.
74 for (int x=1; x<p.width/2; x+=2) {
75 assert(p.width == p.height); // TODO: This solution only supports square rotational symmetry.
76 colorGrid[x][x] = colorGrid[p.width-x-1][p.width-x-1];
77 }
78 }
79
80 std::vector<Pos> cutEdges;
81 for (int i=0; i<numEdges; i++) {
82 while (edges.size() > 0) {
83 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
84 Pos pos = edges[edge];
85 edges.erase(edges.begin() + edge);
86
87 int color1 = 0;
88 int color2 = 0;
89 if (pos.x%2 == 0 && pos.y%2 == 1) { // Vertical
90 if (pos.x > 0) color1 = colorGrid[pos.x-1][pos.y];
91 else color1 = 1;
92
93 if (pos.x < p.width - 1) color2 = colorGrid[pos.x+1][pos.y];
94 else color2 = 1;
95 } else { // Horizontal
96 assert(pos.x%2 == 1 && pos.y%2 == 0);
97 if (pos.y > 0) color1 = colorGrid[pos.x][pos.y-1];
98 else color1 = 1;
99
100 if (pos.y < p.height - 1) color2 = colorGrid[pos.x][pos.y+1];
101 else color2 = 1;
102 }
103 // Enforce color1 < color2
104 if (color1 > color2) std::swap(color1, color2);
105
106 // Colors mismatch, valid cut
107 if (color1 != color2) {
108 // @Performance... have a lookup table instead?
109 for (int x=0; x<p.width; x++) {
110 for (int y=0; y<p.height; y++) {
111 if (colorGrid[x][y] == color2) colorGrid[x][y] = color1;
112 }
113 }
114 cutEdges.emplace_back(pos);
115 break;
116 }
117 }
118 }
119 assert(cutEdges.size() == numEdges);
120 return cutEdges;
121}
122
123#ifndef NDEBUG
124#include <Windows.h>
125#endif
126
127void Randomizer2Core::DebugColorGrid(const std::vector<std::vector<int>>& colorGrid) {
128#ifndef NDEBUG
129 static std::string colors = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
130 for (int y=0; y<colorGrid[0].size(); y++) {
131 std::string row;
132 for (int x=0; x<colorGrid.size(); x++) {
133 row += colors[colorGrid[x][y]];
134 }
135 row += "\n";
136 OutputDebugStringA(row.c_str());
137 }
138 OutputDebugStringA("\n");
139#endif
140}
141
142void Randomizer2Core::FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y) {
143 if (!p.SafeCell(x, y)) return;
144 if (colorGrid[x][y] != 0) return; // Already processed.
145 colorGrid[x][y] = color;
146
147 FloodFill(p, colorGrid, color, x, y+1);
148 FloodFill(p, colorGrid, color, x, y-1);
149 FloodFill(p, colorGrid, color, x+1, y);
150 FloodFill(p, colorGrid, color, x-1, y);
151}
152
153void Randomizer2Core::FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y) {
154 if (!p.SafeCell(x, y)) return;
155 if (colorGrid[x][y] != 0) return; // Already processed.
156 if (x%2 != y%2 && p.grid[x][y].gap == Cell::Gap::NONE) return; // Only flood-fill through gaps
157 colorGrid[x][y] = 1; // Outside color
158
159 FloodFillOutside(p, colorGrid, x, y+1);
160 FloodFillOutside(p, colorGrid, x, y-1);
161 FloodFillOutside(p, colorGrid, x+1, y);
162 FloodFillOutside(p, colorGrid, x-1, y);
163}
164
165// Color key:
166// 0 (default): Uncolored
167// 1: Outside color and separator color
168// 2+: Flood-filled region color
169std::tuple<std::vector<std::vector<int>>, int> Randomizer2Core::CreateColorGrid(const Puzzle& p) {
170 std::vector<std::vector<int>> colorGrid;
171 colorGrid.resize(p.width);
172
173 for (int x=0; x<p.width; x++) {
174 colorGrid[x].resize(p.height);
175 for (int y=0; y<p.height; y++) {
176 if (x%2 == 1 && y%2 == 1) continue;
177 // Mark all unbroken edges and intersections as 'do not color'
178 if (p.grid[x][y].gap == Cell::Gap::NONE) colorGrid[x][y] = 1;
179 }
180 }
181
182 // @Future: Skip this loop if pillar = true;
183 for (int y=0; y<p.height; y++) {
184 FloodFillOutside(p, colorGrid, 0, y);
185 FloodFillOutside(p, colorGrid, p.width - 1, y);
186 }
187
188 for (int x=0; x<p.width; x++) {
189 FloodFillOutside(p, colorGrid, x, 0);
190 FloodFillOutside(p, colorGrid, x, p.height - 1);
191 }
192
193 int color = 1;
194 for (int x=0; x<p.width; x++) {
195 for (int y=0; y<p.height; y++) {
196 if (colorGrid[x][y] != 0) continue; // No dead colors
197 color++;
198 FloodFill(p, colorGrid, color, x, y);
199 }
200 }
201
202 return {colorGrid, color};
203} \ No newline at end of file
diff --git a/Source/Randomizer2Core.h b/Source/Randomizer2Core.h deleted file mode 100644 index df98de8..0000000 --- a/Source/Randomizer2Core.h +++ /dev/null
@@ -1,17 +0,0 @@
1#pragma once
2
3class Randomizer2Core {
4public:
5 // CAUTION: These do not actually cut edges, they just returns a list of suggested cuts.
6 static std::vector<Pos> CutEdges(const Puzzle& p, size_t numEdges);
7 static std::vector<Pos> CutInsideEdges(const Puzzle& p, size_t numEdges);
8 static std::vector<Pos> CutSymmetricalEdgePairs(const Puzzle& p, size_t numEdges);
9
10private:
11 static std::vector<Pos> CutEdgesInternal(const Puzzle& p, int xMin, int xMax, int yMin, int yMax, size_t numEdges);
12 static void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid);
13 static void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y);
14 static void FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y);
15 static std::tuple<std::vector<std::vector<int>>, int> CreateColorGrid(const Puzzle& p);
16};
17
diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj index 1cfb484..8d6104c 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj
@@ -172,8 +172,6 @@
172 <ClInclude Include="PuzzleSerializer.h" /> 172 <ClInclude Include="PuzzleSerializer.h" />
173 <ClInclude Include="Random.h" /> 173 <ClInclude Include="Random.h" />
174 <ClInclude Include="Randomizer.h" /> 174 <ClInclude Include="Randomizer.h" />
175 <ClInclude Include="Randomizer2.h" />
176 <ClInclude Include="Randomizer2Core.h" />
177 <ClInclude Include="Solver.h" /> 175 <ClInclude Include="Solver.h" />
178 <ClInclude Include="Validator.h" /> 176 <ClInclude Include="Validator.h" />
179 </ItemGroup> 177 </ItemGroup>
@@ -190,8 +188,6 @@
190 <ClCompile Include="PuzzleSerializer.cpp" /> 188 <ClCompile Include="PuzzleSerializer.cpp" />
191 <ClCompile Include="Random.cpp" /> 189 <ClCompile Include="Random.cpp" />
192 <ClCompile Include="Randomizer.cpp" /> 190 <ClCompile Include="Randomizer.cpp" />
193 <ClCompile Include="Randomizer2.cpp" />
194 <ClCompile Include="Randomizer2Core.cpp" />
195 <ClCompile Include="Solver.cpp" /> 191 <ClCompile Include="Solver.cpp" />
196 <ClCompile Include="Validator.cpp" /> 192 <ClCompile Include="Validator.cpp" />
197 </ItemGroup> 193 </ItemGroup>