about summary refs log tree commit diff stats
path: root/Source/Randomizer2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Randomizer2.cpp')
-rw-r--r--Source/Randomizer2.cpp187
1 files changed, 150 insertions, 37 deletions
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp index fcfec9a..537c30b 100644 --- a/Source/Randomizer2.cpp +++ b/Source/Randomizer2.cpp
@@ -2,6 +2,38 @@
2#include "Puzzle.h" 2#include "Puzzle.h"
3#include "Random.h" 3#include "Random.h"
4#include "Solver.h" 4#include "Solver.h"
5#include "Memory.h"
6
7void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) {
8 if (x%2 == 1 && y%2 == 1) return;
9 auto cell = p.GetCell(x, y);
10 if (cell.undefined) return;
11 if (cell.gap != Cell::Gap::NONE) return;
12 if (reached[x][y]) return;
13
14 reached[x][y] = true;
15 FloodFillInternal(p, reached, x-1, y);
16 FloodFillInternal(p, reached, x+1, y);
17 FloodFillInternal(p, reached, x, y-1);
18 FloodFillInternal(p, reached, x, y+1);
19}
20
21// Returns true: All nodes reachable / false: Some node disconnected
22bool FloodFill(const Puzzle& p) {
23 std::vector<std::vector<bool>> reached;
24 reached.resize(p.width);
25 for (int x=0; x<p.width; x++) reached[x].resize(p.height);
26 FloodFillInternal(p, reached, 0, 0);
27
28 for (int x=0; x<p.width; x++) {
29 for (int y=0; y<p.height; y++) {
30 if (x%2 == 0 && y%2 == 0) { // @Specialized
31 if (!reached[x][y]) return false;
32 }
33 }
34 }
35 return true;
36}
5 37
6Randomizer2::Randomizer2(const std::shared_ptr<Memory>& memory) : _memory(memory) {} 38Randomizer2::Randomizer2(const std::shared_ptr<Memory>& memory) : _memory(memory) {}
7 39
@@ -12,9 +44,7 @@ void Randomizer2::Randomize() {
12 // end (x=8, y=0) Up 44 // end (x=8, y=0) Up
13 // 1 solution 45 // 1 solution
14 Puzzle p; 46 Puzzle p;
15 int attemptCount = 0;
16 while (true) { 47 while (true) {
17 attemptCount++;
18 p.NewGrid(4, 4); 48 p.NewGrid(4, 4);
19 49
20 std::vector<Pos> corners; 50 std::vector<Pos> corners;
@@ -29,40 +59,34 @@ void Randomizer2::Randomize() {
29 } 59 }
30 60
31 for (int i=0; i<14; i++) { 61 for (int i=0; i<14; i++) {
32 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 62 for (int j=0; j<edges.size(); j++) {
33 Pos pos = edges[edge]; 63 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
34 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; 64 Pos pos = edges[edge];
35 edges.erase(edges.begin() + edge); 65 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
66 edges.erase(edges.begin() + edge);
67
68 if (FloodFill(p)) {
69 break;
70 } else {
71 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
72 }
73 }
36 } 74 }
37 75
38 p.grid[0][8].start = true; 76 p.grid[0][8].start = true;
39 p.grid[8][0].end = Cell::Dir::UP; 77 p.grid[8][0].end = Cell::Dir::UP;
40 78
41 auto solutions = Solver::Solve(p); 79 auto solutions = Solver::Solve(p);
42 if (solutions.size() == 1) { 80 if (solutions.size() > 0) break;
43 auto solution = solutions[0];
44 int solutionLength = 0;
45 for (int x=0; x<solution.width; x++) {
46 for (int y=0; y<solution.height; y++) {
47 if (solution.grid[x][y].color == Cell::Color::BLACK) solutionLength++;
48 }
49 }
50 if (solutionLength == 25) break;
51 }
52 } 81 }
53 PuzzleSerializer(_memory).WritePuzzle(p, 0x293); 82 PuzzleSerializer(_memory).WritePuzzle(p, 0x293);
54 83
55
56
57
58 // 7x7 84 // 7x7
59 // 35 gaps 85 // 35 gaps
60 // start (x=8, y=8) 86 // start (x=8, y=8)
61 // end (x=4, y=0) Up 87 // end (x=4, y=0) Up
62 // 2 solutions, 37 & 39 88 // 2 solutions, 37 & 39
63 attemptCount = 0;
64 while (true) { 89 while (true) {
65 attemptCount++;
66 p.NewGrid(7, 7); 90 p.NewGrid(7, 7);
67 91
68 std::vector<Pos> corners; 92 std::vector<Pos> corners;
@@ -77,28 +101,117 @@ void Randomizer2::Randomize() {
77 } 101 }
78 102
79 for (int i=0; i<35; i++) { 103 for (int i=0; i<35; i++) {
80 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 104 for (int j=0; j<edges.size(); j++) {
81 Pos pos = edges[edge]; 105 // TODO: Precompute edge weights and make a weighted cut.
82 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; 106 // This will also preclude the need for flood filling.
83 edges.erase(edges.begin() + edge); 107 // https://en.wikipedia.org/wiki/Biconnected_component
108
109 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
110 Pos pos = edges[edge];
111 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
112 edges.erase(edges.begin() + edge);
113
114 if (FloodFill(p)) {
115 break;
116 } else {
117 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
118 }
119 }
84 } 120 }
85 121
86 p.grid[8][8].start = true; 122 switch (Random::RandInt(1, 4)) {
87 p.grid[4][0].end = Cell::Dir::UP; 123 case 1:
124 p.grid[Random::RandInt(0, p.width-1)][0].end = Cell::Dir::UP;
125 break;
126 case 2:
127 p.grid[Random::RandInt(0, p.width-1)][p.height-1].end = Cell::Dir::DOWN;
128 break;
129 case 3:
130 p.grid[0][Random::RandInt(0, p.height-1)].end = Cell::Dir::LEFT;
131 break;
132 case 4:
133 p.grid[p.width-1][Random::RandInt(0, p.height-1)].end = Cell::Dir::RIGHT;
134 break;
135 }
136 switch (Random::RandInt(1, 3)) {
137 case 1: // Horiz (6) [5/7][4/6/8]
138 p.grid[Random::RandInt(0, 1)*2 + 5][Random::RandInt(0, 2)*2 + 4].start = true;
139 break;
140 case 2: // Verti (6) [4/6/8][5/7]
141 p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 1)*2 + 5].start = true;
142 break;
143 case 3: // Inter (9) [4/6/8][4/6/8]
144 p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true;
145 break;
146 }
88 147
89 auto solutions = Solver::Solve(p); 148 auto solutions = Solver::Solve(p);
90 if (solutions.size() > 0) break; 149 if (solutions.size() > 0) break;
91 if (solutions.size() > 0 && solutions.size() < 5) {
92 auto solution = solutions[0];
93 int solutionLength = 0;
94 for (int x=0; x<solution.width; x++) {
95 for (int y=0; y<solution.height; y++) {
96 if (solution.grid[x][y].color == Cell::Color::BLACK) solutionLength++;
97 }
98 }
99 if (solutionLength > 30 && solutionLength < 40) break;
100 }
101 } 150 }
102 PuzzleSerializer(_memory).WritePuzzle(p, 0x295); 151 PuzzleSerializer(_memory).WritePuzzle(p, 0x295);
103 152
104} 153}
154
155void Randomizer2::RandomizeKeep() {
156 Puzzle p;
157 p.width = 9;
158 p.height = 10;
159 p.grid.clear();
160 p.grid.resize(p.width);
161 for (int x=0; x<p.width; x++) p.grid[x].resize(p.height);
162
163 p.NewGrid(5, 5);
164 p.grid[2][1].gap = Cell::Gap::FULL;
165 p.grid[4][1].gap = Cell::Gap::FULL;
166 p.grid[6][1].gap = Cell::Gap::FULL;
167 p.grid[8][1].gap = Cell::Gap::FULL;
168 p.grid[3][2].gap = Cell::Gap::FULL;
169 p.grid[5][2].gap = Cell::Gap::FULL;
170 p.grid[8][3].gap = Cell::Gap::FULL;
171 p.grid[2][5].gap = Cell::Gap::FULL;
172 p.grid[6][5].gap = Cell::Gap::FULL;
173 p.grid[7][6].gap = Cell::Gap::FULL;
174 p.grid[2][7].gap = Cell::Gap::FULL;
175 p.grid[4][7].gap = Cell::Gap::FULL;
176 p.grid[0][9].gap = Cell::Gap::FULL;
177 p.grid[2][9].gap = Cell::Gap::FULL;
178 p.grid[6][9].gap = Cell::Gap::FULL;
179 p.grid[8][9].gap = Cell::Gap::FULL;
180
181 p.grid[8][0].end = Cell::Dir::UP;
182 p.grid[4][9].start = true;
183
184 // 0x00496
185 // 0x00344
186 // 0x00495
187 // 0x00488
188 // 0x00489
189
190 SetGate(0x00496, 2, 1);
191}
192
193void Randomizer2::SetGate(int panel, int X, int Y) {
194 // x: (-1.5)X + (0.7)Y + 67.1
195 // y: (0.3)X + (1.5)Y + 106.9
196 // z: -19.1
197 // Horizontal: (0, 0, -.1, 1)
198 // Vertical: (0, 0, -.77, .63)
199
200 float x = -1.5f * X + 0.7f * Y + 67.1f;
201 float y = 0.3f * X + 1.5f * Y + 106.9f;
202 _memory->WritePanelData<float>(panel, POSITION, {x, y, 19.1f});
203
204 float z, w;
205 if (X%2 == 0 && Y%2 == 1) { // Horizontal
206 z = -0.1f;
207 w = 1.0f;
208 } else if (X%2 == 1 && Y%2 == 0) { // Vertical
209 z = -.77f;
210 w = .63f;
211 } else {
212 assert(false);
213 return;
214 }
215
216 _memory->WritePanelData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
217} \ No newline at end of file