diff options
-rw-r--r-- | App/Main.cpp | 4 | ||||
-rw-r--r-- | Source/PuzzleSerializer.cpp (renamed from Source/PuzzlerSerializer.cpp) | 2 | ||||
-rw-r--r-- | Source/PuzzleSerializer.h (renamed from Source/PuzzlerSerializer.h) | 0 | ||||
-rw-r--r-- | Source/Randomizer2.cpp | 197 | ||||
-rw-r--r-- | Source/Randomizer2.h | 4 | ||||
-rw-r--r-- | Source/Randomizer2Core.cpp | 29 | ||||
-rw-r--r-- | Source/Randomizer2Core.h | 7 | ||||
-rw-r--r-- | Source/Source.vcxproj | 4 |
8 files changed, 83 insertions, 164 deletions
diff --git a/App/Main.cpp b/App/Main.cpp index a815746..f780069 100644 --- a/App/Main.cpp +++ b/App/Main.cpp | |||
@@ -24,7 +24,7 @@ | |||
24 | #include "Puzzle.h" | 24 | #include "Puzzle.h" |
25 | #include "Solver.h" | 25 | #include "Solver.h" |
26 | #include "Randomizer2.h" | 26 | #include "Randomizer2.h" |
27 | #include "PuzzlerSerializer.h" | 27 | #include "PuzzleSerializer.h" |
28 | #include <sstream> | 28 | #include <sstream> |
29 | 29 | ||
30 | #define TMP1 0x501 | 30 | #define TMP1 0x501 |
@@ -167,7 +167,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |||
167 | break; | 167 | break; |
168 | case TMP4: | 168 | case TMP4: |
169 | SetRandomSeed(); | 169 | SetRandomSeed(); |
170 | g_randomizer2->RandomizeKeep(); | 170 | g_randomizer2->Randomize(); |
171 | } | 171 | } |
172 | } | 172 | } |
173 | return DefWindowProc(hwnd, message, wParam, lParam); | 173 | return DefWindowProc(hwnd, message, wParam, lParam); |
diff --git a/Source/PuzzlerSerializer.cpp b/Source/PuzzleSerializer.cpp index 2ba0ce7..c1e93a5 100644 --- a/Source/PuzzlerSerializer.cpp +++ b/Source/PuzzleSerializer.cpp | |||
@@ -1,4 +1,4 @@ | |||
1 | #include "PuzzlerSerializer.h" | 1 | #include "PuzzleSerializer.h" |
2 | #include "Memory.h" | 2 | #include "Memory.h" |
3 | 3 | ||
4 | #pragma warning (disable:26451) | 4 | #pragma warning (disable:26451) |
diff --git a/Source/PuzzlerSerializer.h b/Source/PuzzleSerializer.h index d9b9edd..d9b9edd 100644 --- a/Source/PuzzlerSerializer.h +++ b/Source/PuzzleSerializer.h | |||
diff --git a/Source/Randomizer2.cpp b/Source/Randomizer2.cpp index d9c00c0..7a50c7b 100644 --- a/Source/Randomizer2.cpp +++ b/Source/Randomizer2.cpp | |||
@@ -1,128 +1,39 @@ | |||
1 | #include "Memory.h" | ||
1 | #include "Randomizer2.h" | 2 | #include "Randomizer2.h" |
3 | #include "Randomizer2Core.h" | ||
2 | #include "Puzzle.h" | 4 | #include "Puzzle.h" |
3 | #include "Random.h" | 5 | #include "Random.h" |
4 | #include "Solver.h" | 6 | #include "Solver.h" |
5 | #include "Memory.h" | 7 | |
6 | #include "Randomizer2Core.h" | ||
7 | #include "PuzzlerSerializer.h" | ||
8 | #include <cassert> | 8 | #include <cassert> |
9 | #include <string> | 9 | #include <string> |
10 | 10 | ||
11 | void FloodFillInternal(const Puzzle& p, std::vector<std::vector<bool>>& reached, int x, int y) { | 11 | #pragma warning (disable: 26451) |
12 | if (x%2 == 1 && y%2 == 1) return; | ||
13 | auto cell = p.GetCell(x, y); | ||
14 | if (cell.undefined) return; | ||
15 | if (cell.gap != Cell::Gap::NONE) return; | ||
16 | if (reached[x][y]) return; | ||
17 | 12 | ||
18 | reached[x][y] = true; | 13 | Randomizer2::Randomizer2(const std::shared_ptr<Memory>& memory) : _memory(memory), _serializer(PuzzleSerializer(_memory)) {} |
19 | FloodFillInternal(p, reached, x-1, y); | ||
20 | FloodFillInternal(p, reached, x+1, y); | ||
21 | FloodFillInternal(p, reached, x, y-1); | ||
22 | FloodFillInternal(p, reached, x, y+1); | ||
23 | } | ||
24 | 14 | ||
25 | // Returns true: All nodes reachable / false: Some node disconnected | 15 | void Randomizer2::Randomize() { |
26 | bool FloodFill(const Puzzle& p) { | 16 | RandomizeTutorial(); |
27 | std::vector<std::vector<bool>> reached; | 17 | RandomizeKeep(); |
28 | reached.resize(p.width); | ||
29 | for (int x=0; x<p.width; x++) reached[x].resize(p.height); | ||
30 | FloodFillInternal(p, reached, 0, 0); | ||
31 | |||
32 | for (int x=0; x<p.width; x++) { | ||
33 | for (int y=0; y<p.height; y++) { | ||
34 | if (x%2 == 0 && y%2 == 0) { // @Specialized | ||
35 | if (!reached[x][y]) return false; | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | return true; | ||
40 | } | 18 | } |
41 | 19 | ||
42 | Randomizer2::Randomizer2(const std::shared_ptr<Memory>& memory) : _memory(memory) {} | 20 | void Randomizer2::RandomizeTutorial() { |
43 | 21 | { // Far center | |
44 | void Randomizer2::Randomize() { | 22 | Puzzle p; |
45 | // 4x4 | ||
46 | // 14 gaps | ||
47 | // start (x=0, y=8) | ||
48 | // end (x=8, y=0) Up | ||
49 | // 1 solution | ||
50 | Puzzle p; | ||
51 | while (true) { | ||
52 | p.NewGrid(4, 4); | 23 | p.NewGrid(4, 4); |
53 | |||
54 | std::vector<Pos> corners; | ||
55 | std::vector<Pos> cells; | ||
56 | std::vector<Pos> edges; | ||
57 | for (int x=0; x<p.width; x++) { | ||
58 | for (int y=0; y<p.height; y++) { | ||
59 | if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y}); | ||
60 | else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y}); | ||
61 | else edges.emplace_back(Pos{x, y}); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | for (int i=0; i<14; i++) { | ||
66 | for (int j=0; j<edges.size(); j++) { | ||
67 | int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); | ||
68 | Pos pos = edges[edge]; | ||
69 | edges.erase(edges.begin() + edge); | ||
70 | |||
71 | if (FloodFill(p)) { | ||
72 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
73 | break; | ||
74 | } else { | ||
75 | p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | p.grid[0][8].start = true; | 24 | p.grid[0][8].start = true; |
81 | p.grid[8][0].end = Cell::Dir::UP; | 25 | p.grid[8][0].end = Cell::Dir::UP; |
82 | 26 | ||
83 | auto solutions = Solver::Solve(p); | 27 | for (Pos pos : Randomizer2Core::CutEdges(p, 14)) { |
84 | if (solutions.size() > 0) break; | 28 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
29 | } | ||
30 | _serializer.WritePuzzle(p, 0x293); | ||
85 | } | 31 | } |
86 | PuzzleSerializer(_memory).WritePuzzle(p, 0x293); | ||
87 | 32 | ||
88 | // 7x7 | 33 | { |
89 | // 35 gaps | 34 | Puzzle p; |
90 | // start (x=8, y=8) | ||
91 | // end (x=4, y=0) Up | ||
92 | // 2 solutions, 37 & 39 | ||
93 | while (true) { | ||
94 | p.NewGrid(7, 7); | 35 | p.NewGrid(7, 7); |
95 | 36 | ||
96 | std::vector<Pos> corners; | ||
97 | std::vector<Pos> cells; | ||
98 | std::vector<Pos> edges; | ||
99 | for (int x=0; x<p.width; x++) { | ||
100 | for (int y=0; y<p.height; y++) { | ||
101 | if (x%2 == 0 && y%2 == 0) corners.emplace_back(Pos{x, y}); | ||
102 | else if (x%2 == 1 && y%2 == 1) cells.emplace_back(Pos{x, y}); | ||
103 | else edges.emplace_back(Pos{x, y}); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | for (int i=0; i<35; i++) { | ||
108 | for (int j=0; j<edges.size(); j++) { | ||
109 | // TODO: Precompute edge weights and make a weighted cut. | ||
110 | // This will also preclude the need for flood filling. | ||
111 | // https://en.wikipedia.org/wiki/Biconnected_component | ||
112 | |||
113 | int edge = Random::RandInt(0, static_cast<int>(edges.size() - 1)); | ||
114 | Pos pos = edges[edge]; | ||
115 | edges.erase(edges.begin() + edge); | ||
116 | |||
117 | if (FloodFill(p)) { | ||
118 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
119 | break; | ||
120 | } else { | ||
121 | p.grid[pos.x][pos.y].gap = Cell::Gap::NONE; | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | switch (Random::RandInt(1, 4)) { | 37 | switch (Random::RandInt(1, 4)) { |
127 | case 1: | 38 | case 1: |
128 | p.grid[Random::RandInt(0, p.width-1)][0].end = Cell::Dir::UP; | 39 | p.grid[Random::RandInt(0, p.width-1)][0].end = Cell::Dir::UP; |
@@ -148,17 +59,17 @@ void Randomizer2::Randomize() { | |||
148 | p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true; | 59 | p.grid[Random::RandInt(0, 2)*2 + 4][Random::RandInt(0, 2)*2 + 4].start = true; |
149 | break; | 60 | break; |
150 | } | 61 | } |
62 | |||
63 | for (Pos pos : Randomizer2Core::CutEdges(p, 35)) { | ||
64 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
65 | } | ||
151 | 66 | ||
152 | auto solutions = Solver::Solve(p); | 67 | _serializer.WritePuzzle(p, 0x295); |
153 | if (solutions.size() > 0) break; | ||
154 | } | 68 | } |
155 | PuzzleSerializer(_memory).WritePuzzle(p, 0x295); | ||
156 | |||
157 | } | 69 | } |
158 | 70 | ||
159 | void Randomizer2::RandomizeKeep() { | 71 | void Randomizer2::RandomizeKeep() { |
160 | // *** Hedges 1 *** | 72 | { // Hedges 1 |
161 | { | ||
162 | Puzzle p; | 73 | Puzzle p; |
163 | p.NewGrid(4, 4); | 74 | p.NewGrid(4, 4); |
164 | 75 | ||
@@ -177,8 +88,7 @@ void Randomizer2::RandomizeKeep() { | |||
177 | p.grid[4][8].start = true; | 88 | p.grid[4][8].start = true; |
178 | p.grid[6][0].end = Cell::Dir::UP; | 89 | p.grid[6][0].end = Cell::Dir::UP; |
179 | 90 | ||
180 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); | 91 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdges2(p, 5); |
181 | assert(cutEdges.size() == 5); | ||
182 | Puzzle copy = p; | 92 | Puzzle copy = p; |
183 | std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; | 93 | std::vector<int> gates = {0x00344, 0x00488, 0x00489, 0x00495, 0x00496}; |
184 | for (int i=0; i<gates.size(); i++) { | 94 | for (int i=0; i<gates.size(); i++) { |
@@ -186,14 +96,12 @@ void Randomizer2::RandomizeKeep() { | |||
186 | copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | 96 | copy.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; |
187 | SetGate(gates[i], pos.x, pos.y); | 97 | SetGate(gates[i], pos.x, pos.y); |
188 | } | 98 | } |
189 | auto solutions = Solver::Solve(copy); | 99 | auto solution = GetUniqueSolution(copy); |
190 | assert(solutions.size() == 1); | 100 | p.sequence = solution.sequence; |
191 | p.sequence = solutions[0].sequence; | 101 | _serializer.WritePuzzle(p, 0x139); |
192 | PuzzleSerializer(_memory).WritePuzzle(solutions[0], 0x139); | ||
193 | } | 102 | } |
194 | 103 | ||
195 | // *** Hedges 2 *** | 104 | { // Hedges 2 |
196 | { | ||
197 | Puzzle p; | 105 | Puzzle p; |
198 | p.NewGrid(4, 4); | 106 | p.NewGrid(4, 4); |
199 | 107 | ||
@@ -210,29 +118,28 @@ void Randomizer2::RandomizeKeep() { | |||
210 | p.grid[0][8].start = true; | 118 | p.grid[0][8].start = true; |
211 | p.grid[8][0].end = Cell::Dir::RIGHT; | 119 | p.grid[8][0].end = Cell::Dir::RIGHT; |
212 | 120 | ||
213 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); | 121 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdges2(p, 7); |
214 | assert(cutEdges.size() == 7); | ||
215 | for (Pos pos : cutEdges) { | 122 | for (Pos pos : cutEdges) { |
216 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | 123 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
217 | } | 124 | } |
218 | auto solutions = Solver::Solve(p); | 125 | auto solution = GetUniqueSolution(p); |
219 | assert(solutions.size() == 1); | ||
220 | 126 | ||
221 | Puzzle q; | 127 | Puzzle q; |
222 | q.NewGrid(4, 4); | 128 | q.NewGrid(4, 4); |
223 | q.grid[0][8].start = true; | 129 | q.grid[0][8].start = true; |
224 | q.grid[8][0].end = Cell::Dir::RIGHT; | 130 | q.grid[8][0].end = Cell::Dir::RIGHT; |
225 | q.sequence = solutions[0].sequence; | 131 | q.sequence = solution.sequence; |
226 | for (Pos pos : cutEdges) { | 132 | for (Pos pos : cutEdges) { |
227 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | 133 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
228 | } | 134 | } |
229 | // Cut to 4 of 9 additional edges (total: 11) | 135 | // Cut to 6 of 9 additional edges |
230 | Randomizer2Core::CutEdgesNotOutsideNotBreakingSequence(q, 4); | 136 | for (Pos pos : Randomizer2Core::CutEdges2(q, 6)) { |
231 | PuzzleSerializer(_memory).WritePuzzle(q, 0x19DC); | 137 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
138 | } | ||
139 | _serializer.WritePuzzle(q, 0x19DC); | ||
232 | } | 140 | } |
233 | 141 | ||
234 | // *** Hedges 3 ** | 142 | { // Hedges 3 [WIP] |
235 | { | ||
236 | Puzzle p; | 143 | Puzzle p; |
237 | p.NewGrid(4, 4); | 144 | p.NewGrid(4, 4); |
238 | 145 | ||
@@ -251,17 +158,14 @@ void Randomizer2::RandomizeKeep() { | |||
251 | 158 | ||
252 | std::vector<int> pebbleMarkers = {0x034a9, 0x034b1, 0x034be, 0x034c4}; | 159 | std::vector<int> pebbleMarkers = {0x034a9, 0x034b1, 0x034be, 0x034c4}; |
253 | 160 | ||
254 | 161 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdges2(p, 7); | |
255 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); | ||
256 | assert(cutEdges.size() == 7); | ||
257 | for (Pos pos : cutEdges) { | 162 | for (Pos pos : cutEdges) { |
258 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; | 163 | p.grid[pos.x][pos.y].gap = Cell::Gap::BREAK; |
259 | } | 164 | } |
260 | PuzzleSerializer(_memory).WritePuzzle(p, 0x19E7); | 165 | // _serializer.WritePuzzle(p, 0x19E7); |
261 | } | 166 | } |
262 | 167 | ||
263 | // *** Hedges 4 *** | 168 | { // Hedges 4 |
264 | { | ||
265 | Puzzle p; | 169 | Puzzle p; |
266 | p.NewGrid(4, 4); | 170 | p.NewGrid(4, 4); |
267 | 171 | ||
@@ -283,28 +187,33 @@ void Randomizer2::RandomizeKeep() { | |||
283 | p.grid[0][8].start = true; | 187 | p.grid[0][8].start = true; |
284 | p.grid[4][0].end = Cell::Dir::UP; | 188 | p.grid[4][0].end = Cell::Dir::UP; |
285 | 189 | ||
286 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdgesToBeUnique(p); | 190 | std::vector<Pos> cutEdges = Randomizer2Core::CutEdges2(p, 2); |
287 | assert(cutEdges.size() == 2); | ||
288 | for (Pos pos : cutEdges) { | 191 | for (Pos pos : cutEdges) { |
289 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | 192 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
290 | } | 193 | } |
291 | auto solutions = Solver::Solve(p); | 194 | auto solution = GetUniqueSolution(p); |
292 | assert(solutions.size() == 1); | ||
293 | 195 | ||
294 | Puzzle q; | 196 | Puzzle q; |
295 | q.NewGrid(4, 4); | 197 | q.NewGrid(4, 4); |
296 | q.grid[0][8].start = true; | 198 | q.grid[0][8].start = true; |
297 | q.grid[4][0].end = Cell::Dir::UP; | 199 | q.grid[4][0].end = Cell::Dir::UP; |
298 | q.sequence = solutions[0].sequence; | 200 | q.sequence = solution.sequence; |
299 | for (Pos pos : cutEdges) { | 201 | for (Pos pos : cutEdges) { |
300 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | 202 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
301 | } | 203 | } |
302 | // 9 cuts, -2 from existing cuts | 204 | for (Pos pos : Randomizer2Core::CutEdges2(q, 7)) { |
303 | Randomizer2Core::CutEdgesNotOutsideNotBreakingSequence(q, 7); | 205 | q.grid[pos.x][pos.y].gap = Cell::Gap::FULL; |
304 | PuzzleSerializer(_memory).WritePuzzle(q, 0x1A0F); | 206 | } |
207 | _serializer.WritePuzzle(q, 0x1A0F); | ||
305 | } | 208 | } |
306 | } | 209 | } |
307 | 210 | ||
211 | Puzzle Randomizer2::GetUniqueSolution(Puzzle& p) { | ||
212 | auto solutions = Solver::Solve(p); | ||
213 | assert(solutions.size() == 1); | ||
214 | return solutions[0]; | ||
215 | } | ||
216 | |||
308 | void Randomizer2::SetGate(int panel, int X, int Y) { | 217 | void Randomizer2::SetGate(int panel, int X, int Y) { |
309 | float x, y, z, w; | 218 | float x, y, z, w; |
310 | if (X%2 == 0 && Y%2 == 1) { // Horizontal | 219 | if (X%2 == 0 && Y%2 == 1) { // Horizontal |
diff --git a/Source/Randomizer2.h b/Source/Randomizer2.h index 359e357..6e79694 100644 --- a/Source/Randomizer2.h +++ b/Source/Randomizer2.h | |||
@@ -1,16 +1,20 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | #include <memory> | 2 | #include <memory> |
3 | #include "PuzzleSerializer.h" | ||
3 | 4 | ||
4 | class Memory; | 5 | class Memory; |
5 | class Randomizer2 { | 6 | class Randomizer2 { |
6 | public: | 7 | public: |
7 | Randomizer2(const std::shared_ptr<Memory>& memory); | 8 | Randomizer2(const std::shared_ptr<Memory>& memory); |
8 | void Randomize(); | 9 | void Randomize(); |
10 | void RandomizeTutorial(); | ||
9 | void RandomizeKeep(); | 11 | void RandomizeKeep(); |
10 | 12 | ||
11 | private: | 13 | private: |
14 | Puzzle GetUniqueSolution(Puzzle& p); | ||
12 | void SetGate(int panel, int X, int Y); | 15 | void SetGate(int panel, int X, int Y); |
13 | void SetPos(int panel, float x, float y, float z); | 16 | void SetPos(int panel, float x, float y, float z); |
14 | 17 | ||
15 | std::shared_ptr<Memory> _memory; | 18 | std::shared_ptr<Memory> _memory; |
19 | PuzzleSerializer _serializer; | ||
16 | }; | 20 | }; |
diff --git a/Source/Randomizer2Core.cpp b/Source/Randomizer2Core.cpp index 2659076..8bd5765 100644 --- a/Source/Randomizer2Core.cpp +++ b/Source/Randomizer2Core.cpp | |||
@@ -6,40 +6,46 @@ | |||
6 | #include <iostream> | 6 | #include <iostream> |
7 | #include <cassert> | 7 | #include <cassert> |
8 | 8 | ||
9 | std::vector<Pos> Randomizer2Core::CutEdgesToBeUnique(const Puzzle& p) { | 9 | // @Cutnpaste |
10 | auto [colorGrid, numColors] = CreateColorGrid(p); | 10 | std::vector<Pos> Randomizer2Core::CutEdges(const Puzzle& p, size_t numEdges) { |
11 | std::vector<Pos> edges; | 11 | std::vector<Pos> edges; |
12 | for (int x=0; x<p.width; x++) { | 12 | for (int x=0; x<p.width; x++) { |
13 | for (int y=0; y<p.height; y++) { | 13 | for (int y=0; y<p.height; y++) { |
14 | if (x%2 == y%2) continue; | 14 | if (x%2 == y%2) continue; |
15 | if (p.grid[x][y].gap != Cell::Gap::NONE) continue; | 15 | if (p.grid[x][y].gap != Cell::Gap::NONE) continue; |
16 | |||
17 | // If the puzzle already has a sequence, don't cut along it. | ||
18 | bool inSequence = false; | ||
19 | for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y); | ||
20 | if (inSequence) continue; | ||
16 | edges.emplace_back(Pos{x, y}); | 21 | edges.emplace_back(Pos{x, y}); |
17 | } | 22 | } |
18 | } | 23 | } |
19 | return CutEdgesInternal(p, colorGrid, edges, numColors); | 24 | return CutEdgesInternal(p, edges, numEdges); |
20 | } | 25 | } |
21 | 26 | ||
22 | void Randomizer2Core::CutEdgesNotOutsideNotBreakingSequence(Puzzle& p, size_t numEdges) { | 27 | std::vector<Pos> Randomizer2Core::CutEdges2(const Puzzle& p, size_t numEdges) { |
23 | auto [colorGrid, numColors] = CreateColorGrid(p); | ||
24 | assert(numEdges <= numColors); | ||
25 | std::vector<Pos> edges; | 28 | std::vector<Pos> edges; |
29 | // Note the iterator bounds; we skip the outer edges. | ||
26 | for (int x=1; x<p.width-1; x++) { | 30 | for (int x=1; x<p.width-1; x++) { |
27 | for (int y=1; y<p.height-1; y++) { | 31 | for (int y=1; y<p.height-1; y++) { |
28 | if (x%2 == y%2) continue; | 32 | if (x%2 == y%2) continue; |
29 | if (p.grid[x][y].gap != Cell::Gap::NONE) continue; | 33 | if (p.grid[x][y].gap != Cell::Gap::NONE) continue; |
34 | |||
35 | // If the puzzle already has a sequence, don't cut along it. | ||
30 | bool inSequence = false; | 36 | bool inSequence = false; |
31 | for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y); | 37 | for (Pos pos : p.sequence) inSequence |= (pos.x == x && pos.y == y); |
32 | if (inSequence) continue; | 38 | if (inSequence) continue; |
33 | edges.emplace_back(Pos{x, y}); | 39 | edges.emplace_back(Pos{x, y}); |
34 | } | 40 | } |
35 | } | 41 | } |
36 | std::vector<Pos> cutEdges = CutEdgesInternal(p, colorGrid, edges, numEdges); | 42 | return CutEdgesInternal(p, edges, numEdges); |
37 | for (Pos pos : cutEdges) { | ||
38 | p.grid[pos.x][pos.y].gap = Cell::Gap::FULL; | ||
39 | } | ||
40 | } | 43 | } |
41 | 44 | ||
42 | std::vector<Pos> Randomizer2Core::CutEdgesInternal(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, std::vector<Pos>& edges, size_t numEdges) { | 45 | std::vector<Pos> Randomizer2Core::CutEdgesInternal(const Puzzle& p, std::vector<Pos>& edges, size_t numEdges) { |
46 | auto [colorGrid, numColors] = CreateColorGrid(p); | ||
47 | assert(numEdges <= numColors); | ||
48 | |||
43 | std::vector<Pos> cutEdges; | 49 | std::vector<Pos> cutEdges; |
44 | for (int i=0; i<numEdges; i++) { | 50 | for (int i=0; i<numEdges; i++) { |
45 | for (int j=0; j<edges.size(); j++) { | 51 | for (int j=0; j<edges.size(); j++) { |
@@ -79,6 +85,7 @@ std::vector<Pos> Randomizer2Core::CutEdgesInternal(const Puzzle& p, std::vector< | |||
79 | } | 85 | } |
80 | } | 86 | } |
81 | } | 87 | } |
88 | assert(cutEdges.size() == numEdges); | ||
82 | return cutEdges; | 89 | return cutEdges; |
83 | } | 90 | } |
84 | 91 | ||
diff --git a/Source/Randomizer2Core.h b/Source/Randomizer2Core.h index 1b97fba..63ba6a8 100644 --- a/Source/Randomizer2Core.h +++ b/Source/Randomizer2Core.h | |||
@@ -7,12 +7,11 @@ class Puzzle; | |||
7 | class Randomizer2Core { | 7 | class Randomizer2Core { |
8 | public: | 8 | public: |
9 | // CAUTION: Does not actually cut edges, just returns a list of suggested cuts. | 9 | // CAUTION: Does not actually cut edges, just returns a list of suggested cuts. |
10 | // Cuts a number of edges equal to the number of colors in the color grid. | 10 | static std::vector<Pos> CutEdges(const Puzzle& p, size_t numEdges); |
11 | static std::vector<Pos> CutEdgesToBeUnique(const Puzzle& p); | 11 | static std::vector<Pos> CutEdges2(const Puzzle& p, size_t numEdges); |
12 | static void CutEdgesNotOutsideNotBreakingSequence(Puzzle& p, size_t numEdges); | ||
13 | 12 | ||
14 | private: | 13 | private: |
15 | static std::vector<Pos> CutEdgesInternal(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, std::vector<Pos>& edges, size_t numEdges); | 14 | static std::vector<Pos> CutEdgesInternal(const Puzzle& p, std::vector<Pos>& edges, size_t numEdges); |
16 | static void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid); | 15 | static void DebugColorGrid(const std::vector<std::vector<int>>& colorGrid); |
17 | static void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y); | 16 | static void FloodFill(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int color, int x, int y); |
18 | static void FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y); | 17 | static void FloodFillOutside(const Puzzle& p, std::vector<std::vector<int>>& colorGrid, int x, int y); |
diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj index 05f9ffa..33e3697 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj | |||
@@ -161,7 +161,7 @@ | |||
161 | <ClInclude Include="Memory.h" /> | 161 | <ClInclude Include="Memory.h" /> |
162 | <ClInclude Include="Puzzle.h" /> | 162 | <ClInclude Include="Puzzle.h" /> |
163 | <ClInclude Include="Panels.h" /> | 163 | <ClInclude Include="Panels.h" /> |
164 | <ClInclude Include="PuzzlerSerializer.h" /> | 164 | <ClInclude Include="PuzzleSerializer.h" /> |
165 | <ClInclude Include="Random.h" /> | 165 | <ClInclude Include="Random.h" /> |
166 | <ClInclude Include="Randomizer.h" /> | 166 | <ClInclude Include="Randomizer.h" /> |
167 | <ClInclude Include="Randomizer2.h" /> | 167 | <ClInclude Include="Randomizer2.h" /> |
@@ -173,7 +173,7 @@ | |||
173 | <ClCompile Include="ChallengeRandomizer.cpp" /> | 173 | <ClCompile Include="ChallengeRandomizer.cpp" /> |
174 | <ClCompile Include="Memory.cpp" /> | 174 | <ClCompile Include="Memory.cpp" /> |
175 | <ClCompile Include="Puzzle.cpp" /> | 175 | <ClCompile Include="Puzzle.cpp" /> |
176 | <ClCompile Include="PuzzlerSerializer.cpp" /> | 176 | <ClCompile Include="PuzzleSerializer.cpp" /> |
177 | <ClCompile Include="Random.cpp" /> | 177 | <ClCompile Include="Random.cpp" /> |
178 | <ClCompile Include="Randomizer.cpp" /> | 178 | <ClCompile Include="Randomizer.cpp" /> |
179 | <ClCompile Include="Randomizer2.cpp" /> | 179 | <ClCompile Include="Randomizer2.cpp" /> |