commit 2423f39c57e5f8aefa872f5ea2860aedfa1c3afd Author: Matthew Gretton-Dann Date: Sun Aug 31 17:04:59 2025 +0100 Initial C++ version of Partridge solver This is almost a complete copy of the OCaml version About twice as fast. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..625fade --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmake-build*/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..3f53c27 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +partridge_cpp \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..5da2f64 --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,102 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7560c22 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 4.0) +project(partridge_cpp) + +set(CMAKE_CXX_STANDARD 20) + +add_executable(partridge_cpp + main.cc) diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..189ac8d --- /dev/null +++ b/main.cc @@ -0,0 +1,167 @@ +// +// Created by Matthew Gretton-Dann on 31/08/2025. +// + +#include +#include +#include +#include +#include +#include + +using Pos = std::pair; + +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() noexcept = default; + + auto x() const noexcept -> int { return ::x(pos_); } + auto y() const noexcept -> int { return ::y(pos_); } + auto length() const noexcept -> int { return length_; } + + Pos pos_; + int length_; +}; + +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() 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); + grid_[x + y * length_] = c; + } + + auto get(int x, int y) const noexcept -> char { + assert (x < length_); + assert (y < length_); + return grid_[x + y * length_]; + } + + auto add(Square const& sq) -> 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; + } + } + } + } + + 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, '.'); + } + } + } + + auto output() const { + for (auto i = 0; i < length_; ++i) { + std::cout << grid_.substr(i * length_, length_) << '\n'; + } + } + + 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; + } + } + + 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_); + } + return std::make_pair(next_p % length_, next_p / length_); + } + } + + std::string grid_; + int length_; +}; + +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, int idx, Avail& avail_sqs)-> bool { + auto const sq = Square(pos, idx); + if (x(pos) == 0 && y(pos) == grid.length()) { return true; } + if (idx == 0) { return false; } + if (avail_sqs[idx] == 0) { return find_solution_impl(grid, n, pos, idx - 1, avail_sqs); } + if (grid.fits(sq)) { + --avail_sqs[idx]; + grid.add(sq); + auto next_pos = grid.next_pos(pos); + auto const result = find_solution_impl(grid, n, next_pos, n, avail_sqs); + ++avail_sqs[idx]; + if (result) { return true; } + grid.clear(sq); + return find_solution_impl(grid, n, pos, idx - 1, avail_sqs); + } + return find_solution_impl(grid, n, pos, idx - 1, avail_sqs); +} + +auto find_solution(int n) -> Grid{ + auto length = triangle_num(n); + Grid grid(length); + Avail avail_sqs; + for (auto i = 0; i <= n; ++i) { avail_sqs.push_back(i); } + find_solution_impl(grid, n, std::make_pair(0, 0), n, avail_sqs); + return grid; +} + +int main(int argc, char** argv) +{ + auto n = (argc == 1) ? 9 : atoi(argv[1]); + auto grid = find_solution(n); + std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n'; + grid.output(); + return 0; +}