nissy_module.c (10918B)
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_SIZE_DATAID]; 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 dataid[NISSY_SIZE_DATAID]; 267 unsigned char *buf; 268 269 if (!PyArg_ParseTuple(args, "s", &solver)) 270 return NULL; 271 272 size = nissy_solverinfo(solver, dataid); 273 if (!check_error(size)) 274 return NULL; 275 276 buf = PyMem_Malloc(size); 277 278 Py_BEGIN_ALLOW_THREADS 279 err = nissy_gendata(solver, size, buf); 280 Py_END_ALLOW_THREADS 281 282 if (check_error(err)) 283 return PyByteArray_FromStringAndSize((char *)buf, size); 284 else 285 return NULL; 286 } 287 288 PyDoc_STRVAR(checkdata_doc, 289 "checkdata(data)\n" 290 "--\n\n" 291 "Checks if the data (pruning table) given is valid or not\n" 292 "\n" 293 "Parameters:\n" 294 " - data: a bytearray containing the data for a solver" 295 "\n" 296 "Returns: true if the data is valid, false otherwise\n" 297 ); 298 PyObject * 299 checkdata(PyObject *self, PyObject *args) 300 { 301 long long result; 302 PyByteArrayObject *data; 303 304 if (!PyArg_ParseTuple(args, "Y", &data)) 305 return NULL; 306 307 result = nissy_checkdata( 308 data->ob_alloc, (unsigned char *)data->ob_bytes); 309 310 if (check_error(result)) 311 return Py_True; 312 else 313 return Py_False; 314 } 315 316 PyDoc_STRVAR(solve_doc, 317 "solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions," 318 " optimal, threads, data)\n" 319 "--\n\n" 320 "Solves the given 'cube' with the given 'solver' and other parameters." 321 "See the documentation for libnissy (in nissy.h) for details.\n" 322 "\n" 323 "Parameters:\n" 324 " - cube: a cube in B32 format\n" 325 " - solver: the solver to use\n" 326 " - minmoves: the minimum number of moves to use\n" 327 " - maxmoves: the maximum number of moves to use\n" 328 " - maxsolution: the maximum number of solutions to return\n" 329 " - optimal: the largest number of moves from the shortest solution\n" 330 " - threads: the number of threads to use (0 for default)\n" 331 " - data: a bytearray containing the data for the solver\n" 332 "\n" 333 "Returns: a list with the solutions found\n" 334 ); 335 PyObject * 336 solve(PyObject *self, PyObject *args) 337 { 338 long long result; 339 unsigned nissflag, minmoves, maxmoves, maxsolutions; 340 int optimal, i, j, k, threads; 341 const char *cube, *solver; 342 char solutions[MAX_SOLUTIONS_SIZE]; 343 long long stats[NISSY_SIZE_SOLVE_STATS]; 344 PyByteArrayObject *data; 345 PyObject *list, *item; 346 347 if (!PyArg_ParseTuple(args, "ssIIIIIIY", &cube, &solver, &nissflag, 348 &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data)) 349 return NULL; 350 351 Py_BEGIN_ALLOW_THREADS 352 result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves, 353 maxsolutions, optimal, threads, data->ob_alloc, 354 (unsigned char *)data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions, 355 stats); 356 Py_END_ALLOW_THREADS 357 358 if(!check_error(result)) { 359 return NULL; 360 } else { 361 list = PyList_New(result); 362 for (i = 0, j = 0, k = 0; solutions[i] != 0; i++) { 363 if (solutions[i] != '\n') 364 continue; 365 solutions[i] = 0; 366 item = PyUnicode_FromString(&solutions[k]); 367 PyList_SetItem(list, j, item); 368 j++; 369 k = i+1; 370 } 371 return list; 372 } 373 } 374 375 PyDoc_STRVAR(countmoves_doc, 376 "countmoves(moves)\n" 377 "--\n\n" 378 "Count the moves\n" 379 "\n" 380 "Parameters:\n" 381 " - moves: the moves to be counted\n" 382 "\n" 383 "Returns: the number of moves in HTM metric\n" 384 ); 385 PyObject * 386 countmoves(PyObject *self, PyObject *args) 387 { 388 long long count; 389 const char *moves; 390 391 if (!PyArg_ParseTuple(args, "s", &moves)) 392 return NULL; 393 394 count = nissy_countmoves(moves); 395 return long_result(count); 396 } 397 398 static PyMethodDef nissy_methods[] = { 399 { "compose", compose, METH_VARARGS, compose_doc }, 400 { "inverse", inverse, METH_VARARGS, inverse_doc }, 401 { "applymoves", applymoves, METH_VARARGS, applymoves_doc }, 402 { "applytrans", applytrans, METH_VARARGS, applytrans_doc }, 403 { "convert", convert, METH_VARARGS, convert_doc }, 404 { "getcube", getcube, METH_VARARGS, getcube_doc }, 405 { "solverinfo", solverinfo, METH_VARARGS, solverinfo_doc }, 406 { "gendata", gendata, METH_VARARGS, gendata_doc }, 407 { "checkdata", checkdata, METH_VARARGS, checkdata_doc }, 408 { "solve", solve, METH_VARARGS, solve_doc }, 409 { "countmoves", countmoves, METH_VARARGS, countmoves_doc }, 410 { NULL, NULL, 0, NULL } 411 }; 412 413 static struct PyModuleDef nissy_python_module = { 414 .m_base = PyModuleDef_HEAD_INIT, 415 .m_name = "nissy", 416 .m_doc = "python module for libnissy", 417 .m_size = -1, 418 .m_methods = nissy_methods, 419 .m_slots = NULL, 420 .m_traverse = NULL, 421 .m_clear = NULL, 422 .m_free = NULL 423 }; 424 425 static void 426 log_stdout(const char *str, void *unused) 427 { 428 fprintf(stderr, "%s", str); 429 } 430 431 PyMODINIT_FUNC PyInit_nissy_python_module(void) { 432 PyObject *module; 433 434 nissy_setlogger(log_stdout, NULL); 435 module = PyModule_Create(&nissy_python_module); 436 437 PyModule_AddStringConstant(module, "solved_cube", NISSY_SOLVED_CUBE); 438 PyModule_AddIntConstant(module, "nissflag_normal", NISSY_NISSFLAG_NORMAL); 439 PyModule_AddIntConstant(module, "nissflag_inverse", NISSY_NISSFLAG_INVERSE); 440 PyModule_AddIntConstant(module, "nissflag_mixed", NISSY_NISSFLAG_MIXED); 441 PyModule_AddIntConstant(module, "nissflag_linear", NISSY_NISSFLAG_LINEAR); 442 PyModule_AddIntConstant(module, "nissflag_all", NISSY_NISSFLAG_ALL); 443 444 return module; 445 }