about summary refs log tree commit diff stats
path: root/ext/wittle_generator/Panel.h
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-10-27 13:41:27 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-10-27 13:41:27 -0400
commitdde56d26837dc52e05207c0672b3c4a1046f6cb2 (patch)
tree6e2429bfb180b4ce20374bb00b319bbeda9da86c /ext/wittle_generator/Panel.h
downloadwittle-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.h448
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
9struct 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
37class 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
82enum 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
101class 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
135struct 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
146struct SolutionPoint {
147 int pointA, pointB, pointC, pointD;
148 float f1x, f1y, f2x, f2y, f3x, f3y, f4x, f4y;
149 int endnum;
150};
151
152class 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 */