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.c (8698B)


      1 #include <stdlib.h>
      2 #include <inttypes.h>
      3 #include <limits.h>
      4 #include <stdatomic.h>
      5 #include <stdarg.h>
      6 #include <stdbool.h>
      7 #include <string.h>
      8 
      9 #include "nissy.h"
     10 #include "utils/utils.h"
     11 #include "arch/arch.h"
     12 #include "core/core.h"
     13 #include "solvers/solvers.h"
     14 
     15 STATIC long long write_result(oriented_cube_t, char [static NISSY_SIZE_CUBE]);
     16 STATIC long long nissy_dataid(const char *, char [static NISSY_SIZE_DATAID]);
     17 STATIC long long nissy_gendata_unsafe(
     18     const char *, unsigned long long, unsigned char *);
     19 
     20 #define GETCUBE_OPTIONS(S, F) { .option = S, .fix = F }
     21 struct {
     22 	char *option;
     23 	void (*fix)(long long *, long long *,
     24 	    long long *, long long *, long long *);
     25 } getcube_options[] = {
     26 	GETCUBE_OPTIONS("fix", getcube_fix),
     27 	GETCUBE_OPTIONS(NULL, NULL)
     28 };
     29 
     30 STATIC long long
     31 write_result(oriented_cube_t cube, char result[static NISSY_SIZE_CUBE])
     32 {
     33 	writecube(cube, NISSY_SIZE_CUBE, result);
     34 
     35 	if (!issolvable(cube)) {
     36 		LOG("Warning: resulting cube is not solvable\n");
     37 		return NISSY_WARNING_UNSOLVABLE;
     38 	}
     39 
     40 	return NISSY_OK;
     41 }
     42 
     43 long long
     44 nissy_inverse(
     45 	const char cube[static NISSY_SIZE_CUBE],
     46 	char result[static NISSY_SIZE_CUBE]
     47 )
     48 {
     49 	oriented_cube_t c, res;
     50 	long long err;
     51 
     52 	c = readcube(cube);
     53 
     54 	if (iserror(c)) {
     55 		LOG("[inverse] Error: the given cube is invalid\n");
     56 		err = NISSY_ERROR_INVALID_CUBE;
     57 		goto nissy_inverse_error;
     58 	}
     59 
     60 	res = (oriented_cube_t) {
     61 		.cube = inverse(c.cube),
     62 		.orientation = c.orientation
     63 	};
     64 
     65 	if (!isconsistent(res)) {
     66 		LOG("[inverse] Unknown error: inverted cube is invalid\n");
     67 		err = NISSY_ERROR_UNKNOWN;
     68 		goto nissy_inverse_error;
     69 	}
     70 
     71 	return write_result(res, result);
     72 
     73 nissy_inverse_error:
     74 	writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result);
     75 	return err;
     76 }
     77 
     78 long long
     79 nissy_applymoves(
     80 	const char cube[static NISSY_SIZE_CUBE],
     81 	const char *moves,
     82 	char result[static NISSY_SIZE_CUBE]
     83 )
     84 {
     85 	oriented_cube_t c, res;
     86 	long long err;
     87 
     88 	if (moves == NULL) {
     89 		LOG("[applymoves] Error: 'moves' argument is NULL\n");
     90 		err = NISSY_ERROR_NULL_POINTER;
     91 		goto nissy_applymoves_error;
     92 	}
     93 
     94 	c = readcube(cube);
     95 
     96 	if (!isconsistent(c)) {
     97 		LOG("[applymoves] Error: given cube is invalid\n");
     98 		err = NISSY_ERROR_INVALID_CUBE;
     99 		goto nissy_applymoves_error;
    100 	}
    101 
    102 	res = applymoves(c, moves);
    103 
    104 	if (!isconsistent(res)) {
    105 		/* Assume we got a reasonable error message from applymoves */
    106 		err = NISSY_ERROR_INVALID_MOVES;
    107 		goto nissy_applymoves_error;
    108 	}
    109 
    110 	return write_result(res, result);
    111 
    112 nissy_applymoves_error:
    113 	writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result);
    114 	return err;
    115 }
    116 
    117 long long
    118 nissy_applytrans(
    119 	const char cube[static NISSY_SIZE_CUBE],
    120 	const char transformation[static NISSY_SIZE_TRANSFORMATION],
    121 	char result[static NISSY_SIZE_CUBE]
    122 )
    123 {
    124 	oriented_cube_t c, res;
    125 	long long err;
    126 
    127 	c = readcube(cube);
    128 
    129 	if (!isconsistent(c)) {
    130 		LOG("[applytrans] Error: given cube is invalid\n");
    131 		err = NISSY_ERROR_INVALID_CUBE;
    132 		goto nissy_applytrans_error;
    133 	}
    134 
    135 	res = applytrans(c, transformation);
    136 
    137 	if (!isconsistent(res)) {
    138 		/* Assume we got a reasonable error message from applytrans */
    139 		err = NISSY_ERROR_INVALID_TRANS;
    140 		goto nissy_applytrans_error;
    141 	}
    142 
    143 	return write_result(res, result);
    144 
    145 nissy_applytrans_error:
    146 	writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result);
    147 	return err;
    148 }
    149 
    150 long long
    151 nissy_variations(
    152 	const char *moves,
    153 	const char *variation,
    154 	unsigned long long result_size,
    155 	char *result
    156 )
    157 {
    158 	if (moves == NULL) {
    159 		LOG("[variations] Error: 'moves' argument is NULL\n");
    160 		return NISSY_ERROR_NULL_POINTER;
    161 	}
    162 
    163 	if (variation == NULL) {
    164 		LOG("[variations] Error: 'variation' argument is NULL\n");
    165 		return NISSY_ERROR_NULL_POINTER;
    166 	}
    167 
    168 	if (result == NULL) {
    169 		LOG("[variations] Error: 'result' argument is NULL\n");
    170 		return NISSY_ERROR_NULL_POINTER;
    171 	}
    172 
    173 	return move_variations(moves, variation, result_size, result);
    174 }
    175 
    176 long long
    177 nissy_getcube(
    178 	long long ep,
    179 	long long eo,
    180 	long long cp,
    181 	long long co,
    182 	long long orient,
    183 	const char *options,
    184 	char result[static NISSY_SIZE_CUBE]
    185 )
    186 {
    187 	int i;
    188 	oriented_cube_t oc;
    189 
    190 	if (options == NULL) {
    191 		LOG("[getcube] Error: 'options' argument is NULL\n");
    192 		return NISSY_ERROR_NULL_POINTER;
    193 	}
    194 
    195 	for (i = 0; getcube_options[i].option != NULL; i++)
    196 		if (!strcmp(options, getcube_options[i].option))
    197 			getcube_options[i].fix(&ep, &eo, &cp, &co, &orient);
    198 
    199 	oc.cube = getcube(ep, eo, cp, co);
    200 	oc.orientation = orient;
    201 
    202 	if (!isconsistent(oc)) {
    203 		LOG("[getcube] Error: could not get cube with ep=%lld, "
    204 		    "eo=%lld, cp=%lld, co=%lld, orient=%lld.\n",
    205 		    ep, eo, cp, co, orient);
    206 		return NISSY_ERROR_OPTIONS;
    207 	}
    208 
    209 	return write_result(oc, result);
    210 }
    211 
    212 STATIC long long
    213 nissy_dataid(const char *solver, char dataid[static NISSY_SIZE_DATAID])
    214 {
    215 	solver_dispatch_t dispatch;
    216 
    217 	dispatch = match_solver(solver);
    218 	if (dispatch.prefix == NULL) {
    219 		LOG("[dataid] Unknown solver %s\n", solver);
    220 		return NISSY_ERROR_INVALID_SOLVER;
    221 	}
    222 
    223 	return dispatch.dataid(dispatch.solvername, dataid);
    224 }
    225 
    226 long long
    227 nissy_solverinfo(
    228 	const char *solver,
    229 	char dataid[static NISSY_SIZE_DATAID]
    230 )
    231 {
    232 	long long err;
    233 	if ((err = nissy_dataid(solver, dataid)) != NISSY_OK)
    234 		return err;
    235 
    236 	/* gendata() handles a NULL *data as a "dryrun" request */
    237 	return nissy_gendata_unsafe(solver, 0, NULL);
    238 }
    239 
    240 long long
    241 nissy_gendata(
    242 	const char *solver,
    243 	unsigned long long data_size,
    244 	unsigned char *data
    245 )
    246 {
    247 	return nissy_gendata_unsafe(solver, data_size, data);
    248 }
    249 
    250 STATIC long long
    251 nissy_gendata_unsafe(
    252 	const char *solver,
    253 	unsigned long long data_size,
    254 	unsigned char *data
    255 )
    256 {
    257 	solver_dispatch_t dispatch;
    258 
    259 	if (solver == NULL) {
    260 		LOG("[gendata] Error: 'solver' argument is NULL\n");
    261 		return NISSY_ERROR_NULL_POINTER;
    262 	}
    263 
    264 	if ((size_t)data % 8 != 0) {
    265 		LOG("[gendata] Error: buffer is not 8-byte aligned\n");
    266 		return NISSY_ERROR_DATA;
    267 	}
    268 
    269 	dispatch = match_solver(solver);
    270 	if (dispatch.prefix == NULL) {
    271 		LOG("[gendata] Unknown solver %s\n", solver);
    272 		return NISSY_ERROR_INVALID_SOLVER;
    273 	}
    274 
    275 	return dispatch.gendata(dispatch.solvername, data_size, data);
    276 }
    277 
    278 long long
    279 nissy_checkdata(
    280 	const char *solver,
    281 	unsigned long long data_size,
    282 	const unsigned char *data
    283 )
    284 {
    285 	solver_dispatch_t dispatch;
    286 
    287 	dispatch = match_solver(solver);
    288 	if (dispatch.prefix == NULL) {
    289 		LOG("[checkdata] Unknown solver %s\n", solver);
    290 		return NISSY_ERROR_INVALID_SOLVER;
    291 	}
    292 
    293 	return dispatch.checkdata(dispatch.solvername, data_size, data);
    294 }
    295 
    296 long long
    297 nissy_solve(
    298 	const char cube[static NISSY_SIZE_CUBE],
    299 	const char *solver, 
    300 	unsigned nissflag,
    301 	unsigned minmoves,
    302 	unsigned maxmoves,
    303 	unsigned maxsols,
    304 	unsigned optimal,
    305 	unsigned threads,
    306 	unsigned long long data_size,
    307 	const unsigned char *data,
    308 	unsigned sols_size,
    309 	char *sols,
    310 	long long stats[static NISSY_SIZE_SOLVE_STATS],
    311 	int (*poll_status)(void *),
    312 	void *poll_status_data
    313 )
    314 {
    315 	oriented_cube_t oc;
    316 	int t;
    317 	solver_dispatch_t dispatch;
    318 
    319 	if (solver == NULL) {
    320 		LOG("[solve] Error: 'solver' argument is NULL\n");
    321 		return NISSY_ERROR_NULL_POINTER;
    322 	}
    323 
    324 	oc = readcube(cube);
    325 
    326 	if (!isconsistent(oc)) {
    327 		LOG("[solve] Error: cube is invalid\n");
    328 		return NISSY_ERROR_INVALID_CUBE;
    329 	}
    330 
    331 	if (maxmoves > 20) {
    332 		LOG("[solve] 'maxmoves' larger than 20 not supported yet, "
    333 		    "setting it to 20\n");
    334 		maxmoves = 20;
    335 	}
    336 
    337 	if (minmoves > maxmoves) {
    338 		LOG("[solve] value provided for 'minmoves' (%u) is larger "
    339 		    "than that provided for 'maxmoves' (%u), setting "
    340 		    "'minmoves' to %u\n", minmoves, maxmoves, maxmoves);
    341 		minmoves = maxmoves;
    342 	}
    343 
    344 	if (maxsols == 0) {
    345 		LOG("[solve] 'maxsols' is 0, returning no solution\n");
    346 		return 0;
    347 	}
    348 
    349 	if (threads > THREADS)
    350 		LOG("[solve] Selected number of threads (%u) is above the "
    351 		    "maximum value (%d), using %d threads instead\n",
    352 		    threads, THREADS, THREADS);
    353 	t = threads == 0 ? THREADS : MIN(THREADS, threads);
    354 
    355 	if ((size_t)data % 8 != 0) {
    356 		LOG("[solve] Error: data buffer is not 8-byte aligned\n");
    357 		return NISSY_ERROR_DATA;
    358 	}
    359 
    360 	dispatch = match_solver(solver);
    361 	if (dispatch.prefix == NULL) {
    362 		LOG("[solve] Error: unknown solver '%s'\n", solver);
    363 		return NISSY_ERROR_INVALID_SOLVER;
    364 	}
    365 	return dispatch.solve(oc, dispatch.solvername, nissflag, minmoves,
    366 	    maxmoves, maxsols, optimal, t, data_size, data, sols_size, sols,
    367 	    stats, poll_status, poll_status_data);
    368 }
    369 
    370 long long
    371 nissy_countmoves(
    372 	const char *moves
    373 )
    374 {
    375 	if (moves == NULL)
    376 		return NISSY_ERROR_NULL_POINTER;
    377 
    378 	return countmoves(moves);
    379 }
    380 
    381 long long
    382 nissy_comparemoves(
    383 	const char *moves1,
    384 	const char *moves2
    385 )
    386 {
    387 	if (moves1 == NULL || moves2 == NULL)
    388 		return NISSY_ERROR_NULL_POINTER;
    389 
    390 	return comparemoves(moves1, moves2);
    391 }
    392 
    393 long long
    394 nissy_setlogger(
    395 	void (*log)(const char *, void *),
    396 	void *user_data
    397 )
    398 {
    399 	nissy_log = log;
    400 	nissy_log_data = user_data;
    401 	return NISSY_OK;
    402 }