aboutsummaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp')
-rw-r--r--cpp/examples/move_convert.cpp24
-rw-r--r--cpp/examples/solve_h48h3k2.cpp91
-rw-r--r--cpp/nissy.cpp289
-rw-r--r--cpp/nissy.h174
4 files changed, 345 insertions, 233 deletions
diff --git a/cpp/examples/move_convert.cpp b/cpp/examples/move_convert.cpp
new file mode 100644
index 0000000..11398df
--- /dev/null
+++ b/cpp/examples/move_convert.cpp
@@ -0,0 +1,24 @@
1/* A simple example showing how to move a cube and print it in H48 format. */
2
3#include "../nissy.h"
4#include <iostream>
5
6int main() {
7 nissy::cube c;
8 if (!c.move("R' U' F").ok()) {
9 std::cout << "Error moving the cube!" << std::endl;
10 return 1;
11 }
12
13 auto string_or_error = c.to_string("H48");
14 if (std::holds_alternative<nissy::error>(string_or_error)) {
15 std::cout << "Error converting cube!" << std::endl;
16 return 1;
17 }
18
19 auto str = std::get<std::string>(string_or_error);
20 std::cout << "Cube in H48 format after R' U' F:" << std::endl
21 << str << std::endl;
22
23 return 0;
24}
diff --git a/cpp/examples/solve_h48h3k2.cpp b/cpp/examples/solve_h48h3k2.cpp
new file mode 100644
index 0000000..6639c55
--- /dev/null
+++ b/cpp/examples/solve_h48h3k2.cpp
@@ -0,0 +1,91 @@
1/* Simple demo for an H48 solver */
2
3#include "../nissy.h"
4#include <filesystem>
5#include <fstream>
6#include <iostream>
7#include <string>
8
9extern "C" {
10 long long nissy_setlogger(void (*)(const char *));
11}
12
13int main() {
14 // Get verbose output
15 nissy_setlogger([](const char* s) { std::cout << s; });
16
17 // Get the scramble from the user
18 std::cout << "Enter scramble: ";
19 std::string scramble;
20 std::getline(std::cin, scramble);
21
22 // Apply scramble to a solved cube
23 nissy::cube c;
24 if (!c.move(scramble).ok()) {
25 std::cout << "Invalid scramble!" << std::endl;
26 return 1;
27 }
28
29 // Ask for a limit on the solution's length
30 int maxmoves;
31 std::cout << "Maximum number of moves: ";
32 std::cin >> maxmoves;
33
34 // Load the solver
35 auto h48h3k2 = std::get<nissy::solver>(nissy::solver::get("h48h3k2"));
36 std::filesystem::path tableDir("tables");
37 std::filesystem::create_directories(tableDir); // Ignored if dir exists
38 std::filesystem::path tableFile("tables/" + h48h3k2.id);
39
40 // If the table is not present, generate it
41 if (!std::filesystem::exists(tableFile)) {
42 std::cout << "Data for h48h3k2 solver was not found, "
43 << "generating it..." << std::endl;
44 auto err = h48h3k2.generate_data();
45 if (!err.ok()) {
46 std::cout << "Unexpected error! Error code: "
47 << err.value << std::endl;
48 return 1;
49 }
50 std::ofstream ofs(tableFile, std::ios::binary);
51 ofs.write(reinterpret_cast<char *>(h48h3k2.data.data()),
52 h48h3k2.size);
53 std::cout << "Table generated and written to "
54 << tableFile << std::endl;
55 ofs.close();
56 }
57 // Otherwise read it from file
58 else {
59 std::cout << "Loading data table from file" << std::endl;
60 std::ifstream ifs(tableFile, std::ios::binary);
61 h48h3k2.read_data(ifs);
62 ifs.close();
63 std::cout << "Data loaded, checking table..." << std::endl;
64 auto err = h48h3k2.check_data();
65 if (!err.ok()) {
66 std::cout << "Error reading data table from file! "
67 << "Error code: " << err.value << std::endl;
68 return 1;
69 }
70 std::cout << "Data table loaded" << std::endl;
71 }
72
73 // Solve
74 auto solve_result = h48h3k2.solve(c, nissy::nissflag::NORMAL,
75 0, maxmoves, 1, -1, 8);
76
77 // Write the result
78 if (!solve_result.err.ok()) {
79 std::cout << "Error solving! Error code: " <<
80 solve_result.err.value << std::endl;
81 return 1;
82 }
83
84 if (solve_result.solutions.size() == 0)
85 std::cout << "No solution found!" << std::endl;
86 else
87 std::cout << "Solution: " <<
88 solve_result.solutions[0] << std::endl;
89
90 return 0;
91}
diff --git a/cpp/nissy.cpp b/cpp/nissy.cpp
index 7987f6c..ad0eb7c 100644
--- a/cpp/nissy.cpp
+++ b/cpp/nissy.cpp
@@ -6,6 +6,8 @@ TODO: add more documentation (here and in README.md)
6 6
7#include "./nissy.h" 7#include "./nissy.h"
8 8
9#include <fstream>
10
9extern "C" { 11extern "C" {
10 long long nissy_compose(const char *, const char *, char *); 12 long long nissy_compose(const char *, const char *, char *);
11 long long nissy_inverse(const char *, char *); 13 long long nissy_inverse(const char *, char *);
@@ -26,191 +28,214 @@ extern "C" {
26} 28}
27 29
28namespace nissy { 30namespace nissy {
29 namespace utils {
30 void fixstr(std::string& str)
31 {
32 size_t n = str.find('\0');
33 str.resize(n);
34 }
35
36 std::variant<cube_t, error_t>
37 cube_or_error(const char *r, long long e)
38 {
39 if (e < 0)
40 return error_t{e};
41 return cube_t{r};
42 }
43 31
44 std::variant<std::string, error_t> 32 /* Definition of constants with const qualifier */
45 string_or_error(const char *cstr, long long e) 33 const nissflag nissflag::NORMAL{1};
46 { 34 const nissflag nissflag::INVERSE{2};
47 if (e < 0) 35 const nissflag nissflag::MIXED{4};
48 return error_t{e}; 36 const nissflag nissflag::LINEAR{3};
37 const nissflag nissflag::ALL{7};
49 38
50 std::string str{cstr}; 39 const error error::OK{0};
51 fixstr(str); 40 const error error::UNSOLVABLE{-1};
52 return str; 41 const error error::INVALID_CUBE{-10};
53 } 42 const error error::UNSOLVABLE_CUBE{-11};
43 const error error::INVALID_MOVES{-20};
44 const error error::INVALID_TRANS{-30};
45 const error error::INVALID_FORMAT{-40};
46 const error error::INVALID_SOLVER{-50};
47 const error error::NULL_POINTER{-60};
48 const error error::BUFFER_SIZE{-61};
49 const error error::DATA{-70};
50 const error error::OPTIONS{-80};
51 const error error::UNKNOWN{-999};
54 52
55 std::function<void(const char *)> 53 namespace size {
56 char_str_fn(std::function<void(std::string&)> f) 54 constexpr size_t B32 = 22;
57 { 55 constexpr size_t H48 = 88;
58 return [&](const char *cc){ 56 constexpr size_t CUBE_MAX = H48;
59 std::string str{cc}; 57 constexpr size_t TRANSFORMATION = 12;
60 f(str); 58 constexpr size_t DATAID = 255;
61 };
62 }
63 } 59 }
64
65 std::variant<cube_t, error_t>
66 compose(const cube_t& cube, const cube_t& permutation)
67 {
68 char result[size::B32];
69 60
70 auto error = nissy_compose(cube.value.c_str(), 61 bool error::ok() const { return value >= 0; }
71 permutation.value.c_str(), result);
72 62
73 return utils::cube_or_error(result, error); 63 cube::cube() {}
74 }
75 64
76 std::variant<cube_t, error_t> 65 error cube::move(const std::string& moves)
77 inverse(const cube_t& cube)
78 { 66 {
79 char result[size::B32]; 67 char result[size::B32];
80 68 long long err = nissy_applymoves(
81 auto error = nissy_inverse(cube.value.c_str(), result); 69 m_b32.c_str(), moves.c_str(), result);
82 70 if (err < 0)
83 return utils::cube_or_error(result, error); 71 return error{err};
72 m_b32 = result;
73 return error::OK;
84 } 74 }
85 75
86 std::variant<cube_t, error_t> 76 error cube::transform(const std::string& trans)
87 applymoves(const cube_t& cube, const moves_t& moves)
88 { 77 {
89 char result[size::B32]; 78 char result[size::B32];
79 long long err = nissy_applytrans(
80 m_b32.c_str(), trans.c_str(), result);
81 if (err < 0)
82 return error{err};
83 m_b32 = result;
84 return error::OK;
85 }
90 86
91 auto error = nissy_applymoves(cube.value.c_str(), 87 void cube::invert()
92 moves.value.c_str(), result); 88 {
93 89 char result[size::B32];
94 return utils::cube_or_error(result, error); 90 nissy_inverse(m_b32.c_str(), result);
91 m_b32 = result;
95 } 92 }
96 93
97 std::variant<cube_t, error_t> 94 void cube::compose(const cube& other)
98 applytrans(const cube_t& cube, const trans_t& trans)
99 { 95 {
100 char result[size::B32]; 96 char result[size::B32];
97 nissy_compose(
98 m_b32.c_str(), other.to_string().c_str(), result);
99 m_b32 = result;
100 }
101 101
102 auto error = nissy_applytrans(cube.value.c_str(), 102 std::string cube::to_string() const { return m_b32; }
103 trans.value.c_str(), result);
104 103
105 return utils::cube_or_error(result, error); 104 std::variant<std::string, error>
105 cube::to_string(const std::string& format) const
106 {
107 char result[size::CUBE_MAX];
108 auto err = nissy_convert("B32", format.c_str(),
109 m_b32.c_str(), size::CUBE_MAX, result);
110 if (err < 0)
111 return error{err};
112 else
113 return result;
106 } 114 }
107 115
108 std::variant<std::string, error_t> 116 std::variant<cube, error>
109 convert(const cube_format_t& format_in, 117 cube::from_string(const std::string& str)
110 const cube_format_t& format_out, const std::string& cube_string)
111 { 118 {
112 char result[size::CUBE_MAX]; 119 return from_string(str, "B32");
120 }
113 121
114 auto error = nissy_convert(format_in.value.c_str(), 122 std::variant<cube, error>
115 format_out.value.c_str(), cube_string.c_str(), 123 cube::from_string(const std::string& str, const std::string& format)
116 size::CUBE_MAX, result); 124 {
125 char result[size::B32];
126 cube c;
127 auto err = nissy_convert(format.c_str(),
128 "B32", c.m_b32.c_str(), size::B32, result);
129 if (err < 0)
130 return error{err};
131 c.m_b32 = result;
132 return c;
133 }
117 134
118 return utils::string_or_error(result, error); 135 std::variant<cube, error>
136 cube::get(long long ep, long long eo, long long cp, long long co)
137 {
138 return get(ep, eo, cp, co, "fix");
119 } 139 }
120 140
121 std::variant<cube_t, error_t> 141 std::variant<cube, error>
122 getcube(long long ep, long long eo, long long cp, long long co, 142 cube::get(long long ep, long long eo, long long cp, long long co,
123 const std::string& options) 143 const std::string& options)
124 { 144 {
125 char result[size::B32]; 145 char result[size::B32];
126 146 cube c;
127 auto error = nissy_getcube(ep, eo, cp, co, options.c_str(), 147 auto err = nissy_getcube(
128 result); 148 ep, eo, cp, co, options.c_str(), result);
129 149 if (err < 0)
130 return utils::cube_or_error(result, error); 150 return error{err};
151 c.m_b32 = result;
152 return c;
131 } 153 }
132 154
133 std::variant<std::pair<size_t, std::string>, error_t> 155 error solver::generate_data()
134 solverinfo(const solver_t& solver)
135 { 156 {
136 char dataid[size::DATAID]; 157 data.resize(size);
137 158 auto err = nissy_gendata(name.c_str(),
138 auto error = nissy_solverinfo(solver.value.c_str(), dataid); 159 size, reinterpret_cast<char *>(data.data()));
139 if (error < 0) 160 return error{err};
140 return error_t{error};
141
142 return std::pair{error, dataid};
143 } 161 }
144 162
145 std::variant<solver_data_t, error_t> 163 void solver::read_data(std::ifstream& ifs)
146 gendata(const solver_t& solver)
147 { 164 {
148 char dataid[size::DATAID]; 165 data.resize(size);
149 166 ifs.read(reinterpret_cast<char *>(data.data()), size);
150 auto error = nissy_solverinfo(solver.value.c_str(), dataid);
151 if (error < 0)
152 return error_t{error};
153
154 size_t sz = error;
155 std::vector<std::byte> buffer(sz);
156 error = nissy_gendata(solver.value.c_str(), sz,
157 reinterpret_cast<char *>(buffer.data()));
158 if (error < 0)
159 return error_t{error};
160
161 return solver_data_t{buffer};
162 } 167 }
163 168
164 std::optional<error_t> 169 error solver::check_data() const
165 checkdata(const solver_data_t& data)
166 { 170 {
167 auto error = nissy_checkdata(data.value.size(), 171 auto err = nissy_checkdata(data.size(),
168 reinterpret_cast<const char *>(data.value.data())); 172 reinterpret_cast<const char *>(data.data()));
173 return error{err};
174 }
169 175
170 if (error < 0) 176 void solver::unload_data()
171 return error_t{error}; 177 {
172 return std::nullopt; 178 data.resize(0);
173 } 179 }
174 180
175 std::variant<solve_result_t, error_t> 181 solver::solve_result
176 solve(const cube_t& cube, const solver_t& solver, 182 solver::solve(const cube& cube, nissflag niss, unsigned minmoves,
177 nissflag_t niss, unsigned minmoves, unsigned maxmoves, 183 unsigned maxmoves, unsigned maxsols, int optimal, int threads)
178 unsigned maxsolutions, int optimal, int threads,
179 const solver_data_t& data)
180 { 184 {
181 const size_t len = 3 * (maxmoves+1) * maxsolutions; 185 const size_t len = 3 * (maxmoves+1) * maxsols;
182 char csols[len]; 186 std::vector<char> csols(len);
183 long long cstats[size::SOLVE_STATS]; 187 solver::solve_result result;
184 188
185 auto error = nissy_solve(cube.value.c_str(), 189 auto err = nissy_solve(cube.to_string().c_str(),
186 solver.value.c_str(), niss.value, minmoves, maxmoves, 190 name.c_str(), niss.value, minmoves, maxmoves, maxsols,
187 maxsolutions, optimal, threads, data.value.size(), 191 optimal, threads, data.size(),
188 reinterpret_cast<const char *>(data.value.data()), 192 reinterpret_cast<const char *>(data.data()), len,
189 len, csols, cstats); 193 csols.data(), result.stats.data());
194 result.err = error{err};
190 195
191 if (error < 0) 196 if (err < 0)
192 return error_t{error}; 197 return result;
198
199 // TODO: is this special case actually needed? Remove if not
200 if (err == 0) {
201 result.solutions = {};
202 return result;
203 }
193 204
194 std::vector<std::string> sols; 205 std::string_view strsols(csols.data());
195 std::string_view strsols(csols);
196 for (auto r : strsols | std::views::split('\n')) 206 for (auto r : strsols | std::views::split('\n'))
197 sols.push_back(std::string{r.begin(), r.end()}); 207 if (r.begin() != r.end())
208 result.solutions.push_back(
209 std::string{r.begin(), r.end()});
198 210
199 return solve_result_t{sols, std::to_array(cstats)}; 211 return result;
200 } 212 }
201 213
202 std::variant<unsigned, error_t> 214 std::variant<solver, error> solver::get(const std::string& name)
203 countmoves(const moves_t& moves) 215 {
216 char dataid[size::DATAID];
217 auto err = nissy_solverinfo(name.c_str(), dataid);
218 if (err < 0)
219 return error{err};
220 solver s(name);
221 s.size = (unsigned)err;
222 s.id = std::string{dataid};
223 return s;
224 }
225
226 solver::solver(const std::string& str) : name{str} {}
227
228 std::variant<unsigned, error>
229 count_moves(const std::string& moves)
204 { 230 {
205 auto error = nissy_countmoves(moves.value.c_str()); 231 auto err = nissy_countmoves(moves.c_str());
206 if (error < 0) 232 if (err < 0)
207 return error_t{error}; 233 return error{err};
208 return (unsigned)error; 234 return (unsigned)err;
209 } 235 }
210 236
211 void setlogger(std::function<void(std::string&)> log) 237 void set_logger(const std::function<void(const char *)>& log)
212 { 238 {
213 nissy_setlogger( 239 nissy_setlogger(log.target<void(const char *)>());
214 utils::char_str_fn(log).target<void(const char *)>());
215 } 240 }
216} 241}
diff --git a/cpp/nissy.h b/cpp/nissy.h
index e336c7a..7ab466d 100644
--- a/cpp/nissy.h
+++ b/cpp/nissy.h
@@ -15,119 +15,91 @@ C++20 header file for nissy.
15#include <vector> 15#include <vector>
16 16
17namespace nissy { 17namespace nissy {
18 /* Some constants for size for I/O buffers */
19 namespace size {
20 constexpr size_t B32 = 22;
21 constexpr size_t H48 = 88;
22 constexpr size_t CUBE_MAX = H48;
23 constexpr size_t TRANSFORMATION = 12;
24 constexpr size_t SOLVE_STATS = 10;
25 constexpr size_t DATAID = 255;
26 }
27 18
28 /* Some structs definitions for better type safety */ 19 class nissflag {
29 struct nissflag_t { unsigned value; }; 20 public:
30 struct error_t { long long value; }; 21 unsigned value;
31 struct cube_t { std::string value; };
32 struct moves_t { std::string value; };
33 struct trans_t { std::string value; };
34 struct cube_format_t { std::string value; };
35 struct solver_t { std::string value; };
36 struct solver_data_t { std::vector<std::byte> value; };
37 struct solve_result_t {
38 std::vector<std::string> solutions;
39 std::array<long long, size::SOLVE_STATS> stats;
40 };
41
42 /* Flags for NISS options */
43 namespace nissflag {
44 constexpr nissflag_t NORMAL{1};
45 constexpr nissflag_t INVERSE{2};
46 constexpr nissflag_t MIXED{4};
47 constexpr nissflag_t LINEAR{NORMAL.value | INVERSE.value};
48 constexpr nissflag_t ALL{LINEAR.value | MIXED.value};
49 }
50
51 /* Error codes */
52 namespace error {
53 constexpr error_t UNSOLVABLE{-1};
54 constexpr error_t INVALID_CUBE{-10};
55 constexpr error_t UNSOLVABLE_CUBE{-11};
56 constexpr error_t INVALID_MOVES{-20};
57 constexpr error_t INVALID_TRANS{-30};
58 constexpr error_t INVALID_FORMAT{-40};
59 constexpr error_t INVALID_SOLVER{-50};
60 constexpr error_t NULL_POINTER{-60};
61 constexpr error_t BUFFER_SIZE{-61};
62 constexpr error_t DATA{-70};
63 constexpr error_t OPTIONS{-80};
64 constexpr error_t UNKNOWN{-999};
65 }
66
67 /* Cube constants */
68 namespace cube {
69 const cube_t SOLVED{"ABCDEFGH=ABCDEFGHIJKL"};
70 }
71 22
72 std::variant<cube_t, error_t> inverse( 23 static const nissflag NORMAL;
73 const cube_t& cube 24 static const nissflag INVERSE;
74 ); 25 static const nissflag MIXED;
26 static const nissflag LINEAR;
27 static const nissflag ALL;
28 };
75 29
76 std::variant<cube_t, error_t> applymoves( 30 class error {
77 const cube_t& cube, 31 public:
78 const moves_t& moves 32 long long value;
79 ); 33 bool ok() const;
80 34
81 std::variant<cube_t, error_t> applytrans( 35 static const error OK;
82 const cube_t& cube, 36 static const error UNSOLVABLE;
83 const trans_t& trans 37 static const error INVALID_CUBE;
84 ); 38 static const error UNSOLVABLE_CUBE;
39 static const error INVALID_MOVES;
40 static const error INVALID_TRANS;
41 static const error INVALID_FORMAT;
42 static const error INVALID_SOLVER;
43 static const error NULL_POINTER;
44 static const error BUFFER_SIZE;
45 static const error DATA;
46 static const error OPTIONS;
47 static const error UNKNOWN;
48 };
85 49
86 std::variant<std::string, error_t> convert( 50 class cube {
87 const cube_format_t& format_in, 51 public:
88 const cube_format_t& format_out, 52 cube();
89 const std::string& cube_string 53 error move(const std::string&);
90 ); 54 error transform(const std::string&);
55 void invert();
56 void compose(const cube&);
57 std::string to_string() const;
58 std::variant<std::string, error> to_string(
59 const std::string& format) const;
91 60
92 std::variant<cube_t, error_t> getcube( 61 static std::variant<cube, error> from_string(
93 long long ep, 62 const std::string&);
94 long long eo, 63 static std::variant<cube, error> from_string(
95 long long cp, 64 const std::string& str, const std::string& format);
96 long long co, 65 static std::variant<cube, error> get(
97 const std::string& options 66 long long ep, long long eo, long long cp, long long co);
98 ); 67 static std::variant<cube, error> get(
68 long long ep, long long eo, long long cp, long long co,
69 const std::string& options);
99 70
100 std::variant<std::pair<size_t, std::string>, error_t> solverinfo( 71 private:
101 const solver_t& solver 72 std::string m_b32{"ABCDEFGH=ABCDEFGHIJKL"};
102 ); 73 };
103 74
104 std::variant<solver_data_t, error_t> gendata( 75 class solver {
105 const solver_t& solver 76 public:
106 ); 77 struct solve_result {
78 error err;
79 std::vector<std::string> solutions;
80 std::array<long long, 10> stats;
81 };
107 82
108 std::optional<error_t> checkdata( 83 const std::string name;
109 const solver_data_t& data 84 size_t size;
110 ); 85 std::string id;
86 std::vector<std::byte> data;
111 87
112 std::variant<solve_result_t, error_t> solve( 88 error generate_data();
113 const cube_t& cube, 89 void read_data(std::ifstream&);
114 const solver_t& solver, 90 error check_data() const;
115 nissflag_t niss, 91 void unload_data();
116 unsigned minmoves, 92 solve_result solve(const cube&, nissflag, unsigned minmoves,
117 unsigned maxmoves, 93 unsigned maxmoves, unsigned maxsols, int optimal,
118 unsigned maxsolutions, 94 int threads);
119 int optimal,
120 int threads,
121 const solver_data_t& data
122 );
123 95
124 std::variant<unsigned, error_t> countmoves( 96 static std::variant<solver, error> get(const std::string&);
125 const moves_t& moves 97 private:
126 ); 98 solver(const std::string& name);
99 };
127 100
128 void setlogger( 101 std::variant<unsigned, error> count_moves(const std::string&);
129 std::function<void(std::string&)> log 102 void set_logger(const std::function<void(const char*)>&);
130 );
131} 103}
132 104
133#endif 105#endif

Generated with cgit - Back to sebastiano.tronto.net