aboutsummaryrefslogtreecommitdiff
path: root/shell/shell.c
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2024-10-12 10:21:52 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2024-10-12 10:22:50 +0200
commit9b680fadec01789c1fb1317b939c15d186a572c9 (patch)
treed9038076657ec3cf887e5fd2bd14f0155ac25b7e /shell/shell.c
parentc4f64cf2556c0f8597e1fbc19d7c664b8da94612 (diff)
downloadnissy-core-9b680fadec01789c1fb1317b939c15d186a572c9.tar.gz
nissy-core-9b680fadec01789c1fb1317b939c15d186a572c9.zip
Added shelltest
Will add more tests when I feel like it
Diffstat (limited to 'shell/shell.c')
-rw-r--r--shell/shell.c752
1 files changed, 752 insertions, 0 deletions
diff --git a/shell/shell.c b/shell/shell.c
new file mode 100644
index 0000000..de03656
--- /dev/null
+++ b/shell/shell.c
@@ -0,0 +1,752 @@
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
40typedef 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 int8_t minmoves;
54 int8_t maxmoves;
55 int8_t optimal;
56 int64_t maxsolutions;
57} args_t;
58
59static int64_t compose_exec(args_t *);
60static int64_t inverse_exec(args_t *);
61static int64_t applymoves_exec(args_t *);
62static int64_t applytrans_exec(args_t *);
63static int64_t frommoves_exec(args_t *);
64static int64_t convert_exec(args_t *);
65static int64_t randomcube_exec(args_t *);
66static int64_t datasize_exec(args_t *);
67static int64_t gendata_exec(args_t *);
68static int64_t solve_exec(args_t *);
69static int64_t solve_scramble_exec(args_t *);
70static int64_t help_exec(args_t *);
71
72static int parse_args(int, char **, args_t *);
73static bool parse_int8(char *, int8_t *);
74static bool parse_int64(char *, int64_t *);
75
76static bool set_cube(int, char **, args_t *);
77static bool set_cube_perm(int, char **, args_t *);
78static bool set_str_command(int, char **, args_t *);
79static bool set_str_cube(int, char **, args_t *);
80static bool set_str_format(int, char **, args_t *);
81static bool set_str_format_in(int, char **, args_t *);
82static bool set_str_format_out(int, char **, args_t *);
83static bool set_str_moves(int, char **, args_t *);
84static bool set_str_trans(int, char **, args_t *);
85static bool set_str_solver(int, char **, args_t *);
86static bool set_str_nisstype(int, char **, args_t *);
87static bool set_minmoves(int, char **, args_t *);
88static bool set_maxmoves(int, char **, args_t *);
89static bool set_optimal(int, char **, args_t *);
90static bool set_maxsolutions(int, char **, args_t *);
91static bool set_id(int, char **, args_t *);
92
93static uint64_t rand64(void);
94
95#define OPTION(N, A, S) { .name = N, .nargs = A, .set = S }
96struct {
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 }
120struct {
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_CUBE " CUBE",
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 "help",
214 "help [" FLAG_COMMAND " COMMAND]",
215 "If no COMMAND is specified, prints some generic information "
216 "and the list of commands. Otherwise it prints detailed "
217 "information about the specified COMMAND.",
218 help_exec
219 ),
220 COMMAND(NULL, NULL, NULL, NULL)
221};
222
223char *tablepaths[] = {
224 "tables/",
225 "",
226 NULL
227};
228
229static uint64_t
230rand64(void)
231{
232 uint64_t i, ret;
233
234 for (i = 0, ret = 0; i < 64; i++)
235 ret |= (uint64_t)(rand() % 2) << i;
236
237 return ret;
238}
239
240static int64_t
241compose_exec(args_t *args)
242{
243 char result[22];
244 int64_t ret;
245
246 ret = nissy_compose(args->cube, args->cube_perm, result);
247 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
248 printf("%s\n", result);
249
250 return ret;
251}
252
253static int64_t
254inverse_exec(args_t *args)
255{
256 char result[22];
257 int64_t ret;
258
259 ret = nissy_inverse(args->cube, result);
260 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
261 printf("%s\n", result);
262
263 return ret;
264}
265
266static int64_t
267applymoves_exec(args_t *args)
268{
269 char result[22];
270 int64_t ret;
271
272 ret = nissy_applymoves(args->cube, args->str_moves, result);
273 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
274 printf("%s\n", result);
275
276 return ret;
277}
278
279static int64_t
280applytrans_exec(args_t *args)
281{
282 char result[22];
283 int64_t ret;
284
285 ret = nissy_applytrans(args->cube, args->str_trans, result);
286 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
287 printf("%s\n", result);
288
289 return ret;
290}
291
292static int64_t
293frommoves_exec(args_t *args)
294{
295 char result[22];
296 int64_t ret;
297
298 ret = nissy_frommoves(args->str_moves, result);
299 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
300 printf("%s\n", result);
301
302 return ret;
303}
304
305static int64_t
306convert_exec(args_t *args)
307{
308 char result[PRINTCUBE_BUFFER_SIZE];
309 int64_t ret;
310
311 ret = nissy_convert(args->str_format_in, args->str_format_out,
312 args->str_cube, PRINTCUBE_BUFFER_SIZE, result);
313 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
314 printf("%s\n", result);
315
316 return ret;
317}
318
319static int64_t
320randomcube_exec(args_t *args)
321{
322 char result[PRINTCUBE_BUFFER_SIZE];
323 int64_t ret, ep, eo, cp, co;
324
325 ep = rand64();
326 eo = rand64();
327 cp = rand64();
328 co = rand64();
329 ret = nissy_getcube(ep, eo, cp, co, "fix", result);
330 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
331 printf("%s\n", result);
332
333 return ret;
334}
335
336static int64_t
337datasize_exec(args_t *args)
338{
339 int64_t ret;
340
341 ret = nissy_datasize(args->str_solver);
342 if (ret < 0)
343 fprintf(stderr, "Unknown error (make sure solver is valid)\n");
344 printf("%" PRId64 "\n", ret);
345
346 return ret;
347}
348
349static int64_t
350gendata_exec(args_t *args)
351{
352 int i;
353 FILE *file;
354 char *buf, path[MAX_PATH_LENGTH];
355 int64_t ret, size;
356 size_t written;
357
358 /* TODO: should give warning if overwriting existing file */
359 for (i = 0; tablepaths[i] != NULL; i++) {
360 strcpy(path, tablepaths[i]);
361 strcat(path, args->str_solver);
362 file = fopen(path, "wb");
363 if (file != NULL)
364 break;
365 }
366
367 if (tablepaths[i] == NULL) {
368 fprintf(stderr, "Cannot write data to file\n");
369 fclose(file);
370 return -2;
371 }
372
373 size = nissy_datasize(args->str_solver);
374
375 if (size < 0) {
376 fprintf(stderr,
377 "Unknown error in retrieving data size"
378 "(make sure solver is valid)\n");
379 fclose(file);
380 return -3;
381 }
382
383 buf = malloc(size);
384
385 ret = nissy_gendata(args->str_solver, size, buf);
386 if (ret < 0) {
387 fprintf(stderr, "Unknown error in generating data\n");
388 fclose(file);
389 free(buf);
390 return -4;
391 }
392 if (ret != size) {
393 fprintf(stderr, "Unknown error: unexpected data size "
394 "got %" PRId64 ", expected %" PRId64 ")\n", ret, size);
395 fclose(file);
396 free(buf);
397 return -5;
398 }
399
400 written = fwrite(buf, size, 1, file);
401 fclose(file);
402 free(buf);
403
404 if (written != 1) {
405 fprintf(stderr,
406 "Error: data was generated correctly, but could not be "
407 "written to file (generated %" PRId64 " bytes, written "
408 "%zu)\n", size, written);
409 return -6;
410 }
411
412 fprintf(stderr, "Data written to %s\n", path);
413
414 return 0;
415}
416
417static int64_t
418solve_exec(args_t *args)
419{
420 int i;
421 uint8_t nissflag;
422 FILE *file;
423 char *buf, solutions[SOLUTIONS_BUFFER_SIZE], path[MAX_PATH_LENGTH];
424 int64_t ret, gendata_ret, size;
425 size_t read;
426
427 nissflag = NISSY_NISSFLAG_NORMAL; /* TODO: parse str_nisstype */
428
429 for (i = 0; tablepaths[i] != NULL; i++) {
430 strcpy(path, tablepaths[i]);
431 strcat(path, args->str_solver);
432 file = fopen(path, "rb");
433 if (file != NULL)
434 break;
435 }
436
437 if (tablepaths[i] == NULL) {
438 fprintf(stderr,
439 "Cannot read data file, "
440 "generating it (this can take a while)\n");
441 gendata_ret = gendata_exec(args);
442 if (gendata_ret)
443 return gendata_ret;
444 }
445
446 /* Ugh, this is not elegant TODO */
447 if (file == NULL) {
448 for (i = 0; tablepaths[i] != NULL; i++) {
449 strcpy(path, tablepaths[i]);
450 strcat(path, args->str_solver);
451 file = fopen(path, "rb");
452 if (file != NULL)
453 break;
454 }
455 }
456
457 if (tablepaths[i] == NULL) {
458 fprintf(stderr, "Error: data file not found\n");
459 fclose(file);
460 return -1;
461 }
462
463 size = nissy_datasize(args->str_solver);
464 buf = malloc(size);
465 read = fread(buf, size, 1, file);
466 fclose(file);
467 if (read != 1) {
468 fprintf(stderr, "Error reading data from file: "
469 "fread() returned %zu instead of 1 when attempting to"
470 "read %" PRId64 " bytes from file %s\n", read, size, path);
471 return -2;
472 }
473
474 ret = nissy_solve(
475 args->cube, args->str_solver, nissflag, args->minmoves,
476 args->maxmoves, args->maxsolutions, args->optimal,
477 size, buf, SOLUTIONS_BUFFER_SIZE, solutions);
478
479 free(buf);
480
481 if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE)
482 fprintf(stderr, "No solutions found\n");
483 else
484 printf("%s", solutions);
485
486 return 0;
487}
488
489static int64_t
490solve_scramble_exec(args_t *args)
491{
492 nissy_frommoves(args->str_moves, args->cube);
493
494 return solve_exec(args);
495}
496
497static int64_t
498help_exec(args_t *args)
499{
500 int i;
501
502 if (args->str_command == NULL || args->str_command[0] == '\0') {
503 printf("This is a rudimentary shell for the H48 library.\n");
504 printf("Available commands and usage:\n\n");
505 for (i = 0; commands[i].name != NULL; i++)
506 printf("%-15s%s\n", commands[i].name, commands[i].syn);
507 printf("\nUse 'help -command COMMAND' for more information.\n");
508 } else {
509 for (i = 0; commands[i].name != NULL; i++)
510 if (!strcmp(args->str_command, commands[i].name))
511 break;
512 if (commands[i].name == NULL) {
513 printf("Unknown command %s\n", args->str_command);
514 return 1;
515 }
516 printf("Command %s\n\n", commands[i].name);
517 printf("Synopsis: %s\n\n", commands[i].syn);
518 printf("Description: %s\n", commands[i].desc);
519 }
520
521 return 0;
522}
523
524static int
525parse_args(int argc, char **argv, args_t *args)
526{
527 int i, j, n;
528
529 *args = (args_t) {
530 .command_index = -1,
531 .cube = "",
532 .cube_perm = "",
533 .str_cube = "",
534 .str_format = "",
535 .str_format_in = "",
536 .str_format_out = "",
537 .str_moves = "",
538 .str_trans = "",
539 .str_solver = "",
540 .str_nisstype = "",
541 .minmoves = 0,
542 .maxmoves = 20,
543 .optimal = -1,
544 .maxsolutions = 1,
545 };
546
547 if (argc == 0) {
548 printf("No command given\n");
549 return 1;
550 }
551
552 for (i = 0; commands[i].name != NULL; i++) {
553 if (!strcmp(argv[0], commands[i].name)) {
554 args->command_index = i;
555 break;
556 }
557 }
558
559 if (commands[i].name == NULL) {
560 fprintf(stderr, "Unknown command %s\n", argv[0]);
561 return 1;
562 }
563
564 for (i = 1; i < argc; i++) {
565 for (j = 0; options[j].name != NULL; j++) {
566 n = argc - i - 1;
567 if (strcmp(argv[i], options[j].name))
568 continue;
569 if (n < options[j].nargs) {
570 fprintf(stderr,
571 "Too few arguments for option %s\n",
572 options[j].name);
573 return 1;
574 }
575 if (!options[j].set(n, argv+i+1, args)) {
576 fprintf(stderr,
577 "Error parsing arguments for option %s\n",
578 options[j].name);
579 return 1;
580 }
581 i += options[j].nargs;
582 break;
583 }
584 if (options[j].name == NULL) {
585 fprintf(stderr, "Unknown option %s\n", argv[i]);
586 return 1;
587 }
588 }
589
590 return 0;
591}
592
593bool
594parse_int8(char *argv, int8_t *result)
595{
596 bool noerror;
597 int64_t n;
598
599 noerror = parse_int64(argv, &n);
600 *result = (int8_t)n;
601
602 return noerror && n >= INT8_MIN && n <= INT8_MAX;
603}
604
605bool
606parse_int64(char *argv, int64_t *result)
607{
608 *result = strtoll(argv, NULL, 10);
609
610 /* TODO: figure out how errno works and use it */
611 return true;
612}
613
614static bool
615set_cube(int argc, char **argv, args_t *args)
616{
617 memcpy(args->cube, argv[0], 22);
618 args->cube[21] = 0;
619
620 return true;
621}
622
623static bool
624set_cube_perm(int argc, char **argv, args_t *args)
625{
626 memcpy(args->cube_perm, argv[0], 22);
627 args->cube_perm[21] = 0;
628
629 return true;
630}
631
632static bool
633set_str_command(int argc, char **argv, args_t *args)
634{
635 args->str_command = argv[0];
636
637 return true;
638}
639
640static bool
641set_str_cube(int argc, char **argv, args_t *args)
642{
643 args->str_cube = argv[0];
644
645 return true;
646}
647
648static bool
649set_str_format(int argc, char **argv, args_t *args)
650{
651 args->str_format = argv[0];
652
653 return true;
654}
655
656static bool
657set_str_format_in(int argc, char **argv, args_t *args)
658{
659 args->str_format_in = argv[0];
660
661 return true;
662}
663
664static bool
665set_str_format_out(int argc, char **argv, args_t *args)
666{
667 args->str_format_out = argv[0];
668
669 return true;
670}
671
672static bool
673set_str_moves(int argc, char **argv, args_t *args)
674{
675 args->str_moves = argv[0];
676
677 return true;
678}
679
680static bool
681set_str_trans(int argc, char **argv, args_t *args)
682{
683 args->str_trans = argv[0];
684
685 return true;
686}
687
688static bool
689set_str_solver(int argc, char **argv, args_t *args)
690{
691 args->str_solver = argv[0];
692
693 return true;
694}
695
696static bool
697set_str_nisstype(int argc, char **argv, args_t *args)
698{
699 args->str_nisstype = argv[0];
700
701 return true;
702}
703
704static bool
705set_minmoves(int argc, char **argv, args_t *args)
706{
707 return parse_int8(argv[0], &args->minmoves);
708}
709
710static bool
711set_maxmoves(int argc, char **argv, args_t *args)
712{
713 return parse_int8(argv[0], &args->maxmoves);
714}
715
716static bool
717set_optimal(int argc, char **argv, args_t *args)
718{
719 return parse_int8(argv[0], &args->optimal);
720}
721
722static bool
723set_maxsolutions(int argc, char **argv, args_t *args)
724{
725 return parse_int64(argv[0], &args->maxsolutions);
726}
727
728void
729log_stderr(const char *str, ...)
730{
731 va_list args;
732
733 va_start(args, str);
734 vfprintf(stderr, str, args);
735 va_end(args);
736}
737
738int
739main(int argc, char **argv)
740{
741 int parse_error;
742 args_t args;
743
744 srand(time(NULL));
745 nissy_setlogger(log_stderr);
746
747 parse_error = parse_args(argc-1, argv+1, &args);
748 if (parse_error)
749 return parse_error;
750
751 return (int)commands[args.command_index].exec(&args);
752}

Generated with cgit - Back to sebastiano.tronto.net