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


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