diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2023-10-27 13:41:27 -0400 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2023-10-27 13:41:27 -0400 |
commit | dde56d26837dc52e05207c0672b3c4a1046f6cb2 (patch) | |
tree | 6e2429bfb180b4ce20374bb00b319bbeda9da86c /ext/wittle_generator/Panel.h | |
download | wittle-dde56d26837dc52e05207c0672b3c4a1046f6cb2.tar.gz wittle-dde56d26837dc52e05207c0672b3c4a1046f6cb2.tar.bz2 wittle-dde56d26837dc52e05207c0672b3c4a1046f6cb2.zip |
pulled out generation logic from sigma's rando
Diffstat (limited to 'ext/wittle_generator/Panel.h')
-rw-r--r-- | ext/wittle_generator/Panel.h | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/ext/wittle_generator/Panel.h b/ext/wittle_generator/Panel.h new file mode 100644 index 0000000..7444a9a --- /dev/null +++ b/ext/wittle_generator/Panel.h | |||
@@ -0,0 +1,448 @@ | |||
1 | #ifndef PANEL_H_FC471D68 | ||
2 | #define PANEL_H_FC471D68 | ||
3 | |||
4 | #include <stdint.h> | ||
5 | |||
6 | #include <tuple> | ||
7 | #include <vector> | ||
8 | |||
9 | struct Point { | ||
10 | int first; | ||
11 | int second; | ||
12 | Point() { | ||
13 | first = 0; | ||
14 | second = 0; | ||
15 | }; | ||
16 | Point(int x, int y) { | ||
17 | first = x; | ||
18 | second = y; | ||
19 | } | ||
20 | Point operator+(const Point& p) { | ||
21 | return {first + p.first, second + p.second}; | ||
22 | } | ||
23 | Point operator*(int d) { return {first * d, second * d}; } | ||
24 | Point operator/(int d) { return {first / d, second / d}; } | ||
25 | bool operator==(const Point& p) const { | ||
26 | return first == p.first && second == p.second; | ||
27 | }; | ||
28 | bool operator!=(const Point& p) const { | ||
29 | return first != p.first || second != p.second; | ||
30 | }; | ||
31 | friend bool operator<(const Point& p1, const Point& p2) { | ||
32 | if (p1.first == p2.first) return p1.second < p2.second; | ||
33 | return p1.first < p2.first; | ||
34 | }; | ||
35 | }; | ||
36 | |||
37 | class Decoration { | ||
38 | public: | ||
39 | enum Shape : int { | ||
40 | Exit = 0x600001, | ||
41 | Start = 0x600002, | ||
42 | Stone = 0x100, | ||
43 | Star = 0x200, | ||
44 | Poly = 0x400, | ||
45 | Eraser = 0x500, | ||
46 | Triangle = 0x600, | ||
47 | Triangle1 = 0x10600, | ||
48 | Triangle2 = 0x20600, | ||
49 | Triangle3 = 0x30600, | ||
50 | Triangle4 = 0x40600, | ||
51 | Arrow = 0x700, | ||
52 | Arrow1 = 0x1700, | ||
53 | Arrow2 = 0x2700, | ||
54 | Arrow3 = 0x3700, | ||
55 | Can_Rotate = 0x1000, | ||
56 | Negative = 0x2000, | ||
57 | Gap = 0x100000, | ||
58 | Gap_Row = 0x300000, | ||
59 | Gap_Column = 0x500000, | ||
60 | Dot = 0x20, | ||
61 | Dot_Row = 0x240020, | ||
62 | Dot_Column = 0x440020, | ||
63 | Dot_Intersection = 0x600020, | ||
64 | Empty = 0xA00, | ||
65 | }; | ||
66 | enum Color : int { | ||
67 | None = 0, | ||
68 | Black = 0x1, | ||
69 | White = 0x2, | ||
70 | Red = 0x3, // Doesn't work sadly | ||
71 | Purple = 0x4, | ||
72 | Green = 0x5, | ||
73 | Cyan = 0x6, | ||
74 | Magenta = 0x7, | ||
75 | Yellow = 0x8, | ||
76 | Blue = 0x9, | ||
77 | Orange = 0xA, | ||
78 | X = 0xF, | ||
79 | }; | ||
80 | }; | ||
81 | |||
82 | enum IntersectionFlags : int { | ||
83 | ROW = 0x200000, | ||
84 | COLUMN = 0x400000, | ||
85 | INTERSECTION = 0x600000, | ||
86 | ENDPOINT = 0x1, | ||
87 | STARTPOINT = 0x2, | ||
88 | OPEN = 0x3, // Puzzle loader flag - not to be written out | ||
89 | PATH = 0x4, // Generator use only | ||
90 | NO_POINT = 0x8, // Points that nothing connects to | ||
91 | GAP = 0x100000, | ||
92 | DOT = 0x20, | ||
93 | DOT_IS_BLUE = 0x100, | ||
94 | DOT_IS_ORANGE = 0x200, | ||
95 | DOT_IS_INVISIBLE = 0x1000, | ||
96 | DOT_SMALL = 0x2000, | ||
97 | DOT_MEDIUM = 0x4000, | ||
98 | DOT_LARGE = 0x8000, | ||
99 | }; | ||
100 | |||
101 | class Endpoint { | ||
102 | public: | ||
103 | enum Direction { | ||
104 | NONE = 0, | ||
105 | LEFT = 1, | ||
106 | RIGHT = 2, | ||
107 | UP = 4, | ||
108 | DOWN = 8, | ||
109 | UP_LEFT = 5, | ||
110 | UP_RIGHT = 6, | ||
111 | DOWN_LEFT = 9, | ||
112 | DOWN_RIGHT = 10 | ||
113 | }; | ||
114 | |||
115 | Endpoint(int x, int y, Direction dir, int flags) { | ||
116 | _x = x; | ||
117 | _y = y; | ||
118 | _dir = dir; | ||
119 | _flags = flags; | ||
120 | } | ||
121 | |||
122 | int GetX() { return _x; } | ||
123 | void SetX(int x) { _x = x; } | ||
124 | int GetY() { return _y; } | ||
125 | void SetY(int y) { _y = y; } | ||
126 | Direction GetDir() { return _dir; } | ||
127 | int GetFlags() { return _flags; } | ||
128 | void SetDir(Direction dir) { _dir = dir; } | ||
129 | |||
130 | private: | ||
131 | int _x, _y, _flags; | ||
132 | Direction _dir; | ||
133 | }; | ||
134 | |||
135 | struct Color { | ||
136 | float r; | ||
137 | float g; | ||
138 | float b; | ||
139 | float a; | ||
140 | friend bool operator<(const Color& lhs, const Color& rhs) { | ||
141 | return lhs.r * 8 + lhs.g * 4 + lhs.b * 2 + lhs.a > | ||
142 | rhs.r * 8 + rhs.g * 4 + rhs.b * 2 + rhs.a; | ||
143 | } | ||
144 | }; | ||
145 | |||
146 | struct SolutionPoint { | ||
147 | int pointA, pointB, pointC, pointD; | ||
148 | float f1x, f1y, f2x, f2y, f3x, f3y, f4x, f4y; | ||
149 | int endnum; | ||
150 | }; | ||
151 | |||
152 | class Panel { | ||
153 | public: | ||
154 | void SetSymbol(int x, int y, Decoration::Shape symbol, | ||
155 | Decoration::Color color); | ||
156 | void SetShape(int x, int y, int shape, bool rotate, bool negative, | ||
157 | Decoration::Color color); | ||
158 | void ClearSymbol(int x, int y); | ||
159 | void SetGridSymbol(int x, int y, Decoration::Shape symbol, | ||
160 | Decoration::Color color = Decoration::Color::None); | ||
161 | void ClearGridSymbol(int x, int y); | ||
162 | void Resize(int width, int height); | ||
163 | |||
164 | int width() const { return _width; } | ||
165 | int height() const { return _height; } | ||
166 | |||
167 | int get(int x, int y) { return _grid[x][y]; } | ||
168 | void set(int x, int y, int val) { _grid[x][y] = val; } | ||
169 | |||
170 | enum Style { | ||
171 | SYMMETRICAL = 0x2, // Not on the town symmetry puzzles? IDK why. | ||
172 | NO_BLINK = 0x4, | ||
173 | HAS_DOTS = 0x8, | ||
174 | IS_2COLOR = 0x10, | ||
175 | HAS_STARS = 0x40, | ||
176 | HAS_TRIANGLES = 0x80, | ||
177 | HAS_STONES = 0x100, | ||
178 | HAS_ERASERS = 0x1000, | ||
179 | HAS_SHAPERS = 0x2000, | ||
180 | IS_PIVOTABLE = 0x8000, | ||
181 | }; | ||
182 | |||
183 | enum Symmetry { // NOTE - Not all of these are valid symmetries for certain | ||
184 | // puzzles | ||
185 | None, | ||
186 | Horizontal, | ||
187 | Vertical, | ||
188 | Rotational, | ||
189 | RotateLeft, | ||
190 | RotateRight, | ||
191 | FlipXY, | ||
192 | FlipNegXY, | ||
193 | ParallelH, | ||
194 | ParallelV, | ||
195 | ParallelHFlip, | ||
196 | ParallelVFlip, | ||
197 | PillarParallel, | ||
198 | PillarHorizontal, | ||
199 | PillarVertical, | ||
200 | PillarRotational | ||
201 | }; | ||
202 | Symmetry symmetry; | ||
203 | |||
204 | float pathWidth; | ||
205 | enum ColorMode { | ||
206 | Default, | ||
207 | Reset, | ||
208 | Alternate, | ||
209 | WriteColors, | ||
210 | Treehouse, | ||
211 | TreehouseAlternate | ||
212 | }; | ||
213 | ColorMode colorMode; | ||
214 | bool decorationsOnly; | ||
215 | bool enableFlash; | ||
216 | |||
217 | Point get_sym_point(int x, int y, Symmetry symmetry) { | ||
218 | switch (symmetry) { | ||
219 | case None: | ||
220 | return Point(x, y); | ||
221 | case Symmetry::Horizontal: | ||
222 | return Point(x, _height - 1 - y); | ||
223 | case Symmetry::Vertical: | ||
224 | return Point(_width - 1 - x, y); | ||
225 | case Symmetry::Rotational: | ||
226 | return Point(_width - 1 - x, _height - 1 - y); | ||
227 | case Symmetry::RotateLeft: | ||
228 | return Point(y, _width - 1 - x); | ||
229 | case Symmetry::RotateRight: | ||
230 | return Point(_height - 1 - y, x); | ||
231 | case Symmetry::FlipXY: | ||
232 | return Point(y, x); | ||
233 | case Symmetry::FlipNegXY: | ||
234 | return Point(_height - 1 - y, _width - 1 - x); | ||
235 | case Symmetry::ParallelH: | ||
236 | return Point(x, y == _height / 2 | ||
237 | ? _height / 2 | ||
238 | : (y + (_height + 1) / 2) % (_height + 1)); | ||
239 | case Symmetry::ParallelV: | ||
240 | return Point(x == _width / 2 ? _width / 2 | ||
241 | : (x + (_width + 1) / 2) % (_width + 1), | ||
242 | y); | ||
243 | case Symmetry::ParallelHFlip: | ||
244 | return Point(_width - 1 - x, | ||
245 | y == _height / 2 | ||
246 | ? _height / 2 | ||
247 | : (y + (_height + 1) / 2) % (_height + 1)); | ||
248 | case Symmetry::ParallelVFlip: | ||
249 | return Point(x == _width / 2 ? _width / 2 | ||
250 | : (x + (_width + 1) / 2) % (_width + 1), | ||
251 | _height - 1 - y); | ||
252 | case Symmetry::PillarParallel: | ||
253 | return Point(x + _width / 2, y); | ||
254 | case Symmetry::PillarHorizontal: | ||
255 | return Point(x + _width / 2, _height - 1 - y); | ||
256 | case Symmetry::PillarVertical: | ||
257 | return Point(_width / 2 - x, y); | ||
258 | case Symmetry::PillarRotational: | ||
259 | return Point(_width / 2 - x, _height - 1 - y); | ||
260 | } | ||
261 | return Point(x, y); | ||
262 | } | ||
263 | |||
264 | Point get_sym_point(int x, int y) { return get_sym_point(x, y, symmetry); } | ||
265 | Point get_sym_point(Point p) { | ||
266 | return get_sym_point(p.first, p.second, symmetry); | ||
267 | } | ||
268 | Point get_sym_point(Point p, Symmetry symmetry) { | ||
269 | return get_sym_point(p.first, p.second, symmetry); | ||
270 | } | ||
271 | Endpoint::Direction get_sym_dir(Endpoint::Direction direction, | ||
272 | Symmetry symmetry) { | ||
273 | int dirIndex; | ||
274 | if (direction == Endpoint::Direction::LEFT) dirIndex = 0; | ||
275 | if (direction == Endpoint::Direction::RIGHT) dirIndex = 1; | ||
276 | if (direction == Endpoint::Direction::UP) dirIndex = 2; | ||
277 | if (direction == Endpoint::Direction::DOWN) dirIndex = 3; | ||
278 | std::vector<Endpoint::Direction> mapping; | ||
279 | switch (symmetry) { | ||
280 | case Symmetry::Horizontal: | ||
281 | mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, | ||
282 | Endpoint::Direction::DOWN, Endpoint::Direction::UP}; | ||
283 | break; | ||
284 | case Symmetry::Vertical: | ||
285 | mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, | ||
286 | Endpoint::Direction::UP, Endpoint::Direction::DOWN}; | ||
287 | break; | ||
288 | case Symmetry::Rotational: | ||
289 | mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, | ||
290 | Endpoint::Direction::DOWN, Endpoint::Direction::UP}; | ||
291 | break; | ||
292 | case Symmetry::RotateLeft: | ||
293 | mapping = {Endpoint::Direction::DOWN, Endpoint::Direction::UP, | ||
294 | Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT}; | ||
295 | break; | ||
296 | case Symmetry::RotateRight: | ||
297 | mapping = {Endpoint::Direction::UP, Endpoint::Direction::DOWN, | ||
298 | Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT}; | ||
299 | break; | ||
300 | case Symmetry::FlipXY: | ||
301 | mapping = {Endpoint::Direction::UP, Endpoint::Direction::DOWN, | ||
302 | Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT}; | ||
303 | break; | ||
304 | case Symmetry::FlipNegXY: | ||
305 | mapping = {Endpoint::Direction::DOWN, Endpoint::Direction::UP, | ||
306 | Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT}; | ||
307 | break; | ||
308 | case Symmetry::ParallelH: | ||
309 | mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, | ||
310 | Endpoint::Direction::UP, Endpoint::Direction::DOWN}; | ||
311 | break; | ||
312 | case Symmetry::ParallelV: | ||
313 | mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, | ||
314 | Endpoint::Direction::UP, Endpoint::Direction::DOWN}; | ||
315 | break; | ||
316 | case Symmetry::ParallelHFlip: | ||
317 | mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, | ||
318 | Endpoint::Direction::UP, Endpoint::Direction::DOWN}; | ||
319 | break; | ||
320 | case Symmetry::ParallelVFlip: | ||
321 | mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, | ||
322 | Endpoint::Direction::DOWN, Endpoint::Direction::UP}; | ||
323 | break; | ||
324 | default: | ||
325 | mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, | ||
326 | Endpoint::Direction::UP, Endpoint::Direction::DOWN}; | ||
327 | break; | ||
328 | } | ||
329 | return mapping[dirIndex]; | ||
330 | } | ||
331 | |||
332 | int get_num_grid_points() { return ((_width + 1) / 2) * ((_height + 1) / 2); } | ||
333 | int get_num_grid_blocks() { return (_width / 2) * (_height / 2); } | ||
334 | int get_parity() { return (get_num_grid_points() + 1) % 2; } | ||
335 | |||
336 | private: | ||
337 | std::pair<int, int> loc_to_xy(int location) { | ||
338 | int height2 = (_height - 1) / 2; | ||
339 | int width2 = (_width + 1) / 2; | ||
340 | |||
341 | int x = 2 * (location % width2); | ||
342 | int y = 2 * (height2 - location / width2); | ||
343 | return {x, y}; | ||
344 | } | ||
345 | |||
346 | int xy_to_loc(int x, int y) { | ||
347 | int height2 = (_height - 1) / 2; | ||
348 | int width2 = (_width + 1) / 2; | ||
349 | |||
350 | int rowsFromBottom = height2 - y / 2; | ||
351 | return rowsFromBottom * width2 + x / 2; | ||
352 | } | ||
353 | |||
354 | std::pair<int, int> dloc_to_xy(int location) { | ||
355 | int height2 = (_height - 3) / 2; | ||
356 | int width2 = _width / 2; | ||
357 | |||
358 | int x = 2 * (location % width2) + 1; | ||
359 | int y = 2 * (height2 - location / width2) + 1; | ||
360 | return {x, y}; | ||
361 | } | ||
362 | |||
363 | int xy_to_dloc(int x, int y) { | ||
364 | int height2 = (_height - 3) / 2; | ||
365 | int width2 = _width / 2; | ||
366 | |||
367 | int rowsFromBottom = height2 - (y - 1) / 2; | ||
368 | return rowsFromBottom * width2 + (x - 1) / 2; | ||
369 | } | ||
370 | |||
371 | int locate_segment(int x, int y, std::vector<int>& connections_a, | ||
372 | std::vector<int>& connections_b) { | ||
373 | for (int i = 0; i < connections_a.size(); i++) { | ||
374 | std::pair<int, int> coord1 = loc_to_xy(connections_a[i]); | ||
375 | std::pair<int, int> coord2 = loc_to_xy(connections_b[i]); | ||
376 | int x1 = coord1.first, y1 = coord1.second, x2 = coord2.first, | ||
377 | y2 = coord2.second; | ||
378 | if ((x1 == x - 1 && x2 == x + 1 && y1 == y && y2 == y) || | ||
379 | (y1 == y - 1 && y2 == y + 1 && x1 == x && x2 == x)) { | ||
380 | return i; | ||
381 | } | ||
382 | } | ||
383 | return -1; | ||
384 | } | ||
385 | |||
386 | bool break_segment(int x, int y, std::vector<int>& connections_a, | ||
387 | std::vector<int>& connections_b, | ||
388 | std::vector<float>& intersections, | ||
389 | std::vector<int>& intersectionFlags) { | ||
390 | int i = locate_segment(x, y, connections_a, connections_b); | ||
391 | if (i == -1) { | ||
392 | return false; | ||
393 | } | ||
394 | int other_connection = connections_b[i]; | ||
395 | connections_b[i] = static_cast<int>(intersectionFlags.size()); | ||
396 | connections_a.push_back(static_cast<int>(intersectionFlags.size())); | ||
397 | connections_b.push_back(other_connection); | ||
398 | intersections.push_back(static_cast<float>(minx + x * unitWidth)); | ||
399 | intersections.push_back( | ||
400 | static_cast<float>(miny + (_height - 1 - y) * unitHeight)); | ||
401 | intersectionFlags.push_back(_grid[x][y]); | ||
402 | return true; | ||
403 | } | ||
404 | |||
405 | bool break_segment_gap(int x, int y, std::vector<int>& connections_a, | ||
406 | std::vector<int>& connections_b, | ||
407 | std::vector<float>& intersections, | ||
408 | std::vector<int>& intersectionFlags) { | ||
409 | int i = locate_segment(x, y, connections_a, connections_b); | ||
410 | if (i == -1) { | ||
411 | return false; | ||
412 | } | ||
413 | int other_connection = connections_b[i]; | ||
414 | connections_b[i] = static_cast<int>(intersectionFlags.size() + 1); | ||
415 | connections_a.push_back(other_connection); | ||
416 | connections_b.push_back(static_cast<int>(intersectionFlags.size())); | ||
417 | if (!(_grid[x][y] & IntersectionFlags::GAP)) { | ||
418 | _grid[x][y] |= | ||
419 | (x % 2 == 0 ? IntersectionFlags::COLUMN : IntersectionFlags::ROW); | ||
420 | connections_a.push_back(static_cast<int>(intersectionFlags.size())); | ||
421 | connections_b.push_back(static_cast<int>(intersectionFlags.size() + 1)); | ||
422 | } | ||
423 | double xOffset = _grid[x][y] & IntersectionFlags::ROW ? 0.5 : 0; | ||
424 | double yOffset = _grid[x][y] & IntersectionFlags::COLUMN ? 0.5 : 0; | ||
425 | intersections.push_back( | ||
426 | static_cast<float>(minx + (x + xOffset) * unitWidth)); | ||
427 | intersections.push_back( | ||
428 | static_cast<float>(miny + (_height - 1 - y - yOffset) * unitHeight)); | ||
429 | intersections.push_back( | ||
430 | static_cast<float>(minx + (x - xOffset) * unitWidth)); | ||
431 | intersections.push_back( | ||
432 | static_cast<float>(miny + (_height - 1 - y + yOffset) * unitHeight)); | ||
433 | intersectionFlags.push_back(_grid[x][y]); | ||
434 | intersectionFlags.push_back(_grid[x][y]); | ||
435 | return true; | ||
436 | } | ||
437 | |||
438 | int _width, _height; | ||
439 | |||
440 | std::vector<std::vector<int>> _grid; | ||
441 | std::vector<Point> _startpoints; | ||
442 | std::vector<Endpoint> _endpoints; | ||
443 | float minx, miny, maxx, maxy, unitWidth, unitHeight; | ||
444 | int _style; | ||
445 | bool _resized; | ||
446 | }; | ||
447 | |||
448 | #endif /* end of include guard: PANEL_H_FC471D68 */ | ||