Compare commits
7 Commits
1684c289c6
...
ff43ba5287
| Author | SHA1 | Date | |
|---|---|---|---|
|
ff43ba5287
|
|||
|
f1445b7544
|
|||
|
ed71280dc2
|
|||
|
a066fdb552
|
|||
|
e557933590
|
|||
|
65569982ee
|
|||
|
4b9feefbf4
|
240
main.cc
240
main.cc
@@ -13,15 +13,11 @@
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
using size_t = std::size_t;
|
||||
|
||||
namespace {
|
||||
/** (x, y) pair storing a position. */
|
||||
using Pos = std::pair<int, int>;
|
||||
|
||||
/** Get x co-ordinate from position */
|
||||
auto x(Pos const &p) noexcept -> int { return p.first; }
|
||||
|
||||
/** Get y co-ordinate from position */
|
||||
auto y(Pos const &p) noexcept -> int { return p.second; }
|
||||
using Pos = std::size_t;
|
||||
|
||||
/** A square - consisting of position of closest corner to origin, and side-length.
|
||||
*/
|
||||
@@ -30,7 +26,7 @@ namespace {
|
||||
* \param pos Position of closest corner to origin
|
||||
* \param length Side length.
|
||||
*/
|
||||
Square(Pos const &pos, int const length) noexcept : pos_(pos), length_(length) {
|
||||
Square(Pos pos, size_t const length) noexcept : pos_(pos), length_(length) {
|
||||
}
|
||||
|
||||
Square(Square const &other) noexcept = default;
|
||||
@@ -44,23 +40,101 @@ namespace {
|
||||
~Square() noexcept = default;
|
||||
|
||||
/** Get x co-ordinate of closest corner to origin. */
|
||||
auto x() const noexcept -> int { return ::x(pos_); }
|
||||
|
||||
/** Get y co-ordinate of closest corner to origin. */
|
||||
auto y() const noexcept -> int { return ::y(pos_); }
|
||||
auto pos() const noexcept -> Pos { return pos_; }
|
||||
|
||||
/** Get side length. */
|
||||
auto length() const noexcept -> int { return length_; }
|
||||
auto length() const noexcept -> size_t { return length_; }
|
||||
|
||||
private:
|
||||
Pos pos_; ///< Position of corner closest to origin
|
||||
int length_; ///< Side length
|
||||
size_t length_; ///< Side length
|
||||
};
|
||||
|
||||
/** Structure holding the results.
|
||||
*/
|
||||
struct Results {
|
||||
Results(size_t length, std::vector<Square> squares) : length_(length), squares_(std::move(squares)) {
|
||||
}
|
||||
|
||||
Results(Results const &other) noexcept = delete;
|
||||
|
||||
Results &operator=(Results const &other) noexcept = delete;
|
||||
|
||||
Results &operator=(Results &&other) noexcept = default;
|
||||
|
||||
Results(Results &&other) noexcept = default;
|
||||
|
||||
~Results() noexcept = default;
|
||||
|
||||
auto length() const noexcept -> size_t { return length_; }
|
||||
|
||||
/** Output the grid. */
|
||||
auto output() const -> void {
|
||||
std::string out(length_ * length_, '.');
|
||||
for (auto const &sq: squares_) {
|
||||
prettify_sq(out, sq);
|
||||
}
|
||||
for (size_t idx = 0; idx < length_ * length_; idx += length_) {
|
||||
std::cout << std::string_view(out.data() + idx, length_) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
auto set(std::string &s, size_t x, size_t y, char c) const noexcept -> void {
|
||||
assert(x < length_);
|
||||
assert(y < length_);
|
||||
assert(grid_[x + y * length_] != c);
|
||||
s[x + y * length_] = c;
|
||||
}
|
||||
|
||||
auto sq_x(Square const &sq) const noexcept -> size_t { return sq.pos() % length_; }
|
||||
auto sq_y(Square const &sq) const noexcept -> size_t { return sq.pos() / length_; }
|
||||
|
||||
auto prettify_sq(std::string &s, Square const &sq) const noexcept -> void {
|
||||
switch (sq.length()) {
|
||||
case 1: set(s, sq_x(sq), sq_y(sq), '*');
|
||||
break;
|
||||
case 2: set(s, sq_x(sq), sq_y(sq), '+');
|
||||
set(s, sq_x(sq) + 1, sq_y(sq), '+');
|
||||
set(s, sq_x(sq), sq_y(sq) + 1, '+');
|
||||
set(s, sq_x(sq) + 1, sq_y(sq) + 1, '+');
|
||||
break;
|
||||
default: {
|
||||
auto n = sq.length();
|
||||
set(s, sq_x(sq), sq_y(sq), '+');
|
||||
set(s, sq_x(sq) + n - 1, sq_y(sq), '+');
|
||||
set(s, sq_x(sq), sq_y(sq) + n - 1, '+');
|
||||
set(s, sq_x(sq) + n - 1, sq_y(sq) + n - 1, '+');
|
||||
for (size_t i = 1; i < n - 1; ++i) {
|
||||
set(s, sq_x(sq) + i, sq_y(sq), '-');
|
||||
set(s, sq_x(sq) + i, sq_y(sq) + n - 1, '-');
|
||||
set(s, sq_x(sq), sq_y(sq) + i, '|');
|
||||
for (size_t j = 1; j < n - 1; ++j) {
|
||||
set(s, sq_x(sq) + j, sq_y(sq) + i, ' ');
|
||||
}
|
||||
set(s, sq_x(sq) + n - 1, sq_y(sq) + i, '|');
|
||||
}
|
||||
|
||||
size_t i = sq_x(sq) + n - 1;
|
||||
while (n != 0) {
|
||||
set(s, --i, sq_y(sq) + 1, '0' + (n % 10));
|
||||
n /= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t length_;
|
||||
std::vector<Square> squares_;
|
||||
};
|
||||
|
||||
/** An N * N grid of characters. */
|
||||
struct Grid {
|
||||
// Type to use for the grid contents
|
||||
using T = std::int_fast64_t;
|
||||
|
||||
/** Construct a grid of given side-length. */
|
||||
Grid(int length) : grid_(length * length, empty), length_(length) {
|
||||
Grid(size_t length) : grid_(length * length, empty), length_(length) {
|
||||
}
|
||||
|
||||
Grid(Grid const &other) = delete;
|
||||
@@ -74,47 +148,35 @@ namespace {
|
||||
~Grid() noexcept = default;
|
||||
|
||||
/** Get grid length */
|
||||
auto length() const noexcept -> int { return length_; }
|
||||
auto end() const noexcept -> size_t { return grid_.size(); }
|
||||
|
||||
/** Add a square to the grid. */
|
||||
auto add(Square const &sq) noexcept -> void {
|
||||
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, sq.length());
|
||||
/* One would expect the fastest way to do this would be to have x be the
|
||||
* fastest increasing index so we stores [pos, pos + 1,..., pos+length, ...]
|
||||
* But experimentation tells us this isn't so, and storing
|
||||
* [pos, pos + length, ..., pos + 1, ...] is faster!
|
||||
*/
|
||||
for (auto x = 0; x < sq.length(); ++x) {
|
||||
for (auto y = sq.pos(); y < sq.pos() + sq.length() * length_; y += length_) {
|
||||
grid_[x + y] = filled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear a square from the grid. */
|
||||
auto clear(Square const &sq) noexcept -> void {
|
||||
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, empty);
|
||||
for (auto x = 0; x < sq.length(); ++x) {
|
||||
for (auto y = sq.pos(); y < sq.pos() + sq.length() * length_; y += length_) {
|
||||
grid_[x + y] = empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto prettify() noexcept -> void {
|
||||
for (auto idx = 0; idx < grid_.size(); ++idx) {
|
||||
if (grid_[idx] < 32) {
|
||||
auto const pos = std::make_pair(idx % length_, idx / length_);
|
||||
prettify_sq(Square(pos, grid_[idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Output the grid. */
|
||||
auto output() const -> void {
|
||||
for (auto idx = 0; idx < length_ * length_; idx += length_) {
|
||||
std::cout << std::string_view(grid_.data() + idx, length_) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
/** \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_);
|
||||
auto largest_square(Pos pos, size_t n) const noexcept -> size_t {
|
||||
assert(pos < end());
|
||||
|
||||
/* 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
|
||||
@@ -123,93 +185,63 @@ namespace {
|
||||
* This means we only need to look for the first non-clear position along the
|
||||
* current row.
|
||||
*/
|
||||
auto b = x(pos);
|
||||
auto const pos_x = pos % length_;
|
||||
auto const pos_y0 = pos - pos_x;
|
||||
auto b = pos;
|
||||
// Make sure we don't go looking in the next row.
|
||||
auto e = std::min(x(pos) + n, length_);
|
||||
auto const e = std::min(pos + n, pos_y0 + length_);
|
||||
while (b < e) {
|
||||
if (grid_[b + y(pos) * length_] != '.') { break; }
|
||||
if (grid_[b] != empty) { break; }
|
||||
++b;
|
||||
}
|
||||
// Check that this length fits vertically as well.
|
||||
auto len = b - x(pos);
|
||||
auto ye = std::min(y(pos) + len, length_);
|
||||
return ye - y(pos);
|
||||
auto const len = b - pos;
|
||||
auto const pos_y = pos / length_;
|
||||
auto const ye = std::min(pos_y + len, length_);
|
||||
return ye - pos_y;
|
||||
}
|
||||
|
||||
/** Get the next position to check. n is the size of the square we just
|
||||
* added.
|
||||
/** Get the next position to check starting at pos.
|
||||
*
|
||||
* Returns grid_.length() if no more positions avaialble.
|
||||
*/
|
||||
auto next_pos(Pos const &pos, int n) const noexcept -> Pos {
|
||||
auto const b = grid_.begin() + x(pos) + n + y(pos) * length_;
|
||||
auto next_pos(Pos pos) const noexcept -> Pos {
|
||||
auto const b = grid_.begin() + pos;
|
||||
auto const p = std::find(b, grid_.end(), empty);
|
||||
auto const v = p - grid_.begin();
|
||||
return std::make_pair(v % length_, v / length_);
|
||||
return p - grid_.begin();
|
||||
}
|
||||
|
||||
private:
|
||||
auto prettify_sq(Square const &sq) noexcept -> void {
|
||||
switch (sq.length()) {
|
||||
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, '+');
|
||||
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() + 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, ' ');
|
||||
}
|
||||
set(sq.x() + n - 1, sq.y() + i, '|');
|
||||
}
|
||||
|
||||
int i = sq.x() + n - 1;
|
||||
while (n != 0) {
|
||||
set(--i, sq.y() + 1, '0' + (n % 10));
|
||||
n /= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the grid position (x, y) to the character c.
|
||||
*
|
||||
* It is an error if (x, y) is already set to c - as that means we have overlapping
|
||||
* squares.
|
||||
*/
|
||||
auto set(int x, int y, char c) noexcept -> void {
|
||||
auto set(size_t x, size_t y, T c) noexcept -> void {
|
||||
assert(x < length_);
|
||||
assert(y < length_);
|
||||
assert(grid_[x + y * length_] != c);
|
||||
grid_[x + y * length_] = c;
|
||||
}
|
||||
|
||||
std::vector<char> grid_; ///< The grid
|
||||
int length_; ///< Side length
|
||||
std::vector<T> grid_; ///< The grid
|
||||
size_t length_; ///< Side length
|
||||
|
||||
static constexpr char empty = '.'; ///< Character used for an empty cell.
|
||||
static constexpr char empty = 0; ///< Character used for an empty cell.
|
||||
static constexpr char filled = 1; ///< Character used for a filled cell,
|
||||
};
|
||||
|
||||
/** Get the n'th triangular number. */
|
||||
auto triangle_num(int n) noexcept -> int { return (n * (n + 1)) / 2; }
|
||||
auto triangle_num(size_t n) noexcept -> size_t { return (n * (n + 1)) / 2; }
|
||||
|
||||
/** Vector used to identify the available squares. */
|
||||
using Avail = std::vector<int>;
|
||||
using Avail = std::vector<size_t>;
|
||||
|
||||
/** Find a solution to the \a n th Partridge problem.
|
||||
*
|
||||
* Returns the grid of the solution.
|
||||
*/
|
||||
auto find_solution(int const n) noexcept -> Grid {
|
||||
auto find_solution(size_t const n) noexcept -> Results {
|
||||
/* Implementation is iterative, as opposed to recursive.
|
||||
*
|
||||
* The recursive implementation is easier to understand - but is
|
||||
@@ -238,8 +270,8 @@ namespace {
|
||||
sqs.reserve(length);
|
||||
|
||||
// Start at the origin with a square of longest side length.
|
||||
Pos pos{0, 0};
|
||||
int idx = n;
|
||||
Pos pos = 0;
|
||||
size_t idx = n;
|
||||
|
||||
while (true) {
|
||||
/* If the idx is 0 we've looked at all possible square lengths for this
|
||||
@@ -254,13 +286,16 @@ namespace {
|
||||
sqs.pop_back();
|
||||
grid.clear(sq);
|
||||
++avail_sqs[sq.length()];
|
||||
pos = std::make_pair(sq.x(), sq.y());
|
||||
pos = sq.pos();
|
||||
idx = sq.length() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there are no squares available of the current size try the next one.
|
||||
if (avail_sqs[idx] == 0) { --idx; continue; }
|
||||
if (avail_sqs[idx] == 0) {
|
||||
--idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Place a square of side length idx at pos, push this onto the stack and
|
||||
* set up to look at the next position.
|
||||
@@ -270,20 +305,19 @@ namespace {
|
||||
grid.add(sq);
|
||||
sqs.push_back(sq);
|
||||
|
||||
pos = grid.next_pos(pos, idx);
|
||||
pos = grid.next_pos(pos + idx);
|
||||
idx = grid.largest_square(pos, n);
|
||||
|
||||
// Have we reached the end? If so success!
|
||||
if (x(pos) == 0 && y(pos) == grid.length()) { break; }
|
||||
if (pos == grid.end()) { break; }
|
||||
}
|
||||
|
||||
grid.prettify();
|
||||
return grid;
|
||||
return Results(length, sqs);
|
||||
}
|
||||
} // anon namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto n = (argc == 1) ? 8 : atoi(argv[1]);
|
||||
auto n = (argc == 1) ? 8 : std::atol(argv[1]);
|
||||
auto grid = find_solution(n);
|
||||
std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n';
|
||||
grid.output();
|
||||
|
||||
Reference in New Issue
Block a user