diff options
Diffstat (limited to '')
| -rw-r--r-- | README.md | 215 | ||||
| -rw-r--r-- | TODO.txt | 30 | ||||
| -rw-r--r-- | shell.c | 212 | ||||
| -rw-r--r-- | tools/001_gendata_h48/gendata_h48.c (renamed from tools/gendata_h48/gendata_h48.c) | 0 | ||||
| -rw-r--r-- | tools/002_stats_tables_h48/stats_tables_h48.c (renamed from tools/stats_tables_h48/stats_tables_h48.c) | 0 | ||||
| -rw-r--r-- | utils/cube_h_with_format_documentation | 147 |
6 files changed, 399 insertions, 205 deletions
| @@ -1,46 +1,229 @@ | |||
| 1 | # Prototype for a new optimal solver | 1 | # H48: prototype for a new optimal solver and nissy backend |
| 2 | 2 | ||
| 3 | Work in progress. Everything is in a state of flux and can change without | 3 | **Warning**: this library is work in progress, breaking changes can |
| 4 | notice. | 4 | happen without notice. |
| 5 | 5 | ||
| 6 | ## Building and running tests | 6 | H48 is an experimental Rubik's cube solver. The main goal is experimenting |
| 7 | with various optimal solving methods and pruning tables, with | ||
| 8 | [nxopt](https://github.com/rokicki/cube20src/blob/master/nxopt.md) and | ||
| 9 | [vcube](https://github.com/Voltara/vcube) as inspiration and benchmark | ||
| 10 | reference. | ||
| 7 | 11 | ||
| 8 | First run | 12 | In the future this project may evolve as a new "back-end" for the classic |
| 13 | [nissy](https://github.com/sebastianotronto/nissy-classic). | ||
| 14 | |||
| 15 | ## Building | ||
| 16 | |||
| 17 | First run the configuration script to detect the system | ||
| 18 | configuration. This is going to select a C compiler and | ||
| 19 | architecture-specific optimizations. | ||
| 9 | 20 | ||
| 10 | ``` | 21 | ``` |
| 11 | $ ./configure.sh | 22 | $ ./configure.sh |
| 12 | ``` | 23 | ``` |
| 13 | 24 | ||
| 14 | or `TYPE=AVX2 ./configure.sh` if you want to use AVX2 instructions. | 25 | These settings can be overridden, for example: |
| 26 | |||
| 27 | ``` | ||
| 28 | $ CC=clang ./configure.sh # Force use of clang instead of default cc | ||
| 29 | ``` | ||
| 30 | |||
| 31 | The support for ARM-specific optimizations (NEON instructions) is | ||
| 32 | incomplete. To compile correctly on these processors (e.g. Mac M1/M2/M3) | ||
| 33 | you need to manually disable optimizations: | ||
| 34 | |||
| 35 | ``` | ||
| 36 | $ TYPE="" ./configure.sh # Can be combined with CC=... | ||
| 37 | ``` | ||
| 38 | |||
| 39 | Once the configuration is done, you can build with make | ||
| 15 | 40 | ||
| 16 | Then | 41 | ``` |
| 42 | $ make | ||
| 43 | ``` | ||
| 44 | |||
| 45 | ## Running tests | ||
| 46 | |||
| 47 | This project includes a suite of "unit" test. They can be run with: | ||
| 17 | 48 | ||
| 18 | ``` | 49 | ``` |
| 19 | $ make test | 50 | $ make test |
| 20 | ``` | 51 | ``` |
| 21 | 52 | ||
| 22 | to run the tests. You can also run only the tests that match a chosen | 53 | To run only a subset of the tests, set the `TEST` variable to a regular |
| 23 | regex, for example: | 54 | expression that matches only the name of the tests you want to run: |
| 24 | 55 | ||
| 25 | ``` | 56 | ``` |
| 26 | $ TEST=coord make test | 57 | $ TEST=coord make test |
| 27 | ``` | 58 | ``` |
| 28 | 59 | ||
| 29 | Due to ongoing changes, benchmarks are currently broken. | 60 | Each subfolder of the test folder contains a test. A test can consist |
| 61 | of multiple test cases (.in files). Running a test means compiling and | ||
| 62 | running the corresponding test against each test case. When a test case | ||
| 63 | is run, the .in file is read a the output of the program is compared | ||
| 64 | to the corresponding .out filei using diff(1). If the two differ, the | ||
| 65 | difference is printed out and no other test is run. | ||
| 66 | |||
| 67 | The results of the last test case run is saved in test/last.out (standard | ||
| 68 | output, the results compared with the .out files) and test/last.err | ||
| 69 | (standard error). | ||
| 70 | |||
| 71 | Tests are always run in "debug mode": this means that optimizations are | ||
| 72 | disabled and some extra logging is enabled. | ||
| 73 | |||
| 74 | See the test folder and test/test.sh for details. | ||
| 75 | |||
| 76 | ## Running "tools" | ||
| 77 | |||
| 78 | In the tools folder there are some small programs that test various | ||
| 79 | functionality of the H48 library. They work similarly to test, but they | ||
| 80 | are not run in debug mode. | ||
| 81 | |||
| 82 | To run a tool you must select it with the environment variable `TOOL`. | ||
| 83 | For example the command: | ||
| 84 | |||
| 85 | ``` | ||
| 86 | TOOL=stats make tool | ||
| 87 | ``` | ||
| 88 | |||
| 89 | Will run the stats_tables_h48 tool. Like for tests, the value of the | ||
| 90 | `TOOL` variable can be any regular expression matching the name of the | ||
| 91 | tool. Unlike tests, one and only one tool will be selected for each run. | ||
| 92 | |||
| 93 | Each tool run is automatically timed, so these tools can be used as | ||
| 94 | benchmark. The output as well as the time of the run are saved to a | ||
| 95 | file in the tools/results folder. | ||
| 30 | 96 | ||
| 31 | ## Solving | 97 | ## Running commands manually |
| 32 | 98 | ||
| 33 | Notes for myself while this is work in progress | 99 | This project also includes a rudimentary shell that can be used to run |
| 100 | commands manually. To build the shell use: | ||
| 34 | 101 | ||
| 35 | ``` | 102 | ``` |
| 36 | $ make shell | 103 | $ make shell |
| 37 | $ ./run frommoves -moves (scramble) | ||
| 38 | ``` | 104 | ``` |
| 39 | 105 | ||
| 40 | copy the result, then | 106 | This will create an executable called `run`. Then you can for example |
| 107 | get a cube from a sequence of moves: | ||
| 108 | |||
| 109 | ``` | ||
| 110 | $ ./run frommoves -moves "R' U' F" | ||
| 111 | JLQWSVUH=ZLCUABGIVTKH | ||
| 112 | ``` | ||
| 113 | |||
| 114 | Or you can get a random cube | ||
| 115 | |||
| 116 | ``` | ||
| 117 | $ ./run randomcube | ||
| 118 | WDSQREVX=VBKYDUCJXWAb | ||
| 119 | ``` | ||
| 120 | |||
| 121 | If you don't like this format, you can convert it: | ||
| 122 | |||
| 123 | ``` | ||
| 124 | $ ./run convert -fin B32 -fout H48 -cubestr "WDSQREVX=VBKYDUCJXWAb" | ||
| 125 | UL1 UB0 BL0 FR1 DF0 UR1 DB0 FL0 DR1 DL1 UF0 BR1 DFR2 DBR0 DFL2 UFR2 UBL2 UFL0 UBR2 DBL2 | ||
| 126 | ``` | ||
| 127 | |||
| 128 | To solve a cube (experimental) you can use: | ||
| 41 | 129 | ||
| 42 | ``` | 130 | ``` |
| 43 | $ ./run solve -solver "H48" -options "2;20" -n 1 -M 10 -cube (paste here) | 131 | $ ./run solve -solver "h48" -options "0;20" -n 1 -M 4 -cube "JLQWSVUH=ZLCUABGIVTKH" |
| 132 | Found 0 solutions, searching at depth 0 | ||
| 133 | Found 0 solutions, searching at depth 1 | ||
| 134 | Found 0 solutions, searching at depth 2 | ||
| 135 | Found 0 solutions, searching at depth 3 | ||
| 136 | Solution found: F' U R | ||
| 137 | F' U R | ||
| 44 | ``` | 138 | ``` |
| 45 | 139 | ||
| 46 | Options can be changed from `2;20` to `n;20` for larger tables. | 140 | For a full list of available command, use `run help`. |
| 141 | |||
| 142 | ## Cube formats | ||
| 143 | |||
| 144 | The cube is represented as a string in one of the following formats, | ||
| 145 | all explained below: | ||
| 146 | |||
| 147 | * H48 | ||
| 148 | * LST | ||
| 149 | * B32 (the default) | ||
| 150 | |||
| 151 | In all of the formats, the permutation of the center pieces is not | ||
| 152 | stored. This means that the cube is assumed to be in a fixed orientation. | ||
| 153 | |||
| 154 | More formats will become available in the future. | ||
| 155 | |||
| 156 | ### Cube format: H48 | ||
| 157 | |||
| 158 | In the H48 format, each edge is represented by two letters denoting the | ||
| 159 | sides it belongs to and one number denoting its orientation (0 oriented, 1 | ||
| 160 | mis-oriented). Similarly, each corner is represented by three letters and | ||
| 161 | a number (0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise). | ||
| 162 | |||
| 163 | The solved cube looks like this: | ||
| 164 | |||
| 165 | ``` | ||
| 166 | UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0 | ||
| 167 | ``` | ||
| 168 | |||
| 169 | The cube after the move F looks like this: | ||
| 170 | |||
| 171 | ``` | ||
| 172 | FL1 UB0 DB0 FR1 UR0 UL0 DL0 DR0 UF1 DF1 BL0 BR0 UFL1 UBL0 DFR1 DBR0 DFL2 UBR0 UFR2 DBL0 | ||
| 173 | ``` | ||
| 174 | |||
| 175 | Whitespace (including newlines) between pieces is ignored when reading the | ||
| 176 | cube. A single whitespace character is added between pieces when writing. | ||
| 177 | |||
| 178 | You can find more examples of this format in the utils/cubes folder. | ||
| 179 | |||
| 180 | ## Cube format: LST | ||
| 181 | |||
| 182 | In the LST format, a cube is represented by a comma-separated list of | ||
| 183 | integers. Each piece is represented by an (unsigned) 8-bit integer. The 4 | ||
| 184 | least-significant bits determine which piece it is, the other 4 determine | ||
| 185 | the orientation. | ||
| 186 | |||
| 187 | Edges are numbered as follows (see also constants.h): | ||
| 188 | |||
| 189 | UF=0 UB=1 DB=2 DF=3 UR=4 UL=5 DL=6 DR=7 FR=8 FL=9 BL=10 BR=11 | ||
| 190 | |||
| 191 | Corners are numbered as follows: | ||
| 192 | |||
| 193 | UFR=0 UBL=1 DFL=2 DBR=3 UFL=4 UBR=5 DFR=6 DBL=7 | ||
| 194 | |||
| 195 | The orientation of the edges is with respect to F/B, the orientation of | ||
| 196 | corners is with respect to U/D. | ||
| 197 | |||
| 198 | In this format, the solved cube looks like this: | ||
| 199 | |||
| 200 | ``` | ||
| 201 | 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 | ||
| 202 | ``` | ||
| 203 | |||
| 204 | The cube after the move F looks like this: | ||
| 205 | |||
| 206 | ``` | ||
| 207 | 36, 1, 38, 3, 66, 5, 64, 7, 25, 1, 2, 24, 4, 5, 6, 7, 16, 19, 10, 11 | ||
| 208 | ``` | ||
| 209 | |||
| 210 | ### Cube format: B32 | ||
| 211 | |||
| 212 | This format is a "base 32" encoding of the cube. It is not meant to be | ||
| 213 | human-readable, but it is compact while still being plain text. Each | ||
| 214 | piece, including the orientation value, is encoded as a number from 0 | ||
| 215 | to 31, and this number is then converted to an uppercase letter (0-26) | ||
| 216 | or to a lowercase letter (27-31). Edges and corners are separated by a | ||
| 217 | single = character. | ||
| 218 | |||
| 219 | In this format, the solved cube looks like this: | ||
| 220 | |||
| 221 | ``` | ||
| 222 | ABCDEFGH=ABCDEFGHIJKL | ||
| 223 | ``` | ||
| 224 | |||
| 225 | The cube after the move F looks like this: | ||
| 226 | |||
| 227 | ``` | ||
| 228 | MBODSFQH=ZBCYEFGHQTKL | ||
| 229 | ``` | ||
| @@ -1,7 +1,19 @@ | |||
| 1 | H48 table generation | 1 | # Documentation and cleanup |
| 2 | - fix gendata tool | 2 | |
| 3 | - use to test generation of full h0k4 table | 3 | - Add comments to cube.h |
| 4 | - remove ifdef and old code | 4 | - one for each function? |
| 5 | - Internal documentation | ||
| 6 | - h48 solver and co | ||
| 7 | - what is h and k | ||
| 8 | - cube representation and base routines? | ||
| 9 | - Split files | ||
| 10 | - h48 into: | ||
| 11 | - h48_base (coordinate computation) | ||
| 12 | - h48_map (only the hashmap thing) | ||
| 13 | - h48_gendata (table generation) | ||
| 14 | - h48_solve (including stats solver) | ||
| 15 | |||
| 16 | # H48 table generation | ||
| 5 | - compute all tables for h<11 | 17 | - compute all tables for h<11 |
| 6 | x compute visited up to a fixed depth 8 | 18 | x compute visited up to a fixed depth 8 |
| 7 | - compute additional step (if needed) to fill <=base | 19 | - compute additional step (if needed) to fill <=base |
| @@ -10,17 +22,21 @@ H48 table generation | |||
| 10 | - compute table for h=11 | 22 | - compute table for h=11 |
| 11 | - can it be unified to the other computation, or is it much better | 23 | - can it be unified to the other computation, or is it much better |
| 12 | to do it ad hoc? | 24 | to do it ad hoc? |
| 25 | - derive small tables from the large one to check correctness | ||
| 26 | - this requires too much ram, but I can print the summary of the table | ||
| 13 | - Add long-running test for h0k4 (maybe as a tool?) | 27 | - Add long-running test for h0k4 (maybe as a tool?) |
| 14 | - tests for other sizes? | 28 | - tests for other sizes? |
| 29 | - gendata tool: save tables? | ||
| 15 | - optimize | 30 | - optimize |
| 16 | - use only transform_edges (need compose trans) | 31 | - use only transform_edges (need compose trans) |
| 17 | - parallelize with pthread | 32 | - parallelize with pthread |
| 18 | 33 | ||
| 19 | (OLD: | 34 | # Solver (Enrico) |
| 20 | - Fails for UFRUFU, try command | 35 | |
| 36 | - Add a solver for h=0 | ||
| 37 | - check if this (or equivalent) works: | ||
| 21 | ./run solve -solver H48 -options "2;20" -n 1 -M 10 -cube \ | 38 | ./run solve -solver H48 -options "2;20" -n 1 -M 10 -cube \ |
| 22 | "$(./run frommoves -moves "UFRUFU")" | 39 | "$(./run frommoves -moves "UFRUFU")" |
| 23 | ) | ||
| 24 | 40 | ||
| 25 | table base for k=2 (4 most common values start at) | 41 | table base for k=2 (4 most common values start at) |
| 26 | 0 8 | 42 | 0 8 |
| @@ -13,10 +13,36 @@ | |||
| 13 | #define SOLUTIONS_BUFFER_SIZE 500000 /* Should be enough */ | 13 | #define SOLUTIONS_BUFFER_SIZE 500000 /* Should be enough */ |
| 14 | #define MAX_PATH_LENGTH 10000 /* Should be enough */ | 14 | #define MAX_PATH_LENGTH 10000 /* Should be enough */ |
| 15 | 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_options "-options" | ||
| 27 | #define _flag_nisstype "-nisstype" | ||
| 28 | #define _flag_minmoves "-m" | ||
| 29 | #define _flag_maxmoves "-M" | ||
| 30 | #define _flag_optimal "-O" | ||
| 31 | #define _flag_maxsolutions "-n" | ||
| 32 | |||
| 33 | #define _info_cubeformat(cube) cube " must be given in B32 format." | ||
| 34 | #define _info_movesformat "The accepted moves are U, D, R, L, F and B, " \ | ||
| 35 | "optionally followed by a 2, a ' or a 3." | ||
| 36 | #define _info_transformat "The transformation must be given in the format " \ | ||
| 37 | "(rotation|mirrored) (2 letters), for exmple " \ | ||
| 38 | "'rotation UF' or 'mirrored BL'." | ||
| 39 | #define _info_formats "The available formats are H48, B32 and SRC." | ||
| 40 | |||
| 16 | typedef struct { | 41 | typedef struct { |
| 17 | int command_index; | 42 | int command_index; |
| 18 | char cube[22]; | 43 | char cube[22]; |
| 19 | char cube_perm[22]; | 44 | char cube_perm[22]; |
| 45 | char *str_command; | ||
| 20 | char *str_cube; | 46 | char *str_cube; |
| 21 | char *str_format; | 47 | char *str_format; |
| 22 | char *str_format_in; | 48 | char *str_format_in; |
| @@ -45,6 +71,7 @@ static int64_t randomcube_exec(args_t *); | |||
| 45 | static int64_t datasize_exec(args_t *); | 71 | static int64_t datasize_exec(args_t *); |
| 46 | static int64_t gendata_exec(args_t *); | 72 | static int64_t gendata_exec(args_t *); |
| 47 | static int64_t solve_exec(args_t *); | 73 | static int64_t solve_exec(args_t *); |
| 74 | static int64_t help_exec(args_t *); | ||
| 48 | 75 | ||
| 49 | static int parse_args(int, char **, args_t *); | 76 | static int parse_args(int, char **, args_t *); |
| 50 | static bool parse_int8(char *, int8_t *); | 77 | static bool parse_int8(char *, int8_t *); |
| @@ -52,6 +79,7 @@ static bool parse_int64(char *, int64_t *); | |||
| 52 | 79 | ||
| 53 | static bool set_cube(int, char **, args_t *); | 80 | static bool set_cube(int, char **, args_t *); |
| 54 | static bool set_cube_perm(int, char **, args_t *); | 81 | static bool set_cube_perm(int, char **, args_t *); |
| 82 | static bool set_str_command(int, char **, args_t *); | ||
| 55 | static bool set_str_cube(int, char **, args_t *); | 83 | static bool set_str_cube(int, char **, args_t *); |
| 56 | static bool set_str_format(int, char **, args_t *); | 84 | static bool set_str_format(int, char **, args_t *); |
| 57 | static bool set_str_format_in(int, char **, args_t *); | 85 | static bool set_str_format_in(int, char **, args_t *); |
| @@ -68,24 +96,6 @@ static bool set_maxsolutions(int, char **, args_t *); | |||
| 68 | static bool set_id(int, char **, args_t *); | 96 | static bool set_id(int, char **, args_t *); |
| 69 | 97 | ||
| 70 | static uint64_t rand64(void); | 98 | static uint64_t rand64(void); |
| 71 | |||
| 72 | #define COMMAND(N, E) { .name = N, .exec = E } | ||
| 73 | struct { | ||
| 74 | char *name; | ||
| 75 | int64_t (*exec)(args_t *); | ||
| 76 | } commands[] = { | ||
| 77 | COMMAND("compose", compose_exec), | ||
| 78 | COMMAND("inverse", inverse_exec), | ||
| 79 | COMMAND("applymoves", applymoves_exec), | ||
| 80 | COMMAND("applytrans", applytrans_exec), | ||
| 81 | COMMAND("frommoves", frommoves_exec), | ||
| 82 | COMMAND("convert", convert_exec), | ||
| 83 | COMMAND("randomcube", randomcube_exec), | ||
| 84 | COMMAND("datasize", datasize_exec), | ||
| 85 | COMMAND("gendata", gendata_exec), | ||
| 86 | COMMAND("solve", solve_exec), | ||
| 87 | COMMAND(NULL, NULL) | ||
| 88 | }; | ||
| 89 | 99 | ||
| 90 | #define OPTION(N, A, S) { .name = N, .nargs = A, .set = S } | 100 | #define OPTION(N, A, S) { .name = N, .nargs = A, .set = S } |
| 91 | struct { | 101 | struct { |
| @@ -93,23 +103,118 @@ struct { | |||
| 93 | int nargs; | 103 | int nargs; |
| 94 | bool (*set)(int, char **, args_t *); | 104 | bool (*set)(int, char **, args_t *); |
| 95 | } options[] = { | 105 | } options[] = { |
| 96 | OPTION("-cube", 1, set_cube), | 106 | OPTION(_flag_cube, 1, set_cube), |
| 97 | OPTION("-perm", 1, set_cube_perm), | 107 | OPTION(_flag_perm, 1, set_cube_perm), |
| 98 | OPTION("-cubestr", 1, set_str_cube), | 108 | OPTION(_flag_command, 1, set_str_command), |
| 99 | OPTION("-format", 1, set_str_format), | 109 | OPTION(_flag_str_cube, 1, set_str_cube), |
| 100 | OPTION("-fin", 1, set_str_format_in), | 110 | OPTION(_flag_format, 1, set_str_format), |
| 101 | OPTION("-fout", 1, set_str_format_out), | 111 | OPTION(_flag_format_in, 1, set_str_format_in), |
| 102 | OPTION("-moves", 1, set_str_moves), | 112 | OPTION(_flag_format_out, 1, set_str_format_out), |
| 103 | OPTION("-trans", 1, set_str_trans), | 113 | OPTION(_flag_moves, 1, set_str_moves), |
| 104 | OPTION("-solver", 1, set_str_solver), | 114 | OPTION(_flag_trans, 1, set_str_trans), |
| 105 | OPTION("-options", 1, set_str_options), /* TODO: remove, use only solver */ | 115 | OPTION(_flag_solver, 1, set_str_solver), |
| 106 | OPTION("-nisstype", 1, set_str_nisstype), /* TODO: remove, use flags */ | 116 | OPTION(_flag_options, 1, set_str_options), /* TODO: remove, use only solver */ |
| 107 | OPTION("-m", 1, set_minmoves), | 117 | OPTION(_flag_nisstype, 1, set_str_nisstype), /* TODO: remove, use flags */ |
| 108 | OPTION("-M", 1, set_maxmoves), | 118 | OPTION(_flag_minmoves, 1, set_minmoves), |
| 109 | OPTION("-O", 1, set_optimal), | 119 | OPTION(_flag_maxmoves, 1, set_maxmoves), |
| 110 | OPTION("-n", 1, set_maxsolutions), | 120 | OPTION(_flag_optimal, 1, set_optimal), |
| 121 | OPTION(_flag_maxsolutions, 1, set_maxsolutions), | ||
| 111 | OPTION(NULL, 0, NULL) | 122 | OPTION(NULL, 0, NULL) |
| 112 | }; | 123 | }; |
| 124 | |||
| 125 | #define COMMAND(N, S, D, E) { .name = N, .syn = S, .desc = D, .exec = E } | ||
| 126 | struct { | ||
| 127 | char *name; | ||
| 128 | char *syn; | ||
| 129 | char *desc; | ||
| 130 | int64_t (*exec)(args_t *); | ||
| 131 | } commands[] = { | ||
| 132 | /* TODO: add synopsis and description here */ | ||
| 133 | COMMAND( | ||
| 134 | "compose", | ||
| 135 | "compose " _flag_cube " CUBE " _flag_perm " PERM", | ||
| 136 | "Apply on CUBE the permutation defined by PERM. " | ||
| 137 | _info_cubeformat("CUBE and PERM"), | ||
| 138 | compose_exec | ||
| 139 | ), | ||
| 140 | COMMAND( | ||
| 141 | "inverse", | ||
| 142 | "inverse " _flag_cube " CUBE ", | ||
| 143 | "Compute the inverse of the given CUBE. " | ||
| 144 | _info_cubeformat("CUBE"), | ||
| 145 | inverse_exec | ||
| 146 | ), | ||
| 147 | COMMAND( | ||
| 148 | "applymoves", | ||
| 149 | "applymoves " _flag_cube " CUBE " _flag_moves " MOVES", | ||
| 150 | "Apply the given MOVES to the given CUBE. " | ||
| 151 | _info_cubeformat("CUBE") " " _info_movesformat, | ||
| 152 | applymoves_exec | ||
| 153 | ), | ||
| 154 | COMMAND( | ||
| 155 | "applytrans", | ||
| 156 | "applytrans " _flag_cube " CUBE " _flag_trans " TRANS", | ||
| 157 | "Apply the single transformation TRANS to the given CUBE. " | ||
| 158 | _info_cubeformat("CUBE") " " _info_transformat, | ||
| 159 | applytrans_exec | ||
| 160 | ), | ||
| 161 | COMMAND( | ||
| 162 | "frommoves", | ||
| 163 | "frommoves " _flag_moves " MOVES", | ||
| 164 | "Return the cube obtained by applying the given MOVES " | ||
| 165 | "to a solved cube. " _info_movesformat, | ||
| 166 | frommoves_exec | ||
| 167 | ), | ||
| 168 | COMMAND( | ||
| 169 | "convert", | ||
| 170 | "convert " _flag_str_cube " CUBESTR " | ||
| 171 | _flag_format_in " FORMAT_IN " _flag_format_out " FORMAT_OUT", | ||
| 172 | "Convert the cube described by CUBESTR from FORMAT_IN to " | ||
| 173 | "FORMAT_OUT." | ||
| 174 | _info_formats " " | ||
| 175 | "CUBESTR must be a valid cube in the FORMAT_IN format.", | ||
| 176 | convert_exec | ||
| 177 | ), | ||
| 178 | COMMAND( | ||
| 179 | "randomcube", | ||
| 180 | "randomcube", | ||
| 181 | "Returns a random cube in B32 format.", | ||
| 182 | randomcube_exec | ||
| 183 | ), | ||
| 184 | COMMAND( | ||
| 185 | "datasize", | ||
| 186 | "datasize" _flag_solver " SOLVER " _flag_options " OPTIONS", | ||
| 187 | "Return the size in bytes of the data table used by " | ||
| 188 | "SOLVER when called with the given OPTIONS.", | ||
| 189 | datasize_exec | ||
| 190 | ), | ||
| 191 | COMMAND( | ||
| 192 | "gendata", | ||
| 193 | "gendata" _flag_solver " SOLVER " _flag_options " OPTIONS", | ||
| 194 | "Generate the data table used by " | ||
| 195 | "SOLVER when called with the given OPTIONS.", | ||
| 196 | gendata_exec | ||
| 197 | ), | ||
| 198 | COMMAND( | ||
| 199 | "solve", | ||
| 200 | "solve" _flag_solver " SOLVER " _flag_options " OPTIONS " | ||
| 201 | "[" _flag_minmoves " n] [" _flag_maxmoves " N] " | ||
| 202 | _flag_cube " CUBE", | ||
| 203 | "Solve the given CUBE using SOLVER with the given OPTIONS, " | ||
| 204 | "using at least n and at most N moves. " | ||
| 205 | _info_cubeformat("CUBE"), | ||
| 206 | solve_exec | ||
| 207 | ), | ||
| 208 | COMMAND( | ||
| 209 | "help", | ||
| 210 | "help [" _flag_command " COMMAND]", | ||
| 211 | "If no COMMAND is specified, prints some generic information " | ||
| 212 | "and the list of commands. Otherwise it prints detailed " | ||
| 213 | "information about the specified COMMAND.", | ||
| 214 | help_exec | ||
| 215 | ), | ||
| 216 | COMMAND(NULL, NULL, NULL, NULL) | ||
| 217 | }; | ||
| 113 | 218 | ||
| 114 | char *tablepaths[] = { | 219 | char *tablepaths[] = { |
| 115 | "tables/", | 220 | "tables/", |
| @@ -397,6 +502,33 @@ solve_exec(args_t *args) | |||
| 397 | return 0; | 502 | return 0; |
| 398 | } | 503 | } |
| 399 | 504 | ||
| 505 | static int64_t | ||
| 506 | help_exec(args_t *args) | ||
| 507 | { | ||
| 508 | int i; | ||
| 509 | |||
| 510 | if (args->str_command == NULL || args->str_command[0] == '\0') { | ||
| 511 | printf("This is a rudimentary shell for the H48 library.\n"); | ||
| 512 | printf("Available commands and usage:\n\n"); | ||
| 513 | for (i = 0; commands[i].name != NULL; i++) | ||
| 514 | printf("%-15s%s\n", commands[i].name, commands[i].syn); | ||
| 515 | printf("\nUse 'help COMMAND' for more information.\n"); | ||
| 516 | } else { | ||
| 517 | for (i = 0; commands[i].name != NULL; i++) | ||
| 518 | if (!strcmp(args->str_command, commands[i].name)) | ||
| 519 | break; | ||
| 520 | if (commands[i].name == NULL) { | ||
| 521 | printf("Unknown command %s\n", args->str_command); | ||
| 522 | return 1; | ||
| 523 | } | ||
| 524 | printf("Command %s\n\n", commands[i].name); | ||
| 525 | printf("Synopsis: %s\n\n", commands[i].syn); | ||
| 526 | printf("Description: %s\n", commands[i].desc); | ||
| 527 | } | ||
| 528 | |||
| 529 | return 0; | ||
| 530 | } | ||
| 531 | |||
| 400 | static int | 532 | static int |
| 401 | parse_args(int argc, char **argv, args_t *args) | 533 | parse_args(int argc, char **argv, args_t *args) |
| 402 | { | 534 | { |
| @@ -507,6 +639,14 @@ set_cube_perm(int argc, char **argv, args_t *args) | |||
| 507 | } | 639 | } |
| 508 | 640 | ||
| 509 | static bool | 641 | static bool |
| 642 | set_str_command(int argc, char **argv, args_t *args) | ||
| 643 | { | ||
| 644 | args->str_command = argv[0]; | ||
| 645 | |||
| 646 | return true; | ||
| 647 | } | ||
| 648 | |||
| 649 | static bool | ||
| 510 | set_str_cube(int argc, char **argv, args_t *args) | 650 | set_str_cube(int argc, char **argv, args_t *args) |
| 511 | { | 651 | { |
| 512 | args->str_cube = argv[0]; | 652 | args->str_cube = argv[0]; |
| @@ -602,7 +742,8 @@ set_maxsolutions(int argc, char **argv, args_t *args) | |||
| 602 | return parse_int64(argv[0], &args->maxsolutions); | 742 | return parse_int64(argv[0], &args->maxsolutions); |
| 603 | } | 743 | } |
| 604 | 744 | ||
| 605 | void log_stderr(const char *str, ...) | 745 | void |
| 746 | log_stderr(const char *str, ...) | ||
| 606 | { | 747 | { |
| 607 | va_list args; | 748 | va_list args; |
| 608 | 749 | ||
| @@ -611,7 +752,8 @@ void log_stderr(const char *str, ...) | |||
| 611 | va_end(args); | 752 | va_end(args); |
| 612 | } | 753 | } |
| 613 | 754 | ||
| 614 | int main(int argc, char **argv) | 755 | int |
| 756 | main(int argc, char **argv) | ||
| 615 | { | 757 | { |
| 616 | int parse_error; | 758 | int parse_error; |
| 617 | args_t args; | 759 | args_t args; |
diff --git a/tools/gendata_h48/gendata_h48.c b/tools/001_gendata_h48/gendata_h48.c index 0456616..0456616 100644 --- a/tools/gendata_h48/gendata_h48.c +++ b/tools/001_gendata_h48/gendata_h48.c | |||
diff --git a/tools/stats_tables_h48/stats_tables_h48.c b/tools/002_stats_tables_h48/stats_tables_h48.c index 7df396a..7df396a 100644 --- a/tools/stats_tables_h48/stats_tables_h48.c +++ b/tools/002_stats_tables_h48/stats_tables_h48.c | |||
diff --git a/utils/cube_h_with_format_documentation b/utils/cube_h_with_format_documentation deleted file mode 100644 index 3cbc3b5..0000000 --- a/utils/cube_h_with_format_documentation +++ /dev/null | |||
| @@ -1,147 +0,0 @@ | |||
| 1 | /****************************************************************************** | ||
| 2 | Cube type definition | ||
| 3 | |||
| 4 | Each piece is represented by an (unsigned) 8-bit integer. The 4 | ||
| 5 | least-significant bits determine which piece it is, the other 4 determine | ||
| 6 | the orientation. | ||
| 7 | |||
| 8 | Edges are numbered as follows (see also cube.c): | ||
| 9 | UF=0 UB=1 DB=2 DF=3 UR=4 UL=5 DL=6 DR=7 FR=8 FL=9 BL=10 BR=11 | ||
| 10 | |||
| 11 | Corners are numbered as follows: | ||
| 12 | UFR=0 UBL=1 DFL=2 DBR=3 UFL=4 UBR=5 DFR=6 DBL=7 | ||
| 13 | |||
| 14 | The orientation of the edges is with respect to F/B, the orientation of | ||
| 15 | corners is with respect to U/D. | ||
| 16 | |||
| 17 | The permutation of the center pieces is not stored. This means that the | ||
| 18 | cube is assumed to be in a fixed orientation. | ||
| 19 | |||
| 20 | TODO: define EO and CO better, explain how to use them | ||
| 21 | TODO: encode centers? | ||
| 22 | |||
| 23 | The exact cube type structure depends on your system's configuration. If | ||
| 24 | you operate on the cube only via the functions provided below, you don't | ||
| 25 | need to worry about this. | ||
| 26 | ******************************************************************************/ | ||
| 27 | |||
| 28 | /* Apply the second cube on the first as a move sequence */ | ||
| 29 | cube_t compose(cube_t, cube_t); | ||
| 30 | |||
| 31 | /* Invert the cube */ | ||
| 32 | cube_t inverse(cube_t); | ||
| 33 | |||
| 34 | /* Check if a cube represent a valid state (possibly unsolvable) */ | ||
| 35 | |||
| 36 | /* TODO comment on these and the format for moves and trans */ | ||
| 37 | /* For trans, only one trans is supported */ | ||
| 38 | cube_t applymoves(cube_t, const char *); | ||
| 39 | cube_t applytrans(cube_t, const char *); | ||
| 40 | |||
| 41 | /****************************************************************************** | ||
| 42 | Read / write utilities | ||
| 43 | |||
| 44 | Reading and writing is not done directly via stdin / stdout, but via an | ||
| 45 | array of char (called buf in the prototypes below). | ||
| 46 | |||
| 47 | Multiple representations of the cube as text are supported: | ||
| 48 | |||
| 49 | - H48: a human-readable format. | ||
| 50 | Each edge is represented by two letters denoting the sides it | ||
| 51 | belongs to and one number denoting its orientation (0 oriented, 1 | ||
| 52 | mis-oriented). Similarly, each corner is represented by three letters and | ||
| 53 | a number (0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise). | ||
| 54 | |||
| 55 | The solved cube looks like this: | ||
| 56 | |||
| 57 | UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 | ||
| 58 | UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0 | ||
| 59 | |||
| 60 | The cube after the moves R'U'F looks like this: | ||
| 61 | |||
| 62 | FL1 BR0 DB0 UR1 UF0 UB0 DL0 FR0 UL1 DF1 BL0 DR0 | ||
| 63 | UBL1 DBR1 UFR2 DFR2 DFL2 UBL2 UFL2 DBL0 | ||
| 64 | |||
| 65 | Whitespace (including newlines) between pieces is ignored when reading the | ||
| 66 | cube. A single whitespace character is added between pieces when writing. | ||
| 67 | |||
| 68 | - SRC: format used to generate code for internal use. | ||
| 69 | If OUT is the output in SRC format, one can use `cube_t cube = OUT` to | ||
| 70 | declare a new cube object. | ||
| 71 | |||
| 72 | - LST: a format for internal use and generating code. | ||
| 73 | The cube is printed as a comma-separated list of 20 integers, as they appear | ||
| 74 | in cube_t. Corners come first, followed by edge (unlike H48). | ||
| 75 | ******************************************************************************/ | ||
| 76 | |||
| 77 | cube_t readcube(const char *format, const char *buf); | ||
| 78 | void writecube(const char *format, cube_t cube, char *buf); | ||
| 79 | |||
| 80 | /****************************************************************************** | ||
| 81 | Solvers | ||
| 82 | |||
| 83 | The solutions are returned as a newline-separated list of characters. Moves | ||
| 84 | are separated by single spaces. | ||
| 85 | |||
| 86 | Unless specified otherwise, all the solutions are not trivially simplifiable. | ||
| 87 | This means that sequences like U U2 or R L R will not appear in any solution. | ||
| 88 | Moreover, two consecutive parallel moves are always going to be sorted in | ||
| 89 | increasing order. For example, L R2 may never appear in a solution, but R2 L | ||
| 90 | could. | ||
| 91 | ******************************************************************************/ | ||
| 92 | |||
| 93 | int64_t solve( | ||
| 94 | /* The cube to solve. Must be solvable. */ | ||
| 95 | cube_t cube, | ||
| 96 | |||
| 97 | /* Supported solvers: | ||
| 98 | * "optimal" - currently the same as "simple" | ||
| 99 | * "simple" - a simple, slow solver without tables | ||
| 100 | */ | ||
| 101 | const char *solver, | ||
| 102 | |||
| 103 | /* Some solvers accept extra options,like "!filter". */ | ||
| 104 | const char *options, | ||
| 105 | |||
| 106 | /* Can be "normal", "inverse", "mixed" or "linear". */ | ||
| 107 | const char *nisstype, | ||
| 108 | |||
| 109 | /* The minimum number of moves. Must be >= 0. */ | ||
| 110 | int8_t minmoves, | ||
| 111 | |||
| 112 | /* The maximum number of moves. If negative, the maximum length | ||
| 113 | * is unlimited. | ||
| 114 | */ | ||
| 115 | int8_t maxmoves, | ||
| 116 | |||
| 117 | /* The maximum number of solutions. */ | ||
| 118 | int64_t maxsols, | ||
| 119 | |||
| 120 | /* All solutions at most "optimal" moves from the shortest solution | ||
| 121 | * (respecting minmoves) are found. If negative, it is ignored. | ||
| 122 | */ | ||
| 123 | int8_t optimal, | ||
| 124 | |||
| 125 | /* Some solvers require extra data to function properly (for example, | ||
| 126 | * pruning tables). This data can be generated with gendata(). | ||
| 127 | */ | ||
| 128 | const void *data, | ||
| 129 | |||
| 130 | /* The solutions (return parameter) */ | ||
| 131 | char *solutions | ||
| 132 | ); | ||
| 133 | |||
| 134 | /* Solving n cubes optimally, one solutions per cube. Options are similar | ||
| 135 | * to solve(). | ||
| 136 | */ | ||
| 137 | void multisolve( | ||
| 138 | int n, | ||
| 139 | cube_t *cube, | ||
| 140 | const char *solver, | ||
| 141 | const void *data, | ||
| 142 | char *sols | ||
| 143 | ); | ||
| 144 | |||
| 145 | /* Returns the number of bytes written to data, -1 in case of error. | ||
| 146 | * TODO: write down how much memory every solver requires. */ | ||
| 147 | int64_t gendata(const char *solver, const char *options, void *data); | ||
