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


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