From ecbcb91e95a08201fbf90476ee4a18526cfc80bb Mon Sep 17 00:00:00 2001 From: jbzdarkid Date: Sat, 9 Nov 2019 12:04:19 -0800 Subject: Commit early, commit often --- Source/Panel.cpp | 245 ++++++++++++++++++++++++++++++++++++------------------- Source/Panel.h | 48 ++++++----- 2 files changed, 189 insertions(+), 104 deletions(-) (limited to 'Source') diff --git a/Source/Panel.cpp b/Source/Panel.cpp index b6f0403..f8dc36b 100644 --- a/Source/Panel.cpp +++ b/Source/Panel.cpp @@ -10,6 +10,7 @@ Puzzle PuzzleSerializer::ReadPuzzle(int id) { Puzzle p; p.width = 2 * _memory->ReadPanelData(id, GRID_SIZE_X, 1)[0] - 1; p.height = 2 * _memory->ReadPanelData(id, GRID_SIZE_Y, 1)[0] - 1; + if (p.width < 0 || p.height < 0) return p; // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. p.grid.resize(p.width); for (auto& row : p.grid) row.resize(p.height); @@ -22,78 +23,87 @@ Puzzle PuzzleSerializer::ReadPuzzle(int id) { void PuzzleSerializer::ReadIntersections(Puzzle& p, int id) { int numIntersections = _memory->ReadPanelData(id, NUM_DOTS, 1)[0]; std::vector intersectionFlags = _memory->ReadArray(id, DOT_FLAGS, numIntersections); + int numConnections = _memory->ReadPanelData(id, NUM_CONNECTIONS, 1)[0]; + std::vector connections_a = _memory->ReadArray(id, DOT_CONNECTION_A, numConnections); + std::vector connections_b = _memory->ReadArray(id, DOT_CONNECTION_B, numConnections); + std::vector intersectionLocations = _memory->ReadArray(id, DOT_POSITIONS, numIntersections*2); + // @Cleanup: Change defaults? + for (int x=0; x x2) x--; + else if (y1 < y2) y--; + else if (y1 > y2) y++; + p.grid[x][y].gap = Cell::Gap::NONE; + } + + // This iterates bottom-top, left-right int i = 0; for (;; i++) { + int flags = intersectionFlags[i]; auto [x, y] = loc_to_xy(p, i); if (y < 0) break; - if (intersectionFlags[i] & Flags::IS_STARTPOINT) { + if (flags & Flags::IS_STARTPOINT) { p.grid[x][y].start = true; } - p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]); + p.grid[x][y].dot = FlagsToDot(flags); + if (flags & Flags::IS_FULL_GAP) { + p.grid[x][y].gap = Cell::Gap::FULL; + } } - int numConnections = _memory->ReadPanelData(id, NUM_CONNECTIONS, 1)[0]; - std::vector connections_a = _memory->ReadArray(id, DOT_CONNECTION_A, numConnections); - std::vector connections_b = _memory->ReadArray(id, DOT_CONNECTION_B, numConnections); - std::vector intersections = _memory->ReadArray(id, DOT_POSITIONS, numIntersections*2); - // Iterate the remaining intersections (endpoints, dots, gaps) for (; i < numIntersections; i++) { + int location = FindConnection(i, connections_a, connections_b); + if (location == -1) continue; // @Error: Unable to find connection point + // (x1, y1) location of this intersection + // (x2, y2) location of the connected intersection + float x1 = intersectionLocations[2*i]; + float y1 = intersectionLocations[2*i+1]; + float x2 = intersectionLocations[2*location]; + float y2 = intersectionLocations[2*location+1]; + auto [x, y] = loc_to_xy(p, location); + if (intersectionFlags[i] & Flags::IS_ENDPOINT) { - for (int j=0; j intersections[2*location]) { - p.grid[x][y].end = Cell::Dir::RIGHT; - } else if (intersections[2*i + 1] > intersections[2*location + 1]) { // y coordinate is 0 (bottom) 1 (top), so this check is reversed. - p.grid[x][y].end = Cell::Dir::UP; - } else { - p.grid[x][y].end = Cell::Dir::DOWN; - } - break; - } - } + // Our x coordinate is less than the target's + if (x1 < x2) p.grid[x][y].end = Cell::Dir::LEFT; + else if (x1 > x2) p.grid[x][y].end = Cell::Dir::RIGHT; + // Note that Y coordinates are reversed: 0.0 (bottom) 1.0 (top) + else if (y1 < y2) p.grid[x][y].end = Cell::Dir::DOWN; + else if (y1 > y2) p.grid[x][y].end = Cell::Dir::UP; } else if (intersectionFlags[i] & Flags::HAS_DOT) { - for (int j=0; j intersections[2*location]) { // To the right - x++; - } else if (intersections[2*i + 1] > intersections[2*location + 1]) { - // y coordinate is 0 (bottom) 1 (top), so this check is reversed. We are above the target (location) - y--; - } else { // Beleow the target - y++; - } - - p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]); - break; - } - } - } + if (x1 < x2) x--; + else if (x1 > x2) x++; + else if (y1 < y2) y++; + else if (y1 > y2) y--; + p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]); + } else if (intersectionFlags[i] & Flags::HAS_NO_CONN) { + if (x1 < x2) x--; + else if (x1 > x2) x++; + else if (y1 < y2) y++; + else if (y1 > y2) y--; + p.grid[x][y].gap = Cell::Gap::BREAK; + } } } void PuzzleSerializer::ReadDecorations(Puzzle& p, int id) { int numDecorations = _memory->ReadPanelData(id, NUM_DECORATIONS, 1)[0]; std::vector decorations = _memory->ReadArray(id, DECORATIONS, numDecorations); - if (numDecorations != decorations.size()) return; // @Error! + if (numDecorations > 0) p.hasDecorations = true; for (int i=0; iWritePanelData(id, GRID_SIZE_Y, {(p.height + 1)/2}); WriteIntersections(p, id); - WriteDecorations(p, id); + if (p.hasDecorations) WriteDecorations(p, id); _memory->WritePanelData(id, NEEDS_REDRAW, {1}); } void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { - std::vector intersections; + std::vector intersectionLocations; std::vector intersectionFlags; std::vector connections_a; std::vector connections_b; @@ -134,30 +144,56 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { float max = 0.9f; float width_interval = (max - min) / (p.width/2); float height_interval = (max - min) / (p.height/2); + float horiz_gap_size = width_interval / 2; + float verti_gap_size = height_interval / 2; - // @Cleanup: If I write directly to locations, then I can remove this gross loop iterator. + // TODO: Compute HAS_NO_CONN / HAS_HORIZ_CONN / HAS_VERTI_CONN in this loop. + // @Cleanup: If I write directly to locations, then I can simplify this gross loop iterator. + // int numIntersections = (p.width / 2 + 1) * (p.height / 2 + 1); + // Grided intersections for (int y=p.height-1; y>=0; y-=2) { for (int x=0; x(min + (x/2) * width_interval)); - intersections.push_back(static_cast(max - (y/2) * height_interval)); + intersectionLocations.push_back(min + (x/2) * width_interval); + intersectionLocations.push_back(max - (y/2) * height_interval); int flags = 0; if (p.grid[x][y].start) { flags |= Flags::IS_STARTPOINT; } + if (p.grid[x][y].gap == Cell::Gap::FULL) { + flags |= Flags::IS_FULL_GAP; + } + switch (p.grid[x][y].dot) { + case Cell::Dot::BLACK: + flags |= Flags::HAS_DOT; + break; + case Cell::Dot::BLUE: + flags |= Flags::HAS_DOT | Flags::DOT_IS_BLUE; + break; + case Cell::Dot::YELLOW: + flags |= Flags::HAS_DOT | Flags::DOT_IS_ORANGE; + break; + case Cell::Dot::INVISIBLE: + flags |= Flags::HAS_DOT | Flags::DOT_IS_INVISIBLE; + break; + } + intersectionFlags.push_back(flags); - // Create connections for this intersection -- always write low -> high - if (y > 0) { + // Create connections for this intersection -- always write the smaller value into a, the larger into b + // Bottom connection + if (y > 0 && p.grid[x][y-1].gap == Cell::Gap::NONE) { connections_a.push_back(xy_to_loc(p, x, y-2)); connections_b.push_back(xy_to_loc(p, x, y)); } - if (x > 0) { + // Left connection + if (x > 0 && p.grid[x-1][y].gap == Cell::Gap::NONE) { connections_a.push_back(xy_to_loc(p, x-2, y)); connections_b.push_back(xy_to_loc(p, x, y)); } } } + // Endpoints for (int x=0; x(intersectionFlags.size()); // This endpoint + + connections_a.push_back(other_connection); + connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint + break; + } + } + // Add this dot to the end + float xPos = min + (x/2.0f) * width_interval; + float yPos = max - (y/2.0f) * height_interval; + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos); int flags = Flags::HAS_DOT; switch (p.grid[x][y].dot) { @@ -206,15 +263,16 @@ void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { flags |= DOT_IS_INVISIBLE; break; } + intersectionFlags.push_back(flags); + } + } - // Dot is already a point the grid, just overwrite the flags - if (x%2 == 0 && y%2 == 0) { - intersectionFlags[xy_to_loc(p, x, y)] |= flags; - continue; - } + // Gaps + for (int x=0; x(intersectionFlags.size()); // This endpoint - connections_a.push_back(static_cast(intersectionFlags.size())); // This endpoint - connections_b.push_back(other_connection); + connections_a.push_back(other_connection); + connections_b.push_back(static_cast(intersectionFlags.size() + 1)); // Next endpoint break; } } - // Add this dot to the end + // Add the two halves of this gap to the end float xPos = min + (x/2.0f) * width_interval; float yPos = max - (y/2.0f) * height_interval; - intersections.push_back(xPos); - intersections.push_back(yPos); - intersectionFlags.push_back(flags); + // Reminder: Y goes from 0.0 (bottom) to 1.0 (top) + if (x%2 == 0) { // Vertical gap + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos + verti_gap_size / 2); + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos - verti_gap_size / 2); + intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN); + intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN); + } else if (y%2 == 0) { // Horizontal gap + intersectionLocations.push_back(xPos - horiz_gap_size / 2); + intersectionLocations.push_back(yPos); + intersectionLocations.push_back(xPos + horiz_gap_size / 2); + intersectionLocations.push_back(yPos); + intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN); + intersectionFlags.push_back(Flags::HAS_NO_CONN | Flags::HAS_VERTI_CONN); + } } } _memory->WritePanelData(id, NUM_DOTS, {static_cast(intersectionFlags.size())}); - _memory->WriteArray(id, DOT_POSITIONS, intersections); + _memory->WriteArray(id, DOT_POSITIONS, intersectionLocations); _memory->WriteArray(id, DOT_FLAGS, intersectionFlags); _memory->WritePanelData(id, NUM_CONNECTIONS, {static_cast(connections_a.size())}); _memory->WriteArray(id, DOT_CONNECTION_A, connections_a); @@ -262,7 +333,7 @@ void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) { _memory->WriteArray(id, DECORATIONS, decorations); } -std::tuple PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) { +std::tuple PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { int height2 = (p.height - 1) / 2; int width2 = (p.width + 1) / 2; @@ -271,7 +342,7 @@ std::tuple PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) return {x, y}; } -int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) { +int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) const { int height2 = (p.height - 1) / 2; int width2 = (p.width + 1) / 2; @@ -279,7 +350,7 @@ int PuzzleSerializer::xy_to_loc(const Puzzle& p, int x, int y) { return rowsFromBottom * width2 + x/2; } -std::tuple PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) { +std::tuple PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { int height2 = (p.height - 3) / 2; int width2 = (p.width - 1) / 2; @@ -288,7 +359,7 @@ std::tuple PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) return {x, y}; } -int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) { +int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) const { int height2 = (p.height - 3) / 2; int width2 = (p.width - 1) / 2; @@ -296,7 +367,7 @@ int PuzzleSerializer::xy_to_dloc(const Puzzle& p, int x, int y) { return rowsFromBottom * width2 + (x - 1)/2; } -Cell::Dot PuzzleSerializer::FlagsToDot(int flags) { +Cell::Dot PuzzleSerializer::FlagsToDot(int flags) const { if (!(flags & Flags::HAS_DOT)) return Cell::Dot::NONE; if (flags & Flags::DOT_IS_BLUE) { return Cell::Dot::BLUE; @@ -308,3 +379,11 @@ Cell::Dot PuzzleSerializer::FlagsToDot(int flags) { return Cell::Dot::BLACK; } } + +int PuzzleSerializer::FindConnection(int i, const std::vector& connections_a, const std::vector& connections_b) const { + for (int j=0; j decoration; + Dir end = Dir::NONE; + std::shared_ptr decoration = nullptr; enum class Dot {NONE, BLACK, BLUE, YELLOW, INVISIBLE}; - Dot dot; + Dot dot = Dot::NONE; enum class Gap {NONE, BREAK, FULL}; - Gap gap; + Gap gap = Gap::NONE; }; struct Puzzle { int16_t height; int16_t width; + bool hasDecorations = false; std::vector> grid; enum class Symmetry {NONE, X, Y, XY}; - Symmetry sym; - bool pillar; + Symmetry sym = Symmetry::NONE; + bool pillar = false; }; class PuzzleSerializer { @@ -65,13 +66,16 @@ public: private: // @Bug: Blue and orange are swapped? enum Flags { - IS_ENDPOINT = 0x1, - IS_STARTPOINT = 0x2, - DOT_IS_BLUE = 0x100, - DOT_IS_ORANGE = 0x200, - DOT_IS_INVISIBLE = 0x1000, - IS_GAP = 0x10000, - HAS_DOT = 0x40020, + IS_ENDPOINT = 0x1, + IS_STARTPOINT = 0x2, + IS_FULL_GAP = 0x8, + HAS_DOT = 0x20, + DOT_IS_BLUE = 0x100, + DOT_IS_ORANGE = 0x200, + DOT_IS_INVISIBLE = 0x1000, + HAS_NO_CONN = 0x100000, + HAS_VERTI_CONN = 0x200000, + HAS_HORIZ_CONN = 0x400000, }; void ReadIntersections(Puzzle& p, int id); @@ -79,12 +83,14 @@ private: void WriteIntersections(const Puzzle& p, int id); void WriteDecorations(const Puzzle& p, int id); - std::tuple loc_to_xy(const Puzzle& p, int location); - int xy_to_loc(const Puzzle& p, int x, int y); + std::tuple loc_to_xy(const Puzzle& p, int location) const; + int xy_to_loc(const Puzzle& p, int x, int y) const; // Decoration location - std::tuple dloc_to_xy(const Puzzle& p, int location); - int xy_to_dloc(const Puzzle& p, int x, int y); - Cell::Dot FlagsToDot(int flags); + std::tuple dloc_to_xy(const Puzzle& p, int location) const; + int xy_to_dloc(const Puzzle& p, int x, int y) const; + Cell::Dot FlagsToDot(int flags) const; + // Iterate connection lists for another location which is connected to us; return that other location. + int FindConnection(int i, const std::vector& connections_a, const std::vector& connections_b) const; std::shared_ptr _memory; }; -- cgit 1.4.1