diff --git a/main.cc b/main.cc index 0fbde4f..0be0146 100644 --- a/main.cc +++ b/main.cc @@ -11,15 +11,21 @@ using Pos = std::pair; -auto x(Pos const& p) noexcept -> int { return p.first; } -auto y(Pos const& p) noexcept -> int { return p.second; } +auto x(Pos const &p) noexcept -> int { return p.first; } +auto y(Pos const &p) noexcept -> int { return p.second; } struct Square { - Square(Pos const& pos, int length) noexcept : pos_(pos), length_(length) {} - Square(Square const& other) noexcept = default; - Square(Square&& other) noexcept = default; - Square& operator=(Square const& other) noexcept = default; - Square& operator=(Square&& other) noexcept = default; + Square(Pos const &pos, int length) noexcept : pos_(pos), length_(length) { + } + + Square(Square const &other) noexcept = default; + + Square(Square &&other) noexcept = default; + + Square &operator=(Square const &other) noexcept = default; + + Square &operator=(Square &&other) noexcept = default; + ~Square() noexcept = default; auto x() const noexcept -> int { return ::x(pos_); } @@ -30,48 +36,60 @@ struct Square { int length_; }; -struct OverlappingSquares { Pos pos_; }; +struct OverlappingSquares { + Pos pos_; +}; struct Grid { - Grid(int length) : grid_(length * length, '.'), length_(length) {} - Grid(Grid const& other) = default; - Grid(Grid&& other) noexcept = default; - Grid& operator=(Grid const& other) = default; - Grid& operator=(Grid&& other) noexcept = default; + Grid(int length) : grid_(length * length, '.'), length_(length) { + } + + Grid(Grid const &other) = default; + + Grid(Grid &&other) noexcept = default; + + Grid &operator=(Grid const &other) = default; + + Grid &operator=(Grid &&other) noexcept = default; + ~Grid() noexcept = default; auto length() const noexcept -> int { return length_; } + auto set(int x, int y, char c) noexcept -> void { - assert (x < length_); - assert (y < length_); - assert (grid_[x + y * length_] != c); + assert(x < length_); + assert(y < length_); + assert(grid_[x + y * length_] != c); grid_[x + y * length_] = c; } auto get(int x, int y) const noexcept -> char { - assert (x < length_); - assert (y < length_); + assert(x < length_); + assert(y < length_); return grid_[x + y * length_]; } - auto add(Square const& sq) -> void { + auto add(Square const &sq) -> void { switch (sq.length()) { - case 1: set(sq.x(), sq.y(), '*'); break; + case 1: set(sq.x(), sq.y(), '*'); + break; case 2: set(sq.x(), sq.y(), '+'); - set(sq.x() + 1, sq.y(), '+');set(sq.x(), sq.y() + 1, '+');set(sq.x() + 1, sq.y() + 1, '+'); + set(sq.x() + 1, sq.y(), '+'); + set(sq.x(), sq.y() + 1, '+'); + set(sq.x() + 1, sq.y() + 1, '+'); break; default: { auto n = sq.length(); set(sq.x(), sq.y(), '+'); set(sq.x() + n - 1, sq.y(), '+'); - set(sq.x(), sq.y() + n -1, '+'); - set(sq.x() + n - 1, sq.y() + n -1, '+'); - for (int i = 1; i < n -1; ++i) { + set(sq.x(), sq.y() + n - 1, '+'); + set(sq.x() + n - 1, sq.y() + n - 1, '+'); + for (int i = 1; i < n - 1; ++i) { set(sq.x() + i, sq.y(), '-'); set(sq.x() + i, sq.y() + n - 1, '-'); set(sq.x(), sq.y() + i, '|'); - for (int j = 1; j < n -1; ++j) { - set (sq.x() + j, sq.y() + i, ' '); + for (int j = 1; j < n - 1; ++j) { + set(sq.x() + j, sq.y() + i, ' '); } set(sq.x() + n - 1, sq.y() + i, '|'); } @@ -85,7 +103,7 @@ struct Grid { } } - auto clear(Square const& sq) { + auto clear(Square const &sq) { for (auto i = sq.x(); i < sq.x() + sq.length(); ++i) { for (auto j = sq.y(); j < sq.y() + sq.length(); ++j) { set(i, j, '.'); @@ -99,21 +117,31 @@ struct Grid { } } - auto fits(Square const& sq) const noexcept -> bool { - assert (sq.x() < length_); - assert (sq.y() < length_); - if (sq.x() + sq.length() > length_) { return false; } - else if (sq.y() + sq.length() > length_) { return false; } - else { - auto pos = grid_.begin() + sq.x() + sq.y() * length_; - std::string_view v(pos, pos + sq.length()); - return v.find_first_not_of('.') == std::string_view::npos; + /** \brief Get length of the largest square that fits at \a pos in the grid. + */ + auto largest_square(Pos const &pos, int n) const noexcept -> int { + assert(x(pos) < length_); + assert(y(pos) < length_); + + /* Because of how we walk through the grid (starting at 0,0 then increasing + * x followed by y) we can assume that if the position (b, y) is clear + * (i.e. a '.') then (b, y + i) is clear for all i > 0. + * + * This means we only need to look for the first non-clear position along the + * current row. + */ + auto b = x(pos); + // Make sure we don't go looking in the next row. + auto e = std::min(x(pos) + n, length_); + while (b < e) { + if (grid_[b + y(pos) * length_] != '.') { break; } + ++b; } + return b - x(pos); } - auto next_pos(Pos const& pos) const noexcept -> Pos { - if (const auto p = x(pos) + 1 + y(pos) * length_; p >= grid_.size()) { return std::make_pair(0, length_); } - else { + auto next_pos(Pos const &pos) const noexcept -> Pos { + if (const auto p = x(pos) + 1 + y(pos) * length_; p >= grid_.size()) { return std::make_pair(0, length_); } else { const auto next_p = grid_.find('.', p); if (next_p == std::string::npos) { return std::make_pair(0, length_); @@ -130,26 +158,33 @@ auto triangle_num(int n) { return (n * (n + 1)) / 2; } using Avail = std::vector; -auto find_solution_impl(Grid& grid, int n, Pos const& pos, Avail& avail_sqs)-> bool { +auto find_solution_impl(Grid &grid, int n, Pos const &pos, Avail &avail_sqs) -> bool { if (x(pos) == 0 && y(pos) == grid.length()) { return true; } - for (auto idx = n; idx != 0; --idx) { + /* Walk through the possible squares from the largest that will fit down to 0. + * We don't want to walk up from 1 to the largest because we can show that a + * 1x1 piece will never fit along an edge (or one in from the edge) - as + * there is no other 1x1 piece to go alongside it). This means we know the + * first guess is definitely wrong if we start small. + * + * If we know a square of side length N will fit then we know a square of side length + * N - 1 will fit. + */ + for (auto idx = grid.largest_square(pos, n); idx != 0; --idx) { if (avail_sqs[idx] == 0) { continue; } auto const sq = Square(pos, idx); - if (grid.fits(sq)) { - --avail_sqs[idx]; - grid.add(sq); - if (find_solution_impl(grid, n, grid.next_pos(pos), avail_sqs)) { return true; } - ++avail_sqs[idx]; - grid.clear(sq); - } + --avail_sqs[idx]; + grid.add(sq); + if (find_solution_impl(grid, n, grid.next_pos(pos), avail_sqs)) { return true; } + ++avail_sqs[idx]; + grid.clear(sq); } return false; } -auto find_solution(int n) -> Grid{ +auto find_solution(int n) -> Grid { auto length = triangle_num(n); Grid grid(length); Avail avail_sqs; @@ -158,8 +193,7 @@ auto find_solution(int n) -> Grid{ return grid; } -int main(int argc, char** argv) -{ +int main(int argc, char **argv) { auto n = (argc == 1) ? 8 : atoi(argv[1]); auto grid = find_solution(n); std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n';