h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

nissy.c (6637B)


      1 #include <inttypes.h>
      2 #include <stdarg.h>
      3 #include <stdbool.h>
      4 #include <string.h>
      5 
      6 #include "utils/utils.h"
      7 #include "arch/arch.h"
      8 #include "core/core.h"
      9 #include "solvers/solvers.h"
     10 
     11 #include "nissy.h"
     12 
     13 STATIC int parse_h48_options(const char *, uint8_t *, uint8_t *, uint8_t *);
     14 STATIC int64_t write_result(cube_t, char [static 22]);
     15 
     16 /* TODO: add option to get DR, maybe C-only, E-only, eo... */
     17 #define GETCUBE_OPTIONS(S, F) { .option = S, .fix = F }
     18 struct {
     19 	char *option;
     20 	void (*fix)(int64_t *, int64_t *, int64_t *, int64_t *);
     21 } getcube_options[] = {
     22 	GETCUBE_OPTIONS("fix", getcube_fix),
     23 	GETCUBE_OPTIONS(NULL, NULL)
     24 };
     25 
     26 STATIC int
     27 parse_h48_options(const char *buf, uint8_t *h, uint8_t *k, uint8_t *maxdepth)
     28 {
     29 	bool h_valid, k_valid, maxdepth_valid;
     30 	int i;
     31 
     32 	/* TODO temporarily, options are in the form "h;k;maxdepth" */
     33 	if (h != NULL)
     34 		*h = atoi(buf);
     35 	h_valid = h == NULL || *h <= 11;
     36 
     37 	for (i = 0; buf[i] != ';'; i++)
     38 		if (buf[i] == 0)
     39 			goto parse_h48_options_error;
     40 
     41 	if (k != NULL)
     42 		*k = atoi(&buf[i+1]);
     43 	k_valid = k == NULL || (*k == 2 || *k == 4);
     44 
     45 	for (i = i+1; buf[i] != ';'; i++)
     46 		if (buf[i] == 0)
     47 			goto parse_h48_options_error;
     48 
     49 	if (maxdepth != NULL)
     50 		*maxdepth = atoi(&buf[i+1]);
     51 	maxdepth_valid = maxdepth == NULL || *maxdepth <= 20;
     52 
     53 	return h_valid && k_valid && maxdepth_valid ? 0 : 1;
     54 
     55 parse_h48_options_error:
     56 	*h = 0;
     57 	*k = 0;
     58 	*maxdepth = 0;
     59 	LOG("Error parsing options: must be in \"h;k;maxdepth\" format "
     60 	    " (instead it was \"%s\")\n", buf);
     61 	return -1;
     62 }
     63 
     64 STATIC int64_t
     65 write_result(cube_t cube, char result[static 22])
     66 {
     67 	if (!isconsistent(cube)) {
     68 		writecube("B32", ZERO_CUBE, result);
     69 		return 2;
     70 	}
     71 
     72 	writecube("B32", cube, result);
     73 
     74 	return issolvable(cube) ? 0 : 1;
     75 }
     76 
     77 int64_t
     78 nissy_compose(
     79 	const char cube[static 22],
     80 	const char permutation[static 22],
     81 	char result[static 22]
     82 )
     83 {
     84 	cube_t c, p, res;
     85 
     86 	c = readcube("B32", cube);
     87 	p = readcube("B32", permutation);
     88 	res = compose(c, p);
     89 
     90 	return write_result(res, result);
     91 }
     92 
     93 int64_t
     94 nissy_inverse(
     95 	const char cube[static 22],
     96 	char result[static 22]
     97 )
     98 {
     99 	cube_t c, res;
    100 
    101 	c = readcube("B32", cube);
    102 	res = inverse(c);
    103 
    104 	return write_result(res, result);
    105 }
    106 
    107 int64_t
    108 nissy_applymoves(
    109 	const char cube[static 22],
    110 	const char *moves,
    111 	char result[static 22]
    112 )
    113 {
    114 	cube_t c, res;
    115 
    116 	c = readcube("B32", cube);
    117 	res = applymoves(c, moves);
    118 
    119 	return write_result(res, result);
    120 }
    121 
    122 int64_t
    123 nissy_applytrans(
    124 	const char cube[static 22],
    125 	const char *transformation,
    126 	char result[static 22]
    127 )
    128 {
    129 	cube_t c, res;
    130 
    131 	c = readcube("B32", cube);
    132 	res = applytrans(c, transformation);
    133 
    134 	return write_result(res, result);
    135 }
    136 
    137 int64_t
    138 nissy_frommoves(
    139 	const char *moves,
    140 	char result[static 22]
    141 )
    142 {
    143 	cube_t res;
    144 
    145 	res = applymoves(SOLVED_CUBE, moves);
    146 
    147 	return write_result(res, result);
    148 }
    149 
    150 int64_t
    151 nissy_convert(
    152 	const char *format_in,
    153 	const char *format_out,
    154 	const char *cube_string,
    155 	char *result
    156 )
    157 {
    158 	cube_t c;
    159 
    160 	c = readcube(format_in, cube_string);
    161 	writecube(format_out, c, result);
    162 
    163 	return isconsistent(c) ? 0 : 2;
    164 }
    165 
    166 int64_t
    167 nissy_getcube(
    168 	int64_t ep,
    169 	int64_t eo,
    170 	int64_t cp,
    171 	int64_t co,
    172 	const char *options,
    173 	char result[static 22]
    174 )
    175 {
    176 	int i;
    177 	cube_t c;
    178 
    179 	for (i = 0; getcube_options[i].option != NULL; i++)
    180 		if (!strcmp(options, getcube_options[i].option))
    181 			getcube_options[i].fix(&ep, &eo, &cp, &co);
    182 
    183 	c = getcube(ep, eo, cp, co);
    184 
    185 	return write_result(c, result);
    186 }
    187 
    188 int64_t
    189 nissy_datasize(
    190 	const char *solver,
    191 	const char *options
    192 )
    193 {
    194 	/* gendata() handles a NULL *data as a "dryrun" request */
    195 	return nissy_gendata(solver, options, NULL);
    196 }
    197 
    198 int64_t
    199 nissy_datainfo(
    200 	const void *table,
    201 	void (*write)(const char *, ...)
    202 )
    203 {
    204 	uint8_t i;
    205 	tableinfo_t info;
    206 
    207 	readtableinfo(table, &info);
    208 
    209 	write("\n---------\n\n");
    210 	write("Table information for '%s'\n", info.solver);
    211 	write("\n");
    212 	write("Size:      %" PRIu64 " bytes\n", info.fullsize);
    213 	write("Entries:   %" PRIu64 " (%" PRIu8 " bits per entry)",
    214 	    info.entries, info.bits);
    215 	write("\n");
    216 
    217 	switch (info.type) {
    218 	case TABLETYPE_PRUNING:
    219 		write("\n");
    220 		write("Table distribution:\nValue\tPositions\n");
    221 		for (i = 0; i <= info.maxvalue; i++) {
    222 			write("%" PRIu8 "\t%" PRIu64 "\n",
    223 			    i + info.base, info.distribution[i]);
    224 		}
    225 		break;
    226 	case TABLETYPE_SPECIAL:
    227 		write("This is an ad-hoc table\n");
    228 		break;
    229 	default:
    230 		LOG("datainfo: unknown table type\n");
    231 		return 1;
    232 	}
    233 
    234 	if (info.next != 0) {
    235 		return nissy_datainfo((char *)table + info.next, write);
    236 	}
    237 
    238 	write("\n---------\n");
    239 
    240 	return 0;
    241 }
    242 
    243 int64_t
    244 nissy_gendata(
    245 	const char *solver,
    246 	const char *options,
    247 	void *data
    248 )
    249 {
    250 	int p;
    251 	int64_t ret;
    252 	gendata_h48_arg_t arg;
    253 
    254 	arg.buf = data;
    255 	if (!strcmp(solver, "h48")) {
    256 		p = parse_h48_options(options, &arg.h, &arg.k, &arg.maxdepth);
    257 		if (p != 0) {
    258 			LOG("gendata: could not parse options\n");
    259 			ret = -1;
    260 		} else {
    261 			ret = gendata_h48(&arg);
    262 		}
    263 	} else if (!strcmp(solver, "h48stats")) {
    264 		arg.h = 0;
    265 		arg.k = 4;
    266 		arg.maxdepth = 20;
    267 		ret = gendata_h48(&arg);
    268 	} else {
    269 		LOG("gendata: implemented only for h48 solver\n");
    270 		ret = -1;
    271 	}
    272 
    273 	return ret;
    274 }
    275 
    276 int64_t
    277 nissy_solve(
    278 	const char cube[static 22],
    279 	const char *solver, 
    280 	const char *options,
    281 	const char *nisstype,
    282 	int8_t minmoves,
    283 	int8_t maxmoves,
    284 	int64_t maxsolutions,
    285 	int8_t optimal,
    286 	const void *data,
    287 	char *solutions
    288 )
    289 {
    290 	cube_t c;
    291 	int p;
    292 	int64_t ret;
    293 	uint8_t h, k;
    294 
    295 	c = readcube_B32(cube);
    296 
    297 	if (!issolvable(c)) {
    298 		LOG("solve: cube is not solvable\n");
    299 		return -1;
    300 	}
    301 
    302 	if (minmoves < 0) {
    303 		LOG("solve: 'minmoves' is negative, setting it to 0\n");
    304 		minmoves = 0;
    305 	}
    306 
    307 	if (maxmoves < 0) {
    308 		LOG("solve: 'maxmoves' is negative, setting it to 20\n");
    309 		maxmoves = 20;
    310 	}
    311 
    312 	if (maxsolutions < 0) {
    313 		LOG("solve: 'maxsols' is negative, stopping\n");
    314 		return -1;
    315 	}
    316 
    317 	if (maxsolutions == 0) {
    318 		LOG("solve: 'maxsols' is 0, returning no solution\n");
    319 		return 0;
    320 	}
    321 
    322 	if (solutions == NULL) {
    323 		LOG("solve: return parameter 'solutions' is NULL, stopping\n");
    324 		return -1;
    325 	}
    326 
    327 	/* TODO define and use solve_options_t */
    328 	if (!strcmp(solver, "h48")) {
    329 		p = parse_h48_options(options, &h, &k, NULL);
    330 		if (p != 0) {
    331 			LOG("gendata: could not parse options\n");
    332 			ret = -1;
    333 		} else {
    334 			ret = solve_h48(c, minmoves, maxmoves, maxsolutions,
    335 			    data, solutions);
    336 		}
    337 	} else if (!strcmp(solver, "h48stats")) {
    338 		ret = solve_h48stats(c, maxmoves, data, solutions);
    339 	} else if (!strcmp(solver, "simple")) {
    340 		ret = solve_simple(
    341 		    c, minmoves, maxmoves, maxsolutions, optimal, solutions);
    342 	} else {
    343 		LOG("solve: unknown solver '%s'\n", solver);
    344 		ret = -1;
    345 	}
    346 
    347 	return ret;
    348 }
    349 
    350 void
    351 nissy_setlogger(void (*log)(const char *, ...))
    352 {
    353 	nissy_log = log;
    354 }