From 0d0abe2ee56382c5751dd12fbca75af87773879f Mon Sep 17 00:00:00 2001 From: jbzdarkid Date: Thu, 14 Nov 2019 09:12:38 -0800 Subject: Refactoring - split out puzzle from serializer --- Source/Puzzle.cpp | 412 +++---------------------------------------- Source/Puzzle.h | 76 +------- Source/PuzzlerSerializer.cpp | 400 +++++++++++++++++++++++++++++++++++++++++ Source/PuzzlerSerializer.h | 45 +++++ Source/Source.vcxproj | 2 + 5 files changed, 478 insertions(+), 457 deletions(-) create mode 100644 Source/PuzzlerSerializer.cpp create mode 100644 Source/PuzzlerSerializer.h diff --git a/Source/Puzzle.cpp b/Source/Puzzle.cpp index cc552d3..72af129 100644 --- a/Source/Puzzle.cpp +++ b/Source/Puzzle.cpp @@ -1,400 +1,40 @@ #include "Puzzle.h" #include "Memory.h" +#include -#pragma warning (disable:26451) -#pragma warning (disable:26812) -PuzzleSerializer::PuzzleSerializer(const std::shared_ptr& memory) : _memory(memory) {} - -Puzzle PuzzleSerializer::ReadPuzzle(int id) { - int width = 2 * _memory->ReadPanelData(id, GRID_SIZE_X, 1)[0] - 1; - int height = 2 * _memory->ReadPanelData(id, GRID_SIZE_Y, 1)[0] - 1; - if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. - - Puzzle p; - p.NewGrid(width, height); - ReadIntersections(p, id); - ReadDecorations(p, id); - return p; -} - -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 (flags & Flags::IS_STARTPOINT) { - p.grid[x][y].start = true; - } - p.grid[x][y].dot = FlagsToDot(flags); - if (flags & Flags::IS_FULL_GAP) { - p.grid[x][y].gap = Cell::Gap::FULL; - } - } - - // 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) { - // 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) { - 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_ONE_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 > 0) p.hasDecorations = true; - - for (int i=0; i(); - p.grid[x][y].decoration = d; - d->type = static_cast(decorations[i] & 0xFF00); - switch(d->type) { - case Type::Poly: - case Type::RPoly: - case Type::Ylop: - d->polyshape = decorations[i] & 0xFFFF0000; - break; - case Type::Triangle: - d->count = decorations[i] & 0x000F0000; - break; - } - d->color = static_cast(decorations[i] & 0xF); - } +inline Cell Puzzle::GetCell(int x, int y) const { + x = Mod(x); + if (!SafeCell(x, y)) return Cell::Undefined(); + return grid[x][y]; } -void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { - _memory->WritePanelData(id, GRID_SIZE_X, {(p.width + 1)/2}); - _memory->WritePanelData(id, GRID_SIZE_Y, {(p.height + 1)/2}); - - WriteIntersections(p, id); - if (p.hasDecorations) WriteDecorations(p, id); - - _memory->WritePanelData(id, NEEDS_REDRAW, {1}); +inline Cell::Color Puzzle::GetLine(int x, int y) const { + return grid[x][y].color; } -void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { - std::vector intersectionLocations; - std::vector intersectionFlags; - std::vector connections_a; - std::vector connections_b; - - float min = 0.1f; - 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 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 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)); - flags |= Flags::HAS_VERTI_CONN; - numConnections++; - } - // Top connection - if (y < p.height - 1 && p.grid[x][y+1].gap == Cell::Gap::NONE) { - flags |= Flags::HAS_VERTI_CONN; - numConnections++; - } - // 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)); - flags |= Flags::HAS_HORIZ_CONN; - numConnections++; - } - // Right connection - if (x < p.width - 1 && p.grid[x+1][y].gap == Cell::Gap::NONE) { - flags |= Flags::HAS_HORIZ_CONN; - numConnections++; - } - if (numConnections == 1) flags |= HAS_ONE_CONN; - intersectionFlags.push_back(flags); - } - } - - // Endpoints - for (int x=0; x(intersectionFlags.size())); // This endpoint - - float xPos = min + (x/2) * width_interval; - float yPos = max - (y/2) * height_interval; - switch (p.grid[x][y].end) { - case Cell::Dir::LEFT: - xPos -= .05f; - break; - case Cell::Dir::RIGHT: - xPos += .05f; - break; - case Cell::Dir::UP: - yPos += .05f; // Y position goes from 0 (bottom) to 1 (top), so this is reversed. - break; - case Cell::Dir::DOWN: - yPos -= .05f; - break; - } - intersectionLocations.push_back(xPos); - intersectionLocations.push_back(yPos); - intersectionFlags.push_back(Flags::IS_ENDPOINT); - } +inline void Puzzle::NewGrid(int newWidth, int newHeight) { + if (newWidth == 0) { + assert(false); + newWidth = width; + newHeight = height; + } else { + // @Cleanup! This should be in the ctor... + width = 2*newWidth + 1; + height = 2*newHeight + 1; } - - // Dots - 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) { - case Cell::Dot::BLACK: - break; - case Cell::Dot::BLUE: - flags |= DOT_IS_BLUE; - break; - case Cell::Dot::YELLOW: - flags |= DOT_IS_ORANGE; - break; - case Cell::Dot::INVISIBLE: - flags |= DOT_IS_INVISIBLE; - break; - } - intersectionFlags.push_back(flags); - } - } - - // Gaps - for (int x=0; x(intersectionFlags.size())); // This endpoint - intersectionLocations.push_back(xPos); - intersectionLocations.push_back(yPos + verti_gap_size / 2); - intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); - - connections_a.push_back(xy_to_loc(p, x, y+1)); - connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint - intersectionLocations.push_back(xPos); - intersectionLocations.push_back(yPos - verti_gap_size / 2); - intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); - } else if (y%2 == 0) { // Horizontal gap - connections_a.push_back(xy_to_loc(p, x-1, y)); - connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint - intersectionLocations.push_back(xPos - horiz_gap_size / 2); - intersectionLocations.push_back(yPos); - intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); - - connections_a.push_back(xy_to_loc(p, x+1, y)); - connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint - intersectionLocations.push_back(xPos + horiz_gap_size / 2); - intersectionLocations.push_back(yPos); - intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); - } - } - } - - _memory->WritePanelData(id, NUM_DOTS, {static_cast(intersectionFlags.size())}); - _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); - _memory->WriteArray(id, DOT_CONNECTION_B, connections_b); -} - -void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) { - std::vector decorations; - for (int y=p.height-2; y>0; y-=2) { - for (int x=1; xcolor | d->type | d->count | d->polyshape); - } else { - decorations.push_back(0); - } - } - } - - _memory->WritePanelData(id, NUM_DECORATIONS, {static_cast(decorations.size())}); - _memory->WriteArray(id, DECORATIONS, decorations); -} - -std::tuple PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { - int height2 = (p.height - 1) / 2; - int width2 = (p.width + 1) / 2; - - int x = 2 * (location % width2); - int y = 2 * (height2 - location / width2); - return {x, y}; + grid.clear(); + grid.resize(width); + for (int x=0; x PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { - int height2 = (p.height - 3) / 2; - int width2 = (p.width - 1) / 2; - - int x = 2 * (location % width2) + 1; - int y = 2 * (height2 - location / width2) + 1; - return {x, 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; - - int rowsFromBottom = height2 - (y - 1)/2; - return rowsFromBottom * width2 + (x - 1)/2; -} - -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; - } else if (flags & Flags::DOT_IS_ORANGE) { - return Cell::Dot::YELLOW; - } else if (flags & Flags::DOT_IS_INVISIBLE) { - return Cell::Dot::INVISIBLE; - } else { - return Cell::Dot::BLACK; - } +inline int Puzzle::Mod(int x) const { + if (!pillar) return x; + return (x + width * height * 2) % width; } -int PuzzleSerializer::FindConnection(int i, const std::vector& connections_a, const std::vector& connections_b) const { - for (int j=0; j= width) return false; + if (y < 0 || y >= height) return false; + return true; } diff --git a/Source/Puzzle.h b/Source/Puzzle.h index 94cb4b0..3a8e73b 100644 --- a/Source/Puzzle.h +++ b/Source/Puzzle.h @@ -62,7 +62,6 @@ struct Cell { struct Negation {}; struct Pos {int x; int y;}; -#include // TODO: Move this + impl to cpp class Puzzle { public: int16_t height; @@ -77,28 +76,9 @@ public: std::vector negations; std::vector invalidElements; - inline Cell GetCell(int x, int y) const { - x = Mod(x); - if (!SafeCell(x, y)) return Cell::Undefined(); - return grid[x][y]; - } - inline Cell::Color GetLine(int x, int y) const { - return grid[x][y].color; - } - inline void NewGrid(int newWidth, int newHeight) { - if (newWidth == 0) { - assert(false); - newWidth = width; - newHeight = height; - } else { - // @Cleanup! This should be in the ctor... - width = 2*newWidth + 1; - height = 2*newHeight + 1; - } - grid.clear(); - grid.resize(width); - for (int x=0; x> grid; private: - inline int Mod(int x) const { - if (!pillar) return x; - return (x + width * height * 2) % width; - } - - inline bool SafeCell(int x, int y) const { - if (x < 0 || x >= width) return false; - if (y < 0 || y >= height) return false; - return true; - } -}; - -class PuzzleSerializer { -public: - PuzzleSerializer(const std::shared_ptr& memory); - Puzzle ReadPuzzle(int id); - void WritePuzzle(const Puzzle& p, int id); - -private: - // @Bug: Blue and orange are swapped? - enum Flags { - 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_ONE_CONN = 0x100000, - HAS_VERTI_CONN = 0x200000, - HAS_HORIZ_CONN = 0x400000, - }; - - void ReadIntersections(Puzzle& p, int id); - void ReadDecorations(Puzzle& p, int id); - void WriteIntersections(const Puzzle& p, int id); - void WriteDecorations(const Puzzle& p, int id); - - 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) 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; + inline int Mod(int x) const; + inline bool SafeCell(int x, int y) const; }; diff --git a/Source/PuzzlerSerializer.cpp b/Source/PuzzlerSerializer.cpp new file mode 100644 index 0000000..779336d --- /dev/null +++ b/Source/PuzzlerSerializer.cpp @@ -0,0 +1,400 @@ +#include "PuzzlerSerializer.h" +#include "Memory.h" + +#pragma warning (disable:26451) +#pragma warning (disable:26812) + +PuzzleSerializer::PuzzleSerializer(const std::shared_ptr& memory) : _memory(memory) {} + +Puzzle PuzzleSerializer::ReadPuzzle(int id) { + int width = 2 * _memory->ReadPanelData(id, GRID_SIZE_X, 1)[0] - 1; + int height = 2 * _memory->ReadPanelData(id, GRID_SIZE_Y, 1)[0] - 1; + if (width < 0 || height < 0) return Puzzle(); // @Error: Grid size should be always positive? Looks like the starting panels break this rule, though. + + Puzzle p; + p.NewGrid(width, height); + ReadIntersections(p, id); + ReadDecorations(p, id); + return p; +} + +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 (flags & Flags::IS_STARTPOINT) { + p.grid[x][y].start = true; + } + p.grid[x][y].dot = FlagsToDot(flags); + if (flags & Flags::IS_FULL_GAP) { + p.grid[x][y].gap = Cell::Gap::FULL; + } + } + + // 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) { + // 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) { + 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_ONE_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 > 0) p.hasDecorations = true; + + for (int i=0; i(); + p.grid[x][y].decoration = d; + d->type = static_cast(decorations[i] & 0xFF00); + switch(d->type) { + case Type::Poly: + case Type::RPoly: + case Type::Ylop: + d->polyshape = decorations[i] & 0xFFFF0000; + break; + case Type::Triangle: + d->count = decorations[i] & 0x000F0000; + break; + } + d->color = static_cast(decorations[i] & 0xF); + } +} + +void PuzzleSerializer::WritePuzzle(const Puzzle& p, int id) { + _memory->WritePanelData(id, GRID_SIZE_X, {(p.width + 1)/2}); + _memory->WritePanelData(id, GRID_SIZE_Y, {(p.height + 1)/2}); + + WriteIntersections(p, id); + if (p.hasDecorations) WriteDecorations(p, id); + + _memory->WritePanelData(id, NEEDS_REDRAW, {1}); +} + +void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { + std::vector intersectionLocations; + std::vector intersectionFlags; + std::vector connections_a; + std::vector connections_b; + + float min = 0.1f; + 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 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 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)); + flags |= Flags::HAS_VERTI_CONN; + numConnections++; + } + // Top connection + if (y < p.height - 1 && p.grid[x][y+1].gap == Cell::Gap::NONE) { + flags |= Flags::HAS_VERTI_CONN; + numConnections++; + } + // 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)); + flags |= Flags::HAS_HORIZ_CONN; + numConnections++; + } + // Right connection + if (x < p.width - 1 && p.grid[x+1][y].gap == Cell::Gap::NONE) { + flags |= Flags::HAS_HORIZ_CONN; + numConnections++; + } + if (numConnections == 1) flags |= HAS_ONE_CONN; + intersectionFlags.push_back(flags); + } + } + + // Endpoints + for (int x=0; x(intersectionFlags.size())); // This endpoint + + float xPos = min + (x/2) * width_interval; + float yPos = max - (y/2) * height_interval; + switch (p.grid[x][y].end) { + case Cell::Dir::LEFT: + xPos -= .05f; + break; + case Cell::Dir::RIGHT: + xPos += .05f; + break; + case Cell::Dir::UP: + yPos += .05f; // Y position goes from 0 (bottom) to 1 (top), so this is reversed. + break; + case Cell::Dir::DOWN: + yPos -= .05f; + break; + } + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos); + intersectionFlags.push_back(Flags::IS_ENDPOINT); + } + } + + // Dots + 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) { + case Cell::Dot::BLACK: + break; + case Cell::Dot::BLUE: + flags |= DOT_IS_BLUE; + break; + case Cell::Dot::YELLOW: + flags |= DOT_IS_ORANGE; + break; + case Cell::Dot::INVISIBLE: + flags |= DOT_IS_INVISIBLE; + break; + } + intersectionFlags.push_back(flags); + } + } + + // Gaps + for (int x=0; x(intersectionFlags.size())); // This endpoint + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos + verti_gap_size / 2); + intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); + + connections_a.push_back(xy_to_loc(p, x, y+1)); + connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint + intersectionLocations.push_back(xPos); + intersectionLocations.push_back(yPos - verti_gap_size / 2); + intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_VERTI_CONN); + } else if (y%2 == 0) { // Horizontal gap + connections_a.push_back(xy_to_loc(p, x-1, y)); + connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint + intersectionLocations.push_back(xPos - horiz_gap_size / 2); + intersectionLocations.push_back(yPos); + intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); + + connections_a.push_back(xy_to_loc(p, x+1, y)); + connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint + intersectionLocations.push_back(xPos + horiz_gap_size / 2); + intersectionLocations.push_back(yPos); + intersectionFlags.push_back(Flags::HAS_ONE_CONN | Flags::HAS_HORIZ_CONN); + } + } + } + + _memory->WritePanelData(id, NUM_DOTS, {static_cast(intersectionFlags.size())}); + _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); + _memory->WriteArray(id, DOT_CONNECTION_B, connections_b); +} + +void PuzzleSerializer::WriteDecorations(const Puzzle& p, int id) { + std::vector decorations; + for (int y=p.height-2; y>0; y-=2) { + for (int x=1; xcolor | d->type | d->count | d->polyshape); + } else { + decorations.push_back(0); + } + } + } + + _memory->WritePanelData(id, NUM_DECORATIONS, {static_cast(decorations.size())}); + _memory->WriteArray(id, DECORATIONS, decorations); +} + +std::tuple PuzzleSerializer::loc_to_xy(const Puzzle& p, int location) const { + int height2 = (p.height - 1) / 2; + int width2 = (p.width + 1) / 2; + + int x = 2 * (location % width2); + int y = 2 * (height2 - location / width2); + return {x, 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; + + int rowsFromBottom = height2 - y/2; + return rowsFromBottom * width2 + x/2; +} + +std::tuple PuzzleSerializer::dloc_to_xy(const Puzzle& p, int location) const { + int height2 = (p.height - 3) / 2; + int width2 = (p.width - 1) / 2; + + int x = 2 * (location % width2) + 1; + int y = 2 * (height2 - location / width2) + 1; + return {x, 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; + + int rowsFromBottom = height2 - (y - 1)/2; + return rowsFromBottom * width2 + (x - 1)/2; +} + +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; + } else if (flags & Flags::DOT_IS_ORANGE) { + return Cell::Dot::YELLOW; + } else if (flags & Flags::DOT_IS_INVISIBLE) { + return Cell::Dot::INVISIBLE; + } else { + 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 + +#include "Puzzle.h" + + +class Memory; + +class PuzzleSerializer { +public: + PuzzleSerializer(const std::shared_ptr& memory); + Puzzle ReadPuzzle(int id); + void WritePuzzle(const Puzzle& p, int id); + +private: + // @Bug: Blue and orange are swapped? + enum Flags { + 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_ONE_CONN = 0x100000, + HAS_VERTI_CONN = 0x200000, + HAS_HORIZ_CONN = 0x400000, + }; + + void ReadIntersections(Puzzle& p, int id); + void ReadDecorations(Puzzle& p, int id); + void WriteIntersections(const Puzzle& p, int id); + void WriteDecorations(const Puzzle& p, int id); + + 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) 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; +}; diff --git a/Source/Source.vcxproj b/Source/Source.vcxproj index aebeaeb..08e44a1 100644 --- a/Source/Source.vcxproj +++ b/Source/Source.vcxproj @@ -161,6 +161,7 @@ + @@ -171,6 +172,7 @@ + -- cgit 1.4.1