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


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