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 (7882B)


      1 #include <inttypes.h>
      2 #include <limits.h>
      3 #include <pthread.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_getcube(
    152 	long long ep,
    153 	long long eo,
    154 	long long cp,
    155 	long long co,
    156 	long long orient,
    157 	const char *options,
    158 	char result[static NISSY_SIZE_CUBE]
    159 )
    160 {
    161 	int i;
    162 	oriented_cube_t oc;
    163 
    164 	if (options == NULL) {
    165 		LOG("[getcube] Error: 'options' argument is NULL\n");
    166 		return NISSY_ERROR_NULL_POINTER;
    167 	}
    168 
    169 	for (i = 0; getcube_options[i].option != NULL; i++)
    170 		if (!strcmp(options, getcube_options[i].option))
    171 			getcube_options[i].fix(&ep, &eo, &cp, &co, &orient);
    172 
    173 	oc.cube = getcube(ep, eo, cp, co);
    174 	oc.orientation = orient;
    175 
    176 	if (!isconsistent(oc)) {
    177 		LOG("[getcube] Error: could not get cube with ep=%lld, "
    178 		    "eo=%lld, cp=%lld, co=%lld, orient=%lld.\n",
    179 		    ep, eo, cp, co, orient);
    180 		return NISSY_ERROR_OPTIONS;
    181 	}
    182 
    183 	return write_result(oc, result);
    184 }
    185 
    186 STATIC long long
    187 nissy_dataid(const char *solver, char dataid[static NISSY_SIZE_DATAID])
    188 {
    189 	solver_dispatch_t *dispatch;
    190 
    191 	dispatch = match_solver(solver);
    192 	if (dispatch == NULL) {
    193 		LOG("[dataid] Unknown solver %s\n", solver);
    194 		return NISSY_ERROR_INVALID_SOLVER;
    195 	}
    196 
    197 	return dispatch->dataid(solver, dataid);
    198 }
    199 
    200 long long
    201 nissy_solverinfo(
    202 	const char *solver,
    203 	char dataid[static NISSY_SIZE_DATAID]
    204 )
    205 {
    206 	long long err;
    207 	if ((err = nissy_dataid(solver, dataid)) != NISSY_OK)
    208 		return err;
    209 
    210 	/* gendata() handles a NULL *data as a "dryrun" request */
    211 	return nissy_gendata_unsafe(solver, 0, NULL);
    212 }
    213 
    214 long long
    215 nissy_gendata(
    216 	const char *solver,
    217 	unsigned long long data_size,
    218 	unsigned char *data
    219 )
    220 {
    221 	return nissy_gendata_unsafe(solver, data_size, data);
    222 }
    223 
    224 STATIC long long
    225 nissy_gendata_unsafe(
    226 	const char *solver,
    227 	unsigned long long data_size,
    228 	unsigned char *data
    229 )
    230 {
    231 	solver_dispatch_t *dispatch;
    232 
    233 	if (solver == NULL) {
    234 		LOG("[gendata] Error: 'solver' argument is NULL\n");
    235 		return NISSY_ERROR_NULL_POINTER;
    236 	}
    237 
    238 	if ((size_t)data % 8 != 0) {
    239 		LOG("[gendata] Error: buffer is not 8-byte aligned\n");
    240 		return NISSY_ERROR_DATA;
    241 	}
    242 
    243 	dispatch = match_solver(solver);
    244 	if (dispatch == NULL) {
    245 		LOG("[gendata] Unknown solver %s\n", solver);
    246 		return NISSY_ERROR_INVALID_SOLVER;
    247 	}
    248 
    249 	return dispatch->gendata(solver, data_size, data);
    250 }
    251 
    252 long long
    253 nissy_checkdata(
    254 	const char *solver,
    255 	unsigned long long data_size,
    256 	const unsigned char *data
    257 )
    258 {
    259 	solver_dispatch_t *dispatch;
    260 
    261 	dispatch = match_solver(solver);
    262 	if (dispatch == NULL) {
    263 		LOG("[checkdata] Unknown solver %s\n", solver);
    264 		return NISSY_ERROR_INVALID_SOLVER;
    265 	}
    266 
    267 	return dispatch->checkdata(solver, data_size, data);
    268 }
    269 
    270 long long
    271 nissy_solve(
    272 	const char cube[static NISSY_SIZE_CUBE],
    273 	const char *solver, 
    274 	unsigned nissflag,
    275 	unsigned minmoves,
    276 	unsigned maxmoves,
    277 	unsigned maxsols,
    278 	unsigned optimal,
    279 	unsigned threads,
    280 	unsigned long long data_size,
    281 	const unsigned char *data,
    282 	unsigned sols_size,
    283 	char *sols,
    284 	long long stats[static NISSY_SIZE_SOLVE_STATS],
    285 	int (*poll_status)(void *),
    286 	void *poll_status_data
    287 )
    288 {
    289 	oriented_cube_t oc;
    290 	int t;
    291 	solver_dispatch_t *dispatch;
    292 
    293 	if (solver == NULL) {
    294 		LOG("[solve] Error: 'solver' argument is NULL\n");
    295 		return NISSY_ERROR_NULL_POINTER;
    296 	}
    297 
    298 	oc = readcube(cube);
    299 
    300 	if (!isconsistent(oc)) {
    301 		LOG("[solve] Error: cube is invalid\n");
    302 		return NISSY_ERROR_INVALID_CUBE;
    303 	}
    304 
    305 	if (maxmoves > 20) {
    306 		LOG("[solve] 'maxmoves' larger than 20 not supported yet, "
    307 		    "setting it to 20\n");
    308 		maxmoves = 20;
    309 	}
    310 
    311 	if (minmoves > maxmoves) {
    312 		LOG("[solve] value provided for 'minmoves' (%u) is larger "
    313 		    "than that provided for 'maxmoves' (%u), setting "
    314 		    "'minmoves' to %u\n", minmoves, maxmoves, maxmoves);
    315 		minmoves = maxmoves;
    316 	}
    317 
    318 	if (maxsols == 0) {
    319 		LOG("[solve] 'maxsols' is 0, returning no solution\n");
    320 		return 0;
    321 	}
    322 
    323 	if (threads > THREADS)
    324 		LOG("[solve] Selected number of threads (%u) is above the "
    325 		    "maximum value (%d), using %d threads instead\n",
    326 		    threads, THREADS, THREADS);
    327 	t = threads == 0 ? THREADS : MIN(THREADS, threads);
    328 
    329 	if ((size_t)data % 8 != 0) {
    330 		LOG("[solve] Error: data buffer is not 8-byte aligned\n");
    331 		return NISSY_ERROR_DATA;
    332 	}
    333 
    334 	dispatch = match_solver(solver);
    335 	if (dispatch == NULL) {
    336 		LOG("[solve] Error: unknown solver '%s'\n", solver);
    337 		return NISSY_ERROR_INVALID_SOLVER;
    338 	}
    339 	return dispatch->solve(oc, solver, nissflag, minmoves, maxmoves,
    340 	    maxsols, optimal, t, data_size, data, sols_size, sols, stats,
    341 	    poll_status, poll_status_data);
    342 }
    343 
    344 long long
    345 nissy_countmoves(
    346 	const char *moves
    347 )
    348 {
    349 	if (moves == NULL)
    350 		return NISSY_ERROR_NULL_POINTER;
    351 
    352 	return countmoves(moves);
    353 }
    354 
    355 long long
    356 nissy_setlogger(
    357 	void (*log)(const char *, void *),
    358 	void *user_data
    359 )
    360 {
    361 	nissy_log = log;
    362 	nissy_log_data = user_data;
    363 	return NISSY_OK;
    364 }