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 }