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 }