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

shell.c (18000B)


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