nissy_module.c (10913B)
1 #define PY_SSIZE_T_CLEAN 2 #include <Python.h> 3 #include <stdbool.h> 4 5 #include "../src/nissy.h" 6 7 #define MAX_CUBE_STR_LEN 1024 /* Update when adding formats */ 8 #define MAX_SOLUTIONS_SIZE 250000 9 10 static bool 11 check_error(long long err) 12 { 13 char err_string[255]; 14 15 /* A positive value always denotes a success */ 16 if (err > 0) 17 return true; 18 19 switch (err) { 20 case NISSY_OK: /* Fallthrough */ 21 case NISSY_WARNING_UNSOLVABLE: 22 return true; 23 case NISSY_ERROR_INVALID_CUBE: 24 case NISSY_ERROR_UNSOLVABLE_CUBE: /* Fallthrough */ 25 case NISSY_ERROR_INVALID_MOVES: 26 case NISSY_ERROR_INVALID_TRANS: 27 case NISSY_ERROR_INVALID_FORMAT: 28 case NISSY_ERROR_INVALID_SOLVER: 29 case NISSY_ERROR_NULL_POINTER: 30 case NISSY_ERROR_BUFFER_SIZE: 31 case NISSY_ERROR_DATA: 32 case NISSY_ERROR_OPTIONS: 33 case NISSY_ERROR_UNKNOWN: 34 default: 35 sprintf(err_string, "Error from libnissy (%lld)", err); 36 PyErr_SetString(PyExc_Exception, err_string); 37 return false; 38 } 39 40 } 41 42 static PyObject * 43 string_result(long long err, const char *result) 44 { 45 return check_error(err) ? PyUnicode_FromString(result) : NULL; 46 } 47 48 static PyObject * 49 string_result_free(long long err, char *result) 50 { 51 PyObject *ret; 52 53 ret = PyUnicode_FromString(result); 54 free(result); 55 56 return check_error(err) ? ret : NULL; 57 } 58 59 static PyObject * 60 long_result(long long result) 61 { 62 check_error(result); 63 return PyLong_FromLong(result); 64 } 65 66 67 PyDoc_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 ); 78 static PyObject * 79 compose(PyObject *self, PyObject *args) 80 { 81 long long err; 82 const char *cube, *permutation; 83 char result[NISSY_SIZE_B32]; 84 85 if (!PyArg_ParseTuple(args, "ss", &cube, &permutation)) 86 return NULL; 87 88 err = nissy_compose(cube, permutation, result); 89 return string_result(err, result); 90 } 91 92 PyDoc_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 ); 102 static PyObject * 103 inverse(PyObject *self, PyObject *args) 104 { 105 long long err; 106 const char *cube; 107 char result[NISSY_SIZE_B32]; 108 109 if (!PyArg_ParseTuple(args, "s", &cube)) 110 return NULL; 111 112 err = nissy_inverse(cube, result); 113 return string_result(err, result); 114 } 115 116 PyDoc_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 ); 127 static PyObject * 128 applymoves(PyObject *self, PyObject *args) 129 { 130 long long err; 131 const char *cube, *moves; 132 char result[NISSY_SIZE_B32]; 133 134 if (!PyArg_ParseTuple(args, "ss", &cube, &moves)) 135 return NULL; 136 137 err = nissy_applymoves(cube, moves, result); 138 return string_result(err, result); 139 } 140 141 PyDoc_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 ); 154 static PyObject * 155 applytrans(PyObject *self, PyObject *args) 156 { 157 long long err; 158 const char *cube, *trans; 159 char result[NISSY_SIZE_B32]; 160 161 if (!PyArg_ParseTuple(args, "ss", &cube, &trans)) 162 return NULL; 163 164 err = nissy_applytrans(cube, trans, result); 165 return string_result(err, result); 166 } 167 168 PyDoc_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 ); 180 static PyObject * 181 convert(PyObject *self, PyObject *args) 182 { 183 long long err; 184 const char *fin, *fout, *cube; 185 char result[MAX_CUBE_STR_LEN]; 186 187 if (!PyArg_ParseTuple(args, "sss", &fin, &fout, &cube)) 188 return NULL; 189 190 err = nissy_convert(fin, fout, cube, MAX_CUBE_STR_LEN, result); 191 return string_result(err, result); 192 } 193 194 PyDoc_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 ); 208 static PyObject * 209 getcube(PyObject *self, PyObject *args) 210 { 211 long long ep, eo, cp, co, err; 212 const char *options; 213 char result[NISSY_SIZE_B32]; 214 215 if (!PyArg_ParseTuple(args, "LLLLs", &ep, &eo, &cp, &co, &options)) 216 return NULL; 217 218 err = nissy_getcube(ep, eo, cp, co, options, result); 219 return string_result(err, result); 220 } 221 222 PyDoc_STRVAR(solverinfo_doc, 223 "solverinfo(solver)\n" 224 "--\n\n" 225 "Returns the size and the short name of the data for the given solver\n" 226 "\n" 227 "Parameters:\n" 228 " - solver: the name of the solver\n" 229 "\n" 230 "Returns: a pair containing the size and the short name " 231 "of the data for the solver, in bytes\n" 232 ); 233 static PyObject * 234 solverinfo(PyObject *self, PyObject *args) 235 { 236 long long result; 237 const char *solver; 238 char buf[NISSY_DATAID_SIZE]; 239 PyObject *py_result, *py_buf; 240 241 if (!PyArg_ParseTuple(args, "s", &solver)) 242 return NULL; 243 244 result = nissy_solverinfo(solver, buf); 245 246 py_result = PyLong_FromLong(result); 247 py_buf = PyUnicode_FromString(buf); 248 return PyTuple_Pack(2, py_result, py_buf); 249 } 250 251 PyDoc_STRVAR(gendata_doc, 252 "gendata(solver)\n" 253 "--\n\n" 254 "Generates the data (pruning table) for the given solver\n" 255 "\n" 256 "Parameters:\n" 257 " - solver: the name of the solver\n" 258 "\n" 259 "Returns: a bytearray containing the data for the solver\n" 260 ); 261 static PyObject * 262 gendata(PyObject *self, PyObject *args) 263 { 264 long long size, err; 265 const char *solver; 266 char *buf, dataid[NISSY_DATAID_SIZE]; 267 268 if (!PyArg_ParseTuple(args, "s", &solver)) 269 return NULL; 270 271 size = nissy_solverinfo(solver, dataid); 272 if (!check_error(size)) 273 return NULL; 274 275 buf = PyMem_Malloc(size); 276 277 Py_BEGIN_ALLOW_THREADS 278 err = nissy_gendata(solver, size, buf); 279 Py_END_ALLOW_THREADS 280 281 if (check_error(err)) 282 return PyByteArray_FromStringAndSize(buf, size); 283 else 284 return NULL; 285 } 286 287 PyDoc_STRVAR(checkdata_doc, 288 "checkdata(data)\n" 289 "--\n\n" 290 "Checks if the data (pruning table) given is valid or not\n" 291 "\n" 292 "Parameters:\n" 293 " - data: a bytearray containing the data for a solver" 294 "\n" 295 "Returns: true if the data is valid, false otherwise\n" 296 ); 297 PyObject * 298 checkdata(PyObject *self, PyObject *args) 299 { 300 long long result; 301 PyByteArrayObject *data; 302 303 if (!PyArg_ParseTuple(args, "Y", &data)) 304 return NULL; 305 306 result = nissy_checkdata(data->ob_alloc, data->ob_bytes); 307 308 if (check_error(result)) 309 return Py_True; 310 else 311 return Py_False; 312 } 313 314 PyDoc_STRVAR(solve_doc, 315 "solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions," 316 " optimal, threads, data)\n" 317 "--\n\n" 318 "Solves the given 'cube' with the given 'solver' and other parameters." 319 "See the documentation for libnissy (in nissy.h) for details.\n" 320 "\n" 321 "Parameters:\n" 322 " - cube: a cube in B32 format\n" 323 " - solver: the solver to use\n" 324 " - minmoves: the minimum number of moves to use\n" 325 " - maxmoves: the maximum number of moves to use\n" 326 " - maxsolution: the maximum number of solutions to return\n" 327 " - optimal: the largest number of moves from the shortest solution" 328 "(set to -1 to ignore)\n" 329 " - threads: the number of threads to use (0 for default)\n" 330 " - data: a bytearray containing the data for the solver\n" 331 "\n" 332 "Returns: a list with the solutions found\n" 333 ); 334 PyObject * 335 solve(PyObject *self, PyObject *args) 336 { 337 long long result; 338 unsigned nissflag, minmoves, maxmoves, maxsolutions; 339 int optimal, i, j, k, threads; 340 const char *cube, *solver; 341 char solutions[MAX_SOLUTIONS_SIZE]; 342 long long stats[NISSY_SIZE_SOLVE_STATS]; 343 PyByteArrayObject *data; 344 PyObject *list, *item; 345 346 if (!PyArg_ParseTuple(args, "ssIIIIiiY", &cube, &solver, &nissflag, 347 &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data)) 348 return NULL; 349 350 Py_BEGIN_ALLOW_THREADS 351 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves, 352 maxsolutions, optimal, threads, data->ob_alloc, data->ob_bytes, 353 MAX_SOLUTIONS_SIZE, solutions, stats); 354 Py_END_ALLOW_THREADS 355 356 if(!check_error(result)) { 357 return NULL; 358 } else { 359 list = PyList_New(result); 360 for (i = 0, j = 0, k = 0; solutions[i] != 0; i++) { 361 if (solutions[i] != '\n') 362 continue; 363 solutions[i] = 0; 364 item = PyUnicode_FromString(&solutions[k]); 365 PyList_SetItem(list, j, item); 366 j++; 367 k = i+1; 368 } 369 return list; 370 } 371 } 372 373 PyDoc_STRVAR(countmoves_doc, 374 "countmoves(moves)\n" 375 "--\n\n" 376 "Count the moves\n" 377 "\n" 378 "Parameters:\n" 379 " - moves: the moves to be counted\n" 380 "\n" 381 "Returns: the number of moves in HTM metric\n" 382 ); 383 PyObject * 384 countmoves(PyObject *self, PyObject *args) 385 { 386 long long count; 387 const char *moves; 388 389 if (!PyArg_ParseTuple(args, "s", &moves)) 390 return NULL; 391 392 count = nissy_countmoves(moves); 393 return long_result(count); 394 } 395 396 static PyMethodDef nissy_methods[] = { 397 { "compose", compose, METH_VARARGS, compose_doc }, 398 { "inverse", inverse, METH_VARARGS, inverse_doc }, 399 { "applymoves", applymoves, METH_VARARGS, applymoves_doc }, 400 { "applytrans", applytrans, METH_VARARGS, applytrans_doc }, 401 { "convert", convert, METH_VARARGS, convert_doc }, 402 { "getcube", getcube, METH_VARARGS, getcube_doc }, 403 { "solverinfo", solverinfo, METH_VARARGS, solverinfo_doc }, 404 { "gendata", gendata, METH_VARARGS, gendata_doc }, 405 { "checkdata", checkdata, METH_VARARGS, checkdata_doc }, 406 { "solve", solve, METH_VARARGS, solve_doc }, 407 { "countmoves", countmoves, METH_VARARGS, countmoves_doc }, 408 { NULL, NULL, 0, NULL } 409 }; 410 411 static struct PyModuleDef nissy_python_module = { 412 .m_base = PyModuleDef_HEAD_INIT, 413 .m_name = "nissy", 414 .m_doc = "python module for libnissy", 415 .m_size = -1, 416 .m_methods = nissy_methods, 417 .m_slots = NULL, 418 .m_traverse = NULL, 419 .m_clear = NULL, 420 .m_free = NULL 421 }; 422 423 static void 424 log_stdout(const char *str, ...) 425 { 426 va_list args; 427 428 va_start(args, str); 429 vfprintf(stderr, str, args); 430 va_end(args); 431 } 432 433 PyMODINIT_FUNC PyInit_nissy_python_module(void) { 434 PyObject *module; 435 436 nissy_setlogger(log_stdout); 437 module = PyModule_Create(&nissy_python_module); 438 439 PyModule_AddStringConstant(module, "solved_cube", NISSY_SOLVED_CUBE); 440 PyModule_AddIntConstant(module, "nissflag_normal", NISSY_NISSFLAG_NORMAL); 441 PyModule_AddIntConstant(module, "nissflag_inverse", NISSY_NISSFLAG_INVERSE); 442 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED); 443 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR); 444 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL); 445 446 return module; 447 }