#ifndef PANEL_H_FC471D68 #define PANEL_H_FC471D68 #include #include #include struct Point { int first; int second; Point() { first = 0; second = 0; }; Point(int x, int y) { first = x; second = y; } Point operator+(const Point& p) { return {first + p.first, second + p.second}; } Point operator*(int d) { return {first * d, second * d}; } Point operator/(int d) { return {first / d, second / d}; } bool operator==(const Point& p) const { return first == p.first && second == p.second; }; bool operator!=(const Point& p) const { return first != p.first || second != p.second; }; friend bool operator<(const Point& p1, const Point& p2) { if (p1.first == p2.first) return p1.second < p2.second; return p1.first < p2.first; }; }; class Decoration { public: enum Shape : int { Exit = 0x600001, Start = 0x600002, Stone = 0x100, Star = 0x200, Poly = 0x400, Eraser = 0x500, Triangle = 0x600, Triangle1 = 0x10600, Triangle2 = 0x20600, Triangle3 = 0x30600, Triangle4 = 0x40600, Arrow = 0x700, Arrow1 = 0x1700, Arrow2 = 0x2700, Arrow3 = 0x3700, Can_Rotate = 0x1000, Negative = 0x2000, Gap = 0x100000, Gap_Row = 0x300000, Gap_Column = 0x500000, Dot = 0x20, Dot_Row = 0x240020, Dot_Column = 0x440020, Dot_Intersection = 0x600020, Empty = 0xA00, }; enum Color : int { None = 0, Black = 0x1, White = 0x2, Red = 0x3, // Doesn't work sadly Purple = 0x4, Green = 0x5, Cyan = 0x6, Magenta = 0x7, Yellow = 0x8, Blue = 0x9, Orange = 0xA, X = 0xF, }; }; enum IntersectionFlags : int { ROW = 0x200000, COLUMN = 0x400000, INTERSECTION = 0x600000, ENDPOINT = 0x1, STARTPOINT = 0x2, OPEN = 0x3, // Puzzle loader flag - not to be written out PATH = 0x4, // Generator use only NO_POINT = 0x8, // Points that nothing connects to GAP = 0x100000, DOT = 0x20, DOT_IS_BLUE = 0x100, DOT_IS_ORANGE = 0x200, DOT_IS_INVISIBLE = 0x1000, DOT_SMALL = 0x2000, DOT_MEDIUM = 0x4000, DOT_LARGE = 0x8000, }; class Endpoint { public: enum Direction { NONE = 0, LEFT = 1, RIGHT = 2, UP = 4, DOWN = 8, UP_LEFT = 5, UP_RIGHT = 6, DOWN_LEFT = 9, DOWN_RIGHT = 10 }; Endpoint(int x, int y, Direction dir, int flags) { _x = x; _y = y; _dir = dir; _flags = flags; } int GetX() const { return _x; } void SetX(int x) { _x = x; } int GetY() const { return _y; } void SetY(int y) { _y = y; } Direction GetDir() const { return _dir; } int GetFlags() const { return _flags; } void SetDir(Direction dir) { _dir = dir; } private: int _x, _y, _flags; Direction _dir; }; struct Color { float r; float g; float b; float a; friend bool operator<(const Color& lhs, const Color& rhs) { return lhs.r * 8 + lhs.g * 4 + lhs.b * 2 + lhs.a > rhs.r * 8 + rhs.g * 4 + rhs.b * 2 + rhs.a; } }; struct SolutionPoint { int pointA, pointB, pointC, pointD; float f1x, f1y, f2x, f2y, f3x, f3y, f4x, f4y; int endnum; }; class Panel { public: void SetSymbol(int x, int y, Decoration::Shape symbol, Decoration::Color color); void SetShape(int x, int y, int shape, bool rotate, bool negative, Decoration::Color color); void ClearSymbol(int x, int y); void SetGridSymbol(int x, int y, Decoration::Shape symbol, Decoration::Color color = Decoration::Color::None); void ClearGridSymbol(int x, int y); void Resize(int width, int height); void ClearStartpoints() { _startpoints.clear(); } void ClearExits() { _endpoints.clear(); } int width() const { return _width; } int height() const { return _height; } int get(int x, int y) { return _grid[x][y]; } void set(int x, int y, int val) { _grid[x][y] = val; } void SetInvisibleSymmetry(bool val) { _invisible_symmetry = val; } std::string Write(); enum Style { SYMMETRICAL = 0x2, // Not on the town symmetry puzzles? IDK why. NO_BLINK = 0x4, HAS_DOTS = 0x8, IS_2COLOR = 0x10, HAS_STARS = 0x40, HAS_TRIANGLES = 0x80, HAS_STONES = 0x100, HAS_ERASERS = 0x1000, HAS_SHAPERS = 0x2000, IS_PIVOTABLE = 0x8000, }; enum Symmetry { // NOTE - Not all of these are valid symmetries for certain // puzzles None, Horizontal, Vertical, Rotational, RotateLeft, RotateRight, FlipXY, FlipNegXY, ParallelH, ParallelV, ParallelHFlip, ParallelVFlip, PillarParallel, PillarHorizontal, PillarVertical, PillarRotational }; Symmetry symmetry; float pathWidth; enum ColorMode { Default, Reset, Alternate, WriteColors, Treehouse, TreehouseAlternate }; ColorMode colorMode; bool decorationsOnly; bool enableFlash; Point get_sym_point(int x, int y, Symmetry symmetry) { switch (symmetry) { case None: return Point(x, y); case Symmetry::Horizontal: return Point(x, _height - 1 - y); case Symmetry::Vertical: return Point(_width - 1 - x, y); case Symmetry::Rotational: return Point(_width - 1 - x, _height - 1 - y); case Symmetry::RotateLeft: return Point(y, _width - 1 - x); case Symmetry::RotateRight: return Point(_height - 1 - y, x); case Symmetry::FlipXY: return Point(y, x); case Symmetry::FlipNegXY: return Point(_height - 1 - y, _width - 1 - x); case Symmetry::ParallelH: return Point(x, y == _height / 2 ? _height / 2 : (y + (_height + 1) / 2) % (_height + 1)); case Symmetry::ParallelV: return Point(x == _width / 2 ? _width / 2 : (x + (_width + 1) / 2) % (_width + 1), y); case Symmetry::ParallelHFlip: return Point(_width - 1 - x, y == _height / 2 ? _height / 2 : (y + (_height + 1) / 2) % (_height + 1)); case Symmetry::ParallelVFlip: return Point(x == _width / 2 ? _width / 2 : (x + (_width + 1) / 2) % (_width + 1), _height - 1 - y); case Symmetry::PillarParallel: return Point(x + _width / 2, y); case Symmetry::PillarHorizontal: return Point(x + _width / 2, _height - 1 - y); case Symmetry::PillarVertical: return Point(_width / 2 - x, y); case Symmetry::PillarRotational: return Point(_width / 2 - x, _height - 1 - y); } return Point(x, y); } Point get_sym_point(int x, int y) { return get_sym_point(x, y, symmetry); } Point get_sym_point(Point p) { return get_sym_point(p.first, p.second, symmetry); } Point get_sym_point(Point p, Symmetry symmetry) { return get_sym_point(p.first, p.second, symmetry); } Endpoint::Direction get_sym_dir(Endpoint::Direction direction, Symmetry symmetry) { int dirIndex; if (direction == Endpoint::Direction::LEFT) dirIndex = 0; if (direction == Endpoint::Direction::RIGHT) dirIndex = 1; if (direction == Endpoint::Direction::UP) dirIndex = 2; if (direction == Endpoint::Direction::DOWN) dirIndex = 3; std::vector mapping; switch (symmetry) { case Symmetry::Horizontal: mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, Endpoint::Direction::DOWN, Endpoint::Direction::UP}; break; case Symmetry::Vertical: mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, Endpoint::Direction::UP, Endpoint::Direction::DOWN}; break; case Symmetry::Rotational: mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, Endpoint::Direction::DOWN, Endpoint::Direction::UP}; break; case Symmetry::RotateLeft: mapping = {Endpoint::Direction::DOWN, Endpoint::Direction::UP, Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT}; break; case Symmetry::RotateRight: mapping = {Endpoint::Direction::UP, Endpoint::Direction::DOWN, Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT}; break; case Symmetry::FlipXY: mapping = {Endpoint::Direction::UP, Endpoint::Direction::DOWN, Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT}; break; case Symmetry::FlipNegXY: mapping = {Endpoint::Direction::DOWN, Endpoint::Direction::UP, Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT}; break; case Symmetry::ParallelH: mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, Endpoint::Direction::UP, Endpoint::Direction::DOWN}; break; case Symmetry::ParallelV: mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, Endpoint::Direction::UP, Endpoint::Direction::DOWN}; break; case Symmetry::ParallelHFlip: mapping = {Endpoint::Direction::RIGHT, Endpoint::Direction::LEFT, Endpoint::Direction::UP, Endpoint::Direction::DOWN}; break; case Symmetry::ParallelVFlip: mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, Endpoint::Direction::DOWN, Endpoint::Direction::UP}; break; default: mapping = {Endpoint::Direction::LEFT, Endpoint::Direction::RIGHT, Endpoint::Direction::UP, Endpoint::Direction::DOWN}; break; } return mapping[dirIndex]; } int get_num_grid_points() { return ((_width + 1) / 2) * ((_height + 1) / 2); } int get_num_grid_blocks() { return (_width / 2) * (_height / 2); } int get_parity() { return (get_num_grid_points() + 1) % 2; } private: std::pair loc_to_xy(int location) { int height2 = (_height - 1) / 2; int width2 = (_width + 1) / 2; int x = 2 * (location % width2); int y = 2 * (height2 - location / width2); return {x, y}; } int xy_to_loc(int x, int y) { int height2 = (_height - 1) / 2; int width2 = (_width + 1) / 2; int rowsFromBottom = height2 - y / 2; return rowsFromBottom * width2 + x / 2; } std::pair dloc_to_xy(int location) { int height2 = (_height - 3) / 2; int width2 = _width / 2; int x = 2 * (location % width2) + 1; int y = 2 * (height2 - location / width2) + 1; return {x, y}; } int xy_to_dloc(int x, int y) { int height2 = (_height - 3) / 2; int width2 = _width / 2; int rowsFromBottom = height2 - (y - 1) / 2; return rowsFromBottom * width2 + (x - 1) / 2; } int locate_segment(int x, int y, std::vector& connections_a, std::vector& connections_b) { for (int i = 0; i < connections_a.size(); i++) { std::pair coord1 = loc_to_xy(connections_a[i]); std::pair coord2 = loc_to_xy(connections_b[i]); int x1 = coord1.first, y1 = coord1.second, x2 = coord2.first, y2 = coord2.second; if ((x1 == x - 1 && x2 == x + 1 && y1 == y && y2 == y) || (y1 == y - 1 && y2 == y + 1 && x1 == x && x2 == x)) { return i; } } return -1; } bool break_segment(int x, int y, std::vector& connections_a, std::vector& connections_b, std::vector& intersections, std::vector& intersectionFlags) { int i = locate_segment(x, y, connections_a, connections_b); if (i == -1) { return false; } int other_connection = connections_b[i]; connections_b[i] = static_cast(intersectionFlags.size()); connections_a.push_back(static_cast(intersectionFlags.size())); connections_b.push_back(other_connection); intersections.push_back(static_cast(minx + x * unitWidth)); intersections.push_back( static_cast(miny + (_height - 1 - y) * unitHeight)); intersectionFlags.push_back(_grid[x][y]); return true; } bool break_segment_gap(int x, int y, std::vector& connections_a, std::vector& connections_b, std::vector& intersections, std::vector& intersectionFlags) { int i = locate_segment(x, y, connections_a, connections_b); if (i == -1) { return false; } int other_connection = connections_b[i]; connections_b[i] = static_cast(intersectionFlags.size() + 1); connections_a.push_back(other_connection); connections_b.push_back(static_cast(intersectionFlags.size())); if (!(_grid[x][y] & IntersectionFlags::GAP)) { _grid[x][y] |= (x % 2 == 0 ? IntersectionFlags::COLUMN : IntersectionFlags::ROW); connections_a.push_back(static_cast(intersectionFlags.size())); connections_b.push_back(static_cast(intersectionFlags.size() + 1)); } double xOffset = _grid[x][y] & IntersectionFlags::ROW ? 0.5 : 0; double yOffset = _grid[x][y] & IntersectionFlags::COLUMN ? 0.5 : 0; intersections.push_back( static_cast(minx + (x + xOffset) * unitWidth)); intersections.push_back( static_cast(miny + (_height - 1 - y - yOffset) * unitHeight)); intersections.push_back( static_cast(minx + (x - xOffset) * unitWidth)); intersections.push_back( static_cast(miny + (_height - 1 - y + yOffset) * unitHeight)); intersectionFlags.push_back(_grid[x][y]); intersectionFlags.push_back(_grid[x][y]); return true; } int _width, _height; std::vector> _grid; std::vector _startpoints; std::vector _endpoints; float minx, miny, maxx, maxy, unitWidth, unitHeight; int _style; bool _resized; bool _invisible_symmetry = false; }; #endif /* end of include guard: PANEL_H_FC471D68 */