aboutsummaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2024-10-19 19:17:13 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2024-10-19 19:17:13 +0200
commita3a477870cdbdb4b9c4c8b403c09b13502291234 (patch)
treedb26bc921e48ea8c919f19aa05395f100b854fec /python
parentb80b1b78901154240bc5d2ab6f5ea4465bf7fa67 (diff)
downloadnissy-core-a3a477870cdbdb4b9c4c8b403c09b13502291234.tar.gz
nissy-core-a3a477870cdbdb4b9c4c8b403c09b13502291234.zip
Finished Python module
Diffstat (limited to 'python')
-rw-r--r--python/example.py37
-rw-r--r--python/nissy_module.c258
2 files changed, 285 insertions, 10 deletions
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}

Generated with cgit - Back to sebastiano.tronto.net