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


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