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 */ | ||
