aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md10
-rw-r--r--python/example.py37
-rw-r--r--python/nissy_module.c258
-rw-r--r--src/nissy.c13
-rw-r--r--src/solvers/tables.h3
5 files changed, 304 insertions, 17 deletions
diff --git a/README.md b/README.md
index d1ae1ce..4f9be77 100644
--- a/README.md
+++ b/README.md
@@ -182,8 +182,14 @@ From here you can call the library functions directly, for example:
182'ASTUGFBH=DACXEZGBLIKF' 182'ASTUGFBH=DACXEZGBLIKF'
183``` 183```
184 184
185Please note: as this is work in progress, not all functions are currently 185Some methods have a different signature in the Pythn module than in
186available from the Python module. 186libnissy: for example `solve()` returns the solutions as a list of
187strings instead of writing them to a return parameter buffer.
188You can access the documentation for the Python module from within
189a Python interpreter with `help(nissy)`. Cross-check this documentation
190with the comments in nissy.h for more details.
191
192Please note: the support for the Python module is still rudimentary.
187 193
188## Cube formats 194## Cube formats
189 195
diff --git a/python/example.py b/python/example.py
new file mode 100644
index 0000000..fdea2d4
--- /dev/null
+++ b/python/example.py
@@ -0,0 +1,37 @@
1# Small example of nissy_python_module usage
2
3# Compile the python module with "make python", then run this from the main
4# folder containing nissy_python_module.so
5
6# Append the main folder to the python path so we can load the module
7from sys import path
8path.append("./")
9
10# Import with a nicer name
11import nissy_python_module as nissy
12
13# Choose the solver you prefer
14solver = "h48h0k4"
15
16# Load the pruning table from file
17data = bytearray(open("tables/" + solver, "rb").read())
18
19# If you have not generated the table yet, you can do so:
20# data = nissy.gendata("h48h0k4")
21
22# Get a scrambled cube
23cube = nissy.applymoves(nissy.solved_cube, "U F R2");
24
25# Solve!
26solutions = nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, -1, data)
27
28# Print the solutions, one per line
29print("Found ", len(solutions), " solutions:")
30for s in solutions:
31 print(s)
32
33# You can use help(nissy) for more info about the available methods:
34# help(nissy)
35
36# Or help(nissy.methodname) if you want to know more about a specific method:
37# help(nissy.solve)
diff --git a/python/nissy_module.c b/python/nissy_module.c
index d02d9fc..2357b90 100644
--- a/python/nissy_module.c
+++ b/python/nissy_module.c
@@ -5,12 +5,17 @@
5#include "../src/nissy.h" 5#include "../src/nissy.h"
6 6
7#define MAX_CUBE_STR_LEN 1024 /* Update when adding formats */ 7#define MAX_CUBE_STR_LEN 1024 /* Update when adding formats */
8#define MAX_SOLUTIONS_SIZE 250000
8 9
9static bool 10static bool
10check_error(long long err) 11check_error(long long err)
11{ 12{
12 char err_string[255]; 13 char err_string[255];
13 14
15 /* A positive value always denotes a success */
16 if (err > 0)
17 return true;
18
14 switch (err) { 19 switch (err) {
15 case NISSY_OK: /* Fallthrough */ 20 case NISSY_OK: /* Fallthrough */
16 case NISSY_WARNING_UNSOLVABLE: 21 case NISSY_WARNING_UNSOLVABLE:
@@ -58,6 +63,18 @@ long_result(long long result)
58 return PyLong_FromLong(result); 63 return PyLong_FromLong(result);
59} 64}
60 65
66
67PyDoc_STRVAR(compose_doc,
68"compose(cube, permutation)\n"
69"--\n\n"
70"Apply 'permutation' on 'cube'.\n"
71"\n"
72"Parameters:\n"
73" - cube: a cube in B32 format\n"
74" - permutation: another cube in B32 format\n"
75"\n"
76"Returns: the resulting cube string in B32 format\n"
77);
61static PyObject * 78static PyObject *
62compose(PyObject *self, PyObject *args) 79compose(PyObject *self, PyObject *args)
63{ 80{
@@ -72,6 +89,16 @@ compose(PyObject *self, PyObject *args)
72 return string_result(err, result); 89 return string_result(err, result);
73} 90}
74 91
92PyDoc_STRVAR(inverse_doc,
93"inverse(cube)\n"
94"--\n\n"
95"Invert 'cube'.\n"
96"\n"
97"Parameters:\n"
98" - cube: a cube in B32 format\n"
99"\n"
100"Returns: the inverse cube in B32 format\n"
101);
75static PyObject * 102static PyObject *
76inverse(PyObject *self, PyObject *args) 103inverse(PyObject *self, PyObject *args)
77{ 104{
@@ -86,6 +113,17 @@ inverse(PyObject *self, PyObject *args)
86 return string_result(err, result); 113 return string_result(err, result);
87} 114}
88 115
116PyDoc_STRVAR(applymoves_doc,
117"applymoves(cube, moves)\n"
118"--\n\n"
119"Apply 'moves' to 'cube'.\n"
120"\n"
121"Parameters:\n"
122" - cube: a cube in B32 format\n"
123" - moves: the moves to apply on the cube\n"
124"\n"
125"Returns: the resulting cube in B32 format\n"
126);
89static PyObject * 127static PyObject *
90applymoves(PyObject *self, PyObject *args) 128applymoves(PyObject *self, PyObject *args)
91{ 129{
@@ -100,6 +138,19 @@ applymoves(PyObject *self, PyObject *args)
100 return string_result(err, result); 138 return string_result(err, result);
101} 139}
102 140
141PyDoc_STRVAR(applytrans_doc,
142"applytrans(cube, transformation)\n"
143"--\n\n"
144"Apply 'transformation' to 'cube'.\n"
145"\n"
146"Parameters:\n"
147" - cube: a cube in B32 format\n"
148" - transformation: the transformation to apply on the cube, formatted as\n"
149" (rotation|mirrored) (2 letters)\n"
150" for example 'mirrored ur' or 'rotation lf'\n"
151"\n"
152"Returns: the resulting cube in B32 format\n"
153);
103static PyObject * 154static PyObject *
104applytrans(PyObject *self, PyObject *args) 155applytrans(PyObject *self, PyObject *args)
105{ 156{
@@ -114,6 +165,18 @@ applytrans(PyObject *self, PyObject *args)
114 return string_result(err, result); 165 return string_result(err, result);
115} 166}
116 167
168PyDoc_STRVAR(convert_doc,
169"convert(fin, fout, cube)\n"
170"--\n\n"
171"Convert 'cube' from format 'fin' to format 'fout'.\n"
172"\n"
173"Parameters:\n"
174" - fin: the format in which 'cube' is given\n"
175" - fout: the format to which 'cube' has to be converted\n"
176" - cube: a cube in B32 format\n"
177"\n"
178"Returns: 'cube' in 'fout' format\n"
179);
117static PyObject * 180static PyObject *
118convert(PyObject *self, PyObject *args) 181convert(PyObject *self, PyObject *args)
119{ 182{
@@ -128,6 +191,20 @@ convert(PyObject *self, PyObject *args)
128 return string_result(err, result); 191 return string_result(err, result);
129} 192}
130 193
194PyDoc_STRVAR(getcube_doc,
195"getcube(ep, eo, cp, co, options)\n"
196"--\n\n"
197"Constructs the cube from the given coordinates and options\n"
198"\n"
199"Parameters:\n"
200" - ep: the edge permutation coordinate\n"
201" - eo: the edge orientation coordinate\n"
202" - cp: the corner permutation coordinate\n"
203" - co: the corner orientation coordinate\n"
204" - options: a string, for example \"fix\"\n"
205"\n"
206"Returns: the cube constructed from the given coordinates\n"
207);
131static PyObject * 208static PyObject *
132getcube(PyObject *self, PyObject *args) 209getcube(PyObject *self, PyObject *args)
133{ 210{
@@ -142,6 +219,16 @@ getcube(PyObject *self, PyObject *args)
142 return string_result(err, result); 219 return string_result(err, result);
143} 220}
144 221
222PyDoc_STRVAR(datasize_doc,
223"datasize(solver)\n"
224"--\n\n"
225"Returns the size of the data (pruning table) for the given solver\n"
226"\n"
227"Parameters:\n"
228" - solver: the name of the solver\n"
229"\n"
230"Returns: the size of the data for the solver, in bytes\n"
231);
145static PyObject * 232static PyObject *
146datasize(PyObject *self, PyObject *args) 233datasize(PyObject *self, PyObject *args)
147{ 234{
@@ -155,23 +242,168 @@ datasize(PyObject *self, PyObject *args)
155 return long_result(result); 242 return long_result(result);
156} 243}
157 244
158/* TODO: gendata, checkdata and solve and countmoves */ 245PyDoc_STRVAR(gendata_doc,
246"gendata(solver)\n"
247"--\n\n"
248"Generates the data (pruning table) for the given solver\n"
249"\n"
250"Parameters:\n"
251" - solver: the name of the solver\n"
252"\n"
253"Returns: a bytearray containing the data for the solver\n"
254);
255static PyObject *
256gendata(PyObject *self, PyObject *args)
257{
258 long long size, err;
259 const char *solver;
260 char *buf;
261
262 if (!PyArg_ParseTuple(args, "s", &solver))
263 return NULL;
264
265 size = nissy_datasize(solver);
266 if (!check_error(size))
267 return NULL;
268
269 buf = PyMem_Malloc(size);
270
271 Py_BEGIN_ALLOW_THREADS
272 err = nissy_gendata(solver, size, buf);
273 Py_END_ALLOW_THREADS
274
275 if (check_error(err))
276 return PyByteArray_FromStringAndSize(buf, size);
277 else
278 return NULL;
279}
280
281PyDoc_STRVAR(checkdata_doc,
282"checkdata(data)\n"
283"--\n\n"
284"Checks if the data (pruning table) given is valid or not\n"
285"\n"
286"Parameters:\n"
287" - data: a bytearray containing the data for a solver"
288"\n"
289"Returns: true if the data is valid, false otherwise\n"
290);
291PyObject *
292checkdata(PyObject *self, PyObject *args)
293{
294 long long result;
295 PyByteArrayObject *data;
296
297 if (!PyArg_ParseTuple(args, "Y", &data))
298 return NULL;
299
300 result = nissy_checkdata(data->ob_alloc, data->ob_bytes);
301
302 if (check_error(result))
303 return Py_True;
304 else
305 return Py_False;
306}
307
308PyDoc_STRVAR(solve_doc,
309"solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions, optimal, data)\n"
310"--\n\n"
311"Solves the given 'cube' with the given 'solver' and other parameters."
312"See the documentation for libnissy (in nissy.h) for details.\n"
313"\n"
314"Parameters:\n"
315" - cube: a cube in B32 format\n"
316" - solver: the solver to use\n"
317" - minmoves: the minimum number of moves to use\n"
318" - maxmoves: the maximum number of moves to use\n"
319" - maxsolution: the maximum number of solutions to return\n"
320" - optimal: the largest number of moves from the shortest solution"
321"(set to -1 to ignore)\n"
322" - data: a bytearray containing the data for the solver\n"
323"\n"
324"Returns: a list with the solutions found\n"
325);
326PyObject *
327solve(PyObject *self, PyObject *args)
328{
329 long long result;
330 unsigned nissflag, minmoves, maxmoves, maxsolutions;
331 int optimal, i, j, k;
332 const char *cube, *solver;
333 char solutions[MAX_SOLUTIONS_SIZE];
334 long long stats[NISSY_SIZE_SOLVE_STATS];
335 PyByteArrayObject *data;
336 PyObject *list, *item;
337
338 if (!PyArg_ParseTuple(args, "ssIIIIiY", &cube, &solver, &nissflag,
339 &minmoves, &maxmoves, &maxsolutions, &optimal, &data))
340 return NULL;
341
342 Py_BEGIN_ALLOW_THREADS
343 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves,
344 maxsolutions, optimal, data->ob_alloc, data->ob_bytes,
345 MAX_SOLUTIONS_SIZE, solutions, stats);
346 Py_END_ALLOW_THREADS
347
348 if(!check_error(result)) {
349 return NULL;
350 } else {
351 list = PyList_New(result);
352 for (i = 0, j = 0, k = 0; solutions[i] != 0; i++) {
353 if (solutions[i] != '\n')
354 continue;
355 solutions[i] = 0;
356 item = PyUnicode_FromString(&solutions[k]);
357 PyList_SetItem(list, j, item);
358 j++;
359 k = i+1;
360 }
361 return list;
362 }
363}
364
365PyDoc_STRVAR(countmoves_doc,
366"countmoves(moves)\n"
367"--\n\n"
368"Count the moves\n"
369"\n"
370"Parameters:\n"
371" - moves: the moves to be counted\n"
372"\n"
373"Returns: the number of moves in HTM metric\n"
374);
375PyObject *
376countmoves(PyObject *self, PyObject *args)
377{
378 long long count;
379 const char *moves;
380
381 if (!PyArg_ParseTuple(args, "s", &moves))
382 return NULL;
383
384 count = nissy_countmoves(moves);
385 return long_result(count);
386}
159 387
160static PyMethodDef nissy_methods[] = { 388static PyMethodDef nissy_methods[] = {
161 { "compose", compose, METH_VARARGS, NULL }, 389 { "compose", compose, METH_VARARGS, compose_doc },
162 { "inverse", inverse, METH_VARARGS, NULL }, 390 { "inverse", inverse, METH_VARARGS, inverse_doc },
163 { "applymoves", applymoves, METH_VARARGS, NULL }, 391 { "applymoves", applymoves, METH_VARARGS, applymoves_doc },
164 { "applytrans", applytrans, METH_VARARGS, NULL }, 392 { "applytrans", applytrans, METH_VARARGS, applytrans_doc },
165 { "convert", convert, METH_VARARGS, NULL }, 393 { "convert", convert, METH_VARARGS, convert_doc },
166 { "getcube", getcube, METH_VARARGS, NULL }, 394 { "getcube", getcube, METH_VARARGS, getcube_doc },
167 { "datasize", datasize, METH_VARARGS, NULL }, 395 { "datasize", datasize, METH_VARARGS, datasize_doc },
396 { "gendata", gendata, METH_VARARGS, gendata_doc },
397 { "checkdata", checkdata, METH_VARARGS, checkdata_doc },
398 { "solve", solve, METH_VARARGS, solve_doc },
399 { "countmoves", countmoves, METH_VARARGS, countmoves_doc },
168 { NULL, NULL, 0, NULL } 400 { NULL, NULL, 0, NULL }
169}; 401};
170 402
171static struct PyModuleDef nissy_python_module = { 403static struct PyModuleDef nissy_python_module = {
172 .m_base = PyModuleDef_HEAD_INIT, 404 .m_base = PyModuleDef_HEAD_INIT,
173 .m_name = "nissy", 405 .m_name = "nissy",
174 .m_doc = NULL, 406 .m_doc = "python module for libnissy",
175 .m_size = -1, 407 .m_size = -1,
176 .m_methods = nissy_methods, 408 .m_methods = nissy_methods,
177 .m_slots = NULL, 409 .m_slots = NULL,
@@ -195,7 +427,13 @@ PyMODINIT_FUNC PyInit_nissy_python_module(void) {
195 427
196 nissy_setlogger(log_stdout); 428 nissy_setlogger(log_stdout);
197 module = PyModule_Create(&nissy_python_module); 429 module = PyModule_Create(&nissy_python_module);
198 PyModule_AddStringConstant(module, "solvedcube", NISSY_SOLVED_CUBE); 430
431 PyModule_AddStringConstant(module, "solved_cube", NISSY_SOLVED_CUBE);
432 PyModule_AddIntConstant(module, "nissflag_normal", NISSY_NISSFLAG_NORMAL);
433 PyModule_AddIntConstant(module, "nissflag_inverse", NISSY_NISSFLAG_INVERSE);
434 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED);
435 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR);
436 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL);
199 437
200 return module; 438 return module;
201} 439}
diff --git a/src/nissy.c b/src/nissy.c
index c5cf1d5..793d659 100644
--- a/src/nissy.c
+++ b/src/nissy.c
@@ -71,7 +71,10 @@ checkdata(const char *buf, const tableinfo_t *info)
71{ 71{
72 uint64_t distr[INFO_DISTRIBUTION_LEN]; 72 uint64_t distr[INFO_DISTRIBUTION_LEN];
73 73
74 if (!strncmp(info->solver, "cocsep", 6)) { 74 if (info == NULL) {
75 LOG("checkdata: error reading table info\n");
76 return false;
77 } else if (!strncmp(info->solver, "cocsep", 6)) {
75 getdistribution_cocsep( 78 getdistribution_cocsep(
76 (uint32_t *)((char *)buf + INFOSIZE), distr); 79 (uint32_t *)((char *)buf + INFOSIZE), distr);
77 } else if (!strncmp(info->solver, "h48", 3)) { 80 } else if (!strncmp(info->solver, "h48", 3)) {
@@ -442,21 +445,23 @@ nissy_checkdata(
442{ 445{
443 char *buf; 446 char *buf;
444 tableinfo_t info; 447 tableinfo_t info;
448 int64_t err;
445 449
446 for (buf = (char *)data; 450 for (buf = (char *)data;
447 readtableinfo(data_size, buf, &info); 451 (err = readtableinfo(data_size, buf, &info)) == NISSY_OK;
448 buf += info.next, data_size -= info.next) 452 buf += info.next, data_size -= info.next)
449 { 453 {
450 if (!checkdata(buf, &info)) { 454 if (!checkdata(buf, &info)) {
451 LOG("Error: data for %s is inconsistent with info!\n", 455 LOG("Error: data for solver '%s' is corrupted!\n",
452 info.solver); 456 info.solver);
453 return NISSY_ERROR_DATA; 457 return NISSY_ERROR_DATA;
454 } 458 }
459
455 if (info.next == 0) 460 if (info.next == 0)
456 break; 461 break;
457 } 462 }
458 463
459 return NISSY_OK; 464 return err;
460} 465}
461 466
462long long 467long long
diff --git a/src/solvers/tables.h b/src/solvers/tables.h
index 060b018..3147416 100644
--- a/src/solvers/tables.h
+++ b/src/solvers/tables.h
@@ -35,7 +35,8 @@ readtableinfo(uint64_t buf_size, const char *buf, tableinfo_t *info)
35 35
36 if (buf_size < INFOSIZE) { 36 if (buf_size < INFOSIZE) {
37 LOG("Error reading table: buffer size is too small " 37 LOG("Error reading table: buffer size is too small "
38 "(smaller than INFOSIZE = %" PRId64 ")\n", INFOSIZE); 38 "(given size %" PRIu64 " is smaller than INFOSIZE = %"
39 PRId64 ")\n", buf_size, INFOSIZE);
39 return NISSY_ERROR_BUFFER_SIZE; 40 return NISSY_ERROR_BUFFER_SIZE;
40 } 41 }
41 42

Generated with cgit - Back to sebastiano.tronto.net