nissy_module.c (9495B)
1 #define PY_SSIZE_T_CLEAN 2 #include <Python.h> 3 #include <stdbool.h> 4 5 #include "../src/nissy.h" 6 7 #define MAX_SOLUTIONS_SIZE 250000 8 9 static bool 10 check_error(long long err) 11 { 12 char err_string[255]; 13 14 /* A positive value always denotes a success */ 15 if (err > 0) 16 return true; 17 18 switch (err) { 19 case NISSY_OK: /* Fallthrough */ 20 case NISSY_WARNING_UNSOLVABLE: 21 return true; 22 case NISSY_ERROR_INVALID_CUBE: 23 case NISSY_ERROR_UNSOLVABLE_CUBE: /* Fallthrough */ 24 case NISSY_ERROR_INVALID_MOVES: 25 case NISSY_ERROR_INVALID_TRANS: 26 case NISSY_ERROR_INVALID_SOLVER: 27 case NISSY_ERROR_NULL_POINTER: 28 case NISSY_ERROR_BUFFER_SIZE: 29 case NISSY_ERROR_DATA: 30 case NISSY_ERROR_OPTIONS: 31 case NISSY_ERROR_UNKNOWN: 32 default: 33 sprintf(err_string, "Error from libnissy (%lld)", err); 34 PyErr_SetString(PyExc_Exception, err_string); 35 return false; 36 } 37 38 } 39 40 static PyObject * 41 string_result(long long err, const char *result) 42 { 43 return check_error(err) ? PyUnicode_FromString(result) : NULL; 44 } 45 46 static PyObject * 47 string_result_free(long long err, char *result) 48 { 49 PyObject *ret; 50 51 ret = PyUnicode_FromString(result); 52 free(result); 53 54 return check_error(err) ? ret : NULL; 55 } 56 57 static PyObject * 58 long_result(long long result) 59 { 60 check_error(result); 61 return PyLong_FromLong(result); 62 } 63 64 PyDoc_STRVAR(inverse_doc, 65 "inverse(cube)\n" 66 "--\n\n" 67 "Invert 'cube'.\n" 68 "\n" 69 "Parameters:\n" 70 " - cube: a cube in\n" 71 "\n" 72 "Returns: the inverse cube\n" 73 ); 74 static PyObject * 75 inverse(PyObject *self, PyObject *args) 76 { 77 long long err; 78 const char *cube; 79 char result[NISSY_SIZE_CUBE]; 80 81 if (!PyArg_ParseTuple(args, "s", &cube)) 82 return NULL; 83 84 err = nissy_inverse(cube, result); 85 return string_result(err, result); 86 } 87 88 PyDoc_STRVAR(applymoves_doc, 89 "applymoves(cube, moves)\n" 90 "--\n\n" 91 "Apply 'moves' to 'cube'.\n" 92 "\n" 93 "Parameters:\n" 94 " - cube: a cube in\n" 95 " - moves: the moves to apply on the cube\n" 96 "\n" 97 "Returns: the resulting cube\n" 98 ); 99 static PyObject * 100 applymoves(PyObject *self, PyObject *args) 101 { 102 long long err; 103 const char *cube, *moves; 104 char result[NISSY_SIZE_CUBE]; 105 106 if (!PyArg_ParseTuple(args, "ss", &cube, &moves)) 107 return NULL; 108 109 err = nissy_applymoves(cube, moves, result); 110 return string_result(err, result); 111 } 112 113 PyDoc_STRVAR(applytrans_doc, 114 "applytrans(cube, transformation)\n" 115 "--\n\n" 116 "Apply 'transformation' to 'cube'.\n" 117 "\n" 118 "Parameters:\n" 119 " - cube: a cube\n" 120 " - transformation: the transformation to apply on the cube, formatted as\n" 121 " (rotation|mirrored) (2 letters)\n" 122 " for example 'mirrored ur' or 'rotation lf'\n" 123 "\n" 124 "Returns: the resulting cube\n" 125 ); 126 static PyObject * 127 applytrans(PyObject *self, PyObject *args) 128 { 129 long long err; 130 const char *cube, *trans; 131 char result[NISSY_SIZE_CUBE]; 132 133 if (!PyArg_ParseTuple(args, "ss", &cube, &trans)) 134 return NULL; 135 136 err = nissy_applytrans(cube, trans, result); 137 return string_result(err, result); 138 } 139 140 PyDoc_STRVAR(getcube_doc, 141 "getcube(ep, eo, cp, co, orientation, options)\n" 142 "--\n\n" 143 "Constructs the cube from the given coordinates and options\n" 144 "\n" 145 "Parameters:\n" 146 " - ep: the edge permutation coordinate\n" 147 " - eo: the edge orientation coordinate\n" 148 " - cp: the corner permutation coordinate\n" 149 " - co: the corner orientation coordinate\n" 150 " - orientation: the orientation of the cube\n" 151 " - options: a string, for example \"fix\"\n" 152 "\n" 153 "Returns: the cube constructed from the given coordinates\n" 154 ); 155 static PyObject * 156 getcube(PyObject *self, PyObject *args) 157 { 158 long long ep, eo, cp, co, or, err; 159 const char *options; 160 char result[NISSY_SIZE_CUBE]; 161 162 if (!PyArg_ParseTuple( 163 args, "LLLLLs", &ep, &eo, &cp, &co, &or, &options)) 164 return NULL; 165 166 err = nissy_getcube(ep, eo, cp, co, or, options, result); 167 return string_result(err, result); 168 } 169 170 PyDoc_STRVAR(solverinfo_doc, 171 "solverinfo(solver)\n" 172 "--\n\n" 173 "Returns the size and the short name of the data for the given solver\n" 174 "\n" 175 "Parameters:\n" 176 " - solver: the name of the solver\n" 177 "\n" 178 "Returns: a pair containing the size and the short name " 179 "of the data for the solver, in bytes\n" 180 ); 181 static PyObject * 182 solverinfo(PyObject *self, PyObject *args) 183 { 184 long long result; 185 const char *solver; 186 char buf[NISSY_SIZE_DATAID]; 187 PyObject *py_result, *py_buf; 188 189 if (!PyArg_ParseTuple(args, "s", &solver)) 190 return NULL; 191 192 result = nissy_solverinfo(solver, buf); 193 194 py_result = PyLong_FromLong(result); 195 py_buf = PyUnicode_FromString(buf); 196 return PyTuple_Pack(2, py_result, py_buf); 197 } 198 199 PyDoc_STRVAR(gendata_doc, 200 "gendata(solver)\n" 201 "--\n\n" 202 "Generates the data (pruning table) for the given solver\n" 203 "\n" 204 "Parameters:\n" 205 " - solver: the name of the solver\n" 206 "\n" 207 "Returns: a bytearray containing the data for the solver\n" 208 ); 209 static PyObject * 210 gendata(PyObject *self, PyObject *args) 211 { 212 long long size, err; 213 const char *solver; 214 char dataid[NISSY_SIZE_DATAID]; 215 unsigned char *buf; 216 217 if (!PyArg_ParseTuple(args, "s", &solver)) 218 return NULL; 219 220 size = nissy_solverinfo(solver, dataid); 221 if (!check_error(size)) 222 return NULL; 223 224 buf = PyMem_Malloc(size); 225 226 Py_BEGIN_ALLOW_THREADS 227 err = nissy_gendata(solver, size, buf); 228 Py_END_ALLOW_THREADS 229 230 if (check_error(err)) 231 return PyByteArray_FromStringAndSize((char *)buf, size); 232 else 233 return NULL; 234 } 235 236 PyDoc_STRVAR(checkdata_doc, 237 "checkdata(data)\n" 238 "--\n\n" 239 "Checks if the data (pruning table) given is valid or not\n" 240 "\n" 241 "Parameters:\n" 242 " - data: a bytearray containing the data for a solver" 243 "\n" 244 "Returns: true if the data is valid, false otherwise\n" 245 ); 246 PyObject * 247 checkdata(PyObject *self, PyObject *args) 248 { 249 long long result; 250 PyByteArrayObject *data; 251 252 if (!PyArg_ParseTuple(args, "Y", &data)) 253 return NULL; 254 255 result = nissy_checkdata( 256 data->ob_alloc, (unsigned char *)data->ob_bytes); 257 258 if (check_error(result)) 259 return Py_True; 260 else 261 return Py_False; 262 } 263 264 PyDoc_STRVAR(solve_doc, 265 "solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions," 266 " optimal, threads, data)\n" 267 "--\n\n" 268 "Solves the given 'cube' with the given 'solver' and other parameters." 269 "See the documentation for libnissy (in nissy.h) for details.\n" 270 "\n" 271 "Parameters:\n" 272 " - cube: a cube\n" 273 " - solver: the solver to use\n" 274 " - minmoves: the minimum number of moves to use\n" 275 " - maxmoves: the maximum number of moves to use\n" 276 " - maxsolution: the maximum number of solutions to return\n" 277 " - optimal: the largest number of moves from the shortest solution\n" 278 " - threads: the number of threads to use (0 for default)\n" 279 " - data: a bytearray containing the data for the solver\n" 280 "\n" 281 "Returns: a list with the solutions found\n" 282 ); 283 PyObject * 284 solve(PyObject *self, PyObject *args) 285 { 286 long long result; 287 unsigned nissflag, minmoves, maxmoves, maxsolutions; 288 int optimal, i, j, k, threads; 289 const char *cube, *solver; 290 char solutions[MAX_SOLUTIONS_SIZE]; 291 long long stats[NISSY_SIZE_SOLVE_STATS]; 292 PyByteArrayObject *data; 293 PyObject *list, *item; 294 295 if (!PyArg_ParseTuple(args, "ssIIIIIIY", &cube, &solver, &nissflag, 296 &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data)) 297 return NULL; 298 299 Py_BEGIN_ALLOW_THREADS 300 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves, 301 maxsolutions, optimal, threads, data->ob_alloc, 302 (unsigned char *)data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions, 303 stats); 304 Py_END_ALLOW_THREADS 305 306 if(!check_error(result)) { 307 return NULL; 308 } else { 309 list = PyList_New(result); 310 for (i = 0, j = 0, k = 0; solutions[i] != 0; i++) { 311 if (solutions[i] != '\n') 312 continue; 313 solutions[i] = 0; 314 item = PyUnicode_FromString(&solutions[k]); 315 PyList_SetItem(list, j, item); 316 j++; 317 k = i+1; 318 } 319 return list; 320 } 321 } 322 323 PyDoc_STRVAR(countmoves_doc, 324 "countmoves(moves)\n" 325 "--\n\n" 326 "Count the moves\n" 327 "\n" 328 "Parameters:\n" 329 " - moves: the moves to be counted\n" 330 "\n" 331 "Returns: the number of moves in HTM metric\n" 332 ); 333 PyObject * 334 countmoves(PyObject *self, PyObject *args) 335 { 336 long long count; 337 const char *moves; 338 339 if (!PyArg_ParseTuple(args, "s", &moves)) 340 return NULL; 341 342 count = nissy_countmoves(moves); 343 return long_result(count); 344 } 345 346 static PyMethodDef nissy_methods[] = { 347 { "inverse", inverse, METH_VARARGS, inverse_doc }, 348 { "applymoves", applymoves, METH_VARARGS, applymoves_doc }, 349 { "applytrans", applytrans, METH_VARARGS, applytrans_doc }, 350 { "getcube", getcube, METH_VARARGS, getcube_doc }, 351 { "solverinfo", solverinfo, METH_VARARGS, solverinfo_doc }, 352 { "gendata", gendata, METH_VARARGS, gendata_doc }, 353 { "checkdata", checkdata, METH_VARARGS, checkdata_doc }, 354 { "solve", solve, METH_VARARGS, solve_doc }, 355 { "countmoves", countmoves, METH_VARARGS, countmoves_doc }, 356 { NULL, NULL, 0, NULL } 357 }; 358 359 static struct PyModuleDef nissy_python_module = { 360 .m_base = PyModuleDef_HEAD_INIT, 361 .m_name = "nissy", 362 .m_doc = "python module for libnissy", 363 .m_size = -1, 364 .m_methods = nissy_methods, 365 .m_slots = NULL, 366 .m_traverse = NULL, 367 .m_clear = NULL, 368 .m_free = NULL 369 }; 370 371 static void 372 log_stdout(const char *str, void *unused) 373 { 374 fprintf(stderr, "%s", str); 375 } 376 377 PyMODINIT_FUNC PyInit_nissy_python_module(void) { 378 PyObject *module; 379 380 nissy_setlogger(log_stdout, NULL); 381 module = PyModule_Create(&nissy_python_module); 382 383 PyModule_AddStringConstant(module, "solved_cube", NISSY_SOLVED_CUBE); 384 PyModule_AddIntConstant(module, "nissflag_normal", NISSY_NISSFLAG_NORMAL); 385 PyModule_AddIntConstant(module, "nissflag_inverse", NISSY_NISSFLAG_INVERSE); 386 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED); 387 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR); 388 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL); 389 390 return module; 391 }