#include "Panel.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) { 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; p.grid.resize(p.width); for (auto& row : p.grid) row.resize(p.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 i = 0; for (;; i++) { auto [x, y] = loc_to_xy(p, i); if (y < 0) break; if (intersectionFlags[i] & Flags::IS_STARTPOINT) { p.grid[x][y].start = true; } p.grid[x][y].dot = FlagsToDot(intersectionFlags[i]); } 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++) { 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; } } } 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; } } } } } 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! for (int i=0; i(); p.grid[x][y].decoration = d; d->type = static_cast(decorations[i] & 0xFF00); switch(d->type) { case Shape::Poly: case Shape::RPoly: case Shape::Ylop: d->polyshape = decorations[i] & 0xFFFF0000; break; case Shape::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); WriteDecorations(p, id); _memory->WritePanelData(id, NEEDS_REDRAW, {1}); } void PuzzleSerializer::WriteIntersections(const Puzzle& p, int id) { std::vector intersections; 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); // @Cleanup: If I write directly to locations, then I can remove this gross loop iterator. 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)); int flags = 0; if (p.grid[x][y].start) { flags |= Flags::IS_STARTPOINT; } intersectionFlags.push_back(flags); // Create connections for this intersection -- always write low -> high if (y > 0) { connections_a.push_back(xy_to_loc(p, x, y-2)); connections_b.push_back(xy_to_loc(p, x, y)); } if (x > 0) { connections_a.push_back(xy_to_loc(p, x-2, y)); connections_b.push_back(xy_to_loc(p, x, y)); } } } 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; } intersections.push_back(xPos); intersections.push_back(yPos); intersectionFlags.push_back(Flags::IS_ENDPOINT); } } // Dots 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); break; } } // Add this dot 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); } } _memory->WritePanelData(id, NUM_DOTS, {static_cast(intersectionFlags.size())}); _memory->WriteArray(id, DOT_POSITIONS, intersections); _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) { 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) { 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) { 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) { 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) { 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; } }