nissy-core

The "engine" of nissy, including the H48 optimal solver.
git clone https://git.tronto.net/nissy-core
Download | Log | Files | Refs | README | LICENSE

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 }