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.cpp207
1 files changed, 169 insertions, 38 deletions
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp index 537c30b..b5218bf 100644 --- a/Source/Randomizer2.cpp +++ b/Source/Randomizer2.cpp
@@ -3,6 +3,9 @@
3#include "Random.h" 3#include "Random.h"
4#include "Solver.h" 4#include "Solver.h"
5#include "Memory.h" 5#include "Memory.h"
6#include "PuzzlerSerializer.h"
7#include <cassert>
8#include <string>
6 9
7void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) { 10void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) {
8 if (x%2 == 1 && y%2 == 1) return; 11 if (x%2 == 1 && y%2 == 1) return;
@@ -62,10 +65,10 @@ void Randomizer2::Randomize() {
62 for (int j=0; j<edges.size(); j++) { 65 for (int j=0; j<edges.size(); j++) {
63 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 66 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
64 Pos pos = edges[edge]; 67 Pos pos = edges[edge];
65 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
66 edges.erase(edges.begin() + edge); 68 edges.erase(edges.begin() + edge);
67 69
68 if (FloodFill(p)) { 70 if (FloodFill(p)) {
71 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
69 break; 72 break;
70 } else { 73 } else {
71 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; 74 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
@@ -108,10 +111,10 @@ void Randomizer2::Randomize() {
108 111
109 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); 112 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
110 Pos pos = edges[edge]; 113 Pos pos = edges[edge];
111 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
112 edges.erase(edges.begin() + edge); 114 edges.erase(edges.begin() + edge);
113 115
114 if (FloodFill(p)) { 116 if (FloodFill(p)) {
117 p.grid[pos.x][pos.y].gap = Cell::Gap::FULL;
115 break; 118 break;
116 } else { 119 } else {
117 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; 120 p.grid[pos.x][pos.y].gap = Cell::Gap::NONE;
@@ -152,19 +155,100 @@ void Randomizer2::Randomize() {
152 155
153} 156}
154 157
158void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid) {
159 for (int y=0; y<colorGrid[0].size(); y++) {
160 std::wstring row;
161 for (int x=0; x<colorGrid.size(); x++) {
162 row += std::to_wstring(colorGrid[x][y]);
163 }
164 row += L'\n';
165 OutputDebugString(row.c_str());
166 }
167 OutputDebugString(L"\n");
168}
169
170void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y) {
171 if (!p.SafeCell(x, y)) return;
172 if (colorGrid[x][y] != 0) return; // Already processed.
173 colorGrid[x][y] = color;
174
175 FloodFill(p, colorGrid, color, x, y+1);
176 FloodFill(p, colorGrid, color, x, y-1);
177 FloodFill(p, colorGrid, color, x+1, y);
178 FloodFill(p, colorGrid, color, x-1, y);
179}
180
181void FloodFillOutside(const Puzzle&p, std::vector<std::vector<int>>& colorGrid, int x, int y) {
182 if (!p.SafeCell(x, y)) return;
183 if (colorGrid[x][y] != 0) return; // Already processed.
184 if (x%2 != y%2 && p.grid[x][y].gap != Cell::Gap::FULL) return; // Only flood-fill through full gaps
185 colorGrid[x][y] = 1; // Outside color
186
187 FloodFillOutside(p, colorGrid, x, y+1);
188 FloodFillOutside(p, colorGrid, x, y-1);
189 FloodFillOutside(p, colorGrid, x+1, y);
190 FloodFillOutside(p, colorGrid, x-1, y);
191}
192
193/*
194 undefined -> 1 (color of outside) or * (any colored cell) or -1 (edge/intersection not part of any region)
195
196 0 -> {} (this is a special edge case, which I don't need right now)
197 1 -> 0 (uncolored / ready to color)
198 2 ->
199*/
200std::vector<std::vector<int>> CreateColorGrid(const Puzzle& p) {
201 std::vector<std::vector<int>> colorGrid;
202 colorGrid.resize(p.width);
203
204 for (int x=0; x<p.width; x++) {
205 colorGrid[x].resize(p.height);
206 for (int y=0; y<p.height; y++) {
207 // Mark all unbroken edges and intersections as 'do not color'
208 if (x%2 != y%2) {
209 if (p.grid[x][y].gap == Cell::Gap::NONE) colorGrid[x][y] = 1;
210 } else if (x%2 == 0 && y%2 == 0) {
211 // @Future: What about empty intersections?
212 colorGrid[x][y] = 1; // do not color intersections
213 }
214 }
215 }
216
217 // @Future: Skip this loop if pillar = true;
218 for (int y=1; y<p.height; y+=2) {
219 FloodFillOutside(p, colorGrid, 0, y);
220 FloodFillOutside(p, colorGrid, p.width - 1, y);
221 }
222
223 for (int x=1; x<p.width; x+=2) {
224 FloodFillOutside(p, colorGrid, x, 0);
225 FloodFillOutside(p, colorGrid, x, p.height - 1);
226 }
227
228 int color = 1;
229 for (int x=0; x<p.width; x++) {
230 for (int y=0; y<p.height; y++) {
231 if (colorGrid[x][y] != 0) continue; // No dead colors
232 color++;
233 FloodFill(p, colorGrid, color, x, y);
234 }
235 }
236
237 return colorGrid;
238}
239
155void Randomizer2::RandomizeKeep() { 240void Randomizer2::RandomizeKeep() {
156 Puzzle p; 241 Puzzle p;
157 p.width = 9; 242 p.NewGrid(4, 4);
158 p.height = 10; 243 // p.width = 9;
159 p.grid.clear(); 244 // p.height = 10;
160 p.grid.resize(p.width); 245 // p.grid.clear();
161 for (int x=0; x<p.width; x++) p.grid[x].resize(p.height); 246 // p.grid.resize(p.width);
247 // for (int x=0; x<p.width; x++) p.grid[x].resize(p.height);
162 248
163 p.NewGrid(5, 5);
164 p.grid[2][1].gap = Cell::Gap::FULL; 249 p.grid[2][1].gap = Cell::Gap::FULL;
165 p.grid[4][1].gap = Cell::Gap::FULL; 250 p.grid[4][1].gap = Cell::Gap::FULL;
166 p.grid[6][1].gap = Cell::Gap::FULL; 251 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; 252 p.grid[3][2].gap = Cell::Gap::FULL;
169 p.grid[5][2].gap = Cell::Gap::FULL; 253 p.grid[5][2].gap = Cell::Gap::FULL;
170 p.grid[8][3].gap = Cell::Gap::FULL; 254 p.grid[8][3].gap = Cell::Gap::FULL;
@@ -173,45 +257,92 @@ void Randomizer2::RandomizeKeep() {
173 p.grid[7][6].gap = Cell::Gap::FULL; 257 p.grid[7][6].gap = Cell::Gap::FULL;
174 p.grid[2][7].gap = Cell::Gap::FULL; 258 p.grid[2][7].gap = Cell::Gap::FULL;
175 p.grid[4][7].gap = Cell::Gap::FULL; 259 p.grid[4][7].gap = Cell::Gap::FULL;
176 p.grid[0][9].gap = Cell::Gap::FULL; 260 // p.grid[0][9].gap = Cell::Gap::FULL;
177 p.grid[2][9].gap = Cell::Gap::FULL; 261 // p.grid[2][9].gap = Cell::Gap::FULL;
178 p.grid[6][9].gap = Cell::Gap::FULL; 262 // p.grid[6][9].gap = Cell::Gap::FULL;
179 p.grid[8][9].gap = Cell::Gap::FULL; 263 // p.grid[8][9].gap = Cell::Gap::FULL;
180 264
181 p.grid[8][0].end = Cell::Dir::UP; 265 p.grid[6][0].end = Cell::Dir::UP;
182 p.grid[4][9].start = true; 266 p.grid[4][8].start = true;
183 267
184 // 0x00496 268 auto colorGrid = CreateColorGrid(p);
185 // 0x00344
186 // 0x00495
187 // 0x00488
188 // 0x00489
189 269
190 SetGate(0x00496, 2, 1); 270 std::vector<Pos> edges;
191} 271 for (int x=0; x<p.width; x++) {
272 for (int y=0; y<p.height; y++) {
273 if (x%2 == y%2) continue;
274 if (p.grid[x][y].gap != Cell::Gap::NONE) continue;
275 edges.emplace_back(Pos{x, y});
276 }
277 }
192 278
193void Randomizer2::SetGate(int panel, int X, int Y) { 279 Puzzle copy = p;
194 // x: (-1.5)X + (0.7)Y + 67.1 280 std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496};
195 // y: (0.3)X + (1.5)Y + 106.9 281 for (int i=0; i<5; i++) {
196 // z: -19.1 282 for (int j=0; j<edges.size(); j++) {
197 // Horizontal: (0, 0, -.1, 1) 283 int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1));
198 // Vertical: (0, 0, -.77, .63) 284 Pos pos = edges[edge];
285 edges.erase(edges.begin() + edge);
286
287 int color1 = 0;
288 int color2 = 0;
289 if (pos.x%2 == 0 && pos.y%2 == 1) { // Vertical
290 if (pos.x > 0) color1 = colorGrid[pos.x-1][pos.y];
291 else color1 = 1;
292
293 if (pos.x < p.width - 1) color2 = colorGrid[pos.x+1][pos.y];
294 else color2 = 1;
295 } else { // Horizontal
296 assert(pos.x%2 == 1 && pos.y%2 == 0);
297 if (pos.y > 0) color1 = colorGrid[pos.x][pos.y-1];
298 else color1 = 1;
299
300 if (pos.y < p.height - 1) color2 = colorGrid[pos.x][pos.y+1];
301 else color2 = 1;
302 }
303 // Enforce color1 < color2
304 if (color1 > color2) std::swap(color1, color2);
305
306 // Colors mismatch, valid cut
307 if (color1 != color2) {
308 // @Performance... have a lookup table instead?
309 for (int x=0; x<p.width; x++) {
310 for (int y=0; y<p.height; y++) {
311 if (colorGrid[x][y] == color2) colorGrid[x][y] = color1;
312 }
313 }
314 copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK;
315 SetGate(gates[i], pos.x, pos.y);
316 break;
317 }
318 }
319 }
199 320
200 float x = -1.5f * X + 0.7f * Y + 67.1f; 321 auto solutions = Solver::Solve(copy);
201 float y = 0.3f * X + 1.5f * Y + 106.9f; 322 assert(solutions.size() == 1);
202 _memory->WritePanelData<float>(panel, POSITION, {x, y, 19.1f}); 323 p.sequence = solutions[0].sequence;
324 PuzzleSerializer(_memory).WritePuzzle(solutions[0], 0x139);
325}
203 326
204 float z, w; 327void Randomizer2::SetGate(int panel, int X, int Y) {
328 float x, y, z, w;
205 if (X%2 == 0 && Y%2 == 1) { // Horizontal 329 if (X%2 == 0 && Y%2 == 1) { // Horizontal
206 z = -0.1f; 330 x = -1.49f * X + 0.22f * Y + 66.58f;
207 w = 1.0f; 331 y = 0.275f * X + 1.6f * Y + 108.4f;
208 } else if (X%2 == 1 && Y%2 == 0) { // Vertical
209 z = -.77f; 332 z = -.77f;
210 w = .63f; 333 w = .63f;
211 } else { 334 } else { // Vertical
212 assert(false); 335 assert(X%2 == 1 && Y%2 == 0);
213 return; 336 x = -1.6f * X + 0.35f * Y + 66.5f;
337 y = 0.25f * X + 1.6f * Y + 108.55f;
338 z = -0.1f;
339 w = 1.0f;
214 } 340 }
215 341
342 SetPos(panel, x, y, 19.2f);
216 _memory->WritePanelData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w}); 343 _memory->WritePanelData<float>(panel, ORIENTATION, {0.0f, 0.0f, z, w});
344}
345
346void Randomizer2::SetPos(int panel, float x, float y, float z) {
347 _memory->WritePanelData<float>(panel, POSITION, {x, y, z});
217} \ No newline at end of file 348} \ No newline at end of file