commands.c (17960B)
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 AlgList *scrlist; 454 SolveOptions dropts = { 455 .min_moves = 0, 456 .max_moves = 19, 457 .max_solutions = 1, 458 .nthreads = 1, 459 .nisstype = NORMAL, 460 .verbose = false 461 }; 462 463 init_all_movesets(); 464 init_symcoord(); 465 466 srand(time(NULL)); 467 468 for (i = 0; i < args->n; i++) { 469 470 if (!strcmp(args->scrtype, "dr")) { 471 /* Warning: cube is inconsistent because of side CO * 472 * and EO on U/D, but we only solve drudfin on it. */ 473 do { 474 ui = rand() % FACTORIAL8; 475 uj = rand() % FACTORIAL8; 476 uk = rand() % FACTORIAL4; 477 478 index_to_perm(ui, 8, eparr); 479 arr = malloc(sizeof(CubeArray)); 480 arr->ep = eparr; 481 cube = arrays_to_cube(arr, pf_ep); 482 free(arr); 483 484 cube.cp = uj; 485 cube.epose = uk; 486 } while (!is_admissible(cube)); 487 488 scrlist = solve(cube, &drudfin_drud, &dropts); 489 scr = scrlist->first->alg; 490 } else if (!strcmp(args->scrtype, "htr")) { 491 /* antindex_htrfin() returns a consistent * 492 * cube, except possibly for parity */ 493 do { 494 ui = rand() % (24*24/6); 495 cube = (Cube){0}; 496 cube.cp = cornershtrfin_ant[ui]; 497 cube.epose = rand() % 24; 498 cube.eposs = rand() % 24; 499 cube.eposm = rand() % 24; 500 } while (!is_admissible(cube)); 501 502 scrlist = solve(cube, &drudfin_drud, &dropts); 503 scr = scrlist->first->alg; 504 } else { 505 eo = rand() % POW2TO11; 506 ep = rand() % FACTORIAL12; 507 co = rand() % POW3TO7; 508 cp = rand() % FACTORIAL8; 509 510 if (!strcmp(args->scrtype, "eo")) { 511 eo = 0; 512 } else if (!strcmp(args->scrtype, "corners")) { 513 eo = 0; 514 ep = 0; 515 index_to_perm(cp, 8, a); 516 if (perm_sign(a, 8) == 1) { 517 swap(&a[0], &a[1]); 518 cp = perm_to_index(a, 8); 519 } 520 } else if (!strcmp(args->scrtype, "edges")) { 521 co = 0; 522 cp = 0; 523 index_to_perm(ep, 12, a); 524 if (perm_sign(a, 12) == 1) { 525 swap(&a[0], &a[1]); 526 ep = perm_to_index(a, 12); 527 } 528 } 529 cube = fourval_to_cube(eo, ep, co, cp); 530 scr = solve_2phase(cube, 1); 531 } 532 533 if (!strcmp(args->scrtype, "fmc")) { 534 aux = new_alg(""); 535 copy_alg(scr, aux); 536 /* Trick to rufify for free: rotate the scramble * 537 * so that it does not start with F or end with R */ 538 for (j = 0; j < NROTATIONS; j++) { 539 if (base_move(scr->move[0]) != F && 540 base_move(scr->move[0]) != B && 541 base_move(scr->move[scr->len-1]) != R && 542 base_move(scr->move[scr->len-1]) != L) 543 break; 544 copy_alg(aux, scr); 545 transform_alg(j, scr); 546 } 547 copy_alg(scr, aux); 548 ruf = new_alg("R' U' F"); 549 copy_alg(ruf, scr); 550 compose_alg(scr, aux); 551 compose_alg(scr, ruf); 552 free_alg(aux); 553 free_alg(ruf); 554 } 555 print_alg(stdout, scr, false); 556 free_alg(scr); 557 } 558 } 559 560 static void 561 gen_exec(CommandArgs *args) 562 { 563 int i; 564 565 fprintf(stderr, "Generating coordinates...\n"); 566 init_all_movesets(); 567 init_symcoord(); 568 569 fprintf(stderr, "Generating pruning tables...\n"); 570 for (i = 0; all_pd[i] != NULL; i++) 571 genptable(all_pd[i], args->opts->nthreads); 572 573 fprintf(stderr, "Done!\n"); 574 } 575 576 static void 577 invert_exec(CommandArgs *args) 578 { 579 Alg *inv; 580 581 inv = inverse_alg(args->scramble); 582 print_alg(stdout, inv, false); 583 584 free_alg(inv); 585 } 586 587 static void 588 steps_exec(CommandArgs *args) 589 { 590 int i; 591 592 for (i = 0; steps[i] != NULL; i++) 593 printf("%-15s %s\n", steps[i]->shortname, steps[i]->name); 594 } 595 596 static void 597 commands_exec(CommandArgs *args) 598 { 599 int i; 600 601 for (i = 0; commands[i] != NULL; i++) 602 printf("%s\n", commands[i]->usage); 603 604 } 605 606 static void 607 freemem_exec(CommandArgs *args) 608 { 609 int i; 610 611 for (i = 0; all_pd[i] != NULL; i++) 612 free_pd(all_pd[i]); 613 614 for (i = 0; all_sd[i] != NULL; i++) 615 free_sd(all_sd[i]); 616 617 /* TODO: invtables are also large, but for now they are * 618 * statically allocated. Consider releasing those too. */ 619 } 620 621 static void 622 print_exec(CommandArgs *args) 623 { 624 init_moves(); 625 print_cube(apply_alg(args->scramble, (Cube){0})); 626 } 627 628 static void 629 ptable_exec(CommandArgs *args) 630 { 631 int i = 0; 632 if (args->pd == NULL) { 633 printf("Available pruning tables:\n"); 634 for (i = 0; all_pd[i] != NULL; i++) 635 printf("\t%s\n", all_pd[i]->filename); 636 printf("Use ptable [TABLE] to see the table distribution" 637 " and other information\n"); 638 } else { 639 genptable(args->pd, args->opts->nthreads); 640 print_ptable(args->pd); 641 } 642 } 643 644 645 static void 646 twophase_exec(CommandArgs *args) 647 { 648 Cube c; 649 Alg *sol; 650 651 init_all_movesets(); 652 init_symcoord(); 653 654 c = apply_alg(args->scramble, (Cube){0}); 655 sol = solve_2phase(c, 1); 656 657 print_alg(stdout, sol, false); 658 free_alg(sol); 659 } 660 661 static void 662 help_exec(CommandArgs *args) 663 { 664 if (args->command == NULL) { 665 printf( 666 "Use the nissy command \"help COMMAND\" for a short " 667 "description of a specific command.\n" 668 "Use the nissy command \"commands\" for a list of " 669 "available commands.\n" 670 "See the manual page for more details. The manual" 671 " page is available with \"man nissy\" on a UNIX" 672 " system (such as Linux or MacOS) or in pdf and html" 673 " format in the docs folder.\n" 674 "Nissy is available for free at " 675 "https://github.com/sebastianotronto/nissy\n" 676 ); 677 } else { 678 printf("Command %s: %s\nusage: %s\n", args->command->name, 679 args->command->description, args->command->usage); 680 } 681 } 682 683 static void 684 quit_exec(CommandArgs *args) 685 { 686 exit(0); 687 } 688 689 static void 690 cleanup_exec(CommandArgs *args) 691 { 692 Alg *alg; 693 694 init_moves(); 695 696 alg = cleanup(args->scramble); 697 print_alg(stdout, alg, false); 698 699 free_alg(alg); 700 } 701 702 static void 703 unniss_exec(CommandArgs *args) 704 { 705 Alg *aux; 706 707 aux = unniss(args->scramble); 708 print_alg(stdout, aux, false); 709 free(aux); 710 } 711 712 static void 713 version_exec(CommandArgs *args) 714 { 715 printf(VERSION"\n"); 716 } 717 718 /* Local functions implementation ********************************************/ 719 720 static bool 721 read_scramble(int c, char **v, CommandArgs *args) 722 { 723 int i, k, n; 724 unsigned int j; 725 char *algstr; 726 727 if (c < 1) { 728 fprintf(stderr, "Error: no scramble given?\n"); 729 return false; 730 } 731 732 for(n = 0, i = 0; i < c; i++) 733 n += strlen(v[i]); 734 735 algstr = malloc((n + 1) * sizeof(char)); 736 k = 0; 737 for (i = 0; i < c; i++) 738 for (j = 0; j < strlen(v[i]); j++) 739 algstr[k++] = v[i][j]; 740 algstr[k] = 0; 741 742 args->scramble = new_alg(algstr); 743 free(algstr); 744 745 if (args->scramble->len == 0) 746 fprintf(stderr, "Error reading scramble\n"); 747 748 return args->scramble->len > 0; 749 } 750 751 static bool 752 read_scrtype(CommandArgs *args, char *str) 753 { 754 int i; 755 756 for (i = 0; scrtypes[i] != NULL; i++) { 757 if (!strcmp(scrtypes[i], str)) { 758 strcpy(args->scrtype, scrtypes[i]); 759 return true; 760 } 761 } 762 763 return false; 764 } 765 766 static bool 767 read_step(CommandArgs *args, char *str) 768 { 769 int i; 770 771 for (i = 0; steps[i] != NULL; i++) { 772 if (!strcmp(steps[i]->shortname, str)) { 773 args->step = steps[i]; 774 return true; 775 } 776 } 777 778 return false; 779 } 780 781 /* Public functions implementation *******************************************/ 782 783 void 784 free_args(CommandArgs *args) 785 { 786 if (args == NULL) 787 return; 788 789 if (args->scramble != NULL) 790 free_alg(args->scramble); 791 if (args->opts != NULL) 792 free(args->opts); 793 794 /* step and command must not be freed, they are static! */ 795 796 free(args); 797 } 798 799 CommandArgs * 800 new_args(void) 801 { 802 CommandArgs *args = malloc(sizeof(CommandArgs)); 803 804 args->success = false; 805 args->scrstdin = false; 806 args->scramble = NULL; /* initialized in read_scramble */ 807 args->opts = malloc(sizeof(SolveOptions)); 808 809 /* step and command are static */ 810 args->step = steps[0]; /* default: first step in list */ 811 args->command = NULL; 812 args->pd = NULL; 813 814 return args; 815 }