#include "SurfaceMask.h" #include #include namespace { // Rotate direction character 90° clockwise. char rotateDirCharCW(char c) { switch (c) { case '>': return 'v'; case 'v': return '<'; case '<': return '^'; case '^': return '>'; default: return c; } } // Rotate the character grid 90° clockwise. // Input grid[row][col]. Returns a new grid with swapped dimensions. std::vector rotateCW(const std::vector& grid) { if (grid.empty()) { return {}; } const int srcH = static_cast(grid.size()); // Pad all rows to the same width. int srcW = 0; for (const std::string& row : grid) { const int w = static_cast(row.size()); if (w > srcW) { srcW = w; } } // After 90° CW: new width = srcH, new height = srcW. const int dstW = srcH; const int dstH = srcW; std::vector dst(dstH, std::string(dstW, ' ')); for (int row = 0; row < srcH; ++row) { for (int col = 0; col < srcW; ++col) { const char ch = (col < static_cast(grid[row].size())) ? grid[row][col] : ' '; // 90° CW mapping: (col, row) -> (dstCol = srcH-1-row, dstRow = col) const int dstCol = srcH - 1 - row; const int dstRow = col; dst[dstRow][dstCol] = rotateDirCharCW(ch); } } return dst; } } // namespace ParsedSurfaceMask parseSurfaceMask(const std::vector& rows, Rotation rotation) { // Number of 90° CW steps to apply. int cwSteps = 0; switch (rotation) { case Rotation::East: cwSteps = 0; break; case Rotation::South: cwSteps = 1; break; case Rotation::West: cwSteps = 2; break; case Rotation::North: cwSteps = 3; break; } // Apply rotations. std::vector grid = rows; for (int i = 0; i < cwSteps; ++i) { grid = rotateCW(grid); } // Scan grid: collect body cells, ship dock cells, and output port indicators. std::vector rawBodyCells; std::vector rawShipDockCells; struct RawPort { QPoint tile; Rotation direction; }; std::vector rawPorts; for (int row = 0; row < static_cast(grid.size()); ++row) { for (int col = 0; col < static_cast(grid[row].size()); ++col) { const char ch = grid[row][col]; if (ch == 'A') { rawBodyCells.push_back(QPoint(col, row)); } else if (ch == 'S') { rawBodyCells.push_back(QPoint(col, row)); rawShipDockCells.push_back(QPoint(col, row)); } else if (ch == '>') { rawPorts.push_back({QPoint(col, row), Rotation::East}); } else if (ch == '<') { rawPorts.push_back({QPoint(col, row), Rotation::West}); } else if (ch == '^') { rawPorts.push_back({QPoint(col, row), Rotation::North}); } else if (ch == 'v') { rawPorts.push_back({QPoint(col, row), Rotation::South}); } } } // Compute bounding box of body cells and normalize to (0,0). int minCol = INT_MAX; int minRow = INT_MAX; int maxCol = INT_MIN; int maxRow = INT_MIN; for (const QPoint& pt : rawBodyCells) { if (pt.x() < minCol) { minCol = pt.x(); } if (pt.x() > maxCol) { maxCol = pt.x(); } if (pt.y() < minRow) { minRow = pt.y(); } if (pt.y() > maxRow) { maxRow = pt.y(); } } // If there are no body cells, return an empty mask. if (rawBodyCells.empty()) { return ParsedSurfaceMask{}; } const QPoint offset(-minCol, -minRow); ParsedSurfaceMask result; result.footprint = QSize(maxCol - minCol + 1, maxRow - minRow + 1); for (const QPoint& pt : rawBodyCells) { result.bodyCells.push_back(pt + offset); } for (const QPoint& pt : rawShipDockCells) { result.shipDockCells.push_back(pt + offset); } for (const RawPort& rp : rawPorts) { Port port; port.tile = rp.tile + offset; port.direction = rp.direction; result.outputPorts.push_back(port); } return result; }