diff options
Diffstat (limited to 'Source/Randomizer2.cpp')
-rw-r--r-- | Source/Randomizer2.cpp | 197 |
1 files changed, 53 insertions, 144 deletions
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 |