Files
partridge-cpp/benchmarks/benchmark.cc
T
Codex instance d751d1b13e solver: replace cell DFS with skyline search
Represent partial placements as column heights and branch on the narrowest local valley. This removes the board-area cell state and makes first-solution search substantially smaller for feasible orders.

Expose direct-search and candidate-order benchmark controls so the skyline core can be measured independently of odd-order construction. Document the completeness argument and the 10/11 test-tier decision.

Tests: Release, Debug, ASan, and UBSan CTest (10 passed each)

Refs: #4
2026-07-30 17:55:21 +01:00

166 lines
5.8 KiB
C++

/*
* Copyright 2026, Matthew-Gretton-Dann
* SPDX-License-Identifier: Apache-2.0
*/
#define PARTRIDGE_TESTING
#include "../main.cc"
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <sstream>
#include <string>
namespace {
using Clock = std::chrono::steady_clock;
auto seconds(Clock::time_point begin, Clock::time_point end) -> double {
return std::chrono::duration<double>(end - begin).count();
}
struct TimedSolution {
Results result;
double search_seconds;
double construction_seconds;
};
auto solve(std::uint64_t order, bool instrument,
CandidateOrder candidate_order, bool direct_search,
SearchCounters &counters)
-> TimedSolution {
auto const predecessor_order =
!direct_search && uses_odd_construction(order) ? order - 1 : order;
auto const search_begin = Clock::now();
auto predecessor =
instrument
? search_solution_instrumented(predecessor_order, counters,
candidate_order)
: search_solution(predecessor_order, candidate_order);
auto const search_end = Clock::now();
auto const construction_begin = Clock::now();
auto result = !direct_search && uses_odd_construction(order)
? construct_odd_solution(order, std::move(predecessor))
: std::move(predecessor);
auto const construction_end = Clock::now();
return {
std::move(result),
seconds(search_begin, search_end),
!direct_search && uses_odd_construction(order)
? seconds(construction_begin, construction_end)
: 0.0,
};
}
auto valid(std::uint64_t order, Results const &result) -> bool {
auto const expected = triangle_num(order);
if (result.length() != expected) {
return false;
}
std::vector<std::uint64_t> multiplicities(order + 1);
std::vector<bool> occupied(expected * expected);
for (auto const &square: result.squares()) {
auto const side = square.length();
auto const x0 = square.pos() % expected;
auto const y0 = square.pos() / expected;
if (side == 0 || side > order || x0 + side > expected ||
y0 + side > expected) {
return false;
}
++multiplicities[side];
for (auto y = y0; y < y0 + side; ++y) {
for (auto x = x0; x < x0 + side; ++x) {
auto &&cell = occupied[x + y * expected];
if (cell) {
return false;
}
cell = true;
}
}
}
for (std::uint64_t side = 1; side <= order; ++side) {
if (multiplicities[side] != side) {
return result.squares().empty() && order < 8;
}
}
return std::ranges::find(occupied, false) == occupied.end();
}
auto boolean(bool value) -> char const * { return value ? "true" : "false"; }
}
int main(int argc, char **argv) {
if (argc < 3 || argc > 5) {
std::cerr << "usage: partridge_benchmark ORDER counters|plain "
"[ascending|descending] [public|direct]\n";
return 2;
}
auto const order = static_cast<std::uint64_t>(std::strtoull(argv[1], nullptr, 10));
auto const instrument = std::string_view(argv[2]) == "counters";
if (!instrument && std::string_view(argv[2]) != "plain") {
std::cerr << "counter mode must be counters or plain\n";
return 2;
}
auto const candidate_order =
argc < 4 || std::string_view(argv[3]) == "ascending"
? CandidateOrder::ascending
: CandidateOrder::descending;
if (argc >= 4 && std::string_view(argv[3]) != "ascending" &&
std::string_view(argv[3]) != "descending") {
std::cerr << "candidate order must be ascending or descending\n";
return 2;
}
auto const direct_search =
argc == 5 && std::string_view(argv[4]) == "direct";
if (argc == 5 && std::string_view(argv[4]) != "public" &&
std::string_view(argv[4]) != "direct") {
std::cerr << "search route must be public or direct\n";
return 2;
}
SearchCounters counters;
auto timed =
solve(order, instrument, candidate_order, direct_search, counters);
auto const validation_begin = Clock::now();
auto const validation_ok = valid(order, timed.result);
auto const validation_end = Clock::now();
auto const render_begin = Clock::now();
std::ostringstream rendered;
auto *const old_buffer = std::cout.rdbuf(rendered.rdbuf());
timed.result.output();
std::cout.rdbuf(old_buffer);
auto const render_end = Clock::now();
std::cout.precision(17);
std::cout
<< "{\"schema_version\":1"
<< ",\"order\":" << order
<< ",\"instrumented\":" << boolean(instrument)
<< ",\"candidate_order\":\""
<< (candidate_order == CandidateOrder::ascending ? "ascending"
: "descending")
<< "\""
<< ",\"search_route\":\""
<< (direct_search ? "direct" : "public") << "\""
<< ",\"solved\":" << boolean(!timed.result.squares().empty())
<< ",\"valid\":" << boolean(validation_ok)
<< ",\"timing_seconds\":{\"solve\":" << timed.search_seconds
<< ",\"construction\":" << timed.construction_seconds
<< ",\"validation\":" << seconds(validation_begin, validation_end)
<< ",\"render\":" << seconds(render_begin, render_end) << "}"
<< ",\"counters\":{\"search_nodes\":" << counters.search_nodes
<< ",\"loop_iterations\":" << counters.loop_iterations
<< ",\"attempted_placements\":" << counters.attempted_placements
<< ",\"backtracks\":" << counters.backtracks
<< ",\"prune_checks\":" << counters.prune_checks
<< ",\"prune_hits\":" << counters.prune_hits
<< ",\"generated_tasks\":" << counters.generated_tasks
<< ",\"completed_tasks\":" << counters.completed_tasks << "}}\n";
return validation_ok ? 0 : 1;
}