nissy-classic

Stable branch of nissy
git clone https://git.tronto.net/nissy-classic
Download | Log | Files | Refs | README | LICENSE

commands.c (17933B)


      1 #include "commands.h"
      2 
      3 /* Arg parsing functions *****************************************************/
      4 
      5 CommandArgs *           gen_parse_args(int c, char **v);
      6 CommandArgs *           help_parse_args(int c, char **v);
      7 CommandArgs *           parse_only_scramble(int c, char **v);
      8 CommandArgs *           parse_no_arg(int c, char **v);
      9 CommandArgs *           ptable_parse_args(int c, char **v);
     10 CommandArgs *           solve_parse_args(int c, char **v);
     11 CommandArgs *           scramble_parse_args(int c, char **v);
     12 
     13 /* Exec functions ************************************************************/
     14 
     15 static void             gen_exec(CommandArgs *args);
     16 static void             cleanup_exec(CommandArgs *args);
     17 static void             invert_exec(CommandArgs *args);
     18 static void             solve_exec(CommandArgs *args);
     19 static void             scramble_exec(CommandArgs *args);
     20 static void             steps_exec(CommandArgs *args);
     21 static void             commands_exec(CommandArgs *args);
     22 static void             freemem_exec(CommandArgs *args);
     23 static void             print_exec(CommandArgs *args);
     24 static void             ptable_exec(CommandArgs *args);
     25 static void             twophase_exec(CommandArgs *args);
     26 static void             help_exec(CommandArgs *args);
     27 static void             quit_exec(CommandArgs *args);
     28 static void             unniss_exec(CommandArgs *args);
     29 static void             version_exec(CommandArgs *args);
     30 
     31 /* Local functions ***********************************************************/
     32 
     33 static bool             read_step(CommandArgs *args, char *str);
     34 static bool             read_scrtype(CommandArgs *args, char *str);
     35 static bool             read_scramble(int c, char **v, CommandArgs *args);
     36 
     37 /* Commands ******************************************************************/
     38 
     39 Command
     40 solve_cmd = {
     41 	.name        = "solve",
     42 	.usage       = "solve STEP [OPTIONS] SCRAMBLE",
     43 	.description = "Solve a step; see command steps for a list of steps",
     44 	.parse_args  = solve_parse_args,
     45 	.exec        = solve_exec
     46 };
     47 
     48 Command
     49 scramble_cmd = {
     50 	.name        = "scramble",
     51 	.usage       = "scramble [TYPE] [-n N]",
     52 	.description = "Get a random-position scramble",
     53 	.parse_args  = scramble_parse_args,
     54 	.exec        = scramble_exec,
     55 };
     56 
     57 Command
     58 gen_cmd = {
     59 	.name        = "gen",
     60 	.usage       = "gen [-t N]",
     61 	.description = "Generate all tables [using N threads]",
     62 	.parse_args  = gen_parse_args,
     63 	.exec        = gen_exec
     64 };
     65 
     66 Command
     67 invert_cmd = {
     68 	.name        = "invert",
     69 	.usage       = "invert SCRAMBLE]",
     70 	.description = "Invert a scramble",
     71 	.parse_args  = parse_only_scramble,
     72 	.exec        = invert_exec,
     73 };
     74 
     75 Command
     76 steps_cmd = {
     77 	.name        = "steps",
     78 	.usage       = "steps",
     79 	.description = "List available steps",
     80 	.parse_args  = parse_no_arg,
     81 	.exec        = steps_exec
     82 };
     83 
     84 Command
     85 commands_cmd = {
     86 	.name        = "commands",
     87 	.usage       = "commands",
     88 	.description = "List available commands",
     89 	.parse_args  = parse_no_arg,
     90 	.exec        = commands_exec
     91 };
     92 
     93 Command
     94 freemem_cmd = {
     95 	.name        = "freemem",
     96 	.usage       = "freemem",
     97 	.description = "free large tables from RAM",
     98 	.parse_args  = parse_no_arg,
     99 	.exec        = freemem_exec,
    100 };
    101 
    102 Command
    103 print_cmd = {
    104 	.name        = "print",
    105 	.usage       = "print SCRAMBLE",
    106 	.description = "Print written description of the cube",
    107 	.parse_args  = parse_only_scramble,
    108 	.exec        = print_exec,
    109 };
    110 
    111 Command
    112 ptable_cmd = {
    113 	.name        = "ptable",
    114 	.usage       = "ptable [TABLE]",
    115 	.description = "Print information on pruning tables",
    116 	.parse_args  = ptable_parse_args,
    117 	.exec        = ptable_exec,
    118 };
    119 
    120 Command
    121 help_cmd = {
    122 	.name        = "help",
    123 	.usage       = "help [COMMAND]",
    124 	.description = "Display nissy manual page or help on specific command",
    125 	.parse_args  = help_parse_args,
    126 	.exec        = help_exec,
    127 };
    128 
    129 Command
    130 twophase_cmd = {
    131 	.name        = "twophase",
    132 	.usage       = "twophase",
    133 	.description = "Find a solution quickly using a 2-phase method",
    134 	.parse_args  = parse_only_scramble,
    135 	.exec        = twophase_exec,
    136 };
    137 
    138 Command
    139 quit_cmd = {
    140 	.name        = "quit",
    141 	.usage       = "quit",
    142 	.description = "Quit nissy",
    143 	.parse_args  = parse_no_arg,
    144 	.exec        = quit_exec,
    145 };
    146 
    147 Command
    148 cleanup_cmd = {
    149 	.name        = "cleanup",
    150 	.usage       = "cleanup SCRAMBLE",
    151 	.description = "Rewrite a scramble using only standard moves (HTM)",
    152 	.parse_args  = parse_only_scramble,
    153 	.exec        = cleanup_exec,
    154 };
    155 
    156 Command
    157 unniss_cmd = {
    158 	.name        = "unniss",
    159 	.usage       = "unniss SCRAMBLE",
    160 	.description = "Rewrite a scramble without NISS",
    161 	.parse_args  = parse_only_scramble,
    162 	.exec        = unniss_exec,
    163 };
    164 
    165 Command
    166 version_cmd = {
    167 	.name        = "version",
    168 	.usage       = "version",
    169 	.description = "print nissy version",
    170 	.parse_args  = parse_no_arg,
    171 	.exec        = version_exec,
    172 };
    173 
    174 Command *commands[] = {
    175 	&commands_cmd,
    176 	&freemem_cmd,
    177 	&gen_cmd,
    178 	&help_cmd,
    179 	&invert_cmd,
    180 	&print_cmd,
    181 	&ptable_cmd,
    182 	&quit_cmd,
    183 	&solve_cmd,
    184 	&scramble_cmd,
    185 	&steps_cmd,
    186 	&twophase_cmd,
    187 	&cleanup_cmd,
    188 	&unniss_cmd,
    189 	&version_cmd,
    190 	NULL
    191 };
    192 
    193 /* Other constants ***********************************************************/
    194 
    195 char *scrtypes[20] = { "eo", "corners", "edges", "fmc", "dr", "htr", NULL };
    196 
    197 /* Arg parsing functions implementation **************************************/
    198 
    199 CommandArgs *
    200 solve_parse_args(int c, char **v)
    201 {
    202 	int i;
    203 	bool infinitesols, fixedmsols;
    204 	long val;
    205 
    206 	CommandArgs *a = new_args();
    207 
    208 	a->opts->min_moves     = 0;
    209 	a->opts->max_moves     = 20;
    210 	a->opts->max_solutions = 1;
    211 	a->opts->nthreads      = 1;
    212 	a->opts->optimal       = -1;
    213 	a->opts->nisstype      = NORMAL;
    214 	a->opts->verbose       = false;
    215 	a->opts->all           = false;
    216 	a->opts->print_number  = true;
    217 	a->opts->count_only    = false;
    218 
    219 	fixedmsols = false;
    220 	infinitesols = false;
    221 
    222 	for (i = 0; i < c; i++) {
    223 		if (!strcmp(v[i], "-m") && i+1 < c) {
    224 			val = strtol(v[++i], NULL, 10);
    225 			if (val < 0 || val > 100) {
    226 				fprintf(stderr,
    227 					"Invalid min number of moves"
    228 					"(0 <= N <= 100).\n");
    229 				return a;
    230 			}
    231 			a->opts->min_moves = val;
    232 		} else if (!strcmp(v[i], "-M") && i+1 < c) {
    233 			val = strtol(v[++i], NULL, 10);
    234 			if (val < 0 || val > 100) {
    235 				fprintf(stderr,
    236 					"Invalid max number of moves"
    237 					"(0 <= N <= 100).\n");
    238 				return a;
    239 			}
    240 			a->opts->max_moves = val;
    241 			infinitesols = true;
    242 		} else if (!strcmp(v[i], "-t") && i+1 < c) {
    243 			val = strtol(v[++i], NULL, 10);
    244 			if (val < 1 || val > 64) {
    245 				fprintf(stderr,
    246 					"Invalid number of threads."
    247 					"1 <= t <= 64\n");
    248 				return a;
    249 			}
    250 			a->opts->nthreads = val;
    251 		} else if (!strcmp(v[i], "-n") && i+1 < c) {
    252 			val = strtol(v[++i], NULL, 10);
    253 			if (val < 1 || val > 1000000) {
    254 				fprintf(stderr,
    255 					"Invalid number of solutions.\n");
    256 				return a;
    257 			}
    258 			a->opts->max_solutions = val;
    259 			fixedmsols = true;
    260 		} else if (!strcmp(v[i], "-o")) {
    261 			a->opts->optimal = 0;
    262 			infinitesols = true;
    263 		} else if (!strcmp(v[i], "-O") && i+1 < c) {
    264 			val = strtol(v[++i], NULL, 10);
    265 			if (val < 0 || val > 100 ||
    266 			    (val == 0 && strcmp("0", v[i]))) {
    267 				fprintf(stderr,
    268 					"Invalid max number of moves"
    269 					" (0 <= N <= 100).\n");
    270 				return a;
    271 			}
    272 			a->opts->optimal = val;
    273 			infinitesols = true;
    274 		} else if (!strcmp(v[i], "-N")) {
    275 			a->opts->nisstype = NISS;
    276 		} else if (!strcmp(v[i], "-L")) {
    277 			if (a->opts->nisstype != NISS)
    278 				a->opts->nisstype = LINEAR;
    279 		} else if (!strcmp(v[i], "-i")) {
    280 			a->scrstdin = true;
    281 		} else if (!strcmp(v[i], "-v")) {
    282 			a->opts->verbose = true;
    283 		} else if (!strcmp(v[i], "-a")) {
    284 			a->opts->all = true;
    285 		} else if (!strcmp(v[i], "-p")) {
    286 			a->opts->print_number = false;
    287 		} else if (!strcmp(v[i], "-c")) {
    288 			a->opts->count_only = true;
    289 		} else if (!read_step(a, v[i])) {
    290 			break;
    291 		}
    292 	}
    293 
    294 	if (infinitesols && !fixedmsols)
    295 		a->opts->max_solutions = 1000000; /* 1M = +infty */
    296 
    297 	a->success = (a->scrstdin && i == c) || read_scramble(c-i, &v[i], a);
    298 	return a;
    299 }
    300 
    301 CommandArgs *
    302 scramble_parse_args(int c, char **v)
    303 {
    304 	int i;
    305 	long val;
    306 
    307 	CommandArgs *a = new_args();
    308 
    309 	a->success = true;
    310 	a->n       = 1;
    311 
    312 	for (i = 0; i < c; i++) {
    313 		if (!strcmp(v[i], "-n") && i+1 < c) {
    314 			val = strtol(v[++i], NULL, 10);
    315 			if (val < 1 || val > 1000000) {
    316 				fprintf(stderr,
    317 					"Invalid number of scrambles.\n");
    318 				a->success = false;
    319 				return a;
    320 			}
    321 			a->n = val;
    322 		} else if (!read_scrtype(a, v[i])) {
    323 			a->success = false;
    324 			return a;
    325 		}
    326 	}
    327 
    328 	return a;
    329 }
    330 
    331 CommandArgs *
    332 gen_parse_args(int c, char **v)
    333 {
    334 	int val;
    335 	CommandArgs *a = new_args();
    336 
    337 	a->opts->nthreads = 64;
    338 	a->success  = false;
    339 
    340 	if (c == 0) {
    341 		a->success = true;
    342 	} else {
    343 		if (!strcmp(v[0], "-t") && c > 1) {
    344 			val = strtol(v[1], NULL, 10);
    345 			if (val < 1 || val > 64) {
    346 				fprintf(stderr,
    347 					"Invalid number of threads."
    348 					"1 <= t <= 64\n");
    349 				return a;
    350 			}
    351 			a->opts->nthreads = val;
    352 			a->success = true;
    353 		}
    354 	}
    355 	
    356 	return a;
    357 }
    358 
    359 CommandArgs *
    360 help_parse_args(int c, char **v)
    361 {
    362 	int i;
    363 	CommandArgs *a = new_args();
    364 
    365 	if (c == 1) {
    366 		for (i = 0; commands[i] != NULL; i++)
    367 			if (!strcmp(v[0], commands[i]->name))
    368 				a->command = commands[i];
    369 		if (a->command == NULL)
    370 			fprintf(stderr, "%s: command not found\n", v[0]);
    371 	}
    372 
    373 	a->success = c == 0 || (c == 1 && a->command != NULL);
    374 	return a;
    375 }
    376 
    377 CommandArgs *
    378 parse_only_scramble(int c, char **v)
    379 {
    380 	CommandArgs *a = new_args();
    381 
    382 	if (!strcmp(v[0], "-i")) {
    383 		a->scrstdin = true;
    384 		a->success = c == 1;
    385 	} else {
    386 		a->success = read_scramble(c, v, a);
    387 	}
    388 
    389 	return a;
    390 }
    391 
    392 CommandArgs *
    393 parse_no_arg(int c, char **v)
    394 {
    395 	CommandArgs *a = new_args();
    396 
    397 	a->success = true;
    398 
    399 	return a;
    400 }
    401 
    402 CommandArgs *
    403 ptable_parse_args(int c, char **v)
    404 {
    405 	int i;
    406 	CommandArgs *a = new_args();
    407 
    408 	if (c == 1) {
    409 		for (i = 0; all_pd[i] != NULL; i++)
    410 			if (!strcmp(v[0], all_pd[i]->filename))
    411 				a->pd = all_pd[i];
    412 		if (a->pd == NULL)
    413 			fprintf(stderr, "%s: pruning table not found\n", v[0]);
    414 	}
    415 
    416 	a->success = c == 0 || (c == 1 && a->pd != NULL);
    417 	return a;
    418 }
    419 
    420 /* Exec functions implementation *********************************************/
    421 
    422 static void
    423 solve_exec(CommandArgs *args)
    424 {
    425 	Cube c;
    426 	AlgList *sols;
    427 
    428 	init_all_movesets();
    429 	init_symcoord();
    430 
    431 	c = apply_alg(args->scramble, (Cube){0});
    432 	sols = solve(c, args->step, args->opts);
    433 
    434 	if (args->opts->count_only) {
    435 		printf("%d\n", sols->len);
    436 	} else {
    437 		sort_alglist(sols);
    438 		print_alglist(stdout, sols, args->opts->print_number);
    439 	}
    440 
    441 	free_alglist(sols);
    442 }
    443 
    444 static void
    445 scramble_exec(CommandArgs *args)
    446 {
    447 	Cube cube;
    448 	CubeArray *arr;
    449 	Alg *scr, *ruf, *aux;
    450 	int i, j, eo, ep, co, cp, a[12];
    451 	int eparr[12] = { [8] = 8, [9] = 9, [10] = 10, [11] = 11 };
    452 	uint64_t ui, uj, uk;
    453 
    454 	init_all_movesets();
    455 	init_symcoord();
    456 
    457 	srand(time(NULL));
    458 
    459 	for (i = 0; i < args->n; i++) {
    460 
    461 		if (!strcmp(args->scrtype, "dr")) {
    462 			/* Warning: cube is inconsistent because of side CO  *
    463 			 * and EO on U/D. But solve_2phase only solves drfin *
    464 			 * in this case, so it should be ok.                 *
    465 			 * TODO: check this properly                         *
    466 			 * Moreover we again need to fix parity after        *
    467 			 * generating epose manually                         */
    468 			do {
    469 				ui = rand() % FACTORIAL8;
    470 				uj = rand() % FACTORIAL8;
    471 				uk = rand() % FACTORIAL4;
    472 
    473 				index_to_perm(ui, 8, eparr);
    474 				arr = malloc(sizeof(CubeArray));
    475 				arr->ep = eparr;
    476 				cube = arrays_to_cube(arr, pf_ep);
    477 				free(arr);
    478 
    479 				cube.cp = uj;
    480 				cube.epose = uk;
    481 			} while (!is_admissible(cube));
    482 		} else if (!strcmp(args->scrtype, "htr")) {
    483 			/* antindex_htrfin() returns a consistent *
    484 			 * cube, except possibly for parity       */
    485 			do {
    486 				ui = rand() % (24*24/6);
    487 				cube = (Cube){0};
    488 				cube.cp = cornershtrfin_ant[ui];
    489 				cube.epose = rand() % 24;
    490 				cube.eposs = rand() % 24;
    491 				cube.eposm = rand() % 24;
    492 			} while (!is_admissible(cube));
    493 		} else {
    494 			eo = rand() % POW2TO11;
    495 			ep = rand() % FACTORIAL12;
    496 			co = rand() % POW3TO7;
    497 			cp = rand() % FACTORIAL8;
    498 
    499 			if (!strcmp(args->scrtype, "eo")) {
    500 				eo = 0;
    501 			} else if (!strcmp(args->scrtype, "corners")) {
    502 				eo = 0;
    503 				ep = 0;
    504 				index_to_perm(cp, 8, a);
    505 				if (perm_sign(a, 8) == 1) {
    506 					swap(&a[0], &a[1]);
    507 					cp = perm_to_index(a, 8);
    508 				}
    509 			} else if (!strcmp(args->scrtype, "edges")) {
    510 				co = 0;
    511 				cp = 0;
    512 				index_to_perm(ep, 12, a);
    513 				if (perm_sign(a, 12) == 1) {
    514 					swap(&a[0], &a[1]);
    515 					ep = perm_to_index(a, 12);
    516 				}
    517 			}
    518 			cube = fourval_to_cube(eo, ep, co, cp);
    519 		}
    520 
    521 		/* TODO: can be optimized for htr and dr using htrfin, drfin */
    522 		scr = solve_2phase(cube, 1);
    523 
    524 		if (!strcmp(args->scrtype, "fmc")) {
    525 			aux = new_alg("");
    526 			copy_alg(scr, aux);
    527 			/* Trick to rufify for free: rotate the scramble  *
    528 			 * so that it does not start with F or end with R */
    529 			for (j = 0; j < NROTATIONS; j++) {
    530 				if (base_move(scr->move[0]) != F &&
    531 				    base_move(scr->move[0]) != B &&
    532 				    base_move(scr->move[scr->len-1]) != R &&
    533 				    base_move(scr->move[scr->len-1]) != L)
    534 					break;
    535 				copy_alg(aux, scr);
    536 				transform_alg(j, scr);
    537 			}
    538 			copy_alg(scr, aux);
    539 			ruf = new_alg("R' U' F");
    540 			copy_alg(ruf, scr);
    541 			compose_alg(scr, aux);
    542 			compose_alg(scr, ruf);
    543 			free_alg(aux);
    544 			free_alg(ruf);
    545 		}
    546 		print_alg(stdout, scr, false);
    547 		free_alg(scr);
    548 	}
    549 }
    550 
    551 static void
    552 gen_exec(CommandArgs *args)
    553 {
    554 	int i;
    555 
    556 	fprintf(stderr, "Generating coordinates...\n");
    557 	init_all_movesets();
    558 	init_symcoord();
    559 
    560 	fprintf(stderr, "Generating pruning tables...\n");
    561 	for (i = 0; all_pd[i] != NULL; i++)
    562 		genptable(all_pd[i], args->opts->nthreads);
    563 
    564 	fprintf(stderr, "Done!\n");
    565 }
    566 
    567 static void
    568 invert_exec(CommandArgs *args)
    569 {
    570 	Alg *inv;
    571 
    572 	inv = inverse_alg(args->scramble);
    573 	print_alg(stdout, inv, false);
    574 
    575 	free_alg(inv);
    576 }
    577 
    578 static void
    579 steps_exec(CommandArgs *args)
    580 {
    581 	int i;
    582 
    583 	for (i = 0; steps[i] != NULL; i++)
    584 		printf("%-15s %s\n", steps[i]->shortname, steps[i]->name);
    585 }
    586 
    587 static void
    588 commands_exec(CommandArgs *args)
    589 {
    590 	int i;
    591 
    592 	for (i = 0; commands[i] != NULL; i++)
    593 		printf("%s\n", commands[i]->usage);
    594 
    595 }
    596 
    597 static void
    598 freemem_exec(CommandArgs *args)
    599 {
    600 	int i;
    601 
    602 	for (i = 0; all_pd[i] != NULL; i++)
    603 		free_pd(all_pd[i]);
    604 
    605 	for (i = 0; all_sd[i] != NULL; i++)
    606 		free_sd(all_sd[i]);
    607 
    608 	/* TODO: invtables are also large, but for now they are *
    609 	 * statically allocated. Consider releasing those too.  */
    610 }
    611 
    612 static void
    613 print_exec(CommandArgs *args)
    614 {
    615 	init_moves();
    616 	print_cube(apply_alg(args->scramble, (Cube){0}));
    617 }
    618 
    619 static void
    620 ptable_exec(CommandArgs *args)
    621 {
    622 	int i = 0;
    623 	if (args->pd == NULL) {
    624 		printf("Available pruning tables:\n");
    625 		for (i = 0; all_pd[i] != NULL; i++)
    626 			printf("\t%s\n", all_pd[i]->filename);
    627 		printf("Use ptable [TABLE] to see the table distribution"
    628 		       " and other information\n");
    629 	} else {
    630 		genptable(args->pd, args->opts->nthreads);
    631 		print_ptable(args->pd);
    632 	}
    633 }
    634 
    635 
    636 static void
    637 twophase_exec(CommandArgs *args)
    638 {
    639 	Cube c;
    640 	Alg *sol;
    641 
    642 	init_all_movesets();
    643 	init_symcoord();
    644 
    645 	c = apply_alg(args->scramble, (Cube){0});
    646 	sol = solve_2phase(c, 1);
    647 
    648 	print_alg(stdout, sol, false);
    649 	free_alg(sol);
    650 }
    651 
    652 static void
    653 help_exec(CommandArgs *args)
    654 {
    655 	if (args->command == NULL) {
    656 		printf(
    657 		       "Use the nissy command \"help COMMAND\" for a short "
    658 		       "description of a specific command.\n"
    659 		       "Use the nissy command \"commands\" for a list of "
    660 		       "available commands.\n"
    661 		       "See the manual page for more details. The manual"
    662 		       " page is available with \"man nissy\" on a UNIX"
    663 		       " system (such as Linux or MacOS) or in pdf and html"
    664 		       " format in the docs folder.\n"
    665 		       "Nissy is available for free at "
    666 		       "https://github.com/sebastianotronto/nissy\n"
    667 		      );
    668 	} else {
    669 		printf("Command %s: %s\nusage: %s\n", args->command->name,
    670 		       args->command->description, args->command->usage);
    671 	}
    672 }
    673 
    674 static void
    675 quit_exec(CommandArgs *args)
    676 {
    677 	exit(0);
    678 }
    679 
    680 static void
    681 cleanup_exec(CommandArgs *args)
    682 {
    683 	Alg *alg;
    684 
    685 	init_moves();
    686 
    687 	alg = cleanup(args->scramble);
    688 	print_alg(stdout, alg, false);
    689 
    690 	free_alg(alg);
    691 }
    692 
    693 static void
    694 unniss_exec(CommandArgs *args)
    695 {
    696 	Alg *aux;
    697 
    698 	aux = unniss(args->scramble);
    699 	print_alg(stdout, aux, false);
    700 	free(aux);
    701 }
    702 
    703 static void
    704 version_exec(CommandArgs *args)
    705 {
    706 	printf(VERSION"\n");
    707 }
    708 
    709 /* Local functions implementation ********************************************/
    710 
    711 static bool
    712 read_scramble(int c, char **v, CommandArgs *args)
    713 {
    714 	int i, k, n;
    715 	unsigned int j;
    716 	char *algstr;
    717 
    718 	if (c < 1) {
    719 		fprintf(stderr, "Error: no scramble given?\n");
    720 		return false;
    721 	}
    722 
    723 	for(n = 0, i = 0; i < c; i++)
    724 		n += strlen(v[i]);
    725 
    726 	algstr = malloc((n + 1) * sizeof(char));
    727 	k = 0;
    728 	for (i = 0; i < c; i++)
    729 		for (j = 0; j < strlen(v[i]); j++)
    730 			algstr[k++] = v[i][j];
    731 	algstr[k] = 0;
    732 
    733 	args->scramble = new_alg(algstr);
    734 	free(algstr);
    735 
    736 	if (args->scramble->len == 0)
    737 		fprintf(stderr, "Error reading scramble\n");
    738 
    739 	return args->scramble->len > 0;
    740 }
    741 
    742 static bool
    743 read_scrtype(CommandArgs *args, char *str)
    744 {
    745 	int i;
    746 
    747 	for (i = 0; scrtypes[i] != NULL; i++) {
    748 		if (!strcmp(scrtypes[i], str)) {
    749 			strcpy(args->scrtype, scrtypes[i]);
    750 			return true;
    751 		}
    752 	}
    753 
    754 	return false;
    755 }
    756 
    757 static bool
    758 read_step(CommandArgs *args, char *str)
    759 {
    760 	int i;
    761 
    762 	for (i = 0; steps[i] != NULL; i++) {
    763 		if (!strcmp(steps[i]->shortname, str)) {
    764 			args->step = steps[i];
    765 			return true;
    766 		}
    767 	}
    768 
    769 	return false;
    770 }
    771 
    772 /* Public functions implementation *******************************************/
    773 
    774 void
    775 free_args(CommandArgs *args)
    776 {
    777 	if (args == NULL)
    778 		return;
    779 
    780 	if (args->scramble != NULL)
    781 		free_alg(args->scramble);
    782 	if (args->opts != NULL)
    783 		free(args->opts);
    784 
    785 	/* step and command must not be freed, they are static! */
    786 
    787 	free(args);
    788 }
    789 
    790 CommandArgs *
    791 new_args(void)
    792 {
    793 	CommandArgs *args = malloc(sizeof(CommandArgs));
    794 
    795 	args->success = false;
    796 	args->scrstdin = false;
    797 	args->scramble = NULL; /* initialized in read_scramble */
    798 	args->opts = malloc(sizeof(SolveOptions));
    799 
    800 	/* step and command are static */
    801 	args->step = steps[0]; /* default: first step in list */
    802 	args->command = NULL;
    803 	args->pd = NULL;
    804 
    805 	return args;
    806 }