h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

commit f7d6bec5a3b79425dab88043a10efad80e26fcb4
parent ff7cee6df128cbf6b05c57585fd9b8ca8f3665f4
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Wed,  9 Apr 2025 16:49:51 +0200

Add an opinionated C++20 adapter

Diffstat:
Acpp/nissy.cpp | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acpp/nissy.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/nissy.h | 2+-
3 files changed, 350 insertions(+), 1 deletion(-)

diff --git a/cpp/nissy.cpp b/cpp/nissy.cpp @@ -0,0 +1,216 @@ +/* +C++20 interface for nissy. + +TODO: add more documentation (here and in README.md) +*/ + +#include "./nissy.h" + +extern "C" { + long long nissy_compose(const char *, const char *, char *); + long long nissy_inverse(const char *, char *); + long long nissy_applymoves(const char *, const char *, char *); + long long nissy_applytrans(const char *, const char *, char *); + long long nissy_convert(const char *, const char *, const char *, + unsigned, char *); + long long nissy_getcube(long long, long long, long long, long long, + const char *, char *); + long long nissy_solverinfo(const char *, char *); + long long nissy_gendata(const char *, unsigned long long, char *); + long long nissy_checkdata(unsigned long long, const char *); + long long nissy_solve(const char *, const char *, unsigned, unsigned, + unsigned, unsigned, int, int, unsigned long long, const char *, + unsigned, char *, long long *); + long long nissy_countmoves(const char *); + long long nissy_setlogger(void (*)(const char *)); +} + +namespace nissy { + namespace utils { + void fixstr(std::string& str) + { + size_t n = str.find('\0'); + str.resize(n); + } + + std::variant<cube_t, error_t> + cube_or_error(const char *r, long long e) + { + if (e < 0) + return error_t{e}; + return cube_t{r}; + } + + std::variant<std::string, error_t> + string_or_error(const char *cstr, long long e) + { + if (e < 0) + return error_t{e}; + + std::string str{cstr}; + fixstr(str); + return str; + } + + std::function<void(const char *)> + char_str_fn(std::function<void(std::string&)> f) + { + return [&](const char *cc){ + std::string str{cc}; + f(str); + }; + } + } + + std::variant<cube_t, error_t> + compose(const cube_t& cube, const cube_t& permutation) + { + char result[size::B32]; + + auto error = nissy_compose(cube.value.c_str(), + permutation.value.c_str(), result); + + return utils::cube_or_error(result, error); + } + + std::variant<cube_t, error_t> + inverse(const cube_t& cube) + { + char result[size::B32]; + + auto error = nissy_inverse(cube.value.c_str(), result); + + return utils::cube_or_error(result, error); + } + + std::variant<cube_t, error_t> + applymoves(const cube_t& cube, const moves_t& moves) + { + char result[size::B32]; + + auto error = nissy_applymoves(cube.value.c_str(), + moves.value.c_str(), result); + + return utils::cube_or_error(result, error); + } + + std::variant<cube_t, error_t> + applytrans(const cube_t& cube, const trans_t& trans) + { + char result[size::B32]; + + auto error = nissy_applytrans(cube.value.c_str(), + trans.value.c_str(), result); + + return utils::cube_or_error(result, error); + } + + std::variant<std::string, error_t> + convert(const cube_format_t& format_in, + const cube_format_t& format_out, const std::string& cube_string) + { + char result[size::CUBE_MAX]; + + auto error = nissy_convert(format_in.value.c_str(), + format_out.value.c_str(), cube_string.c_str(), + size::CUBE_MAX, result); + + return utils::string_or_error(result, error); + } + + std::variant<cube_t, error_t> + getcube(long long ep, long long eo, long long cp, long long co, + const std::string& options) + { + char result[size::B32]; + + auto error = nissy_getcube(ep, eo, cp, co, options.c_str(), + result); + + return utils::cube_or_error(result, error); + } + + std::variant<std::pair<size_t, std::string>, error_t> + solverinfo(const solver_t& solver) + { + char dataid[size::DATAID]; + + auto error = nissy_solverinfo(solver.value.c_str(), dataid); + if (error < 0) + return error_t{error}; + + return std::pair{error, dataid}; + } + + std::variant<solver_data_t, error_t> + gendata(const solver_t& solver) + { + char dataid[size::DATAID]; + + auto error = nissy_solverinfo(solver.value.c_str(), dataid); + if (error < 0) + return error_t{error}; + + size_t sz = error; + std::vector<std::byte> buffer(sz); + error = nissy_gendata(solver.value.c_str(), sz, + reinterpret_cast<char *>(buffer.data())); + if (error < 0) + return error_t{error}; + + return solver_data_t{buffer}; + } + + std::optional<error_t> + checkdata(const solver_data_t& data) + { + auto error = nissy_checkdata(data.value.size(), + reinterpret_cast<const char *>(data.value.data())); + + if (error < 0) + return error_t{error}; + return std::nullopt; + } + + std::variant<solve_result_t, error_t> + solve(const cube_t& cube, const solver_t& solver, + nissflag_t niss, unsigned minmoves, unsigned maxmoves, + unsigned maxsolutions, int optimal, int threads, + const solver_data_t& data) + { + const size_t len = 3 * (maxmoves+1) * maxsolutions; + char csols[len]; + long long cstats[size::SOLVE_STATS]; + + auto error = nissy_solve(cube.value.c_str(), + solver.value.c_str(), niss.value, minmoves, maxmoves, + maxsolutions, optimal, threads, data.value.size(), + reinterpret_cast<const char *>(data.value.data()), + len, csols, cstats); + + if (error < 0) + return error_t{error}; + + std::vector<std::string> sols; + std::string_view strsols(csols); + for (auto r : strsols | std::views::split('\n')) + sols.push_back(std::string{r.begin(), r.end()}); + + return solve_result_t{sols, std::to_array(cstats)}; + } + + std::variant<unsigned, error_t> + countmoves(const moves_t& moves) + { + auto error = nissy_countmoves(moves.value.c_str()); + if (error < 0) + return error_t{error}; + return (unsigned)error; + } + + void setlogger(std::function<void(std::string&)> log) + { + nissy_setlogger( + utils::char_str_fn(log).target<void(const char *)>()); + } +} diff --git a/cpp/nissy.h b/cpp/nissy.h @@ -0,0 +1,133 @@ +/* +C++20 header file for nissy. +*/ + +#ifndef NISSY_H +#define NISSY_H + +#include <array> +#include <functional> +#include <optional> +#include <ranges> +#include <string> +#include <string_view> +#include <variant> +#include <vector> + +namespace nissy { + /* Some constants for size for I/O buffers */ + namespace size { + constexpr size_t B32 = 22; + constexpr size_t H48 = 88; + constexpr size_t CUBE_MAX = H48; + constexpr size_t TRANSFORMATION = 12; + constexpr size_t SOLVE_STATS = 10; + constexpr size_t DATAID = 255; + } + + /* Some structs definitions for better type safety */ + struct nissflag_t { unsigned value; }; + struct error_t { long long value; }; + struct cube_t { std::string value; }; + struct moves_t { std::string value; }; + struct trans_t { std::string value; }; + struct cube_format_t { std::string value; }; + struct solver_t { std::string value; }; + struct solver_data_t { std::vector<std::byte> value; }; + struct solve_result_t { + std::vector<std::string> solutions; + std::array<long long, size::SOLVE_STATS> stats; + }; + + /* Flags for NISS options */ + namespace nissflag { + constexpr nissflag_t NORMAL{1}; + constexpr nissflag_t INVERSE{2}; + constexpr nissflag_t MIXED{4}; + constexpr nissflag_t LINEAR{NORMAL.value | INVERSE.value}; + constexpr nissflag_t ALL{LINEAR.value | MIXED.value}; + } + + /* Error codes */ + namespace error { + constexpr error_t UNSOLVABLE{-1}; + constexpr error_t INVALID_CUBE{-10}; + constexpr error_t UNSOLVABLE_CUBE{-11}; + constexpr error_t INVALID_MOVES{-20}; + constexpr error_t INVALID_TRANS{-30}; + constexpr error_t INVALID_FORMAT{-40}; + constexpr error_t INVALID_SOLVER{-50}; + constexpr error_t NULL_POINTER{-60}; + constexpr error_t BUFFER_SIZE{-61}; + constexpr error_t DATA{-70}; + constexpr error_t OPTIONS{-80}; + constexpr error_t UNKNOWN{-999}; + } + + /* Cube constants */ + namespace cube { + const cube_t SOLVED{"ABCDEFGH=ABCDEFGHIJKL"}; + } + + std::variant<cube_t, error_t> inverse( + const cube_t& cube + ); + + std::variant<cube_t, error_t> applymoves( + const cube_t& cube, + const moves_t& moves + ); + + std::variant<cube_t, error_t> applytrans( + const cube_t& cube, + const trans_t& trans + ); + + std::variant<std::string, error_t> convert( + const cube_format_t& format_in, + const cube_format_t& format_out, + const std::string& cube_string + ); + + std::variant<cube_t, error_t> getcube( + long long ep, + long long eo, + long long cp, + long long co, + const std::string& options + ); + + std::variant<std::pair<size_t, std::string>, error_t> solverinfo( + const solver_t& solver + ); + + std::variant<solver_data_t, error_t> gendata( + const solver_t& solver + ); + + std::optional<error_t> checkdata( + const solver_data_t& data + ); + + std::variant<solve_result_t, error_t> solve( + const cube_t& cube, + const solver_t& solver, + nissflag_t niss, + unsigned minmoves, + unsigned maxmoves, + unsigned maxsolutions, + int optimal, + int threads, + const solver_data_t& data + ); + + std::variant<unsigned, error_t> countmoves( + const moves_t& moves + ); + + void setlogger( + std::function<void(std::string&)> log + ); +} + +#endif diff --git a/src/nissy.h b/src/nissy.h @@ -23,6 +23,7 @@ for example 'rotation UF' or 'mirrored BL'. /* Some constants for size for I/O buffers */ #define NISSY_SIZE_B32 22U #define NISSY_SIZE_H48 88U +#define NISSY_SIZE_CUBE_MAX NISSY_SIZE_H48 #define NISSY_SIZE_TRANSFORMATION 12U #define NISSY_SIZE_SOLVE_STATS 10U #define NISSY_SIZE_DATAID 255U @@ -39,7 +40,6 @@ for example 'rotation UF' or 'mirrored BL'. /* The solved cube in B32 format */ #define NISSY_SOLVED_CUBE "ABCDEFGH=ABCDEFGHIJKL" - /* Error codes ***************************************************************/ /*