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

shell.c (17197B)


      1 #include <inttypes.h>
      2 #include <limits.h>
      3 #include <errno.h>
      4 #include <stdarg.h>
      5 #include <stdbool.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <time.h>
     10 
     11 #include "../src/nissy.h"
     12 
     13 #define PRINTCUBE_BUFFER_SIZE UINT64_C(1024)
     14 #define SOLUTIONS_BUFFER_SIZE UINT64_C(500000)
     15 #define MAX_PATH_LENGTH       UINT64_C(10000)
     16 
     17 #define FLAG_CUBE         "-cube"
     18 #define FLAG_COMMAND      "-command"
     19 #define FLAG_STR_CUBE     "-cubestr"
     20 #define FLAG_MOVES        "-moves"
     21 #define FLAG_MOVES2       "-moves2"
     22 #define FLAG_TRANS        "-trans"
     23 #define FLAG_SOLVER       "-solver"
     24 #define FLAG_VARIATION    "-variation"
     25 #define FLAG_NISSTYPE     "-nisstype"
     26 #define FLAG_MINMOVES     "-m"
     27 #define FLAG_MAXMOVES     "-M"
     28 #define FLAG_OPTIMAL      "-O"
     29 #define FLAG_MAXSOLUTIONS "-n"
     30 #define FLAG_THREADS      "-t"
     31 
     32 #define INFO_MOVESFORMAT "The accepted moves are U, D, R, L, F and B, " \
     33     "optionally followed by a 2, a ' or a 3."
     34 #define INFO_TRANSFORMAT "The transformation must be given in the format " \
     35     "(rotation|mirrored) (2 letters), for exmple " \
     36     "'rotation UF' or 'mirrored BL'."
     37 
     38 typedef struct {
     39 	int command_index;
     40 	char cube[NISSY_SIZE_CUBE];
     41 	char *str_command;
     42 	char *str_cube;
     43 	char *str_moves;
     44 	char *str_moves2;
     45 	char *str_trans;
     46 	char *str_solver;
     47 	char *str_variation;
     48 	char *str_nisstype;
     49 	unsigned minmoves;
     50 	unsigned maxmoves;
     51 	unsigned optimal;
     52 	unsigned maxsolutions;
     53 	unsigned threads;
     54 } args_t;
     55 
     56 static int64_t inverse_exec(args_t *);
     57 static int64_t applymoves_exec(args_t *);
     58 static int64_t applytrans_exec(args_t *);
     59 static int64_t frommoves_exec(args_t *);
     60 static int64_t randomcube_exec(args_t *);
     61 static int64_t solverinfo_exec(args_t *);
     62 static int64_t gendata_exec(args_t *);
     63 static int64_t solve_exec(args_t *);
     64 static int64_t solve_scramble_exec(args_t *);
     65 static int64_t countmoves_exec(args_t *);
     66 static int64_t comparemoves_exec(args_t *);
     67 static int64_t variation_exec(args_t *);
     68 static int64_t help_exec(args_t *);
     69 
     70 static int parse_args(int, char **, args_t *);
     71 static bool parse_uint(const char *, unsigned *);
     72 static uint8_t parse_nisstype(const char *);
     73 
     74 static bool set_cube(int, char **, args_t *);
     75 static bool set_str_command(int, char **, args_t *);
     76 static bool set_str_cube(int, char **, args_t *);
     77 static bool set_str_moves(int, char **, args_t *);
     78 static bool set_str_moves2(int, char **, args_t *);
     79 static bool set_str_trans(int, char **, args_t *);
     80 static bool set_str_solver(int, char **, args_t *);
     81 static bool set_str_variation(int, char **, args_t *);
     82 static bool set_str_nisstype(int, char **, args_t *);
     83 static bool set_minmoves(int, char **, args_t *);
     84 static bool set_maxmoves(int, char **, args_t *);
     85 static bool set_optimal(int, char **, args_t *);
     86 static bool set_maxsolutions(int, char **, args_t *);
     87 static bool set_threads(int, char **, args_t *);
     88 static bool set_id(int, char **, args_t *);
     89 
     90 static uint64_t rand64(void);
     91 
     92 #define OPTION(N, A, S) { .name = N, .nargs = A, .set = S }
     93 struct {
     94 	char *name;
     95 	int nargs;
     96 	bool (*set)(int, char **, args_t *);
     97 } options[] = {
     98 	OPTION(FLAG_CUBE, 1, set_cube),
     99 	OPTION(FLAG_COMMAND, 1, set_str_command),
    100 	OPTION(FLAG_STR_CUBE, 1, set_str_cube),
    101 	OPTION(FLAG_MOVES, 1, set_str_moves),
    102 	OPTION(FLAG_MOVES2, 1, set_str_moves2),
    103 	OPTION(FLAG_TRANS, 1, set_str_trans),
    104 	OPTION(FLAG_SOLVER, 1, set_str_solver),
    105 	OPTION(FLAG_VARIATION, 1, set_str_variation),
    106 	OPTION(FLAG_NISSTYPE, 1, set_str_nisstype),
    107 	OPTION(FLAG_MINMOVES, 1, set_minmoves),
    108 	OPTION(FLAG_MAXMOVES, 1, set_maxmoves),
    109 	OPTION(FLAG_OPTIMAL, 1, set_optimal),
    110 	OPTION(FLAG_MAXSOLUTIONS, 1, set_maxsolutions),
    111 	OPTION(FLAG_THREADS, 1, set_threads),
    112 	OPTION(NULL, 0, NULL)
    113 };
    114  
    115 #define COMMAND(N, S, D, E) { .name = N, .syn = S, .desc = D, .exec = E }
    116 struct {
    117 	char *name;
    118 	char *syn;
    119 	char *desc;
    120 	int64_t (*exec)(args_t *);
    121 } commands[] = {
    122 	COMMAND(
    123 		"inverse",
    124 		"inverse " FLAG_CUBE " CUBE ",
    125 		"Compute the inverse of the given CUBE.",
    126 		inverse_exec
    127 	),
    128 	COMMAND(
    129 		"applymoves",
    130 		"applymoves " FLAG_CUBE " CUBE " FLAG_MOVES " MOVES",
    131 		"Apply the given MOVES to the given CUBE. "
    132 		INFO_MOVESFORMAT,
    133 		applymoves_exec
    134 	),
    135 	COMMAND(
    136 		"applytrans",
    137 		"applytrans " FLAG_CUBE " CUBE " FLAG_TRANS " TRANS",
    138 		"Apply the single transformation TRANS to the given CUBE. "
    139 		INFO_TRANSFORMAT,
    140 		applytrans_exec
    141 	),
    142 	COMMAND(
    143 		"frommoves",
    144 		"frommoves " FLAG_MOVES " MOVES",
    145 		"Return the cube obtained by applying the given MOVES "
    146 		"to a solved cube. " INFO_MOVESFORMAT,
    147 		frommoves_exec
    148 	),
    149 	COMMAND(
    150 		"randomcube",
    151 		"randomcube",
    152 		"Returns a random.",
    153 		randomcube_exec
    154 	),
    155 	COMMAND(
    156 		"solverinfo",
    157 		"solverinfo " FLAG_SOLVER " SOLVER",
    158 		"Return the size in bytes and the id of the data table "
    159 		"used by SOLVER when called with the given OPTIONS.",
    160 		solverinfo_exec
    161 	),
    162 	COMMAND(
    163 		"gendata",
    164 		"gendata " FLAG_SOLVER " SOLVER",
    165 		"Generate the data table used by "
    166 		"SOLVER when called with the given OPTIONS.",
    167 		gendata_exec
    168 	),
    169 	COMMAND(
    170 		"solve",
    171 		"solve " FLAG_SOLVER " SOLVER"
    172 		"[" FLAG_MINMOVES " n] [" FLAG_MAXMOVES " N] "
    173 		FLAG_CUBE " CUBE"
    174 		FLAG_THREADS " T",
    175 		"Solve the given CUBE using SOLVER, "
    176 		"using at least n and at most N moves, and T threads.",
    177 		solve_exec
    178 	),
    179 	COMMAND(
    180 		"solve_scramble",
    181 		"solve_scramble " FLAG_SOLVER " SOLVER"
    182 		"[" FLAG_MINMOVES " n] [" FLAG_MAXMOVES " N] "
    183 		FLAG_MOVES " MOVES",
    184 		"Solve the given SCRAMBLE using SOLVER, "
    185 		"using at least n and at most N moves. "
    186 		INFO_MOVESFORMAT,
    187 		solve_scramble_exec
    188 	),
    189 	COMMAND(
    190 		"countmoves",
    191 		"countmoves " FLAG_MOVES " MOVES",
    192 		"Count the given MOVES in HTM. "
    193 		INFO_MOVESFORMAT,
    194 		countmoves_exec
    195 	),
    196 	COMMAND(
    197 		"compare",
    198 		"compare " FLAG_MOVES " MOVES " FLAG_MOVES2 " MOVES2",
    199 		"Compare the two move sequences."
    200 		INFO_MOVESFORMAT,
    201 		comparemoves_exec
    202 	),
    203 	COMMAND(
    204 		"variations",
    205 		"variations " FLAG_MOVES " MOVES " FLAG_VARIATION " VARIATION",
    206 		"Find variations of a move sequence."
    207 		INFO_MOVESFORMAT,
    208 		variation_exec
    209 	),
    210 	COMMAND(
    211 		"help",
    212 		"help [" FLAG_COMMAND " COMMAND]",
    213 		"If no COMMAND is specified, prints some generic information "
    214 		"and the list of commands. Otherwise it prints detailed "
    215 		"information about the specified COMMAND.",
    216 		help_exec
    217 	),
    218 	COMMAND(NULL, NULL, NULL, NULL)
    219 };
    220 
    221 char *tablepaths[] = {
    222 	"tables/",
    223 	"",
    224 	NULL
    225 };
    226 
    227 static uint64_t
    228 rand64(void)
    229 {
    230 	uint64_t i, ret;
    231 
    232 	for (i = 0, ret = 0; i < 64; i++)
    233 		ret |= (uint64_t)(rand() % 2) << i;
    234 
    235 	return ret;
    236 }
    237 
    238 static int64_t
    239 inverse_exec(args_t *args)
    240 {
    241 	char result[NISSY_SIZE_CUBE];
    242 	int64_t ret;
    243 
    244 	ret = nissy_inverse(args->cube, result);
    245 	if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
    246 		printf("%s\n", result);
    247 
    248 	return ret;
    249 }
    250 
    251 static int64_t
    252 applymoves_exec(args_t *args)
    253 {
    254 	char result[NISSY_SIZE_CUBE];
    255 	int64_t ret;
    256 
    257 	ret = nissy_applymoves(args->cube, args->str_moves, result);
    258 	if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
    259 		printf("%s\n", result);
    260 
    261 	return ret;
    262 }
    263 
    264 static int64_t
    265 applytrans_exec(args_t *args)
    266 {
    267 	char result[NISSY_SIZE_CUBE];
    268 	int64_t ret;
    269 
    270 	ret = nissy_applytrans(args->cube, args->str_trans, result);
    271 	if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
    272 		printf("%s\n", result);
    273 
    274 	return ret;
    275 }
    276 
    277 static int64_t
    278 frommoves_exec(args_t *args)
    279 {
    280 	sprintf(args->cube, NISSY_SOLVED_CUBE);
    281 	return applymoves_exec(args);
    282 }
    283 
    284 static int64_t
    285 randomcube_exec(args_t *args)
    286 {
    287 	char result[PRINTCUBE_BUFFER_SIZE];
    288 	int64_t ret, ep, eo, cp, co;
    289 
    290 	ep = rand64();
    291 	eo = rand64();
    292 	cp = rand64();
    293 	co = rand64();
    294 	ret = nissy_getcube(ep, eo, cp, co, 0, "fix", result);
    295 	if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
    296 		printf("%s\n", result);
    297 
    298 	return ret;
    299 }
    300 
    301 static int64_t
    302 solverinfo_exec(args_t *args)
    303 {
    304 	int64_t ret;
    305 	char buf[NISSY_SIZE_DATAID];
    306 
    307 	ret = nissy_solverinfo(args->str_solver, buf);
    308 	if (ret < 0) {
    309 		fprintf(stderr, "Unknown error (make sure solver is valid)\n");
    310 	} else {
    311 		printf("%" PRId64 "\n%s\n", ret, buf);
    312 		ret = 0;
    313 	}
    314 
    315 	return ret;
    316 }
    317 
    318 static int64_t
    319 gendata_exec(args_t *args)
    320 {
    321 	int i;
    322 	FILE *file;
    323 	char path[MAX_PATH_LENGTH], dataid[NISSY_SIZE_DATAID];
    324 	unsigned char *buf;
    325 	int64_t ret, size;
    326 	size_t written;
    327 
    328 	size = nissy_solverinfo(args->str_solver, dataid);
    329 
    330 	if (size < 0) {
    331 		fprintf(stderr, "gendata: unknown solver %s\n",
    332 		    args->str_solver);
    333 		return -3;
    334 	}
    335 
    336 	/* TODO: should give warning if overwriting existing file */
    337 	for (i = 0; tablepaths[i] != NULL; i++) {
    338 		strcpy(path, tablepaths[i]);
    339 		strcat(path, dataid);
    340 		file = fopen(path, "wb");
    341 		if (file != NULL)
    342 			break;
    343 	}
    344 
    345 	if (tablepaths[i] == NULL) {
    346 		fprintf(stderr, "Cannot write data to file\n");
    347 		fclose(file);
    348 		return -2;
    349 	}
    350 
    351 	buf = malloc(size);
    352 
    353 	ret = nissy_gendata(args->str_solver, size, buf);
    354 	if (ret < 0) {
    355 		fprintf(stderr, "Unknown error in generating data\n");
    356 		fclose(file);
    357 		free(buf);
    358 		return -4;
    359 	}
    360 	if (ret != size) {
    361 		fprintf(stderr, "Unknown error: unexpected data size "
    362 		    "got %" PRId64 ", expected %" PRId64 ")\n", ret, size);
    363 		fclose(file);
    364 		free(buf);
    365 		return -5;
    366 	}
    367 
    368 	written = fwrite(buf, size, 1, file);
    369 	fclose(file);
    370 	free(buf);
    371 
    372 	if (written != 1) {
    373 		fprintf(stderr,
    374 		    "Error: data was generated correctly, but could not be "
    375 		    "written to file (generated %" PRId64 " bytes, written "
    376 		    "%zu)\n", size, written);
    377 		return -6;
    378 	}
    379 
    380 	fprintf(stderr, "Data written to %s\n", path);
    381 
    382 	return 0;
    383 }
    384 
    385 static int64_t
    386 solve_exec(args_t *args)
    387 {
    388 	int i;
    389 	uint8_t nissflag;
    390 	FILE *file;
    391 	char solutions[SOLUTIONS_BUFFER_SIZE], path[MAX_PATH_LENGTH];
    392 	unsigned char *buf;
    393 	char dataid[NISSY_SIZE_DATAID];
    394 	long long stats[NISSY_SIZE_SOLVE_STATS];
    395 	int64_t ret, gendata_ret, size;
    396 	size_t read;
    397 
    398 	nissflag = parse_nisstype(args->str_nisstype);
    399 	if (nissflag == UINT8_MAX) {
    400 		fprintf(stderr, "solve: unknown niss type '%s', use one "
    401 		    "of the following:\nnormal\ninverse\nlinear\nmixed\nall",
    402 		    args->str_nisstype);
    403 		return -1;
    404 	}
    405 
    406 	size = nissy_solverinfo(args->str_solver, dataid);
    407 
    408 	if (size < 0) {
    409 		fprintf(stderr, "solve: unknown solver '%s'\n",
    410 		    args->str_solver);
    411 		return size;
    412 	}
    413 
    414 	for (i = 0; tablepaths[i] != NULL; i++) {
    415 		strcpy(path, tablepaths[i]);
    416 		strcat(path, dataid);
    417 		file = fopen(path, "rb");
    418 		if (file != NULL)
    419 			break;
    420 	}
    421 
    422 	if (tablepaths[i] == NULL) {
    423 		fprintf(stderr,
    424 		    "Cannot read data file, "
    425 		    "generating it (this can take a while)\n");
    426 		gendata_ret = gendata_exec(args);
    427 		if (gendata_ret)
    428 			return gendata_ret;
    429 	}
    430 
    431 	/* Ugh, this is not elegant TODO */
    432 	if (file == NULL) {
    433 		for (i = 0; tablepaths[i] != NULL; i++) {
    434 			strcpy(path, tablepaths[i]);
    435 			strcat(path, dataid);
    436 			file = fopen(path, "rb");
    437 			if (file != NULL)
    438 				break;
    439 		}
    440 	}
    441 
    442 	if (tablepaths[i] == NULL) {
    443 		fprintf(stderr, "Error: data file not found\n");
    444 		fclose(file);
    445 		return -1;
    446 	}
    447 
    448 	if (args->maxsolutions == 0)
    449 		args->maxsolutions = args->optimal == 20 ? 1 : UINT_MAX;
    450 
    451 	buf = malloc(size);
    452 	read = fread(buf, size, 1, file);
    453 	fclose(file);
    454 	if (read != 1) {
    455   		fprintf(stderr, "Error reading data from file: "
    456 		    "fread() returned %zu instead of 1 when attempting to "
    457 		    "read %" PRId64 " bytes from file %s\n", read, size, path);
    458 		goto solve_exec_error;
    459 	}
    460 
    461 	ret = nissy_solve(
    462 	    args->cube, args->str_solver, nissflag, args->minmoves,
    463 	    args->maxmoves, args->maxsolutions, args->optimal, args->threads,
    464 	    size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats, NULL, NULL);
    465 
    466 	free(buf);
    467 
    468 	if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
    469 		fprintf(stderr, "No solutions found\n");
    470 	else
    471 		printf("%s", solutions);
    472 
    473 	return 0;
    474 
    475 solve_exec_error:
    476 	free(buf);
    477 	return -2;
    478 }
    479 
    480 static int64_t
    481 solve_scramble_exec(args_t *args)
    482 {
    483 	nissy_applymoves(NISSY_SOLVED_CUBE, args->str_moves, args->cube);
    484 
    485 	return solve_exec(args);
    486 }
    487 
    488 static int64_t
    489 countmoves_exec(args_t *args)
    490 {
    491 	long long count;
    492 
    493 	count = nissy_countmoves(args->str_moves);
    494 
    495 	if (count >= 0)
    496 		printf("%lld\n", count);
    497 
    498 	return count >= 0 ? 0 : count;
    499 }
    500 
    501 static int64_t
    502 comparemoves_exec(args_t *args)
    503 {
    504 	long long cmp;
    505 
    506 	if ((cmp = nissy_comparemoves(args->str_moves, args->str_moves2)) < 0)
    507 		return cmp;
    508 
    509 	switch (cmp) {
    510 	case NISSY_COMPARE_MOVES_EQUAL:
    511 		printf("The two move sequences are equal\n");
    512 		break;
    513 	case NISSY_COMPARE_MOVES_DIFFERENT:
    514 		printf("The two move sequences are different\n");
    515 		break;
    516 	default:
    517 		printf("Error: unknown case\n");
    518 		return -1;
    519 	}
    520 
    521 	return 0;
    522 }
    523 
    524 static int64_t
    525 variation_exec(args_t *args)
    526 {
    527 	long long err;
    528 	char result[SOLUTIONS_BUFFER_SIZE];
    529 
    530 	err = nissy_variations(args->str_moves, args->str_variation,
    531 	    SOLUTIONS_BUFFER_SIZE, result);
    532 
    533 	if (err < 0)
    534 		return err;
    535 	printf("%s", result);
    536 
    537 	return 0;
    538 }
    539 
    540 static int64_t
    541 help_exec(args_t *args)
    542 {
    543 	int i;
    544 
    545 	if (args->str_command == NULL || args->str_command[0] == '\0') {
    546 		printf("This is a rudimentary shell for the H48 library.\n");
    547 		printf("Available commands and usage:\n\n");
    548 		for (i = 0; commands[i].name != NULL; i++)
    549 			printf("%-15s%s\n", commands[i].name, commands[i].syn);
    550 		printf("\nUse 'help -command COMMAND' for more information.\n");
    551 	} else {
    552 		for (i = 0; commands[i].name != NULL; i++)
    553 			if (!strcmp(args->str_command, commands[i].name))
    554 				break;
    555 		if (commands[i].name == NULL) {
    556 			printf("Unknown command %s\n", args->str_command);
    557 			return 1;
    558 		}
    559 		printf("Command %s\n\n", commands[i].name);
    560 		printf("Synopsis: %s\n\n", commands[i].syn);
    561 		printf("Description: %s\n", commands[i].desc);
    562 	}
    563 
    564 	return 0;
    565 }
    566 
    567 static int
    568 parse_args(int argc, char **argv, args_t *args)
    569 {
    570 	int i, j, n;
    571 
    572 	*args = (args_t) {
    573 		.command_index = -1,
    574 		.cube = "",
    575 		.str_cube = "",
    576 		.str_moves = "",
    577 		.str_moves2 = "",
    578 		.str_trans = "",
    579 		.str_solver = "",
    580 		.str_variation = "",
    581 		.str_nisstype = "",
    582 		.minmoves = 0,
    583 		.maxmoves = 20,
    584 		.optimal = 20,
    585 		.maxsolutions = 0,
    586 		.threads = 0,
    587 	};
    588 
    589 	if (argc == 0) {
    590 		printf("No command given\n");
    591 		return 1;
    592 	}
    593 
    594 	for (i = 0; commands[i].name != NULL; i++) {
    595 		if (!strcmp(argv[0], commands[i].name)) {
    596 			args->command_index = i;
    597 			break;
    598 		}
    599 	}
    600 
    601 	if (commands[i].name == NULL) {
    602 		fprintf(stderr, "Unknown command %s\n", argv[0]);
    603 		return 1;
    604 	}
    605 
    606 	for (i = 1; i < argc; i++) {
    607 		for (j = 0; options[j].name != NULL; j++) {
    608 			n = argc - i - 1;
    609 			if (strcmp(argv[i], options[j].name))
    610 				continue;
    611 			if (n < options[j].nargs) {
    612 				fprintf(stderr,
    613 				    "Too few arguments for option %s\n",
    614 				    options[j].name);
    615 				return 1;
    616 			}
    617 			if (!options[j].set(n, argv+i+1, args)) {
    618 				fprintf(stderr,
    619 				    "Error parsing arguments for option %s\n",
    620 				    options[j].name);
    621 				return 1;
    622 			}
    623 			i += options[j].nargs;
    624 			break;
    625 		}
    626 		if (options[j].name == NULL) {
    627 			fprintf(stderr, "Unknown option %s\n", argv[i]);
    628 			return 1;
    629 		}
    630 	}
    631 
    632 	return 0;
    633 }
    634 
    635 static bool
    636 parse_uint(const char *argv, unsigned *result)
    637 {
    638 	*result = strtol(argv, NULL, 10);
    639 
    640 	/* TODO: figure out how errno works and use it */
    641 	return true;
    642 }
    643 
    644 static uint8_t
    645 parse_nisstype(const char *arg)
    646 {
    647 	if (!strcmp("normal", arg) || !strcmp("", arg))
    648 		return NISSY_NISSFLAG_NORMAL;
    649 
    650 	if (!strcmp("inverse", arg))
    651 		return NISSY_NISSFLAG_INVERSE;
    652 
    653 	if (!strcmp("linear", arg))
    654 		return NISSY_NISSFLAG_LINEAR;
    655 
    656 	if (!strcmp("mixed", arg))
    657 		return NISSY_NISSFLAG_MIXED;
    658 
    659 	if (!strcmp("all", arg))
    660 		return NISSY_NISSFLAG_ALL;
    661 
    662 	return UINT8_MAX;
    663 }
    664 
    665 static bool
    666 set_cube(int argc, char **argv, args_t *args)
    667 {
    668 	memcpy(args->cube, argv[0], NISSY_SIZE_CUBE);
    669 	args->cube[21] = 0;
    670 
    671 	return true;
    672 }
    673 
    674 static bool
    675 set_str_command(int argc, char **argv, args_t *args)
    676 {
    677 	args->str_command = argv[0];
    678 
    679 	return true;
    680 }
    681 
    682 static bool
    683 set_str_cube(int argc, char **argv, args_t *args)
    684 {
    685 	args->str_cube = argv[0];
    686 
    687 	return true;
    688 }
    689 
    690 static bool
    691 set_str_moves(int argc, char **argv, args_t *args)
    692 {
    693 	args->str_moves = argv[0];
    694 
    695 	return true;
    696 }
    697 
    698 static bool
    699 set_str_moves2(int argc, char **argv, args_t *args)
    700 {
    701 	args->str_moves2 = argv[0];
    702 
    703 	return true;
    704 }
    705 
    706 static bool
    707 set_str_trans(int argc, char **argv, args_t *args)
    708 {
    709 	args->str_trans = argv[0];
    710 
    711 	return true;
    712 }
    713 
    714 static bool
    715 set_str_solver(int argc, char **argv, args_t *args)
    716 {
    717 	args->str_solver = argv[0];
    718 
    719 	return true;
    720 }
    721 
    722 static bool
    723 set_str_variation(int argc, char **argv, args_t *args)
    724 {
    725 	args->str_variation = argv[0];
    726 
    727 	return true;
    728 }
    729 
    730 static bool
    731 set_str_nisstype(int argc, char **argv, args_t *args)
    732 {
    733 	args->str_nisstype = argv[0];
    734 
    735 	return true;
    736 }
    737 
    738 static bool
    739 set_minmoves(int argc, char **argv, args_t *args)
    740 {
    741 	return parse_uint(argv[0], &args->minmoves);
    742 }
    743 
    744 static bool
    745 set_maxmoves(int argc, char **argv, args_t *args)
    746 {
    747 	return parse_uint(argv[0], &args->maxmoves);
    748 }
    749 
    750 static bool
    751 set_optimal(int argc, char **argv, args_t *args)
    752 {
    753 	return parse_uint(argv[0], &args->optimal);
    754 }
    755 
    756 static bool
    757 set_maxsolutions(int argc, char **argv, args_t *args)
    758 {
    759 	return parse_uint(argv[0], &args->maxsolutions);
    760 }
    761 
    762 static bool
    763 set_threads(int argc, char **argv, args_t *args)
    764 {
    765 	return parse_uint(argv[0], &args->threads);
    766 }
    767 
    768 void
    769 log_stderr(const char *str, void *unused)
    770 {
    771 	fprintf(stderr, "%s", str);
    772 }
    773 
    774 int
    775 main(int argc, char **argv)
    776 {
    777 	int parse_error;
    778 	args_t args;
    779 
    780 	srand(time(NULL));
    781 	nissy_setlogger(log_stderr, NULL);
    782 
    783 	parse_error = parse_args(argc-1, argv+1, &args);
    784 	if (parse_error)
    785 		return parse_error;
    786 
    787 	return (int)commands[args.command_index].exec(&args);
    788 }