From 3568412f8f230774d0d11d7ed1c897424f95d3ef Mon Sep 17 00:00:00 2001 From: Sebastiano Tronto Date: Thu, 11 Nov 2021 21:37:34 +0100 Subject: Rewritten from scratch. Welocme nissy 2.0! --- src/alg.c | 364 +++++++++++++++++ src/alg.h | 35 ++ src/commands.c | 346 +++++++++++++++++ src/commands.h | 13 + src/coord.c | 522 +++++++++++++++++++++++++ src/coord.h | 23 ++ src/coordinates.c | 286 -------------- src/coordinates.h | 92 ----- src/cube.c | 701 +++++++++++++++++++++++++++++++++ src/cube.h | 40 ++ src/cubetypes.h | 286 ++++++++++++++ src/env.c | 45 +++ src/env.h | 15 + src/helppages.h | 689 -------------------------------- src/io.c | 232 ----------- src/io.h | 21 - src/main.c | 937 -------------------------------------------- src/moves.c | 956 ++++++++++++++++++++++----------------------- src/moves.h | 91 +---- src/pf.c | 80 ++++ src/pf.h | 18 + src/pruning.c | 271 +++++++++++++ src/pruning.h | 23 ++ src/pruning_tables.c | 483 ----------------------- src/pruning_tables.h | 66 ---- src/shell.c | 93 +++++ src/shell.h | 13 + src/solve.c | 173 +++++++++ src/solve.h | 9 + src/solver.c | 1058 -------------------------------------------------- src/solver.h | 16 - src/steps.c | 941 ++++++++++++++++++++++++++++++++++++++++++++ src/steps.h | 10 + src/symcoord.c | 355 +++++++++++++++++ src/symcoord.h | 15 + src/trans.c | 375 ++++++++++++++++++ src/trans.h | 13 + src/utils.c | 398 +++++++++++++------ src/utils.h | 99 ++--- 39 files changed, 5557 insertions(+), 4646 deletions(-) create mode 100644 src/alg.c create mode 100644 src/alg.h create mode 100644 src/commands.c create mode 100644 src/commands.h create mode 100644 src/coord.c create mode 100644 src/coord.h delete mode 100644 src/coordinates.c delete mode 100644 src/coordinates.h create mode 100644 src/cube.c create mode 100644 src/cube.h create mode 100644 src/cubetypes.h create mode 100644 src/env.c create mode 100644 src/env.h delete mode 100644 src/helppages.h delete mode 100644 src/io.c delete mode 100644 src/io.h delete mode 100644 src/main.c create mode 100644 src/pf.c create mode 100644 src/pf.h create mode 100644 src/pruning.c create mode 100644 src/pruning.h delete mode 100644 src/pruning_tables.c delete mode 100644 src/pruning_tables.h create mode 100644 src/shell.c create mode 100644 src/shell.h create mode 100644 src/solve.c create mode 100644 src/solve.h delete mode 100644 src/solver.c delete mode 100644 src/solver.h create mode 100644 src/steps.c create mode 100644 src/steps.h create mode 100644 src/symcoord.c create mode 100644 src/symcoord.h create mode 100644 src/trans.c create mode 100644 src/trans.h (limited to 'src') diff --git a/src/alg.c b/src/alg.c new file mode 100644 index 0000000..354bdb6 --- /dev/null +++ b/src/alg.c @@ -0,0 +1,364 @@ +#include "alg.h" + +/* Local functions ***********************************************************/ + +static void free_alglistnode(AlgListNode *aln); +static void realloc_alg(Alg *alg, int n); + +/* Movesets ******************************************************************/ + +bool +moveset_HTM(Move m) +{ + return m >= U && m <= B3; +} + +bool +moveset_URF(Move m) +{ + Move b = base_move(m); + + return b == U || b == R || b == F; +} + +bool +moveset_eofb(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || b == R || b == L || + ((b == F || b == B) && m == b+1); +} + +bool +moveset_drud(Move m) +{ + Move b = base_move(m); + + return b == U || b == D || + ((b == R || b == L || b == F || b == B) && m == b + 1); +} + +bool +moveset_htr(Move m) +{ + Move b = base_move(m); + + return moveset_HTM(m) && m == b + 1; +} + + +/* Functions *****************************************************************/ + +void +append_alg(AlgList *l, Alg *alg) +{ + AlgListNode *node = malloc(sizeof(AlgListNode)); + int i; + + node->alg = new_alg(""); + for (i = 0; i < alg->len; i++) + append_move(node->alg, alg->move[i], alg->inv[i]); + node->next = NULL; + + if (++l->len == 1) + l->first = node; + else + l->last->next = node; + l->last = node; +} + +void +append_move(Alg *alg, Move m, bool inverse) +{ + if (alg->len == alg->allocated) + realloc_alg(alg, 2*alg->len); + + alg->move[alg->len] = m; + alg->inv [alg->len] = inverse; + alg->len++; +} + +Move +base_move(Move m) +{ + if (m == NULLMOVE) + return NULLMOVE; + else + return m - (m-1)%3; +} + +void +compose_alg(Alg *alg1, Alg *alg2) +{ + int i; + + for (i = 0; i < alg2->len; i++) + append_move(alg1, alg2->move[i], alg2->inv[i]); +} + +void +free_alg(Alg *alg) +{ + free(alg->move); + free(alg->inv); + free(alg); +} + +void +free_alglist(AlgList *l) +{ + AlgListNode *aux, *i = l->first; + + while (i != NULL) { + aux = i->next; + free_alglistnode(i); + i = aux; + } + free(l); +} + +static void +free_alglistnode(AlgListNode *aln) +{ + free_alg(aln->alg); + free(aln); +} + +Alg * +inverse_alg(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = alg->len-1; i >= 0; i--) + append_move(ret, inverse_move(alg->move[i]), alg->inv[i]); + + return ret; +} + +Move +inverse_move(Move m) +{ + return m == NULLMOVE ? NULLMOVE : m + 2 - 2*((m-1) % 3); +} + +char * +move_string(Move m) +{ + static char move_string_aux[NMOVES][7] = { + [NULLMOVE] = "-", + [U] = "U", [U2] = "U2", [U3] = "U\'", + [D] = "D", [D2] = "D2", [D3] = "D\'", + [R] = "R", [R2] = "R2", [R3] = "R\'", + [L] = "L", [L2] = "L2", [L3] = "L\'", + [F] = "F", [F2] = "F2", [F3] = "F\'", + [B] = "B", [B2] = "B2", [B3] = "B\'", + [Uw] = "Uw", [Uw2] = "Uw2", [Uw3] = "Uw\'", + [Dw] = "Dw", [Dw2] = "Dw2", [Dw3] = "Dw\'", + [Rw] = "Rw", [Rw2] = "Rw2", [Rw3] = "Rw\'", + [Lw] = "Lw", [Lw2] = "Lw2", [Lw3] = "Lw\'", + [Fw] = "Fw", [Fw2] = "Fw2", [Fw3] = "Fw\'", + [Bw] = "Bw", [Bw2] = "Bw2", [Bw3] = "Bw\'", + [M] = "M", [M2] = "M2", [M3] = "M\'", + [E] = "E", [E2] = "E2", [E3] = "E\'", + [S] = "S", [S2] = "S2", [S3] = "S\'", + [x] = "x", [x2] = "x2", [x3] = "x\'", + [y] = "y", [y2] = "y2", [y3] = "y\'", + [z] = "z", [z2] = "z2", [z3] = "z\'", + }; + + return move_string_aux[m]; +} + +void +movelist_to_position(Move *movelist, int *position) +{ + Move m; + + for (m = 0; m < NMOVES && movelist[m] != NULLMOVE; m++) + position[movelist[m]] = m; +} + +void +moveset_to_list(Moveset ms, Move *r) +{ + int n = 0; + Move i; + + if (ms == NULL) { + fprintf(stderr, "Error: no moveset given\n"); + return; + } + + for (i = U; i < NMOVES; i++) + if (ms(i)) + r[n++] = i; + + r[n] = NULLMOVE; +} + +Alg * +new_alg(char *str) +{ + Alg *alg = malloc(sizeof(Alg)); + int i; + bool niss = false, move_read; + Move j, m; + + alg->move = malloc(30 * sizeof(Move)); + alg->inv = malloc(30 * sizeof(bool)); + alg->allocated = 30; + alg->len = 0; + + for (i = 0; str[i]; i++) { + if (str[i] == ' ' || str[i] == '\t' || str[i] == '\n') + continue; + + if (str[i] == '(' && niss) { + fprintf(stderr, "Error reading moves: nested ( )\n"); + return alg; + } + + if (str[i] == ')' && !niss) { + fprintf(stderr, "Error reading moves: unmatched )\n"); + return alg; + } + + if (str[i] == '(' || str[i] == ')') { + niss = !niss; + continue; + } + + move_read = false; + for (j = 0; j < NMOVES; j++) { + if (str[i] == move_string(j)[0] || + (str[i] >= 'a' && str[i] <= 'z' && + str[i] == move_string(j)[0]-('A'-'a') && j<=B)) { + m = j; + if (str[i] >= 'a' && str[i] <= 'z' && j<=B) { + m += Uw - U; + } + if (m <= B && str[i+1]=='w') { + m += Uw - U; + i++; + } + if (str[i+1]=='2') { + m += 1; + i++; + } else if (str[i+1] == '\'' || + str[i+1] == '3' || + str[i+1] == '`' ) { + m += 2; + i++; + } else if ((int)str[i+1] == -62 && + (int)str[i+2] == -76) { + /* Weird apostrophe */ + m += 2; + i += 2; + } else if ((int)str[i+1] == -30 && + (int)str[i+2] == -128 && + (int)str[i+3] == -103) { + /* MacOS apostrophe */ + m += 2; + i += 3; + } + append_move(alg, m, niss); + move_read = true; + break; + } + } + + if (!move_read) { + alg = new_alg(""); + return alg; + } + } + + return alg; +} + +AlgList * +new_alglist() +{ + AlgList *ret = malloc(sizeof(AlgList)); + + ret->len = 0; + ret->first = NULL; + ret->last = NULL; + + return ret; +} + +Alg * +on_inverse(Alg *alg) +{ + Alg *ret = new_alg(""); + int i; + + for (i = 0; i < alg->len; i++) + append_move(ret, alg->move[i], !alg->inv[i]); + + return ret; +} + +void +print_alg(Alg *alg, bool l) +{ + char fill[4]; + int i; + bool niss = false; + + for (i = 0; i < alg->len; i++) { + if (!niss && alg->inv[i]) + strcpy(fill, i == 0 ? "(" : " ("); + if (niss && !alg->inv[i]) + strcpy(fill, ") "); + if (niss == alg->inv[i]) + strcpy(fill, i == 0 ? "" : " "); + + printf("%s%s", fill, move_string(alg->move[i])); + niss = alg->inv[i]; + } + + if (niss) + printf(")"); + if (l) + printf(" (%d)", alg->len); + + printf("\n"); +} + +void +print_alglist(AlgList *al, bool l) +{ + AlgListNode *i; + + for (i = al->first; i != NULL; i = i->next) + print_alg(i->alg, l); +} + +static void +realloc_alg(Alg *alg, int n) +{ + if (alg == NULL) { + fprintf(stderr, "Error: trying to reallocate NULL alg.\n"); + return; + } + + if (n < alg->len) { + fprintf(stderr, "Error: alg too long for reallocation "); + fprintf(stderr, "(%d vs %d)\n", alg->len, n); + return; + } + + if (n > 1000000) { + fprintf(stderr, "Warning: very long alg,"); + fprintf(stderr, "something might go wrong.\n"); + } + + alg->move = realloc(alg->move, n * sizeof(int)); + alg->inv = realloc(alg->inv, n * sizeof(int)); + alg->allocated = n; +} + diff --git a/src/alg.h b/src/alg.h new file mode 100644 index 0000000..98900b4 --- /dev/null +++ b/src/alg.h @@ -0,0 +1,35 @@ +#ifndef ALG_H +#define ALG_H + +#include +#include +#include + +#include "cubetypes.h" +#include "utils.h" + +bool moveset_HTM(Move m); +bool moveset_URF(Move m); +bool moveset_eofb(Move m); +bool moveset_drud(Move m); +bool moveset_htr(Move m); + +void append_alg(AlgList *l, Alg *alg); +void append_move(Alg *alg, Move m, bool inverse); +void compose_alg(Alg *alg1, Alg *alg2); +Move base_move(Move m); +void free_alg(Alg *alg); +void free_alglist(AlgList *l); +Alg * inverse_alg(Alg *alg); +Move inverse_move(Move m); +char * move_string(Move m); +void movelist_to_position(Move *ml, int *pos); +void moveset_to_list(Moveset ms, Move *lst); +Alg * new_alg(char *str); +AlgList * new_alglist(); +Alg * on_inverse(Alg *alg); +void print_alg(Alg *alg, bool l); +void print_alglist(AlgList *al, bool l); + +#endif + diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..10e4eb8 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,346 @@ +#include "commands.h" + +/* Arg parsing functions *****************************************************/ + +CommandArgs * solve_parse_args(int c, char **v); +CommandArgs * help_parse_args(int c, char **v); +CommandArgs * print_parse_args(int c, char **v); +CommandArgs * parse_no_arg(int c, char **v); + +/* Exec functions ************************************************************/ + +static void solve_exec(CommandArgs *args); +static void steps_exec(CommandArgs *args); +static void commands_exec(CommandArgs *args); +static void print_exec(CommandArgs *args); +static void help_exec(CommandArgs *args); +static void quit_exec(CommandArgs *args); +static void version_exec(CommandArgs *args); + +/* Local functions ***********************************************************/ + +static bool read_step(CommandArgs *args, char *str); +static bool read_scramble(int c, char **v, CommandArgs *args); + +/* Commands ******************************************************************/ + +Command +solve_cmd = { + .name = "solve", + .usage = "solve STEP [OPTIONS] SCRAMBLE", + .description = "Solve a step", + .parse_args = solve_parse_args, + .exec = solve_exec +}; + +Command +steps_cmd = { + .name = "steps", + .usage = "steps", + .description = "List available steps", + .parse_args = parse_no_arg, + .exec = steps_exec +}; + +Command +commands_cmd = { + .name = "commands", + .usage = "commands", + .description = "List available commands", + .parse_args = parse_no_arg, + .exec = commands_exec +}; + +Command +print_cmd = { + .name = "print", + .usage = "print SCRAMBLE", + .description = "Print written description of the cube", + .parse_args = print_parse_args, + .exec = print_exec, +}; + +Command +help_cmd = { + .name = "help", + .usage = "help [COMMAND]", + .description = "Display nissy manual page or help on specific command", + .parse_args = help_parse_args, + .exec = help_exec, +}; + +Command +quit_cmd = { + .name = "quit", + .usage = "quit", + .description = "Quit nissy", + .parse_args = parse_no_arg, + .exec = quit_exec, +}; + +Command +version_cmd = { + .name = "version", + .usage = "version", + .description = "print nissy version", + .parse_args = parse_no_arg, + .exec = version_exec, +}; + +Command *commands[NCOMMANDS] = { + &commands_cmd, + &help_cmd, + &print_cmd, + &quit_cmd, + &solve_cmd, + &steps_cmd, + &version_cmd, +}; + +/* Arg parsing functions implementation **************************************/ + +CommandArgs * +solve_parse_args(int c, char **v) +{ + int i; + long val; + + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = false; + a->opts = malloc(sizeof(SolveOptions)); + a->step = steps[0]; + a->command = NULL; + a->scramble = NULL; + + a->opts->min_moves = 0; + a->opts->max_moves = 20; + a->opts->max_solutions = 1; + a->opts->optimal_only = false; + a->opts->can_niss = false; + a->opts->verbose = false; + a->opts->all = false; + a->opts->print_number = true; + + for (i = 0; i < c; i++) { + if (!strcmp(v[i], "-m")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid min number of moves.\n"); + return a; + } + a->opts->min_moves = val; + } else if (!strcmp(v[i], "-M")) { + val = strtol(v[++i], NULL, 10); + if (val < 0 || val > 100) { + fprintf(stderr, + "Invalid max number of moves.\n"); + return a; + } + a->opts->max_moves = val; + } else if (!strcmp(v[i], "-s")) { + val = strtol(v[++i], NULL, 10); + if (val < 1 || val > 1000000) { + fprintf(stderr, + "Invalid number of solutions.\n"); + return a; + } + a->opts->max_solutions = val; + } else if (!strcmp(v[i], "-o")) { + a->opts->optimal_only = true; + } else if (!strcmp(v[i], "-n")) { + a->opts->can_niss = true; + } else if (!strcmp(v[i], "-v")) { + a->opts->verbose = true; + } else if (!strcmp(v[i], "-a")) { + a->opts->all = true; + } else if (!strcmp(v[i], "-p")) { + a->opts->print_number = false; + } else if (!read_step(a, v[i])) { + break; + } + } + + a->success = read_scramble(c-i, &v[i], a); + return a; +} + +CommandArgs * +help_parse_args(int c, char **v) +{ + int i; + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->scramble = NULL; + a->opts = NULL; + a->step = NULL; + a->command = NULL; + + if (c == 1) { + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && + !strcmp(v[0], commands[i]->name)) + a->command = commands[i]; + if (a->command == NULL) + fprintf(stderr, "%s: command not found\n", v[0]); + } + + a->success = c == 0 || (c == 1 && a->command != NULL); + return a; +} + +CommandArgs * +parse_no_arg(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = true; + + return a; +} + +CommandArgs * +print_parse_args(int c, char **v) +{ + CommandArgs *a = malloc(sizeof(CommandArgs)); + + a->success = read_scramble(c, v, a); + return a; +} + +/* Exec functions implementation *********************************************/ + +static void +solve_exec(CommandArgs *args) +{ + Cube c; + AlgList *sols; + + init_symcoord(); + + c = apply_alg(args->scramble, (Cube){0}); + sols = solve(c, args->step, args->opts); + + print_alglist(sols, args->opts->print_number); + free_alglist(sols); +} + +static void +steps_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NSTEPS && steps[i] != NULL; i++) + printf("%-15s %s\n", steps[i]->shortname, steps[i]->name); +} + +static void +commands_exec(CommandArgs *args) +{ + int i; + + for (i = 0; i < NCOMMANDS && commands[i] != NULL; i++) + printf("%s\n", commands[i]->usage); + +} + +static void +print_exec(CommandArgs *args) +{ + init_moves(); + print_cube(apply_alg(args->scramble, (Cube){0})); +} + +static void +help_exec(CommandArgs *args) +{ + /* TODO: print full nissy manpage */ + if (args->command == NULL) { + printf("Type help COMMAND for information on a "); + printf("specific command.\n"); + printf("A more complete manual page is work in progress.\n"); + } else { + printf("Command %s: %s\nusage: %s\n", args->command->name, + args->command->description, args->command->usage); + } +} + +static void +quit_exec(CommandArgs *args) +{ + exit(0); +} + +static void +version_exec(CommandArgs *args) +{ + printf(VERSION"\n"); +} + +/* Local functions implementation ********************************************/ + +static bool +read_step(CommandArgs *args, char *str) +{ + int i; + + for (i = 0; i < NSTEPS; i++) { + if (steps[i] != NULL && !strcmp(steps[i]->shortname, str)) { + args->step = steps[i]; + return true; + } + } + + return false; +} + +static bool +read_scramble(int c, char **v, CommandArgs *args) +{ + int i, k, n; + unsigned int j; + char *algstr; + + if (new_alg(v[0])->len == 0) { + fprintf(stderr, "%s: moves or option unrecognized\n", v[0]); + return false; + } + + n = 0; + for(i = 0; i < c; i++) + n += strlen(v[i]); + + algstr = malloc((n + 1) * sizeof(char)); + k = 0; + for (i = 0; i < c; i++) + for (j = 0; j < strlen(v[i]); j++) + algstr[k++] = v[i][j]; + algstr[k] = 0; + + args->scramble = new_alg(algstr); + free(algstr); + + if (args->scramble->len == 0) + fprintf(stderr, "Error reading scramble\n"); + + return args->scramble->len > 0; +} + +/* Public functions implementation *******************************************/ + +void +free_args(CommandArgs *args) +{ + if (args == NULL) + return; + + if (args->scramble != NULL) + free_alg(args->scramble); + if (args->opts != NULL) + free(args->opts); + + /* step and command must not be freed, they are static! */ + + free(args); +} diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 0000000..f2703fa --- /dev/null +++ b/src/commands.h @@ -0,0 +1,13 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +#include "solve.h" +#include "steps.h" + +#define NCOMMANDS 10 + +void free_args(CommandArgs *args); + +extern Command * commands[NCOMMANDS]; + +#endif diff --git a/src/coord.c b/src/coord.c new file mode 100644 index 0000000..8c978bc --- /dev/null +++ b/src/coord.c @@ -0,0 +1,522 @@ +#include "coord.h" + +static Cube antindex_eofb(uint64_t ind); +static Cube antindex_eofbepos(uint64_t ind); +static Cube antindex_epud(uint64_t ind); +static Cube antindex_coud(uint64_t ind); +static Cube antindex_corners(uint64_t ind); +static Cube antindex_cp(uint64_t ind); +static Cube antindex_cphtr(uint64_t); +static Cube antindex_cornershtr(uint64_t ind); +static Cube antindex_cornershtrfin(uint64_t ind); +static Cube antindex_drud(uint64_t ind); +static Cube antindex_drud_eofb(uint64_t ind); +static Cube antindex_htr_drud(uint64_t ind); +static Cube antindex_htrfin(uint64_t ind); + +static uint64_t index_eofb(Cube cube); +static uint64_t index_eofbepos(Cube cube); +static uint64_t index_epud(Cube cube); +static uint64_t index_coud(Cube cube); +static uint64_t index_corners(Cube cube); +static uint64_t index_cp(Cube cube); +static uint64_t index_cphtr(Cube cube); +static uint64_t index_cornershtr(Cube cube); +static uint64_t index_cornershtrfin(Cube cube); +static uint64_t index_drud(Cube cube); +static uint64_t index_drud_eofb(Cube cube); +static uint64_t index_htr_drud(Cube cube); +static uint64_t index_htrfin(Cube cube); + +static void init_cphtr_cosets(); +static void init_cphtr_left_cosets_bfs(int i, int c); +static void init_cphtr_right_cosets_color(int i, int c); +static void init_cornershtrfin(); + + +/* All sorts of useful costants and tables **********************************/ + +static int cphtr_left_cosets[FACTORIAL8]; +static int cphtr_right_cosets[FACTORIAL8]; +static int cphtr_right_rep[BINOM8ON4*6]; +static int cornershtrfin_ind[FACTORIAL8]; +static int cornershtrfin_ant[24*24/6]; + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofb = { + .index = index_eofb, + .cube = antindex_eofb, + .max = POW2TO11, + .ntrans = 1, +}; + +Coordinate +coord_eofbepos = { + .index = index_eofbepos, + .cube = antindex_eofbepos, + .max = POW2TO11 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_coud = { + .index = index_coud, + .cube = antindex_coud, + .max = POW3TO7, + .ntrans = 1, +}; + +Coordinate +coord_corners = { + .index = index_corners, + .cube = antindex_corners, + .max = POW3TO7 * FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cp = { + .index = index_cp, + .cube = antindex_cp, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_cphtr = { + .index = index_cphtr, + .cube = antindex_cphtr, + .max = BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtr = { + .index = index_cornershtr, + .cube = antindex_cornershtr, + .max = POW3TO7 * BINOM8ON4 * 6, + .ntrans = 1, +}; + +Coordinate +coord_cornershtrfin = { + .index = index_cornershtrfin, + .cube = antindex_cornershtrfin, + .max = 24*24/6, + .ntrans = 1, +}; + +Coordinate +coord_epud = { + .index = index_epud, + .cube = antindex_epud, + .max = FACTORIAL8, + .ntrans = 1, +}; + +Coordinate +coord_drud = { + .index = index_drud, + .cube = antindex_drud, + .max = POW2TO11 * POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +Coordinate +coord_htr_drud = { + .index = index_htr_drud, + .cube = antindex_htr_drud, + .max = BINOM8ON4 * 6 * BINOM8ON4, + .ntrans = 1, +}; + +Coordinate +coord_htrfin = { + .index = index_htrfin, + .cube = antindex_htrfin, + .max = 24 * 24 * 24 *24 * 24 / 6, /* should be /12 but it's ok */ + .ntrans = 1, +}; + +Coordinate +coord_drud_eofb = { + .index = index_drud_eofb, + .cube = antindex_drud_eofb, + .max = POW3TO7 * BINOM12ON4, + .ntrans = 1, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_eofb(uint64_t ind) +{ + return (Cube){ .eofb = ind, .eorl = ind, .eoud = ind }; +} + +static Cube +antindex_eofbepos(uint64_t ind) +{ + Cube ret = {0}; + + ret.eofb = ind % POW2TO11; + ret.epose = (ind / POW2TO11) * 24; + + return ret; +} + +static Cube +antindex_epud(uint64_t ind) +{ + static bool initialized = false; + static Cube epud_aux[FACTORIAL8]; + int a[12]; + uint64_t ui; + CubeArray arr; + + if (!initialized) { + a[FR] = FR; + a[FL] = FL; + a[BL] = BL; + a[BR] = BR; + for (ui = 0; ui < FACTORIAL8; ui++) { + index_to_perm(ui, 8, a); + arr.ep = a; + epud_aux[ui] = arrays_to_cube(&arr, pf_ep); + } + + initialized = true; + } + + return epud_aux[ind]; +} + +static Cube +antindex_coud(uint64_t ind) +{ + return (Cube){ .coud = ind, .corl = ind, .cofb = ind }; +} + +static Cube +antindex_corners(uint64_t ind) +{ + Cube c = {0}; + + c.coud = ind / FACTORIAL8; + c.cp = ind % FACTORIAL8; + + return c; +} + +static Cube +antindex_cp(uint64_t ind) +{ + Cube c = {0}; + + c.cp = ind; + + return c; +} + +static Cube +antindex_cphtr(uint64_t ind) +{ + return (Cube) { .cp = cphtr_right_rep[ind] }; +} + +static Cube +antindex_cornershtr(uint64_t ind) +{ + Cube c = antindex_cphtr(ind % (BINOM8ON4 * 6)); + + c.coud = ind / (BINOM8ON4 * 6); + + return c; +} + +static Cube +antindex_cornershtrfin(uint64_t ind) +{ + return (Cube){ .cp = cornershtrfin_ant[ind] }; +} + +static Cube +antindex_drud(uint64_t ind) +{ + uint64_t epos, eofb; + Cube c; + + eofb = ind % POW2TO11; + epos = ind / (POW2TO11 * POW3TO7); + c = antindex_eofbepos(eofb + POW2TO11 * epos); + + c.coud = (ind / POW2TO11) % POW3TO7; + + return c; +} + +static Cube +antindex_drud_eofb(uint64_t ind) +{ + return antindex_drud(ind * POW2TO11); +} + +static Cube +antindex_htr_drud(uint64_t ind) +{ + Cube ret; + + ret = antindex_cphtr(ind / BINOM8ON4); + ret.eposs = (ind % BINOM8ON4) * FACTORIAL4; + + return ret; +} + +static Cube +antindex_htrfin(uint64_t ind) +{ + Cube ret; + + ret = antindex_cornershtrfin(ind/(24*24*24)); + + ret.eposm = ind % 24; + ind /= 24; + ret.eposs = ind % 24; + ind /= 24; + ret.epose = ind % 24; + + return ret; +} + +static uint64_t +index_eofb(Cube cube) +{ + return cube.eofb; +} + +static uint64_t +index_eofbepos(Cube cube) +{ + return (cube.epose / FACTORIAL4) * POW2TO11 + cube.eofb; +} + +static uint64_t +index_epud(Cube cube) +{ + uint64_t ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = perm_to_index(arr->ep, 8); + free_cubearray(arr, pf_ep); + + return ret; +} + +static uint64_t +index_coud(Cube cube) +{ + return cube.coud; +} + +static uint64_t +index_corners(Cube cube) +{ + return cube.coud * FACTORIAL8 + cube.cp; +} + +static uint64_t +index_cp(Cube cube) +{ + return cube.cp; +} + +static uint64_t +index_cphtr(Cube cube) +{ + return cphtr_right_cosets[cube.cp]; +} + +static uint64_t +index_cornershtr(Cube cube) +{ + return cube.coud * BINOM8ON4 * 6 + index_cphtr(cube); +} + +static uint64_t +index_cornershtrfin(Cube cube) +{ + return cornershtrfin_ind[cube.cp]; +} + +static uint64_t +index_drud(Cube cube) +{ + uint64_t a, b, c; + + a = cube.eofb; + b = cube.coud; + c = cube.epose / FACTORIAL4; + + b *= POW2TO11; + c *= POW2TO11 * POW3TO7; + + return a + b + c; +} + +static uint64_t +index_drud_eofb(Cube cube) +{ + return index_drud(cube) / POW2TO11; +} + +static uint64_t +index_htr_drud(Cube cube) +{ + return index_cphtr(cube) * BINOM8ON4 + + (cube.eposs / FACTORIAL4) % BINOM8ON4; +} + +static uint64_t +index_htrfin(Cube cube) +{ + uint64_t epe, eps, epm, cp, ep; + + epe = cube.epose % 24; + eps = cube.eposs % 24; + epm = cube.eposm % 24; + ep = (epe * 24 + eps) *24 + epm; + cp = index_cornershtrfin(cube); + + return cp * 24 * 24 * 24 + ep; +} + +/* Init functions implementation *********************************************/ + +/* + * There is certainly a better way to do this, but for now I just use + * a "graph coloring" algorithm to compute the left cosets, and I compose + * with every possible cp to get the right cosets (it is possible that I am + * mixing up left and right). + * + * For doing it better "Mathematically", we need 3 things: + * - Checking that cp separates the orbits (UFR,UBL,DFL,DBR) and the other + * This is easy and it is done in the commented function cphtr_cp(). + * - Check that there is no ep/cp parity + * - Check that we are not in the "3c" case; this is the part I don't + * know how to do. + */ +static void +init_cphtr_cosets() +{ + unsigned int i; + int c = 0, d = 0; + + for (i = 0; i < FACTORIAL8; i++) { + cphtr_left_cosets[i] = -1; + cphtr_right_cosets[i] = -1; + } + + /* First we compute left cosets with a bfs */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_left_cosets[i] == -1) + init_cphtr_left_cosets_bfs(i, c++); + + /* Then we compute right cosets using compose() */ + for (i = 0; i < FACTORIAL8; i++) + if (cphtr_right_cosets[i] == -1) + init_cphtr_right_cosets_color(i, d++); +} + +static void +init_cphtr_left_cosets_bfs(int i, int c) +{ + int j, jj, next[FACTORIAL8], next2[FACTORIAL8], n, n2; + + Move k; + + n = 1; + next[0] = i; + cphtr_left_cosets[i] = c; + + while (n != 0) { + for (j = 0, n2 = 0; j < n; j++) { + for (k = U2; k < B3; k++) { + if (!moveset_htr(k)) + continue; + jj = apply_move(k, (Cube){ .cp = next[j] }).cp; + + if (cphtr_left_cosets[jj] == -1) { + cphtr_left_cosets[jj] = c; + next2[n2++] = jj; + } + } + } + + for (j = 0; j < n2; j++) + next[j] = next2[j]; + n = n2; + } +} + +static void +init_cphtr_right_cosets_color(int i, int d) +{ + int cp; + unsigned int j; + + cphtr_right_rep[d] = i; + for (j = 0; j < FACTORIAL8; j++) { + if (cphtr_left_cosets[j] == 0) { + cp = compose((Cube){.cp = i}, (Cube){.cp = j}).cp; + cphtr_right_cosets[cp] = d; + } + } +} + +static void +init_cornershtrfin() +{ + unsigned int i, j; + int n, c; + Move m; + + for (i = 0; i < FACTORIAL8; i++) + cornershtrfin_ind[i] = -1; + cornershtrfin_ind[0] = 0; + + /* 10-pass, I think 5 is enough, but just in case */ + n = 1; + for (i = 0; i < 10; i++) { + for (j = 0; j < FACTORIAL8; j++) { + if (cornershtrfin_ind[j] == -1) + continue; + for (m = U; m < NMOVES; m++) { + if (moveset_htr(m)) { + c = apply_move(m, (Cube){.cp = j}).cp; + if (cornershtrfin_ind[c] == -1) { + cornershtrfin_ind[c] = n; + cornershtrfin_ant[n] = c; + n++; + } + } + } + } + } +} + +void +init_coord() +{ + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_trans(); + + init_cphtr_cosets(); + init_cornershtrfin(); +} + diff --git a/src/coord.h b/src/coord.h new file mode 100644 index 0000000..be41b96 --- /dev/null +++ b/src/coord.h @@ -0,0 +1,23 @@ +#ifndef COORD_H +#define COORD_H + +#include "trans.h" + +extern Coordinate coord_eofb; +extern Coordinate coord_eofbepos; +extern Coordinate coord_coud; +extern Coordinate coord_cp; +extern Coordinate coord_cphtr; +extern Coordinate coord_corners; +extern Coordinate coord_cornershtr; +extern Coordinate coord_cornershtrfin; +extern Coordinate coord_epud; +extern Coordinate coord_drud; +extern Coordinate coord_drud_eofb; +extern Coordinate coord_htr_drud; +extern Coordinate coord_htrfin; + +void init_coord(); + +#endif + diff --git a/src/coordinates.c b/src/coordinates.c deleted file mode 100644 index be61698..0000000 --- a/src/coordinates.c +++ /dev/null @@ -1,286 +0,0 @@ -#include - -#include "utils.h" -#include "coordinates.h" - -/* Names of pieces and moves. */ -char edge_string_list[12][5] = { - "UF", "UL", "UB", "UR", "DF", "DL", "DB", "DR", "FR", "FL", "BL", "BR" -}; - -char corner_string_list[8][5] = { - "UFR", "UFL", "UBL", "UBR", "DFR", "DFL", "DBL", "DBR" -}; - -char move_string_list[19][5] = { - "-", - "U", "U2", "U\'", "D", "D2", "D\'", "R", "R2", "R\'", - "L", "L2", "L\'", "F", "F2", "F\'", "B", "B2", "B\'" -}; - -int inverse_move[19] = { - -1, U3, U2, U, D3, D2, D, R3, R2, R, L3, L2, L, F3, F2, F, B3, B2, B -}; - -/* Convert piece representation from integer to array. - * Come convertions are not "perfect": for example, and epud type of piece - * is represented by a permutation index in 8! elements, but it as an array - * it is converted to the first 8 elements of a 12 elements ep array (with - * meaningless values for the other 4 elements). */ - -void ep_int_to_array(int ep, int a[12]) { - index_to_perm(ep, 12, a); -} - -void epud_int_to_array(int epud, int a[12]) { - index_to_perm(epud, 8, a); /* Last 4 elements are left untouched. */ -} - -void epfb_int_to_array(int epfb, int a[12]) { - int edges[] = {UF, UB, DF, DB, FR, FL, BL, BR}; - int b[8]; - index_to_perm(epfb, 8, b); - for (int i = 0; i < 8; i++) - a[edges[i]] = edges[b[i]]; -} - -void eprl_int_to_array(int eprl, int a[12]) { - int edges[] = {UL, UR, DL, DR, FR, FL, BL, BR}; - int b[8]; - index_to_perm(eprl, 8, b); - for (int i = 0; i < 8; i++) - a[edges[i]] = edges[b[i]]; -} - -void epose_int_to_array(int epos, int a[12]) { - int edges[] = {FR, FL, BL, BR}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; -} - -void eposs_int_to_array(int epos, int a[12]) { - int edges[] = {UL, UR, DL, DR}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); -} - -void eposm_int_to_array(int epos, int a[12]) { - int edges[] = {UF, UB, DF, DB}; - index_to_subset(epos, 12, 4, a); - for (int i = 0, j = 0; i < 12; i++) - a[i] = (a[i] == 1) ? edges[j++] : -1; - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); -} - -void epe_int_to_array(int epe, int a[12]) { - index_to_perm(epe, 4, a+8); - for (int i = 0; i < 4; i++) - a[i+8] += 8; -} - -void eps_int_to_array(int eps, int a[12]) { - int edges[] = {UL, UR, DL, DR}; - int b[4]; - index_to_perm(eps, 4, b); - for (int i = 0; i < 4; i++) - a[edges[i]] = edges[b[i]]; -} - -void epm_int_to_array(int epm, int a[12]) { - int edges[] = {UF, UB, DF, DB}; - int b[4]; - index_to_perm(epm, 4, b); - for (int i = 0; i < 4; i++) - a[edges[i]] = edges[b[i]]; -} - -void emslices_int_to_array(int emslices, int a[12]) { - int b[] = {0,0,0,0,0,0,0,0}; - int eslice[] = {FR, FL, BL, BR}; - int mslice[] = {UF, UB, DF, DB}; - - index_to_subset(emslices % binom12on4, 12, 4, a); - index_to_subset(emslices / binom12on4, 8, 4, b); - - if (emslices % binom12on4 == 0) { - swap(&b[UF], &b[DL]); - swap(&b[UB], &b[DR]); - /*for (int i = 0; i < 4; i++) - swap(&b[mslice[i]], &b[i+4]);*/ - } - - for (int i = 0, j = 0; j < 8; i++, j++) { - while (a[i]) - i++; - a[i] = b[j] ? 2 : -1; - } - for (int i = 0, j1 = 0, j2 = 0; i < 12; i++) { - if (a[i] == 1) - a[i] = eslice[j1++]; - if (a[i] == 2) - a[i] = mslice[j2++]; - } -} - -void cp_int_to_array(int cp, int a[8]) { - index_to_perm(cp, 8, a); -} - -void eo_11bits_to_array(int eo, int a[12]) { - int_to_sum_zero_array(eo, 2, 12, a); -} - -void co_7trits_to_array(int co, int a[8]) { - int_to_sum_zero_array(co, 3, 8, a); -} - - - - - -int ep_array_to_int(int ep[12]) { - return perm_to_index(ep, 12); -} - -int epud_array_to_int(int ep[12]) { - return perm_to_index(ep, 8); /* Last 4 elements are ignored */ -} - -int epfb_array_to_int(int ep[12]) { - int index[] = {0, -1, 1, -1, 2, -1, 3, -1, 4, 5, 6, 7}; - int b[8]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 8); -} - -int eprl_array_to_int(int ep[12]) { - int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, 4, 5, 6, 7}; - int b[8]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 8); -} - -int epose_array_to_int(int ep[12]) { - int a[12]; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] >= FR); - return subset_to_index(a, 12, 4); -} - -int eposs_array_to_int(int ep[12]) { - int a[12]; - int edges[] = {UL, UR, DL, DR}; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] == UL || ep[i] == UR || ep[i] == DL || ep[i] == DR); - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); - return subset_to_index(a, 12, 4); -} - -int eposm_array_to_int(int ep[12]) { - int a[12]; - int edges[] = {UF, UB, DF, DB}; - for (int i = 0; i < 12; i++) - a[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB); - /* Swap with last 4, so 0 is alway solved state */ - for (int i = 0; i < 4; i++) - swap(&a[edges[i]], &a[i+8]); - return subset_to_index(a, 12, 4); -} - -int epe_array_to_int(int ep[12]) { - int b[4]; - for (int i = 0; i < 4; i++) - b[i] = ep[i+8] - 8; - return perm_to_index(b, 4); -} - -int eps_array_to_int(int ep[12]) { - int index[] = {-1, 0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1}; - int b[4]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 4); -} - -int epm_array_to_int(int ep[12]) { - int index[] = {0, -1, 1, -1, 2, -1, 3, -1, -1, -1, -1, -1}; - int b[4]; - for (int i = 0; i < 12; i++) - if (index[i] != -1) - b[index[i]] = index[ep[i]]; - return perm_to_index(b, 4); -} - -int emslices_array_to_int(int ep[12]) { - int a[12], b[12], c[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - /*int edges[] = {UF, UB, DF, DB};*/ - for (int i = 0; i < 12; i++) { - a[i] = (ep[i] >= FR) ? 1 : 0; - b[i] = (ep[i] == UF || ep[i] == UB || ep[i] == DF || ep[i] == DB) ? 1 : 0; - } - - /*for ( int i = 0; i < 12; i++) - printf("%d ", ep[i]); - printf("\n");*/ - - for (int i = 0, j = 0; i < 12; i++, j++) { - if (a[i]) - j--; - if (b[i]) - c[j] = 1; - } - - int epose = subset_to_index(a, 12, 4); - - /*if (epose == 0) { - printf("Before: "); - for (int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n"); - for (int i = 0; i < 4; i++) - swap(&c[edges[i]], &c[i+4]); - printf("After: "); - for (int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n"); - }*/ - if (epose == 0) { - swap(&c[UF], &c[DL]); - swap(&c[UB], &c[DR]); - } - - /*for ( int i = 0; i < 8; i++) - printf("%d ", c[i]); - printf("\n");*/ - int eposm = subset_to_index(c, 8, 4); - - return epose + 495*eposm; -} - - -int cp_array_to_int(int cp[8]) { - return perm_to_index(cp, 8); -} - -int eo_array_to_11bits(int a[12]) { - return digit_array_to_int(a, 11, 2); -} - -int co_array_to_7trits(int a[8]) { - return digit_array_to_int(a, 7, 3); -} - diff --git a/src/coordinates.h b/src/coordinates.h deleted file mode 100644 index 10f7d18..0000000 --- a/src/coordinates.h +++ /dev/null @@ -1,92 +0,0 @@ -/* General rule for piece numbering (visually nicer): - * - * 0 1 2 3 4 5 6 7 8 9 10 11 - * UF UL UB UR DF DL DB DR FR FL BL BR - * UFR UFL UBL UBR DFR DFL DBL DBR - * - * The order of moves is - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 - * - U U2 U' D D2 D' R R2 R' L L2 L' F F2 F' B B2 B' - * (0 is reserved for no move) */ - -#define UF 0 -#define UL 1 -#define UB 2 -#define UR 3 -#define DF 4 -#define DL 5 -#define DB 6 -#define DR 7 -#define FR 8 -#define FL 9 -#define BL 10 -#define BR 11 - -#define UFR 0 -#define UFL 1 -#define UBL 2 -#define UBR 3 -#define DFR 4 -#define DFL 5 -#define DBL 6 -#define DBR 7 - -#define U 1 -#define U2 2 -#define U3 3 -#define D 4 -#define D2 5 -#define D3 6 -#define R 7 -#define R2 8 -#define R3 9 -#define L 10 -#define L2 11 -#define L3 12 -#define F 13 -#define F2 14 -#define F3 15 -#define B 16 -#define B2 17 -#define B3 18 - -extern char edge_string_list[12][5]; -extern char corner_string_list[8][5]; -extern char move_string_list[19][5]; -extern int inverse_move[19]; - -/* Convert piece representation from integer to array. - * Come convertions are not "perfect": for example, and epud type of piece - * is represented by a permutation index in 8! elements, but it as an array - * it is converted to the first 8 elements of a 12 elements ep array (with - * meaningless values for the other 4 elements). */ - -void ep_int_to_array(int ep, int a[12]); -void epud_int_to_array(int epud, int a[12]); -void epfb_int_to_array(int epfb, int a[12]); -void eprl_int_to_array(int eprl, int a[12]); -void epose_int_to_array(int epos, int a[12]); -void eposs_int_to_array(int epos, int a[12]); -void eposm_int_to_array(int epos, int a[12]); -void epe_int_to_array(int epe, int a[12]); -void epm_int_to_array(int epe, int a[12]); -void eps_int_to_array(int epe, int a[12]); -void emslices_int_to_array(int emslices, int a[12]); -void cp_int_to_array(int cp, int a[8]); -void eo_11bits_to_array(int eo, int a[12]); -void co_7trits_to_array(int co, int a[8]); - -int ep_array_to_int(int ep[12]); -int epud_array_to_int(int ep[12]); -int epfb_array_to_int(int ep[12]); -int eprl_array_to_int(int ep[12]); -int epose_array_to_int(int ep[12]); -int eposs_array_to_int(int ep[12]); -int eposm_array_to_int(int ep[12]); -int epe_array_to_int(int epe[12]); -int epm_array_to_int(int epe[12]); -int eps_array_to_int(int epe[12]); -int emslices_array_to_int(int ep[12]); -int cp_array_to_int(int cp[8]); -int eo_array_to_11bits(int a[12]); -int co_array_to_7trits(int a[8]); diff --git a/src/cube.c b/src/cube.c new file mode 100644 index 0000000..06c8b8c --- /dev/null +++ b/src/cube.c @@ -0,0 +1,701 @@ +#include "cube.h" + +/* Local functions **********************************************************/ + +static int array_ep_to_epos(int *ep, int *eps_solved); +static int epos_from_arrays(int *epos, int *ep); + +/* Local functions implementation ********************************************/ + +static int +array_ep_to_epos(int *ep, int *ss) +{ + int epos[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int eps[4]; + int i, j, is; + + for (i = 0, is = 0; i < 12; i++) { + for (j = 0; j < 4; j++) { + if (ep[i] == ss[j]) { + eps[is++] = j; + epos[i] = 1; + } + } + } + + for (i = 0; i < 4; i++) + swap(&epos[ss[i]], &epos[i+8]); + + return epos_from_arrays(epos, eps); +} + +static int +epos_from_arrays(int *epos, int *ep) +{ + return FACTORIAL4 * subset_to_index(epos,12,4) + perm_to_index(ep,4); +} + +/* Public functions implementation *******************************************/ + +Cube +arrays_to_cube(CubeArray *arr, PieceFilter f) +{ + Cube ret = {0}; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose) + ret.epose = array_ep_to_epos(arr->ep, epe_solved); + if (f.eposs) + ret.eposs = array_ep_to_epos(arr->ep, eps_solved); + if (f.eposm) + ret.eposm = array_ep_to_epos(arr->ep, epm_solved); + if (f.eofb) + ret.eofb = digit_array_to_int(arr->eofb, 11, 2); + if (f.eorl) + ret.eorl = digit_array_to_int(arr->eorl, 11, 2); + if (f.eoud) + ret.eoud = digit_array_to_int(arr->eoud, 11, 2); + if (f.cp) + ret.cp = perm_to_index(arr->cp, 8); + if (f.coud) + ret.coud = digit_array_to_int(arr->coud, 7, 3); + if (f.corl) + ret.corl = digit_array_to_int(arr->corl, 7, 3); + if (f.cofb) + ret.cofb = digit_array_to_int(arr->cofb, 7, 3); + if (f.cpos) + ret.cpos = perm_to_index(arr->cpos, 6); + + return ret; +} + +Cube +compose_filtered(Cube c2, Cube c1, PieceFilter f) +{ + CubeArray *arr = new_cubearray(c2, f); + Cube ret; + + ret = move_via_arrays(arr, c1, f); + free_cubearray(arr, f); + + return ret; +} + +void +cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f) +{ + int i; + + static int epe_solved[4] = {FR, FL, BL, BR}; + static int eps_solved[4] = {UL, UR, DL, DR}; + static int epm_solved[4] = {UF, UB, DF, DB}; + + if (f.epose || f.eposs || f.eposm) + for (i = 0; i < 12; i++) + arr->ep[i] = -1; + + if (f.epose) + epos_to_partial_ep(cube.epose, arr->ep, epe_solved); + if (f.eposs) + epos_to_partial_ep(cube.eposs, arr->ep, eps_solved); + if (f.eposm) + epos_to_partial_ep(cube.eposm, arr->ep, epm_solved); + if (f.eofb) + int_to_sum_zero_array(cube.eofb, 2, 12, arr->eofb); + if (f.eorl) + int_to_sum_zero_array(cube.eorl, 2, 12, arr->eorl); + if (f.eoud) + int_to_sum_zero_array(cube.eoud, 2, 12, arr->eoud); + if (f.cp) + index_to_perm(cube.cp, 8, arr->cp); + if (f.coud) + int_to_sum_zero_array(cube.coud, 3, 8, arr->coud); + if (f.corl) + int_to_sum_zero_array(cube.corl, 3, 8, arr->corl); + if (f.cofb) + int_to_sum_zero_array(cube.cofb, 3, 8, arr->cofb); + if (f.cpos) + index_to_perm(cube.cpos, 6, arr->cpos); +} + +void +epos_to_partial_ep(int epos, int *ep, int *ss) +{ + int i, is, eposs[12], eps[4]; + + index_to_perm(epos % FACTORIAL4, 4, eps); + index_to_subset(epos / FACTORIAL4, 12, 4, eposs); + + for (i = 0; i < 4; i++) + swap(&eposs[ss[i]], &eposs[i+8]); + + for (i = 0, is = 0; i < 12; i++) + if (eposs[i]) + ep[i] = ss[eps[is++]]; +} + +void +free_cubearray(CubeArray *arr, PieceFilter f) +{ + if (f.epose || f.eposs || f.eposm) + free(arr->ep); + if (f.eofb) + free(arr->eofb); + if (f.eorl) + free(arr->eorl); + if (f.eoud) + free(arr->eoud); + if (f.cp) + free(arr->cp); + if (f.coud) + free(arr->coud); + if (f.corl) + free(arr->corl); + if (f.cofb) + free(arr->cofb); + if (f.cpos) + free(arr->cpos); + + free(arr); +} + +Cube +move_via_arrays(CubeArray *arr, Cube c, PieceFilter f) +{ + CubeArray *arrc = new_cubearray(c, f); + Cube ret; + + if (f.epose || f.eposs || f.eposm) + apply_permutation(arr->ep, arrc->ep, 12); + + if (f.eofb) { + apply_permutation(arr->ep, arrc->eofb, 12); + sum_arrays_mod(arr->eofb, arrc->eofb, 12, 2); + } + + if (f.eorl) { + apply_permutation(arr->ep, arrc->eorl, 12); + sum_arrays_mod(arr->eorl, arrc->eorl, 12, 2); + } + + if (f.eoud) { + apply_permutation(arr->ep, arrc->eoud, 12); + sum_arrays_mod(arr->eoud, arrc->eoud, 12, 2); + } + + if (f.cp) + apply_permutation(arr->cp, arrc->cp, 8); + + if (f.coud) { + apply_permutation(arr->cp, arrc->coud, 8); + sum_arrays_mod(arr->coud, arrc->coud, 8, 3); + } + + if (f.corl) { + apply_permutation(arr->cp, arrc->corl, 8); + sum_arrays_mod(arr->corl, arrc->corl, 8, 3); + } + + if (f.cofb) { + apply_permutation(arr->cp, arrc->cofb, 8); + sum_arrays_mod(arr->cofb, arrc->cofb, 8, 3); + } + + if (f.cpos) + apply_permutation(arr->cpos, arrc->cpos, 6); + + ret = arrays_to_cube(arrc, f); + free_cubearray(arrc, f); + + return ret; +} + +CubeArray * +new_cubearray(Cube cube, PieceFilter f) +{ + CubeArray *arr = malloc(sizeof(CubeArray)); + + if (f.epose || f.eposs || f.eposm) + arr->ep = malloc(12 * sizeof(int)); + if (f.eofb) + arr->eofb = malloc(12 * sizeof(int)); + if (f.eorl) + arr->eorl = malloc(12 * sizeof(int)); + if (f.eoud) + arr->eoud = malloc(12 * sizeof(int)); + if (f.cp) + arr->cp = malloc(8 * sizeof(int)); + if (f.coud) + arr->coud = malloc(8 * sizeof(int)); + if (f.corl) + arr->corl = malloc(8 * sizeof(int)); + if (f.cofb) + arr->cofb = malloc(8 * sizeof(int)); + if (f.cpos) + arr->cpos = malloc(6 * sizeof(int)); + + cube_to_arrays(cube, arr, f); + + return arr; +} + +Cube +admissible_ep(Cube cube, PieceFilter f) +{ + CubeArray *arr = new_cubearray(cube, f); + Cube ret; + bool used[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i, j; + + for (i = 0; i < 12; i++) + if (arr->ep[i] != -1) + used[arr->ep[i]] = true; + + for (i = 0, j = 0; i < 12; i++) { + for ( ; j < 11 && used[j]; j++); + if (arr->ep[i] == -1) + arr->ep[i] = j++; + } + + ret = arrays_to_cube(arr, pf_ep); + free_cubearray(arr, f); + + return ret; +} + +Cube +compose(Cube c2, Cube c1) +{ + return compose_filtered(c2, c1, pf_all); +} + +int +edge_slice(Edge e) { + if (e < 0 || e > 11) + return -1; + + if (e == FR || e == FL || e == BL || e == BR) + return 0; + if (e == UR || e == UL || e == DR || e == DL) + return 1; + + return 2; +} + +bool +equal(Cube c1, Cube c2) +{ + return c1.eofb == c2.eofb && + c1.epose == c2.epose && + c1.eposs == c2.eposs && + c1.eposm == c2.eposm && + c1.coud == c2.coud && + c1.cp == c2.cp && + c1.cpos == c2.cpos; +} + +Cube +inverse_cube(Cube cube) +{ + CubeArray *arr = new_cubearray(cube, pf_all); + CubeArray *inv = new_cubearray((Cube){0}, pf_all); + Cube ret; + int i; + + for (i = 0; i < 12; i++) { + inv->ep[arr->ep[i]] = i; + inv->eofb[arr->ep[i]] = arr->eofb[i]; + inv->eorl[arr->ep[i]] = arr->eorl[i]; + inv->eoud[arr->ep[i]] = arr->eoud[i]; + } + + for (i = 0; i < 8; i++) { + inv->cp[arr->cp[i]] = i; + inv->coud[arr->cp[i]] = (3 - arr->coud[i]) % 3; + inv->corl[arr->cp[i]] = (3 - arr->corl[i]) % 3; + inv->cofb[arr->cp[i]] = (3 - arr->cofb[i]) % 3; + } + + for (int i = 0; i < 6; i++) + inv->cpos[arr->cpos[i]] = i; + + ret = arrays_to_cube(inv, pf_all); + free_cubearray(arr, pf_all); + free_cubearray(inv, pf_all); + + return ret; +} + +bool +is_admissible(Cube cube) +{ + /* TODO: this should check consistency of different orientations */ + /* check also that centers are opposite and admissible */ + + CubeArray *a = new_cubearray(cube, pf_all); + int parity; + bool perm; + + perm = is_perm(a->ep, 12) && + is_perm(a->cp, 8) && + is_perm(a->cpos, 6); + parity = perm_sign(a->ep, 12) + + perm_sign(a->cp, 8) + + perm_sign(a->cpos, 6); + + return perm && parity % 2 == 0; +} + +bool +is_solved(Cube cube) +{ + return equal(cube, (Cube){0}); +} + +bool +is_block_solved(Cube cube, Block block) +{ + int i; + + for (i = 0; i < 12; i++) + if (block.edge[i] && !is_solved_edge(cube, i)) + return false; + for (i = 0; i < 8; i++) + if (block.corner[i] && !is_solved_corner(cube, i)) + return false; + for (i = 0; i < 6; i++) + if (block.center[i] && !is_solved_center(cube, i)) + return false; + + return true; +} + +bool +is_solved_center(Cube cube, Center c) +{ + return what_center_at(cube, c) == c; +} + +bool +is_solved_corner(Cube cube, Corner c) +{ + return what_corner_at(cube, c) == c && + what_orientation_corner(cube.coud, c); +} + +bool +is_solved_edge(Cube cube, Edge e) +{ + return what_edge_at(cube, e) == e && + what_orientation_edge(cube.eofb, e); +} + +int +piece_orientation(Cube cube, int piece, char *orientation) +{ + int arr[12], n, b, x; + + if (!strcmp(orientation, "eofb")) { + x = cube.eofb; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eorl")) { + x = cube.eorl; + n = 12; + b = 2; + } else if (!strcmp(orientation, "eoud")) { + x = cube.eoud; + n = 12; + b = 2; + } else if (!strcmp(orientation, "coud")) { + x = cube.coud; + n = 8; + b = 3; + } else if (!strcmp(orientation, "corl")) { + x = cube.corl; + n = 8; + b = 3; + } else if (!strcmp(orientation, "cofb")) { + x = cube.cofb; + n = 8; + b = 3; + } else { + return -1; + } + + int_to_sum_zero_array(x, b, n, arr); + if (piece < n) + return arr[piece]; + + return -1; +} + +void +print_cube(Cube cube) +{ + static char edge_string[12][7] = { + [UF] = "UF", [UL] = "UL", [UB] = "UB", [UR] = "UR", + [DF] = "DF", [DL] = "DL", [DB] = "DB", [DR] = "DR", + [FR] = "FR", [FL] = "FL", [BL] = "BL", [BR] = "BR" + }; + + static char corner_string[8][7] = { + [UFR] = "UFR", [UFL] = "UFL", [UBL] = "UBL", [UBR] = "UBR", + [DFR] = "DFR", [DFL] = "DFL", [DBL] = "DBL", [DBR] = "DBR" + }; + + static char center_string[6][7] = { + [U_center] = "U", [D_center] = "D", + [R_center] = "R", [L_center] = "L", + [F_center] = "F", [B_center] = "B" + }; + + for (int i = 0; i < 12; i++) + printf(" %s ", edge_string[what_edge_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 12; i++) + printf(" %d ", what_orientation_edge(cube.eofb, i)); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf("%s ", corner_string[what_corner_at(cube, i)]); + printf("\n"); + + for (int i = 0; i < 8; i++) + printf(" %d ", what_orientation_corner(cube.coud, i)); + printf("\n"); + + for (int i = 0; i < 6; i++) + printf(" %s ", center_string[what_center_at(cube, i)]); + printf("\n"); +} + +Cube +random_cube() +{ + CubeArray *arr = new_cubearray((Cube){0}, pf_4val); + Cube ret; + int ep, cp, eo, co; + + ep = rand() % FACTORIAL12; + cp = rand() % FACTORIAL8; + eo = rand() % POW2TO11; + co = rand() % POW3TO7; + + index_to_perm(ep, 12, arr->ep); + index_to_perm(cp, 8, arr->cp); + int_to_sum_zero_array(eo, 2, 12, arr->eofb); + int_to_sum_zero_array(co, 3, 8, arr->coud); + + if (perm_sign(arr->ep, 12) != perm_sign(arr->cp, 8)) + swap(&(arr->ep[0]), &(arr->ep[1])); + + ret = arrays_to_cube(arr, pf_4val); + free_cubearray(arr, pf_4val); + + return ret; +} + +Center +what_center_at(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][i] = arr->cpos[i]; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +what_corner_at(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][i] = arr->cp[i]; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + + return aux[cube.cp][c]; +} + +Edge +what_edge_at(Cube cube, Edge e) +{ + Edge ret; + CubeArray *arr = new_cubearray(cube, pf_ep); + + ret = arr->ep[e]; + + free_cubearray(arr, pf_ep); + return ret; +} + +int +what_orientation_corner(int co, Corner c) +{ + static bool initialized = false; + static int auxlast[POW3TO7]; + static int auxarr[8]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, auxarr); + auxlast[ui] = auxarr[7]; + } + + initialized = true; + } + + if (c < 7) + return (co / powint(3, c)) % 3; + else + return auxlast[co]; +} + +int +what_orientation_edge(int eo, Edge e) +{ + static bool initialized = false; + static int auxlast[POW2TO11]; + static int auxarr[12]; + static unsigned int ui; + + if (!initialized) { + for (ui = 0; ui < POW2TO11; ui++) { + int_to_sum_zero_array(ui, 2, 12, auxarr); + auxlast[ui] = auxarr[11]; + } + + initialized = true; + } + + if (e < 11) + return (eo & (1 << e)) ? 1 : 0; + else + return auxlast[eo]; +} + +Center +where_is_center(Cube cube, Center c) +{ + static bool initialized = false; + static Center aux[FACTORIAL6][6]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL6; ui++) { + arr = new_cubearray((Cube){.cpos = ui}, pf_cpos); + for (i = 0; i < 6; i++) + aux[ui][arr->cpos[i]] = i; + free_cubearray(arr, pf_cpos); + } + + initialized = true; + } + + return aux[cube.cpos][c]; +} + +Corner +where_is_corner(Cube cube, Corner c) +{ + static bool initialized = false; + static Corner aux[FACTORIAL8][8]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.cp = ui}, pf_cp); + for (i = 0; i < 8; i++) + aux[ui][arr->cp[i]] = i; + free_cubearray(arr, pf_cp); + } + + initialized = true; + } + return aux[cube.cp][c]; +} + +Edge +where_is_edge(Cube cube, Edge e) +{ + /* TODO: when I wrote this code I forgot to add the final + part, and now I can't remember how it was supposed to + work (i.e. how to recover the location of the edge + from these tables. I think it is either very easy or + wrong, in any case it is not a priority now. + Future Seba can deal with it. + + static bool initialized = false; + static Edge aux[3][FACTORIAL12/FACTORIAL8][12]; + static int i; + static unsigned int ui; + static CubeArray *arr; + + if (!initialized) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + arr = new_cubearray((Cube){.epose = ui}, pf_e); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 0) + aux[0][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_e); + + arr = new_cubearray((Cube){.eposs = ui}, pf_s); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 1) + aux[1][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_s); + + arr = new_cubearray((Cube){.eposm = ui}, pf_m); + for (i = 0; i < 12; i++) + if (edge_slice(arr->ep[i]) == 2) + aux[2][ui][arr->ep[i]] = i; + free_cubearray(arr, pf_m); + } + + initialized = true; + } + */ + + int i; + CubeArray *arr = new_cubearray(cube, pf_ep); + + for (i = 0; i < 12; i++) + if ((Edge)arr->ep[i] == e) + return i; + + return -1; +} diff --git a/src/cube.h b/src/cube.h new file mode 100644 index 0000000..d1c5bfc --- /dev/null +++ b/src/cube.h @@ -0,0 +1,40 @@ +#ifndef CUBE_H +#define CUBE_H + +#include +#include + +#include "pf.h" +#include "utils.h" + +Cube admissible_ep(Cube cube, PieceFilter f); +Cube arrays_to_cube(CubeArray *arr, PieceFilter f); +Cube compose(Cube c2, Cube c1); /* Use c2 as an alg on c1 */ +Cube compose_filtered(Cube c2, Cube c1, PieceFilter f); +void cube_to_arrays(Cube cube, CubeArray *arr, PieceFilter f); +int edge_slice(Edge e); /* E=0, S=1, M=2 */ +bool equal(Cube c1, Cube c2); +Cube inverse_cube(Cube cube); +bool is_admissible(Cube cube); +bool is_solved(Cube cube); +bool is_block_solved(Cube cube, Block); +bool is_solved_center(Cube cube, Center c); +bool is_solved_corner(Cube cube, Corner c); +bool is_solved_edge(Cube cube, Edge e); +void epos_to_partial_ep(int epos, int *ep, int *ss); +void free_cubearray(CubeArray *arr, PieceFilter f); +Cube move_via_arrays(CubeArray *arr, Cube c, PieceFilter pf); +CubeArray * new_cubearray(Cube cube, PieceFilter f); +void print_cube(Cube cube); +Cube random_cube(); +Center what_center_at(Cube cube, Center c); +Corner what_corner_at(Cube cube, Corner c); +Edge what_edge_at(Cube cube, Edge e); +int what_orientation_corner(int co, Corner c); +int what_orientation_edge(int eo, Edge e); +Center where_is_center(Cube cube, Center c); +Corner where_is_corner(Cube cube, Corner c); +Edge where_is_edge(Cube cube, Edge e); + +#endif + diff --git a/src/cubetypes.h b/src/cubetypes.h new file mode 100644 index 0000000..6602a68 --- /dev/null +++ b/src/cubetypes.h @@ -0,0 +1,286 @@ +#ifndef CUBETYPES_H +#define CUBETYPES_H + +#include +#include + +#define NMOVES 55 /* Actually 55, but one is NULLMOVE */ +#define NTRANS 48 +#define NROTATIONS 24 + +/* Enums *********************************************************************/ + +typedef enum +center +{ + U_center, D_center, + R_center, L_center, + F_center, B_center +} Center; + +typedef enum +corner +{ + UFR, UFL, UBL, UBR, + DFR, DFL, DBL, DBR +} Corner; + +typedef enum +edge +{ + UF, UL, UB, UR, + DF, DL, DB, DR, + FR, FL, BL, BR +} Edge; + +typedef enum +move +{ + NULLMOVE, + U, U2, U3, D, D2, D3, + R, R2, R3, L, L2, L3, + F, F2, F3, B, B2, B3, + Uw, Uw2, Uw3, Dw, Dw2, Dw3, + Rw, Rw2, Rw3, Lw, Lw2, Lw3, + Fw, Fw2, Fw3, Bw, Bw2, Bw3, + M, M2, M3, + S, S2, S3, + E, E2, E3, + x, x2, x3, + y, y2, y3, + z, z2, z3, +} Move; + +typedef enum +trans +{ + uf, ur, ub, ul, + df, dr, db, dl, + rf, rd, rb, ru, + lf, ld, lb, lu, + fu, fr, fd, fl, + bu, br, bd, bl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, + rf_mirror, rd_mirror, rb_mirror, ru_mirror, + lf_mirror, ld_mirror, lb_mirror, lu_mirror, + fu_mirror, fr_mirror, fd_mirror, fl_mirror, + bu_mirror, br_mirror, bd_mirror, bl_mirror, +} Trans; + + +/* Typedefs ******************************************************************/ + +typedef struct alg Alg; +typedef struct alglist AlgList; +typedef struct alglistnode AlgListNode; +typedef struct block Block; +typedef struct command Command; +typedef struct commandargs CommandArgs; +typedef struct coordinate Coordinate; +typedef struct cube Cube; +typedef struct cubearray CubeArray; +typedef struct cubetarget CubeTarget; +typedef struct dfsdata DfsData; +typedef struct piecefilter PieceFilter; +typedef struct prunedata PruneData; +typedef struct solveoptions SolveOptions; +typedef struct step Step; +typedef struct symdata SymData; + +typedef Cube (*AntiIndexer) (uint64_t); +typedef bool (*Checker) (Cube); +typedef int (*Estimator) (CubeTarget); +typedef bool (*Validator) (Alg *); +typedef void (*Exec) (CommandArgs *); +typedef uint64_t (*Indexer) (Cube); +typedef bool (*Moveset) (Move); +typedef CommandArgs * (*ArgParser) (int, char **); +typedef Trans (*TransDetector) (Cube); + + +/* Structs *******************************************************************/ + +struct +alg +{ + Move * move; + bool * inv; + int len; + int allocated; +}; + +struct +alglist +{ + AlgListNode * first; + AlgListNode * last; + int len; +}; + +struct +alglistnode +{ + Alg * alg; + AlgListNode * next; +}; + +struct +block +{ + bool edge[12]; + bool corner[8]; + bool center[6]; +}; + +struct +command +{ + char * name; + char * usage; + char * description; + ArgParser parse_args; + Exec exec; +}; + +struct +commandargs +{ + bool success; + Alg * scramble; + SolveOptions * opts; + Step * step; + Command * command; /* For help */ +}; + +struct +coordinate +{ + Indexer index; + AntiIndexer cube; + uint64_t max; + int ntrans; + Trans * trans; +}; + +struct +cube +{ + int epose; + int eposs; + int eposm; + int eofb; + int eorl; + int eoud; + int cp; + int coud; + int cofb; + int corl; + int cpos; +}; + +struct +cubearray +{ + int * ep; + int * eofb; + int * eorl; + int * eoud; + int * cp; + int * coud; + int * corl; + int * cofb; + int * cpos; +}; + +struct +cubetarget +{ + Cube cube; + int target; +}; + +struct +dfsdata +{ + int d; + int m; + int lb; + bool niss; + Move last1; + Move last2; + AlgList * sols; + Alg * current_alg; + Move sorted_moves[NMOVES]; + int move_position[NMOVES]; +}; + +struct +piecefilter +{ + bool epose; + bool eposs; + bool eposm; + bool eofb; + bool eorl; + bool eoud; + bool cp; + bool coud; + bool cofb; + bool corl; + bool cpos; +}; + +struct +prunedata +{ + char * filename; + uint8_t * ptable; + bool generated; + uint64_t n; + Coordinate * coord; + Moveset moveset; +}; + +struct +solveoptions +{ + int min_moves; + int max_moves; + int max_solutions; + bool optimal_only; + bool can_niss; + bool verbose; + bool all; + bool print_number; +}; + +struct +step +{ + char * shortname; + char * name; + Estimator estimate; + Checker ready; + char * ready_msg; + Validator is_valid; + Moveset moveset; + Trans pre_trans; + TransDetector detect; +}; + +struct +symdata +{ + char * filename; + bool generated; + Coordinate * coord; + Coordinate * sym_coord; + int ntrans; + Trans * trans; + uint64_t * class; + Cube * rep; + Trans * transtorep; +}; + +#endif diff --git a/src/env.c b/src/env.c new file mode 100644 index 0000000..d13642f --- /dev/null +++ b/src/env.c @@ -0,0 +1,45 @@ +#include "env.h" + +bool initialized_env = false; +char *tabledir; + +void +init_env() +{ + char *nissydata = getenv("NISSYDATA"); + char *localdata = getenv("XDG_DATA_HOME"); + char *home = getenv("HOME"); + bool read, write; + + if (initialized_env) + return; + + if (nissydata != NULL) { + tabledir = malloc(strlen(nissydata) * sizeof(char) + 20); + strcpy(tabledir, nissydata); + } else if (localdata != NULL) { + tabledir = malloc(strlen(localdata) * sizeof(char) + 20); + strcpy(tabledir, localdata); + strcat(tabledir, "/nissy"); + } else if (home != NULL) { + tabledir = malloc(strlen(home) * sizeof(char) + 20); + strcpy(tabledir, home); + strcat(tabledir, "/.nissy"); + } + + mkdir(tabledir, 0777); + strcat(tabledir, "/tables"); + mkdir(tabledir, 0777); + + read = !access(tabledir, R_OK); + write = !access(tabledir, W_OK); + + if (!read) { + fprintf(stderr, "Table files cannot be read.\n"); + } else if (!write) { + fprintf(stderr, "Data directory not writable: "); + fprintf(stderr, "tables can be loaded, but not saved.\n"); + } + + initialized_env = true; +} diff --git a/src/env.h b/src/env.h new file mode 100644 index 0000000..871a9c1 --- /dev/null +++ b/src/env.h @@ -0,0 +1,15 @@ +#ifndef ENV_H +#define ENV_H + +#include +#include +#include +#include +#include +#include + +extern char *tabledir; + +void init_env(); + +#endif diff --git a/src/helppages.h b/src/helppages.h deleted file mode 100644 index 178de37..0000000 --- a/src/helppages.h +++ /dev/null @@ -1,689 +0,0 @@ -/* To generate this help page, use the script makedoc.sh */ - -int Npages = 22; - -char *helppages[][10] = { - -{ "add", -"\ -\n\ -HELP PAGE FOR COMMAND add\n\ -\n\ -SYNTAX\n\ -add [MOVES|$ID1|@ID1] $ID2\n\ -\n\ -DESCRIPTION\n\ -Appends either MOVES, the scramble memorized under $ID1 or the output sequence\n\ -memorized under @ID1 at the end of the scramble memorized under $ID2. If none\n\ -of MOVES, $ID1 or @ID1 is specified, the user will be asked to type the moves.\n\ -Menmonic: \"add x to y\" or just \"add to y\".\n\ -\n\ -EXAMPLES\n\ -add $1\n\ - The user is required to type the moves that will be appended to $1.\n\ -add F R B $1\n\ - Appends the moves F R B to scramble $1. Now scramble $1 ends with F R B.\n\ -\n\ -" -}, - -{ "change", -"\ -\n\ -HELP PAGE FOR COMMAND change\n\ -\n\ -SYNTAX\n\ -change $ID1 [MOVES|$ID2|@ID2]\n\ -\n\ -DESCRIPTION\n\ -Changes the scramble $ID1 to either MOVES, the scramble $ID2, the output @ID2\n\ -or, if none is specified, the moves entered by the user. The scramble that was\n\ -memorized under $ID1 is then lost.\n\ -Mnemonic: \"change x to y\", or just \"change x\".\n\ -\n\ -EXAMPLES\n\ -change $1\n\ - The user is required to type the moves that will replace $1.\n\ -change $2 $3\n\ - Saves the scramble that was saved under $3 in $2. Now $2 and $3 are the same\n\ - scrambles, and the old $2 is lost.\n\ -change $1 U R\n\ - Saves U R as scramble $1.\n\ -\n\ -" -}, - -{ "clear", -"\ -\n\ -HELP PAGE FOR COMMAND clear\n\ -\n\ -SYNTAX\n\ -clear\n\ -\n\ -DESCRIPTION\n\ -Resets all saved scrambles and output sequences.\n\ -\n\ -" -}, - -{ "co", -"\ -\n\ -HELP PAGE FOR COMMAND co\n\ -\n\ -SYNTAX\n\ -co [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves CO for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the CO. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: CO on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden COs.\n\ - Default, if an CO ending in e.g. F is shown, the equivalent\n\ - one ending in F' is hidden.\n\ -i Ignore centers. By default the CO is aligned with centers.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of COs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -co axis=fb $1\n\ - Finds one optimal CO on fb for the first saved scramble.\n\ -\n\ -co n=5 b=4 U R F \n\ - Finds up to 5 COs of length at most 4 for scramble U R F.\n\ -\n\ -co n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\ - Finds up to 100 COs of lenth at most 4, possibly using NISS, including\n\ - \"hidden\" COs, excluding the rl axis.\n\ -\n\ -" -}, - -{ "dr", -"\ -\n\ -HELP PAGE FOR COMMAND dr\n\ -\n\ -SYNTAX\n\ -dr [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves DR for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -If the option \"from\" is given (see below), it solves DR from an EO (if edges\n\ -are oriented) without breaking that EO.\n\ -The first time this command is called without the option from (and, to some\n\ -extent, also the first time it is called with the option from), nissy loads\n\ -some pruning tables that were not loaded on startup, causing a small but\n\ -noticeable delay.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the DR. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: DR on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden DRs.\n\ - Default, if an DR ending in e.g. R is shown, the equivalent\n\ - one ending in R' is hidden.\n\ -from {fb|rl|ud} Solve DR from the specified EO, which must be solved,\n\ - without breaking the EO.\n\ -niss Use NISS. It works only if solving DR from EO.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of EOs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -dr from rl axis=ud $1\n\ - Finds optimal DR on ud, starting from EO on rl, for the first saved scramble.\n\ -\n\ -dr niss from fb n=10 m=6 F2 R L B' F D U' R2 L' F D B\n\ - Finds up to 10 DRs of length at most 6 from EO on fb, possibly using NISS.\n\ -\n\ -dr n=100 axis=ud h\n\ - Finds 100 DRs on ud, including \"hidden\" DRs.\n\ -\n\ -" -}, - -{ "drcorners", -"\ -\n\ -HELP PAGE FOR COMMAND drcorners\n\ -\n\ -SYNTAX\n\ -drcorners [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Similar to drfinish, but only solves corners. CO must be solved. The scramble\n\ -can be given as the last argument of the command, or it may be given as an $ID\n\ -or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the CO is solved. It is\n\ - usually not necessary, since nissy will find a CO on any\n\ - axis.\n\ -i Ignores E-layer centers. By default cornersare solved\n\ - relatively to centers; this options allows for solutions\n\ - which solve corners relatively to each other and to the\n\ - U and D sides, but not to the E layer (or any equivalent\n\ - if the CO is not on U/D).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number of solutions to be output. N must\n\ - be a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -drcorners n=3 R' D R2 D' R' U2 R D R' U2 R' D' R\n\ - Produces the following output:\n\ -Found 3 results.\n\ -@1: U' F2 U R2 U2 F2 U F2 U R2 (10)\n\ -@2: U' F2 U R2 U2 B2 U R2 D R2 (10)\n\ -@3: U' F2 U R2 U2 B2 U L2 U L2 (10)\n\ -\n\ -" -}, - -{ "drfinish", -"\ -\n\ -HELP PAGE FOR COMMAND drfinish\n\ -\n\ -SYNTAX\n\ -drfinish [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves the given scramble using the DR moveset. DR must be solved. The scramble\n\ -can be given as the last argument of the command, or it may be given as an $ID\n\ -or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the DR is solved. It is\n\ - usually not necessary, since nissy will find a DR on any\n\ - axis, but it can be useful if one want to e.g. solve an HTR\n\ - state allowing quarter-turns from a specific DR.\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number of solutions to be output. N must\n\ - be a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -dr from ud R L' U2 R' L F2\n\ - Solves the given scramble using the moveset . In this case:\n\ -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\ -drfinish b=7 n=10 $1\n\ - Finds (at most) 10 solutions of length at most 7 for the scramble $1.\n\ -\n\ -" -}, - -{ "eo", -"\ -\n\ -HELP PAGE FOR COMMAND eo\n\ -\n\ -SYNTAX\n\ -eo [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves EO for a given scramble. A scramble can be given as last argument of the\n\ -command, or an ID of a saved scramble can be provided. If none of the two is\n\ -given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -axis={fb,rl,ud} Specify the axis for the EO. One to three axes can be given,\n\ - comma separated, no spaces.\n\ - Default: EO on any of the three axis (omitting the option is\n\ - the same as specifying axis=fb,rl,ud).\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden EOs.\n\ - Default, if an EO ending in e.g. F is shown, the equivalent\n\ - one ending in F' is hidden.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of EOs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -eo axis=fb $1\n\ - Finds one optimal EO on fb for the first saved scramble.\n\ -\n\ -eo n=5 b=4 U R F \n\ - Finds up to 5 EOs of length at most 4 for scramble U R F.\n\ -\n\ -eo n=100 b=5 niss axis=fb,ud h R' U' F L R'U'F\n\ - Finds up to 100 EOs of lenth at most 4, possibly using NISS, including\n\ - \"hidden\" EOs, excluding the rl axis.\n\ -\n\ -" -}, - -{ "exit", -"\ -\n\ -HELP PAGE FOR COMMAND exit\n\ -\n\ -SYNTAX\n\ -exit\n\ -\n\ -DESCRIPTION\n\ -Exits nissy.\n\ -\n\ -" -}, - -{ "help", -"\ -\n\ -HELP PAGE FOR COMMAND help\n\ -\n\ -SYNTAX\n\ -help [nissy|COMMAND]\n\ -\n\ -DESCRIPTION\n\ -'help nissy' prints a general user manual. 'help COMMAND' prints a detailed\n\ -help page for the command COMMAND, if it exists. 'help' prints a list of all\n\ -available commands a short description for each.\n\ -\n\ -EXAMPLES\n\ -help help\n\ - Prints this help page.\n\ -\n\ -" -}, - -{ "htr", -"\ -\n\ -HELP PAGE FOR COMMAND htr\n\ -\n\ -SYNTAX\n\ -htr [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Finds HTR for a given scramble. DR must be solved. A scramble can be given as\n\ -last argument of the command, or an ID of a saved scramble can be provided. If\n\ -none of the two is given, a prompt will ask the user to input a new scramble.\n\ -\n\ -OPTIONS\n\ -from {ud|fb|rl} Allows to specify on which axis the DR is. This is usually\n\ - not needed, since nissy will automatically find it out.\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -h Show hidden HTRs.\n\ - Default, if an HTR ending in e.g. R is shown, the equivalent\n\ - one ending in R' is hidden.\n\ -niss Use NISS.\n\ - Default: does not use NISS.\n\ -n=N Specify a maximum number of HTRs to be output. N must be a\n\ - number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -eo n=10 b=7 niss $1\n\ - Finds up to 100 HTRs of lenth at most 7, possibly using NISS, including\n\ - \"hidden\" HTRs, for scramble $1. DR must be solved.\n\ -\n\ -" -}, - -{ "htrfinish", -"\ -\n\ -HELP PAGE FOR COMMAND htrfinish\n\ -\n\ -SYNTAX\n\ -htrfinish [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Similar to drfinish, but uses the moveset . HTR must be\n\ -solved. The scramble can be given as the last argument of the command, or it\n\ -may be given as an $ID or @ID, or it can be typed out on the following line.\n\ -\n\ -OPTIONS\n\ -b=N Specify a bound for the number of moves. N must be a number.\n\ - Default value: 20.\n\ -n=N Specify a maximum number ofsolutions to be output. N must be\n\ - a number.\n\ - Default value: 1.\n\ -\n\ -EXAMPLES\n\ -htrfinish R L' U2 R' L F2\n\ - Produces the following solution:\n\ -@1: U2 R2 F2 R2 U2 R2 F2 R2 (8)\n\ -\n\ -" -}, - -{ "invert", -"\ -\n\ -HELP PAGE FOR COMMAND invert\n\ -\n\ -SYNTAX\n\ -invert [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Inverts a sequence of moves, which can be given also as $ID or @ID. The given\n\ -sequence must not use NISS (if it does, use the command unniss first).\n\ -\n\ -EXAMPLES\n\ -invert F R D'\n\ - Prints D R' F'\n\ -\n\ -" -}, - -{ "nissy", -"\ -\n\ -*******************************************************************************\n\ -********************* NISSY: a cube solver and FMC helper *********************\n\ -*******************************************************************************\n\ -\n\ -If you just want to solve the cube, type 'solve' followed by the scramble. This\n\ -will not always give you an optimal solution, unless it is 10 moves or less or\n\ -you use the \"o\" option. Finding the optimal solution might take very long if it\n\ -is 16 moves or more, especially for the first time.\n\ -\n\ -Now the fun stuff. With nissy you can save and manipulate move sequences, for\n\ -example:\n\ -\n\ -nissy-# save R' U' F\n\ -$1: R' U' F\n\ -nissy-# add L2D' $1\n\ -$1: R' U' F L2 D'\n\ -\n\ -You can then ask nissy to solve certain substepson a saved scramble:\n\ -\n\ -nissy-# eo axis=rl $1\n\ -@1: U D F' R (4)\n\ -\n\ -And of course it uses also NISS, if you ask:\n\ -\n\ -nissy-# eo niss axis=rl $1\n\ -@1: (R) (1)\n\ -\n\ -Notice that the sequences you save are marked with a $, while the \"output\"\n\ -sequences are marked with @. The difference between these two type of sequences\n\ -is that those marked with @ are temporary and get lost once you get new output.\n\ -Most commands accept as input either a move sequence typed out, a $-sequence or\n\ -a @-sequence. For example, you can however save a @-sequence and make it\n\ -persistent:\n\ -\n\ -nissy-# save @1\n\ -$2: (R)\n\ -\n\ -Nissy also understands NISS. Let's see a more complicated example where you\n\ -save a scramble, ask for some EOs (using NISS) and then a DR on inverse:\n\ -\n\ -nissy-# save R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\ -nissy-# eo n=10 niss axis=fb,rl $3\n\ -Found 10 results.\n\ -@1: (U' L B D F) (5)\n\ -@2: (U' L B' D F) (5)\n\ -@3: (L B U D F) (5)\n\ -@4: (L B' U D F) (5)\n\ -@5: R U B U L (5)\n\ -@6: R U' L (B L) (5)\n\ -@7: R U' B U L (5)\n\ -@8: R L (L B L) (5)\n\ -@9: R (U2 D' F R) (5)\n\ -@10: R (U2 F D' R) (5)\n\ -nissy-# add @6 $3\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\ -nissy-# unniss $3\n\ -@1: L' B' R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L \n\ -nissy-# invert @1\n\ -@1: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\ -nissy-# save @1\n\ -$5: L' U R' F' U R F2 U2 D' R D' R' U D' B D2 R2 U' L2 U' R2 D' R2 F2 R2 U' R' F' U R B L \n\ -nissy-# dr from rl $5\n\ -@1: F2 U D2 F' B D B (7)\n\ -nissy-# \n\ -\n\ -If you ask nissy to solve a substep (or the whole cube) using a sequence with\n\ -NISS as scramble, it will first un-NISS it (but without saving the unNISSed\n\ -scramble anywhere):\n\ -\n\ -print $3\n\ -$3: R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F R U' L (B L) \n\ -nissy-# solve $3\n\ -@1: F U' R2 F2 U2 F U2 R2 L2 D R2 U B2 D' L2 D B2 D2 (18)\n\ -\n\ -Nissy knows how to solve certain common sub-steps for DR (or Thistlethwaite /\n\ -Kociemba algorithms). For now it does know more common speedsolving methods.\n\ -\n\ -For a full list of commands type \"help\". For a more detailed help on a specific\n\ -command, type \"help (command)\". The help pages can also be found in the docs\n\ -folder.\n\ -\n\ -If you want to report a bug (I'm sure there are many!) or give a suggestion,\n\ -you can send an email to sebastiano.tronto@gmail.com.\n\ -\n\ -Have fun!\n\ -\n\ -" -}, - -{ "pic", -"\ -\n\ -HELP PAGE FOR COMMAND pic\n\ -\n\ -SYNTAX\n\ -pic [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Prints the cube state after applying the given scramble.\n\ -\n\ -EXAMPLES\n\ -pic R' U' F R U R2 F2 R2 D R2 U L2 U R2 D2 B' D U' R D R' D U2 F2 R' U' F \n\ - Gives the following output:\n\ - UF UL UB UR DF DL DB DR FR FL BL BR \n\ -EP: FR UL FL UR UF DB DR BL UB BR DL DF \n\ -EO(F/B): x x x x x x x x \n\ -\n\ - UFR UFL UBL UBR DFR DFL DBL DBR \n\ -CP: UBR UFR DFL DBL UFL UBL DBR DFR \n\ -CO(U/D): ccw cw ccw cw\n\ -\n\ -" -}, - -{ "print", -"\ -\n\ -HELP PAGE FOR COMMAND print\n\ -\n\ -SYNTAX\n\ -print [$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Prints memorized sequences. If no argument is given, it prints all memorized\n\ -scrambles ($ only). If $ID or @ID is specified, it only prints the relative\n\ -memorized sequence.\n\ -\n\ -EXAMPLES\n\ -print\n\ - Prints a list of all memorized scrambles (only $).\n\ -print $2\n\ - Prints the second memorized scramble.\n\ -print @13\n\ - Prints the 13th sequence that was part of the output of the last command.\n\ -\n\ -" -}, - -{ "quit", -"\ -\n\ -HELP PAGE FOR COMMAND quit\n\ -\n\ -SYNTAX\n\ -quit\n\ -\n\ -DESCRIPTION\n\ -Exits nissy.\n\ -\n\ -" -}, - -{ "replace", -"\ -\n\ -HELP PAGE FOR COMMAND replace\n\ -\n\ -SYNTAX\n\ -eo [OPTIONS] [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Looks for non-optimal subsequences and replaces them to shorten the given\n\ -sequence. By default it tries to shorten every subsequence of up to 10 moves,\n\ -but this can be change with the \"b\" option.\n\ -It outputs at most 10 equivalent optimal sequences for each replaceable part.\n\ -\n\ -OPTIONS\n\ -b=N Finds non-optimal subsequences of up to N moves.\n\ -\n\ -EXAMPLES\n\ -replace D2 F' D2 U2 F' L2 R2 U' D B2 D B2 U B2 F L2 R' F' D U' \n\ - Produces the following output:\n\ -Replace [ R2 U' D B2 D B2 ] (moves 7-12) with: [ D R2 D U' ] (-6+4)\n\ -Replace [ R2 U' D B2 D B2 U ] (moves 7-13) with: [ D R2 D ] (-7+3)\n\ -Replace [ U' D B2 D B2 U ] (moves 8-13) with: [ R2 D R2 D ] (-6+4)\n\ -\n\ -" -}, - -{ "save", -"\ -\n\ -HELP PAGE FOR COMMAND save\n\ -\n\ -SYNTAX\n\ -save [MOVES|@ID|$ID]\n\ -\n\ -DESCRIPTION\n\ -Memorizes the scramble specified by MOVES, given as input or temporarily saved\n\ -as @ID, where ID is a number ('help nissy' for for more on IDs). If an $ID is\n\ -given, it makes a copy of the scramble. An identifier of the form $ID, where ID\n\ -is a number, is assigned to the memorized scramble.\n\ -\n\ -EXAMPLES\n\ -save R U R' U'\n\ - Saves the scramble R U R' U'.\n\ -save F (B)\n\ - Saves the scramble F (B) (NISS notation).\n\ -save @3\n\ - Saves the third output sequence of the last command.\n\ -save $2\n\ - Makes a copy of the second saved scramble.\n\ -\n\ -" -}, - -{ "scramble", -"\ -\n\ -HELP PAGE FOR COMMAND scramble\n\ -\n\ -SYNTAX\n\ -scramble [OPTIONS]\n\ -\n\ -DESCRIPTION\n\ -Produces a random-state scramble. There are options to get a corners-only,\n\ -edges-only or dr-state scramble.\n\ -\n\ -OPTIONS\n\ -c Scrambles corners only (edges are solved).\n\ -e Scrambles edges only (corners are solved).\n\ -dr DR-state scramble. The DR is always on the U/D axis.\n\ -\n\ -\n\ -EXAMPLES\n\ -scramble\n\ - Gives a random-state scramble\n\ -scramble dr\n\ - Gives a random-DR-state scramble\n\ -\n\ -" -}, - -{ "solve", -"\ -\n\ -HELP PAGE FOR COMMAND solve\n\ -\n\ -SYNTAX\n\ -solve [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Solves the given scramble, which can be given as a sequence of moves or as $ID\n\ -or @ID. If none is given, the user can type it on the next line.\n\ -The algorithm first tries to find a short (<=10 moves) solution, and then\n\ -switches to a 2-step algorithm (unless the option \"o\" is specified, in which\n\ -case it keeps looking for an optimal solution).\n\ -The first time it uses the 2-step algorithm it needs to load some tables, which\n\ -can take a few seconds. It runs much faster after that. If the option \"o\" is\n\ -specified, the first time it loads some large tables, which can take a minute\n\ -or two.\n\ -\n\ -OPTIONS\n\ -b=N Only looks for solutions up to N moves.\n\ -n=N Tries to find multiple solutions, at most N. Multiple\n\ - Solutions will only be found if they are <=10 moves.\n\ -o Looks for optimal solution.\n\ -\n\ -\n\ -EXAMPLES\n\ -solve R' U' F\n\ - Solves the scramble R' U' F.\n\ -solve o b=14 $1\n\ - Tries to solve the scramble $1 optimally, but stops if no solution of 14\n\ - moves or shorter is found.\n\ -solve n=16 R L' U2 R' L F2\n\ - Finds the 16 shortest solutions for the scramble above.\n\ -\n\ -" -}, - -{ "unniss", -"\ -\n\ -HELP PAGE FOR COMMAND unniss\n\ -\n\ -SYNTAX\n\ -unniss [MOVES|$ID|@ID]\n\ -\n\ -DESCRIPTION\n\ -Removes NISS from a sequence of moves, which can be given also as $ID or @ID.\n\ -A sequence of the form A (B) is translated to B' A.\n\ -\n\ -EXAMPLES\n\ -invert F R (D' L2)\n\ - Prints L2 D F R\n\ -\n\ -" -}, -}; diff --git a/src/io.c b/src/io.c deleted file mode 100644 index ab0f658..0000000 --- a/src/io.c +++ /dev/null @@ -1,232 +0,0 @@ -#include -#include -#include - -#include "coordinates.h" -#include "moves.h" -#include "utils.h" - -/* Functions for nice output */ -char *edge_string(int i) { - return (i > -1 && i < 12) ? edge_string_list[i] : "-"; -} - -char *corner_string(int i) { - return (i > -1 && i < 8) ? corner_string_list[i] : "-"; -} - -char *move_string(int i) { - return (i > -1 && i < 19) ? move_string_list[i] : "err"; -} - -void print_ep_array(int ep[12]) { - for (int i = 0; i < 12; i++) - printf(" %s ", edge_string(ep[i])); -} - -void print_ep_int(int ep) { - int aux[12]; - ep_int_to_array(ep, aux); - print_ep_array(aux); -} - -void print_cp_array(int cp[8]) { - for (int i = 0; i < 8; i++) - printf(" %s ", corner_string(cp[i])); -} - -void print_cp_int(int cp) { - int aux[8]; - cp_int_to_array(cp, aux); - print_cp_array(aux); -} - -void print_eo_array(int eo[12]) { - for (int i = 0; i < 12; i++) { - if (eo[i]) - printf(" x "); - else - printf(" "); - } -} - -void print_eo_int(int eo) { - int aux[12]; - eo_11bits_to_array(eo, aux); - print_eo_array(aux); -} - -void print_co_array(int co[8]) { - for (int i = 0; i < 8; i++) { - if (co[i] == 0) - printf(" "); - if (co[i] == 1) - printf(" cw "); - if (co[i] == 2) - printf(" ccw "); - } -} - -void print_co_int(int co) { - int aux[8]; - co_7trits_to_array(co, aux); - print_co_array(aux); -} - -void print_cube_scram(int *scram) { - int ep = 0, cp = 0, eofb = 0, coud = 0; - for (int i = 0; scram[i]; i++) { - ep = apply_move_ep_int(scram[i], ep); - cp = cp_transition_table[cp][scram[i]]; - eofb = eofb_transition_table[eofb][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - } - printf("\t\t"); print_ep_int(0); printf("\n"); - printf("EP:\t\t"); print_ep_int(ep); printf("\n"); - printf("EO(F/B):\t"); print_eo_int(eofb); printf("\n"); - printf("\n"); - printf("\t\t"); print_cp_int(0); printf("\n"); - printf("CP:\t\t"); print_cp_int(cp); printf("\n"); - printf("CO(U/D):\t"); print_co_int(coud); printf("\n"); -} - - -void copy_moves(int *src, int *dst) { - for (int i = 0; (dst[i] = src[i]); i++); -} - -void append_moves(int *src, int *dst) { - int n = 0; - for (; dst[n]; n++); - copy_moves(src, dst+n); -} - -/* Parse a string and saves the move in a. Supports NISS notation. - * Returns the number of moves, or -1 in case of error. */ -int read_moves(char *str, int *a) { - int count = 0; - int niss = 0; - for (int i = 0; str[i] && str[i] != '\n'; i++) { - while (str[i] == ' ' || str[i] == '\t') i++; - switch (str[i]) { - case 'U': - a[count++] = niss ? -U : U; - break; - case 'D': - a[count++] = niss ? -D : D; - break; - case 'R': - a[count++] = niss ? -R : R; - break; - case 'L': - a[count++] = niss ? -L : L; - break; - case 'F': - a[count++] = niss ? -F : F; - break; - case 'B': - a[count++] = niss ? -B : B; - break; - case '(': - if (niss) - return -1; - else - niss = 1; - break; - case ')': - if (!niss) - return -1; - else - niss = 0; - break; - default: - return -1; - } - switch (str[++i]) { - case '2': - a[count-1] += niss ? -1 : 1; - break; - case '\'': - case '3': - a[count-1] += niss ? -2 : 2; - break; - case '1': - default: - --i; - } - } - a[count] = 0; - return count; -} - -/* Read moves from standard input, after a prompt. */ -int read_moves_from_prompt(int *a) { - char str[1000]; - printf("Enter moves: "); - if (fgets(str, 1000, stdin) == NULL) - return -1; - return read_moves(str, a); -} - -/* Read moves from a list of token, each containing one or more moves. */ -int read_moves_from_tok(int n, char tok[][100], int *a) { - char str[1000] = ""; - for (int i = 0; i < n; i++) - strcat(str, tok[i]); - return read_moves(str, a); -} - -/* Checks if a sequence of moves uses NISS */ -int uses_niss(int *str) { - for (int i = 0; str[i]; i++) - if (str[i] < 0) - return 1; - return 0; -} - -/* A (B) -> B' A */ -int unniss(int *src, int *dst) { - int n = 0; - for (int i = 0; src[i]; i++) - if (src[i] < 0) - n++; - - int norm_count = n, inv_count = n-1; - for (int i = 0; src[i]; i++) - if (src[i] > 0) - dst[norm_count++] = src[i]; - else - dst[inv_count--] = inverse_move[-src[i]]; - - dst[norm_count] = 0; - - return n; -} - -int invert(int *src, int *dst) { - int aux[255]; - for (int i = 0; (aux[i] = -src[i]); i++); - return unniss(aux, dst); -} - -int len(int *scram) { - int m; - for (m = 0; scram[m]; m++); - return m; -} - -void print_moves(int moves_list[]) { - int niss = 0; - for (int i = 0; moves_list[i]; i++) { - if (!niss && moves_list[i] < 0) { - printf("("); - niss = 1; - } - printf("%s", move_string_list[abs(moves_list[i])]); - if (niss && moves_list[i+1] >= 0) { - niss = 0; - printf(")"); - } - printf(" "); - } -} diff --git a/src/io.h b/src/io.h deleted file mode 100644 index f1b585d..0000000 --- a/src/io.h +++ /dev/null @@ -1,21 +0,0 @@ -#include - -char *edge_string(int edge); -char *corner_string(int edge); -char *move_string(int move); - -void print_cube_scram(int *scram); - -void copy_moves(int *src, int *dst); -void append_moves(int *src, int *dst); - -int read_moves(char *str, int *a); -int read_moves_from_prompt(int *a); -int read_moves_from_tok(int n, char tok[][100], int *a); - -int uses_niss(int *str); -int unniss(int *src, int *dst); -int invert(int *src, int *dst); -int len(int *scram); - -void print_moves(int move_list[]); diff --git a/src/main.c b/src/main.c deleted file mode 100644 index a14e365..0000000 --- a/src/main.c +++ /dev/null @@ -1,937 +0,0 @@ -#include -#include -#include -#include "utils.h" -#include "coordinates.h" -#include "io.h" -#include "moves.h" -#include "solver.h" -#include "string.h" -#include "helppages.h" -/* Saved sequences of moves */ -int scr_count=1, tmp_count=1, max_tmp=999; -int scrambles[255][255], tmp[1000][255]; - -int read_moves_from_variable(char *id, int *dst) { - char c = id[0]; - if (c != '$' && c != '@') - return -1; - int n = atoi(id+1); - if (n <= 0 || n >= (c == '$' ? scr_count : tmp_count)) - return -1; - copy_moves(c == '$' ? scrambles[n] : tmp[n], dst); - return n; -} - -int read_moves_from_argument(int n, char tok[][100], int *dst) { - int r = read_moves_from_variable(tok[0], dst); - return (r != -1) ? r : read_moves_from_tok(n, tok, dst); -} - -void print_results(int n, int res[][30]) { - if (n == -1) - printf("Pre-conditions not satisfied (or other error).\n"); - - if (n == 0) - printf("No result found (try different bounds).\n"); - - if (n > 1) - printf("Found %d results.\n", n); - tmp_count = 1; /* Reset temporary count */ - for (int i = 0; i < n; i++) { - if (i < max_tmp) { - copy_moves(res[i], tmp[tmp_count]); - printf("@%d:\t", tmp_count++); - } else { - printf(" \t"); - } - print_moves(res[i]); - printf("(%d)\n", len(res[i])); - } -} - -/* Removes extra white spaces from the input string */ -int parsecmd(char *cmd, char cmdtok[][100]) { - char *i = cmd, *j = cmd; - while (*j != '\n' && *j != EOF) { - *i = *j; - if (*i == ' ' || *i == '\t') - *i = ' '; - ++j; - if (*i == ' ' || *i == '\t') - while (*j == ' ' || *j == '\t') - ++j; - ++i; - } - if (*(i-1) == ' ') - *(i-1) = 0; - else - *i = 0; - - int n = 0; - char *s = strtok(cmd, " "); - while (s != NULL) { - strcpy(cmdtok[n++], s); - s = strtok(NULL, " "); - } - return n; -} - -void scramble_cmd(int n, char cmdtok[][100]) { - int c = 0, e = 0, dr = 0; - - if (n > 2 || cmdtok[0][0] != 's') { /* Second case avoids warning */ - printf("scramble: wrong syntax\n"); - return; - } else if (n == 2) { - if (!strcmp(cmdtok[1], "c")) { - c = 1; - } else if (!strcmp(cmdtok[1], "e")) { - e = 1; - } else if (!strcmp(cmdtok[1], "dr")) { - dr = 1; - } else { - printf("scramble: wrong syntax\n"); - return; - } - } - - int scram[2][30]; - - srand(time(NULL)); - int eofb = rand() % pow2to11; - int coud = rand() % pow3to7; - int ep = rand() % factorial12; - int cp = rand() % factorial8; - - if (c) { - eofb = ep = 0; - } else if (e) { - coud = cp = 0; - } else if (dr) { - eofb = coud = 0; - int epud = rand() % factorial8; - int epe = rand() % factorial4; - int ep_arr[12]; - epud_int_to_array(epud, ep_arr); - epe_int_to_array(epe, ep_arr); - ep = ep_array_to_int(ep_arr); - while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8)) - cp = (cp+1) % factorial8; - } else { - while (perm_sign_int(ep, 12) != perm_sign_int(cp, 8)) - cp = (cp+1) % factorial8; - } - - reach_state(eofb, coud, ep, cp, scram); - /* Debug */ - /* printf("State: %d %d %d %d\n", eofb, coud, ep, cp); */ - print_results(1, scram); -} - -void save_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("save: error reading moves. Not saved.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("save: error reading moves or ID. Not saved.\n"); - return; - } - - copy_moves(scram, scrambles[scr_count]); - - printf("$%d:\t", scr_count); - print_moves(scrambles[scr_count]); - printf("\n"); - scr_count++; -} - -void change_cmd(int n, char cmdtok[][100]) { - int id, scram[255]; - if (n == 1) { - printf("change: you must specify an $ID.\n"); - return; - } else if (cmdtok[1][0] != '$') { - printf("change: invalid $ID.\n"); - return; - } else { - id = atoi(cmdtok[1]+1); - if (id <= 0 || id >= scr_count) { - printf("change: invalid $ID.\n"); - return; - } - if (n == 2) { - if (read_moves_from_prompt(scram) == -1) { - printf("change: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-2, cmdtok+2, scram) == -1 ) { - printf("change: error reading moves or ID.\n"); - return; - } - } - - copy_moves(scram, scrambles[id]); - - printf("$%d:\t", id); - print_moves(scrambles[id]); - printf("\n"); -} - -void print_cmd(int n, char cmdtok[][100]) { - if (n == 1) { - for (int i = 1; i < scr_count; i++) { - printf("$%d:\t", i); - print_moves(scrambles[i]); - printf("\n"); - } - } else if (n == 2) { - int i = atoi(cmdtok[1]+1); - char sign = cmdtok[1][0]; - if (sign != '$' && sign != '@') { - printf("print: invalid ID (must start with $ or @).\n"); - return; - } - if (i > 0 && i < (sign == '$' ? scr_count : tmp_count)) { - printf("%c%d:\t", sign, i); - print_moves(sign == '$' ? scrambles[i] : tmp[i]); - printf("\n"); - } else { - printf("print: invalid ID.\n"); - return; - } - } else { - printf("print: wrong syntax.\n"); - } -} - -void add_cmd(int n, char cmdtok[][100]) { - int id, scram[255]; - if (n == 1) { - printf("add: you must specify a destination $ID.\n"); - return; - } else if (cmdtok[n-1][0] != '$') { - printf("add: invalid destination $ID.\n"); - return; - } else { - id = atoi(cmdtok[n-1]+1); - if (id <= 0 || id >= scr_count) { - printf("add: invalid destination $ID.\n"); - return; - } - if (n == 2) { - if (read_moves_from_prompt(scram) == -1) { - printf("add: error reading moves.\n"); - return; - } - } else { - if (read_moves_from_argument(n-2, cmdtok+1, scram) == -1) { - printf("add: error reading moves or ID.\n"); - return; - } - } - } - - append_moves(scram, scrambles[id]); - - printf("$%d:\t", id); - print_moves(scrambles[id]); - printf("\n"); -} - -void invert_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("invert: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("invert: error reading moves or ID.\n"); - return; - } - - if (uses_niss(scram)) { - printf("invert: cannot invert NISS.\n"); - return; - } - - invert(scram, tmp[1]); - tmp_count = 2; - - printf("@1:\t"); - print_moves(tmp[1]); - printf("\n"); -} - -void unniss_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("unniss: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("unniss: error reading moves or ID.\n"); - return; - } - - unniss(scram, tmp[1]); - tmp_count = 2; - - printf("@1:\t"); - print_moves(tmp[1]); - printf("\n"); -} - -void pic_cmd(int n, char cmdtok[][100]) { - int scram[255]; - if (n == 1) { - if (read_moves_from_prompt(scram) == -1) { - printf("pic: error reading moves.\n"); - return; - } - } else if (read_moves_from_argument(n-1, cmdtok+1, scram) == -1) { - printf("pic: error reading moves or ID.\n"); - return; - } - print_cube_scram(scram); -} - -void solve_cmd(int n, char cmdtok[][100]) { - int m = 1, b = 25, optimal = 0; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("solve: bad option b.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("solve: bad option n.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "o")) { - optimal = 1; - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("solve: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("solve: error reading moves.\n"); - return; - } - } - - /* Call solver and print results */ - unniss(scram, scram_unnissed); - int sol[m+2][30]; - int s = solve_scram(scram_unnissed, sol, m, b, optimal); - print_results(s, sol); -} - -void replace_cmd(int n, char cmdtok[][100]) { - int m = 10; /* max length */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "b=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("replace: bad option n.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("replace: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("replace: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - int l = len(scram_unnissed); - int aux1[255], aux2[15][30], aux3[30]; - for (int i = 0; i < l; i++) { - for (int j = 2; j <= m && i + j <= l; j++) { - copy_moves(scram_unnissed+i, aux1); - aux1[j] = 0; - int s = solve_scram(aux1, aux2, 10, j-1, 1); - for (int k = 0; k < s; k++) { - invert(aux2[k], aux3); - /* TODO: the following part should also chek for the case when - * the last moves are R L or similar. */ - if (aux3[0] != aux1[0] && aux3[len(aux3)-1] != aux1[len(aux1)-1]) { - printf("Replace [ "); - print_moves(aux1); - printf("] (moves %d-%d) with: [ ", i+1, i+j); - print_moves(aux3); - printf("] (-%d+%d)\n", j, len(aux3)); - } - } - } - } -} - -void clear_cmd(int n, char cmdtok[][100]) { - if (n > 1 || cmdtok[0][0] != 'c') { /* Avoid unused variable warning */ - printf("clear: syntax error.\n"); - return; - } - scr_count = tmp_count = 1; -} - -void eo_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("eo: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("eo: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("eo: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("eo: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("eo: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver and print results */ - int eo_list[m+5][30]; - int neo = eo_scram_spam(scram_unnissed, eo_list, fb, rl, ud, m, b, niss, - hide); - print_results(neo, eo_list); -} - -void co_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20, ignore = 0; - int niss = 0, hide = 1; - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strcmp(cmdtok[i], "i")) { - ignore = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("co: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("co: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("co: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("co: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("co: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver and print results */ - int co_list[m+5][30]; - int nco = co_scram_spam(scram_unnissed, co_list, fb, rl, ud, m, b, niss, - hide, ignore); - print_results(nco, co_list); -} - -void dr_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int from = 0; /* 0: direct dr; {1,2,3}: from {eofb,eorl,eoud} */ - int fb = 1, rl = 1, ud = 1; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "axis=", 5)) { - fb = rl = ud = 0; - if (strstr(cmdtok[i], "fb") != NULL) - fb = 1; - if (strstr(cmdtok[i], "rl") != NULL) - rl = 1; - if (strstr(cmdtok[i], "ud") != NULL) - ud = 1; - if (fb + rl + ud == 0) { - printf("dr: bad axis option.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("dr: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("dr: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"fb", "rl", "ud"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("dr: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("dr: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("dr: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int dr_list[m+5][30], ndr; - if (from) { - ndr = drfrom_scram_spam(scram_unnissed, dr_list, from, fb, rl, ud, - m, b, niss, hide); - if (ndr == -1) { - printf("dr: from given, but EO not found (possibly other error).\n"); - return; - } - } else { - if (niss) - printf("Warning: not using NISS for direct DR.\n"); - ndr = dr_scram_spam(scram_unnissed, dr_list, fb, rl, ud, m, b, hide); - } - print_results(ndr, dr_list); -} - -void htr_cmd(int n, char cmdtok[][100]) { - - /* Default values */ - int m = 1, b = 20; - int niss = 0, hide = 1; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strcmp(cmdtok[i], "h")) { - hide = 0; - } else if (!strcmp(cmdtok[i], "niss")) { - niss = 1; - } else if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("htr: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("htr: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("htr: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("htr: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("htr: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int htr_list[m+5][30], nhtr; - nhtr = htr_scram_spam(scram_unnissed, htr_list, from, m, b, niss, hide); - print_results(nhtr, htr_list); -} - -void drfinish_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("drfinish: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("drfinish: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("drfinish: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("drfinish: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("drfinish: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = dr_finish_scram_spam(scram_unnissed, c_list, from, m, b); - print_results(nc, c_list); -} - -void htrfinish_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20; - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("htrfinish: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("htrfinish: bad option b.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("htrfinish: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("htrfinish: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = htr_finish_scram_spam(scram_unnissed, c_list, m, b); - print_results(nc, c_list); -} - -void drcorners_cmd(int n, char cmdtok[][100]) { - /* Default values */ - int m = 1, b = 20, ignore = 0; - int from = 0; /* 0: unspecified; {1,2,3}: from {ud,fb,rl} */ - int scram[255] = {[0] = 0}; - int scram_unnissed[255]; - - /* Parse options */ - for (int i = 1; i < n && scram[0] == 0; i++) { - if (!strncmp(cmdtok[i], "n=", 2)) { - m = atoi(cmdtok[i]+2); - if (m <= 0) { - printf("drcorners: bad option n.\n"); - return; - } - } else if (!strncmp(cmdtok[i], "b=", 2)) { - b = atoi(cmdtok[i]+2); - if (b <= 0) { - printf("drcorners: bad option b.\n"); - return; - } - } else if (!strcmp(cmdtok[i], "i")) { - ignore = 1; - } else if (!strcmp(cmdtok[i], "from")) { - i++; - char x[3][3] = {"ud", "fb", "rl"}; - for (int j = 0; j < 3; j++) - if (!strcmp(cmdtok[i], x[j])) - from = j+1; - if (!from) { - printf("drcorners: bad from option.\n"); - return; - } - } else if (read_moves_from_argument(n-i, cmdtok+i, scram) == -1) { - printf("drcorners: error reading moves or ID.\n"); - return; - } - } - - if (scram[0] == 0) { - if (read_moves_from_prompt(scram) == -1) { - printf("drcorners: error reading moves.\n"); - return; - } - } - - unniss(scram, scram_unnissed); - - /* Call solver */ - int c_list[m+5][30], nc; - nc = dr_corners_scram_spam(scram_unnissed, c_list, from, m, b, ignore); - print_results(nc, c_list); -} - - -void exit_quit_cmd(int n, char cmdtok[][100]) { - if (n == 1) - exit(0); - else - printf("%s: wrong synstax.\n", cmdtok[0]); -} - -/***************************************************************/ -/* List of all commands */ -/* Important: they must be in the same order in the two arrays */ -/***************************************************************/ - -char *commands[][10] = { - {"help", "[COMMAND]", - "Print this help, or a help page for COMMAND."}, - {"scramble", "[OPTIONS]", - "Prints a random-state scramble."}, - {"save", "[MOVES|@ID|$ID]", - "Save or copy a scramble."}, - {"change", "$ID1 [MOVES|$ID2|@ID2]", - "Change a memorized scramble."}, - {"print", "[$ID|@ID]", - "Print memorized sequences."}, - {"add", "[MOVES|$ID1|@ID1] $ID2", - "Add moves at the end of a memorized scramble."}, - {"invert", "[MOVES|$ID|@ID]", - "Inverts the given sequence of moves."}, - {"unniss", "[MOVES|$ID|@ID]}", - "Removes NISS: A (B) -> B\' A."}, - {"pic", "[MOVES|$ID|@ID]", - "Show a text description of the scrambled cube."}, - {"solve", "[MOVES|$ID|@ID]", - "Solves a scramble."}, - {"replace", "[MOVES|$ID|@ID]", - "Find non-optimal subsequences."}, - {"clear", "", - "Delete saved scrambles and output sequences."}, - {"eo", "[MOVES|$ID|@ID]", - "Solves EO."}, - {"co", "[MOVES|$ID|@ID]", - "Solves CO."}, - {"dr", "[MOVES|$ID|@ID]", - "Solves DR, either directly or from eo."}, - {"htr", "[MOVES|$ID|@ID]", - "Solves HTR from DR."}, - {"drfinish", "[MOVES|$ID|@ID]", - "Solves the cube after DR."}, - {"htrfinish", "[MOVES|$ID|@ID]", - "Solves the cube using only half turns."}, - {"drcorners", "[MOVES|$ID|@ID]", - "Solves corners after DR."}, - {"exit", "", - "Exit nissy."}, - {"quit", "", - "Exit nissy."}, - {"", "", ""} -}; - -void help_cmd(int n, char cmdtok[][100]) { - if (n == 1) { - printf("\n"); - for (int i = 0; commands[i][0][0]; i++) - printf("%-10s%-25s%s\n", commands[i][0], commands[i][1], commands[i][2]); - printf("\n"); - printf("Type \'help\' followed by a command for a detailed help page.\n"); - printf("Type \'help nissy\' for a general user guide.\n"); - } else if (n == 2) { - for (int i = 0; i < Npages; i++) { - if (!strcmp(helppages[i][0], cmdtok[1])) { - printf("%s", helppages[i][1]); - return; - } - } - printf("No help page for %s.\n", cmdtok[1]); - return; - } else { - printf("help: wrong syntax.\n"); - } -} - -void (*cmd_list[])(int n, char cmdtok[][100]) = { - help_cmd, scramble_cmd, save_cmd, change_cmd, print_cmd, - add_cmd, invert_cmd, unniss_cmd, pic_cmd, - solve_cmd, replace_cmd, clear_cmd, - eo_cmd, co_cmd, dr_cmd, htr_cmd, - drfinish_cmd, htrfinish_cmd, drcorners_cmd, - exit_quit_cmd, exit_quit_cmd, NULL -}; - - -void execcmd(int n, char cmdtok[][100]) { - int i = 0; - while (strcmp(commands[i][0], cmdtok[0]) && strcmp(commands[i][0], "")) - i++; - if (strcmp(commands[i][0], "")) - (*cmd_list[i])(n, cmdtok); - else - printf("%s: not a command.\n", cmdtok[0]); -} - - -/* Main loop */ - -int main() { - init_transition_table(); - init_possible_next(); - - printf("Type help for a list of commands.\n"); - - char cmd[1000] = ""; - while (1) { - printf("nissy-# "); - if (fgets(cmd, 1000, stdin) == NULL) - break; - char cmdtok[100][100]; - int n = parsecmd(cmd, cmdtok); - if (n == 0) - continue; - execcmd(n, cmdtok); - } - - return 0; -} diff --git a/src/moves.c b/src/moves.c index ce47e8f..25ce779 100644 --- a/src/moves.c +++ b/src/moves.c @@ -1,521 +1,475 @@ -/* This file contains the definitions of the basic moves of the cube. - * There is no object or type representing the cube. - * Data about the cube can be represented by arrays (describing the position - * of pieces of certain types), integers (representing for example a bitmask - * for the orientation of pieces of certain type, or the permutation index of - * an array representing the permutation of pieces). - * Each of the moves functions operates on one such piece of data. - * - * For example, a way of representing the cube can be: - * - An integer eo, which is a bitmask for the orientation of the edges. - * - An integer co, same for corners. - * - An array ep[12], where a[i]=j means that the edge j is in place i. - * - An integer cp representing the permutation index of a permutation array - * which is the analogue of that described for edges. - * - * Different representations will be used for different use-cases. */ - -#include "coordinates.h" #include "moves.h" -/* possible_next[i][j] is a bitmask representing the possible - * next moves we can apply. For example, if the last moves a 0 R then it does - * not make sense to apply R, R2 or R'. If they are U D2 it does not make - * sense to apply any U* or D*. */ -int possible_next[19][19]; +/* Local functions ***********************************************************/ -int parallel(int m1, int m2) { - if (m1 == 0 || m2 == 0) return 0; - return ((m1-1)/6 == (m2-1)/6); -} - -int compute_possible_next(int last1, int last2) { - if (last1 == 0) return move_mask_all; - - /* Removes the 2 or ' (e.g. turns U2 to U, R' to R). */ - last2 = (last2 == 0) ? last2 : 3*((last2-1)/3) + 1; - last1 = 3*((last1-1)/3) + 1; +static Cube apply_move_cubearray(Move m, Cube cube, PieceFilter f); +static bool read_mtables_file(); +static bool write_mtables_file(); - int mask = move_mask_all ^ (7 << last1); +/* Tables and other data *****************************************************/ - if (parallel(last1, last2)) - mask ^= 7 << last2; - else if (last1 % 6 == 4) /*Always U before D, R before L, F before B*/ - mask ^= 7 << (last1-3); +/* Every move is translated to a an alg before filling the + transition tables, see init_moves() */ - return mask; -} - -void init_possible_next() { - for (int i = 0; i < 19; i++) - for (int j = 0; j < 19; j++) - possible_next[i][j] = compute_possible_next(i, j); -} - -/* Piece cycles depending on the move. For example edge_cycle[U2][UF] - * gives the piece in position UF after applying U2 to a solved cube */ - -int edge_cycle[19][12] = { - {UF, UL, UB, UR, DF, DL, DB, DR, FR, FL, BL, BR}, /* - */ - {UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR}, /* U */ - {UB, UR, UF, UL, DF, DL, DB, DR, FR, FL, BL, BR}, /* U2 */ - {UL, UB, UR, UF, DF, DL, DB, DR, FR, FL, BL, BR}, /* U' */ - {UF, UL, UB, UR, DL, DB, DR, DF, FR, FL, BL, BR}, /* D */ - {UF, UL, UB, UR, DB, DR, DF, DL, FR, FL, BL, BR}, /* D2 */ - {UF, UL, UB, UR, DR, DF, DL, DB, FR, FL, BL, BR}, /* D' */ - {UF, UL, UB, FR, DF, DL, DB, BR, DR, FL, BL, UR}, /* R */ - {UF, UL, UB, DR, DF, DL, DB, UR, BR, FL, BL, FR}, /* R2 */ - {UF, UL, UB, BR, DF, DL, DB, FR, UR, FL, BL, DR}, /* R' */ - {UF, BL, UB, UR, DF, FL, DB, DR, FR, UL, DL, BR}, /* L */ - {UF, DL, UB, UR, DF, UL, DB, DR, FR, BL, FL, BR}, /* L2 */ - {UF, FL, UB, UR, DF, BL, DB, DR, FR, DL, UL, BR}, /* L' */ - {FL, UL, UB, UR, FR, DL, DB, DR, UF, DF, BL, BR}, /* F */ - {DF, UL, UB, UR, UF, DL, DB, DR, FL, FR, BL, BR}, /* F2 */ - {FR, UL, UB, UR, FL, DL, DB, DR, DF, UF, BL, BR}, /* F' */ - {UF, UL, BR, UR, DF, DL, BL, DR, FR, FL, UB, DB}, /* B */ - {UF, UL, DB, UR, DF, DL, UB, DR, FR, FL, BR, BL}, /* B2 */ - {UF, UL, BL, UR, DF, DL, BR, DR, FR, FL, DB, UB} /* B' */ +static int edge_cycle[NMOVES][12] = +{ + [U] = { UR, UF, UL, UB, DF, DL, DB, DR, FR, FL, BL, BR }, + [x] = { DF, FL, UF, FR, DB, BL, UB, BR, DR, DL, UL, UR }, + [y] = { UR, UF, UL, UB, DR, DF, DL, DB, BR, FR, FL, BL } }; -int corner_cycle[19][8] = { - {UFR, UFL, UBL, UBR, DFR, DFL, DBL, DBR}, /* - */ - {UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR}, /* U */ - {UBL, UBR, UFR, UFL, DFR, DFL, DBL, DBR}, /* U2 */ - {UFL, UBL, UBR, UFR, DFR, DFL, DBL, DBR}, /* U' */ - {UFR, UFL, UBL, UBR, DFL, DBL, DBR, DFR}, /* D */ - {UFR, UFL, UBL, UBR, DBL, DBR, DFR, DFL}, /* D2 */ - {UFR, UFL, UBL, UBR, DBR, DFR, DFL, DBL}, /* D' */ - {DFR, UFL, UBL, UFR, DBR, DFL, DBL, UBR}, /* R */ - {DBR, UFL, UBL, DFR, UBR, DFL, DBL, UFR}, /* R2 */ - {UBR, UFL, UBL, DBR, UFR, DFL, DBL, DFR}, /* R' */ - {UFR, UBL, DBL, UBR, DFR, UFL, DFL, DBR}, /* L */ - {UFR, DBL, DFL, UBR, DFR, UBL, UFL, DBR}, /* L2 */ - {UFR, DFL, UFL, UBR, DFR, DBL, UBL, DBR}, /* L' */ - {UFL, DFL, UBL, UBR, UFR, DFR, DBL, DBR}, /* F */ - {DFL, DFR, UBL, UBR, UFL, UFR, DBL, DBR}, /* F2 */ - {DFR, UFR, UBL, UBR, DFL, UFL, DBL, DBR}, /* F' */ - {UFR, UFL, UBR, DBR, DFR, DFL, UBL, DBL}, /* B */ - {UFR, UFL, DBR, DBL, DFR, DFL, UBR, UBL}, /* B2 */ - {UFR, UFL, DBL, UBL, DFR, DFL, DBR, UBR}, /* U' */ +static int corner_cycle[NMOVES][8] = +{ + [U] = { UBR, UFR, UFL, UBL, DFR, DFL, DBL, DBR }, + [x] = { DFR, DFL, UFL, UFR, DBR, DBL, UBL, UBR }, + [y] = { UBR, UFR, UFL, UBL, DBR, DFR, DFL, DBL } }; -/* Transition tables */ - -int eofb_transition_table[pow2to11][19]; -int eorl_transition_table[pow2to11][19]; -int eoud_transition_table[pow2to11][19]; -int coud_transition_table[pow3to7][19]; -int cofb_transition_table[pow3to7][19]; -int corl_transition_table[pow3to7][19]; -int epud_transition_table[factorial8][19]; -int eprl_transition_table[factorial8][19]; -int epfb_transition_table[factorial8][19]; -int epose_transition_table[binom12on4][19]; -int eposs_transition_table[binom12on4][19]; -int eposm_transition_table[binom12on4][19]; -int epe_transition_table[factorial4][19]; -int eps_transition_table[factorial4][19]; -int epm_transition_table[factorial4][19]; -int emslices_transition_table[binom12on4*binom8on4][19]; -int cp_transition_table[factorial8][19]; - -/***/ -/* Functions for permuting pieces (given in array format) */ -/***/ - -void apply_move_ep_array(int move, int ep[12]) { - int aux[12]; - for (int i = 0; i < 12; i++) - aux[i] = ep[i]; - for (int i = 0; i < 12; i++) - ep[i] = aux[edge_cycle[move][i]]; -} - -void apply_move_cp_array(int move, int cp[8]) { - int aux[8]; - for (int i = 0; i < 8; i++) - aux[i] = cp[i]; - for (int i = 0; i < 8; i++) - cp[i] = aux[corner_cycle[move][i]]; -} - -/***/ -/* Functions for permuting pieces (given in integer format) */ -/***/ - -int apply_move_ep_int(int move, int ep) { - int a[12]; - ep_int_to_array(ep, a); - apply_move_ep_array(move, a); - return ep_array_to_int(a); -} - -int apply_move_epud_int(int move, int ep) { - int a[12]; - epud_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epud_array_to_int(a); -} - -int apply_move_eprl_int(int move, int ep) { - int a[12]; - eprl_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eprl_array_to_int(a); -} - -int apply_move_epfb_int(int move, int ep) { - int a[12]; - epfb_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epfb_array_to_int(a); -} - -int apply_move_epose_int(int move, int ep) { - int a[12]; - epose_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epose_array_to_int(a); -} - -int apply_move_eposs_int(int move, int ep) { - int a[12]; - eposs_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eposs_array_to_int(a); -} - -int apply_move_eposm_int(int move, int ep) { - int a[12]; - eposm_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eposm_array_to_int(a); -} - -int apply_move_epe_int(int move, int ep) { - int a[12]; - epe_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epe_array_to_int(a); -} - -int apply_move_eps_int(int move, int ep) { - int a[12]; - eps_int_to_array(ep, a); - apply_move_ep_array(move, a); - return eps_array_to_int(a); -} - -int apply_move_epm_int(int move, int ep) { - int a[12]; - epm_int_to_array(ep, a); - apply_move_ep_array(move, a); - return epm_array_to_int(a); -} - -int apply_move_emslices_int(int move, int e) { - int a[12]; - emslices_int_to_array(e, a); - apply_move_ep_array(move, a); - return emslices_array_to_int(a); -} - -int apply_move_cp_int(int move, int cp) { - int a[8]; - cp_int_to_array(cp, a); - apply_move_cp_array(move, a); - return cp_array_to_int(a); -} - -int apply_move_eofb_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == F || move == F3) { - a[UF] = 1 - a[UF]; - a[DF] = 1 - a[DF]; - a[FR] = 1 - a[FR]; - a[FL] = 1 - a[FL]; - } - if (move == B || move == B3) { - a[UB] = 1 - a[UB]; - a[DB] = 1 - a[DB]; - a[BL] = 1 - a[BL]; - a[BR] = 1 - a[BR]; - } - return eo_array_to_11bits(a); -} - -int apply_move_eorl_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == R || move == R3) { - a[UR] = 1 - a[UR]; - a[DR] = 1 - a[DR]; - a[FR] = 1 - a[FR]; - a[BR] = 1 - a[BR]; - } - if (move == L || move == L3) { - a[UL] = 1 - a[UL]; - a[DL] = 1 - a[DL]; - a[FL] = 1 - a[FL]; - a[BL] = 1 - a[BL]; - } - return eo_array_to_11bits(a); -} - -int apply_move_eoud_int(int move, int eo) { - int a[12]; - eo_11bits_to_array(eo, a); - apply_move_ep_array(move, a); - /* Change edge orientation */ - if (move == U || move == U3) { - a[UF] = 1 - a[UF]; - a[UL] = 1 - a[UL]; - a[UB] = 1 - a[UB]; - a[UR] = 1 - a[UR]; - } - if (move == D || move == D3) { - a[DF] = 1 - a[DF]; - a[DL] = 1 - a[DL]; - a[DB] = 1 - a[DB]; - a[DR] = 1 - a[DR]; - } - return eo_array_to_11bits(a); -} - -int apply_move_coud_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == R || move == R3) { - a[UFR] = (a[UFR] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - } - if (move == L || move == L3) { - a[UBL] = (a[UBL] + 2) % 3; - a[UFL] = (a[UFL] + 1) % 3; - a[DFL] = (a[DFL] + 2) % 3; - a[DBL] = (a[DBL] + 1) % 3; - } - if (move == F || move == F3) { - a[UFL] = (a[UFL] + 2) % 3; - a[UFR] = (a[UFR] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - a[DFL] = (a[DFL] + 1) % 3; - } - if (move == B || move == B3) { - a[UBR] = (a[UBR] + 2) % 3; - a[UBL] = (a[UBL] + 1) % 3; - a[DBL] = (a[DBL] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - } - return co_array_to_7trits(a); -} - -int apply_move_cofb_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == R || move == R3) { - a[UFR] = (a[UFR] + 1) % 3; - a[UBR] = (a[UBR] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - } - if (move == L || move == L3) { - a[UBL] = (a[UBL] + 1) % 3; - a[UFL] = (a[UFL] + 2) % 3; - a[DFL] = (a[DFL] + 1) % 3; - a[DBL] = (a[DBL] + 2) % 3; - } - if (move == U || move == U3) { - a[UFL] = (a[UFL] + 1) % 3; - a[UFR] = (a[UFR] + 2) % 3; - a[UBL] = (a[UBL] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - } - if (move == D || move == D3) { - a[DFL] = (a[DFL] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - a[DBL] = (a[DBL] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - } - return co_array_to_7trits(a); -} - -int apply_move_corl_int(int move, int co) { - int a[8]; - co_7trits_to_array(co, a); - apply_move_cp_array(move, a); - /* Change corner orientation */ - if (move == F || move == F3) { - a[UFR] = (a[UFR] + 2) % 3; - a[UFL] = (a[UFL] + 1) % 3; - a[DFL] = (a[DFL] + 2) % 3; - a[DFR] = (a[DFR] + 1) % 3; - } - if (move == B || move == B3) { - a[UBL] = (a[UBL] + 2) % 3; - a[UBR] = (a[UBR] + 1) % 3; - a[DBR] = (a[DBR] + 2) % 3; - a[DBL] = (a[DBL] + 1) % 3; - } - if (move == U || move == U3) { - a[UFL] = (a[UFL] + 2) % 3; - a[UFR] = (a[UFR] + 1) % 3; - a[UBL] = (a[UBL] + 1) % 3; - a[UBR] = (a[UBR] + 2) % 3; - } - if (move == D || move == D3) { - a[DFL] = (a[DFL] + 1) % 3; - a[DFR] = (a[DFR] + 2) % 3; - a[DBL] = (a[DBL] + 2) % 3; - a[DBR] = (a[DBR] + 1) % 3; - } - return co_array_to_7trits(a); -} - - - -/* Initialize transition tables */ - -void init_epud_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drud & (1 << j)) - epud_transition_table[i][j] = apply_move_epud_int(j, i); -} - -void init_eprl_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drrl & (1 << j)) - eprl_transition_table[i][j] = apply_move_eprl_int(j, i); -} - -void init_epfb_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - if (move_mask_drfb & (1 << j)) - epfb_transition_table[i][j] = apply_move_epfb_int(j, i); -} - -void init_epose_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - epose_transition_table[i][j] = apply_move_epose_int(j, i); -} - -void init_eposs_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - eposs_transition_table[i][j] = apply_move_eposs_int(j, i); -} - -void init_eposm_transition_table() { - for (int i = 0; i < binom12on4; i++) - for (int j = 0; j < 19; j++) - eposm_transition_table[i][j] = apply_move_eposm_int(j, i); -} - -void init_epe_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drud & (1 << j)) - epe_transition_table[i][j] = apply_move_epe_int(j, i); - } -} - -void init_eps_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drfb & (1 << j)) - eps_transition_table[i][j] = apply_move_eps_int(j, i); - } -} - -void init_epm_transition_table() { - for (int i = 0; i < factorial4; i++) { - for (int j = 0; j < 19; j++) - if (move_mask_drrl & (1 << j)) - epm_transition_table[i][j] = apply_move_epm_int(j, i); - } -} - -void init_emslices_transition_table() { - for (int i = 0; i < binom12on4*binom8on4; i++) { - for (int j = 0; j < 19; j++) - emslices_transition_table[i][j] = apply_move_emslices_int(j, i); - } -} +static int center_cycle[NMOVES][6] = +{ + [x] = { F_center, B_center, R_center, L_center, D_center, U_center }, + [y] = { U_center, D_center, B_center, F_center, R_center, L_center } +}; -void init_cp_transition_table() { - for (int i = 0; i < factorial8; i++) - for (int j = 0; j < 19; j++) - cp_transition_table[i][j] = apply_move_cp_int(j, i); -} +static int eofb_flipped[NMOVES][12] = { + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; -void init_eofb_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eofb_transition_table[i][j] = apply_move_eofb_int(j, i); -} +static int eorl_flipped[NMOVES][12] = { + [x] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [y] = { [FR] = 1, [FL] = 1, [BL] = 1, [BR] = 1 } +}; -void init_eorl_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eorl_transition_table[i][j] = apply_move_eorl_int(j, i); -} +static int eoud_flipped[NMOVES][12] = { + [U] = { [UF] = 1, [UL] = 1, [UB] = 1, [UR] = 1 }, + [x] = { [UF] = 1, [UB] = 1, [DF] = 1, [DB] = 1 }, + [y] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } +}; -void init_eoud_transition_table() { - for (int i = 0; i < pow2to11; i++) - for (int j = 0; j < 19; j++) - eoud_transition_table[i][j] = apply_move_eoud_int(j, i); -} +static int coud_flipped[NMOVES][8] = { + [x] = { + [UFR] = 2, [UBR] = 1, [UFL] = 1, [UBL] = 2, + [DBR] = 2, [DFR] = 1, [DBL] = 1, [DFL] = 2 + } +}; -void init_coud_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - coud_transition_table[i][j] = apply_move_coud_int(j, i); -} +static int corl_flipped[NMOVES][8] = { + [U] = { [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2 }, + [y] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + } +}; -void init_cofb_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - cofb_transition_table[i][j] = apply_move_cofb_int(j, i); -} +static int cofb_flipped[NMOVES][8] = { + [U] = { [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1 }, + [x] = { + [UFR] = 1, [UBR] = 2, [UBL] = 1, [UFL] = 2, + [DFR] = 2, [DBR] = 1, [DBL] = 2, [DFL] = 1 + }, + [y] = { + [UFR] = 2, [UBR] = 1, [UBL] = 2, [UFL] = 1, + [DFR] = 1, [DBR] = 2, [DBL] = 1, [DFL] = 2 + } +}; -void init_corl_transition_table() { - for (int i = 0; i < pow3to7; i++) - for (int j = 0; j < 19; j++ ) - corl_transition_table[i][j] = apply_move_corl_int(j, i); -} +static char equiv_alg_string[100][NMOVES] = { + [NULLMOVE] = "", + + [U] = " U ", + [U2] = " UU ", + [U3] = " UUU ", + [D] = " xx U xx ", + [D2] = " xx UU xx ", + [D3] = " xx UUU xx ", + [R] = " yx U xxxyyy ", + [R2] = " yx UU xxxyyy ", + [R3] = " yx UUU xxxyyy ", + [L] = " yyyx U xxxy ", + [L2] = " yyyx UU xxxy ", + [L3] = " yyyx UUU xxxy ", + [F] = " x U xxx ", + [F2] = " x UU xxx ", + [F3] = " x UUU xxx ", + [B] = " xxx U x ", + [B2] = " xxx UU x ", + [B3] = " xxx UUU x ", + + [Uw] = " xx U xx y ", + [Uw2] = " xx UU xx yy ", + [Uw3] = " xx UUU xx yyy ", + [Dw] = " U yyy ", + [Dw2] = " UU yy ", + [Dw3] = " UUU y ", + [Rw] = " yyyx U xxxy x ", + [Rw2] = " yyyx UU xxxy xx ", + [Rw3] = " yyyx UUU xxxy xxx ", + [Lw] = " yx U xxxyyy xxx ", + [Lw2] = " yx UU xxxyyy xx ", + [Lw3] = " yx UUU xxxyyy x ", + [Fw] = " xxx U x yxxxyyy ", + [Fw2] = " xxx UU x yxxyyy ", + [Fw3] = " xxx UUU x yxyyy ", + [Bw] = " x U xxx yxyyy ", + [Bw2] = " x UU xxx yxxyyy ", + [Bw3] = " x UUU xxx yxxxyyy ", + + [M] = " yx U xx UUU yxyyy ", + [M2] = " yx UU xx UU xxxy ", + [M3] = " yx UUU xx U yxxxy ", + [S] = " x UUU xx U yyyx ", + [S2] = " x UU xx UU yyx ", + [S3] = " x U xx UUU yx ", + [E] = " U xx UUU xxyyy ", + [E2] = " UU xx UU xxyy ", + [E3] = " UUU xx U xxy ", + + [x] = " x ", + [x2] = " xx ", + [x3] = " xxx ", + [y] = " y ", + [y2] = " yy ", + [y3] = " yyy ", + [z] = " yyy x y ", + [z2] = " yy xx ", + [z3] = " y x yyy " +}; -void init_transition_table() { - init_epud_transition_table(); - init_eprl_transition_table(); - init_epfb_transition_table(); - init_epose_transition_table(); - init_eposs_transition_table(); - init_eposm_transition_table(); - init_epe_transition_table(); - init_eps_transition_table(); - init_epm_transition_table(); - init_emslices_transition_table(); - init_cp_transition_table(); - init_eofb_transition_table(); - init_eorl_transition_table(); - init_eoud_transition_table(); - init_coud_transition_table(); - init_cofb_transition_table(); - init_corl_transition_table(); +/* Transition tables, to be loaded up at the beginning */ +static int epose_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposs_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eposm_mtable[NMOVES][FACTORIAL12/FACTORIAL8]; +static int eofb_mtable[NMOVES][POW2TO11]; +static int eorl_mtable[NMOVES][POW2TO11]; +static int eoud_mtable[NMOVES][POW2TO11]; +static int cp_mtable[NMOVES][FACTORIAL8]; +static int coud_mtable[NMOVES][POW3TO7]; +static int cofb_mtable[NMOVES][POW3TO7]; +static int corl_mtable[NMOVES][POW3TO7]; +static int cpos_mtable[NMOVES][FACTORIAL6]; + + +/* Local functions implementation ********************************************/ + +static Cube +apply_move_cubearray(Move m, Cube cube, PieceFilter f) +{ + /*init_moves();*/ + + CubeArray m_arr = { + edge_cycle[m], + eofb_flipped[m], + eorl_flipped[m], + eoud_flipped[m], + corner_cycle[m], + coud_flipped[m], + corl_flipped[m], + cofb_flipped[m], + center_cycle[m] + }; + + return move_via_arrays(&m_arr, cube, f); +} + +/* Public functions **********************************************************/ + +Cube +apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a) +{ + Cube ret = {0}; + int i; + + for (i = 0; i < alg->len; i++) + if (alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + ret = compose_filtered(c, inverse_cube(ret), f); + + for (i = 0; i < alg->len; i++) + if (!alg->inv[i]) + ret = a ? apply_move(alg->move[i], ret) : + apply_move_cubearray(alg->move[i], ret, f); + + return ret; +} + +Cube +apply_alg(Alg *alg, Cube cube) +{ + return apply_alg_generic(alg, cube, pf_all, true); +} + +Cube +apply_move(Move m, Cube cube) +{ + /*init_moves();*/ + + return (Cube) { + .epose = epose_mtable[m][cube.epose], + .eposs = eposs_mtable[m][cube.eposs], + .eposm = eposm_mtable[m][cube.eposm], + .eofb = eofb_mtable[m][cube.eofb], + .eorl = eorl_mtable[m][cube.eorl], + .eoud = eoud_mtable[m][cube.eoud], + .coud = coud_mtable[m][cube.coud], + .cofb = cofb_mtable[m][cube.cofb], + .corl = corl_mtable[m][cube.corl], + .cp = cp_mtable[m][cube.cp], + .cpos = cpos_mtable[m][cube.cpos] + }; +} + +static bool +read_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fread(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fread(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fread(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fread(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fread(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fread(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fread(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fread(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fread(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +static bool +write_mtables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int m, b = sizeof(int); + bool r = true; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = POW2TO11, + [5] = POW2TO11, + [6] = FACTORIAL8, + [7] = POW3TO7, + [8] = POW3TO7, + [9] = POW3TO7, + [10] = FACTORIAL6 + }; + + strcpy(fname, tabledir); + strcat(fname, "/mtables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NMOVES; m++) { + r = r && fwrite(epose_mtable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_mtable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_mtable[m], b, me[2], f) == me[2]; + r = r && fwrite(eofb_mtable[m], b, me[3], f) == me[3]; + r = r && fwrite(eorl_mtable[m], b, me[4], f) == me[4]; + r = r && fwrite(eoud_mtable[m], b, me[5], f) == me[5]; + r = r && fwrite(cp_mtable[m], b, me[6], f) == me[6]; + r = r && fwrite(coud_mtable[m], b, me[7], f) == me[7]; + r = r && fwrite(corl_mtable[m], b, me[8], f) == me[8]; + r = r && fwrite(cofb_mtable[m], b, me[9], f) == me[9]; + r = r && fwrite(cpos_mtable[m], b, me[10], f) == me[10]; + } + + fclose(f); + return r; +} + +bool +commute(Move m1, Move m2) +{ + static bool initialized = false; + static bool commute_aux[NMOVES][NMOVES]; + + if (!initialized) { + Cube c1, c2; + int i, j; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + c1 = apply_move(i, apply_move(j, (Cube){0})); + c2 = apply_move(j, apply_move(i, (Cube){0})); + commute_aux[i][j] = equal(c1, c2) && i && j; + } + } + + initialized = true; + } + + return commute_aux[m1][m2]; +} + +bool +possible_next(Move m1, Move m2, Move m3) +{ + static bool initialized = false; + static bool paux[NMOVES][NMOVES][NMOVES]; + + if (!initialized) { + int i, j, k; + bool p, q, c; + + for (i = 0; i < NMOVES; i++) { + for (j = 0; j < NMOVES; j++) { + for (k = 0; k < NMOVES; k++) { + p = j && base_move(j) == base_move(k); + q = i && base_move(i) == base_move(k); + c = commute(i, j); + paux[i][j][k] = !(p || (c && q)); + } + } + } + + initialized = true; + } + + return paux[m1][m2][m3]; +} + +void +init_moves() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + Cube c; + CubeArray arrs; + int i; + unsigned int ui; + Move m; + Alg *equiv_alg[NMOVES]; + + for (i = 0; i < NMOVES; i++) + equiv_alg[i] = new_alg(equiv_alg_string[i]); + + /* Generate all move cycles and flips; I do this regardless */ + for (i = 0; i < NMOVES; i++) { + if (i == U || i == x || i == y) + continue; + + c = apply_alg_generic(equiv_alg[i], (Cube){0}, pf_all, false); + + arrs = (CubeArray) { + edge_cycle[i], + eofb_flipped[i], + eorl_flipped[i], + eoud_flipped[i], + corner_cycle[i], + coud_flipped[i], + corl_flipped[i], + cofb_flipped[i], + center_cycle[i] + }; + cube_to_arrays(c, &arrs, pf_all); + } + + if (read_mtables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "mtables"); + + /* Initialize transition tables */ + for (m = 0; m < NMOVES; m++) { + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c = (Cube){ .epose = ui }; + c = apply_move_cubearray(m, c, pf_e); + epose_mtable[m][ui] = c.epose; + + c = (Cube){ .eposs = ui }; + c = apply_move_cubearray(m, c, pf_s); + eposs_mtable[m][ui] = c.eposs; + + c = (Cube){ .eposm = ui }; + c = apply_move_cubearray(m, c, pf_m); + eposm_mtable[m][ui] = c.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + c = (Cube){ .eofb = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eofb_mtable[m][ui] = c.eofb; + + c = (Cube){ .eorl = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eorl_mtable[m][ui] = c.eorl; + + c = (Cube){ .eoud = ui }; + c = apply_move_cubearray(m, c, pf_eo); + eoud_mtable[m][ui] = c.eoud; + } + for (ui = 0; ui < POW3TO7; ui++) { + c = (Cube){ .coud = ui }; + c = apply_move_cubearray(m, c, pf_co); + coud_mtable[m][ui] = c.coud; + + c = (Cube){ .corl = ui }; + c = apply_move_cubearray(m, c, pf_co); + corl_mtable[m][ui] = c.corl; + + c = (Cube){ .cofb = ui }; + c = apply_move_cubearray(m, c, pf_co); + cofb_mtable[m][ui] = c.cofb; + } + for (ui = 0; ui < FACTORIAL8; ui++) { + c = (Cube){ .cp = ui }; + c = apply_move_cubearray(m, c, pf_cp); + cp_mtable[m][ui] = c.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + c = (Cube){ .cpos = ui }; + c = apply_move_cubearray(m, c, pf_cpos); + cpos_mtable[m][ui] = c.cpos; + } + } + + if (!write_mtables_file()) + fprintf(stderr, "Error writing mtables\n"); + + for (i = 0; i < NMOVES; i++) + free_alg(equiv_alg[i]); } diff --git a/src/moves.h b/src/moves.h index 99d0ec3..082a080 100644 --- a/src/moves.h +++ b/src/moves.h @@ -1,83 +1,16 @@ -#include "utils.h" +#ifndef MOVES_H +#define MOVES_H -/* Bitmask that define certain movesets. */ -#define move_mask_all 524287 /* Reverse 1111111111111111111 */ -#define move_mask_eofb 155647 /* Reverse 1111111111111010010 */ -#define move_mask_eorl 518527 /* Reverse 1111111010101111111 */ -#define move_mask_eoud 524197 /* Reverse 1010010111111111111 */ -#define move_mask_drud 149887 /* Reverse 1111111010010010010 */ -#define move_mask_drfb 518437 /* Reverse 1010010010010111111 */ -#define move_mask_drrl 155557 /* Reverse 1010010111111010010 */ -#define move_mask_htr 149797 /* Reverse 1010010010010010010 */ +#include "alg.h" +#include "cube.h" +#include "env.h" -extern int possible_next[19][19]; +Cube apply_alg(Alg *alg, Cube cube); +Cube apply_alg_generic(Alg *alg, Cube c, PieceFilter f, bool a); +Cube apply_move(Move m, Cube cube); +bool commute(Move m1, Move m2); +bool possible_next(Move m1, Move m2, Move m3); -int parallel(int m1, int m2); -void init_possible_next(); - -/* Transition tables */ -extern int eofb_transition_table[pow2to11][19]; -extern int eorl_transition_table[pow2to11][19]; -extern int eoud_transition_table[pow2to11][19]; -extern int coud_transition_table[pow3to7][19]; -extern int cofb_transition_table[pow3to7][19]; -extern int corl_transition_table[pow3to7][19]; -extern int epud_transition_table[factorial8][19]; -extern int epfb_transition_table[factorial8][19]; -extern int eprl_transition_table[factorial8][19]; -extern int epose_transition_table[binom12on4][19]; -extern int eposs_transition_table[binom12on4][19]; -extern int eposm_transition_table[binom12on4][19]; -extern int epe_transition_table[factorial4][19]; -extern int eps_transition_table[factorial4][19]; -extern int epm_transition_table[factorial4][19]; -extern int emslices_transition_table[binom12on4*binom8on4][19]; -extern int cp_transition_table[factorial8][19]; - - -/* Functions for permuting pieces (given in array format) */ - -void apply_move_ep_array(int move, int ep[12]); -void apply_move_cp_array(int move, int cp[8]); - -/* Functions for permuting pieces (given in integer format) */ - -int apply_move_ep_int(int move, int ep); -int apply_move_epud_int(int move, int ep); -int apply_move_epfb_int(int move, int ep); -int apply_move_eprl_int(int move, int ep); -int apply_move_epose_int(int move, int ep); -int apply_move_eposs_int(int move, int ep); -int apply_move_eposm_int(int move, int ep); -int apply_move_epe_int(int move, int ep); -int apply_move_eps_int(int move, int ep); -int apply_move_epm_int(int move, int ep); -int apply_move_cp_int(int move, int cp); -int apply_move_eofb_int(int move, int eo); -int apply_move_eorl_int(int move, int eo); -int apply_move_eoud_int(int move, int eo); -int apply_move_coud_int(int move, int co); -int apply_move_cofb_int(int move, int co); -int apply_move_corl_int(int move, int co); - -/* Initialize transition tables */ - -void init_epud_transition_table(); -void init_epfb_transition_table(); -void init_eprl_transition_table(); -void init_epose_transition_table(); -void init_eposs_transition_table(); -void init_eposm_transition_table(); -void init_epe_transition_table(); -void init_eps_transition_table(); -void init_epm_transition_table(); -void init_cp_transition_table(); -void init_eofb_transition_table(); -void init_eorl_transition_table(); -void init_eoud_transition_table(); -void init_coud_transition_table(); -void init_cofb_transition_table(); -void init_corl_transition_table(); - -void init_transition_table(); +void init_moves(); +#endif diff --git a/src/pf.c b/src/pf.c new file mode 100644 index 0000000..34be4fd --- /dev/null +++ b/src/pf.c @@ -0,0 +1,80 @@ +#include "pf.h" + +PieceFilter +pf_all = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .eorl = true, + .eoud = true, + .cp = true, + .cofb = true, + .corl = true, + .coud = true, + .cpos = true +}; + +PieceFilter +pf_4val = { + .epose = true, + .eposs = true, + .eposm = true, + .eofb = true, + .coud = true, + .cp = true +}; + +PieceFilter +pf_epcp = { + .epose = true, + .eposs = true, + .eposm = true, + .cp = true +}; + +PieceFilter +pf_cpos = { + .cpos = true +}; + +PieceFilter +pf_cp = { + .cp = true +}; + +PieceFilter +pf_ep = { + .epose = true, + .eposs = true, + .eposm = true +}; + +PieceFilter +pf_e = { + .epose = true +}; + +PieceFilter +pf_s = { + .eposs = true +}; + +PieceFilter +pf_m = { + .eposm = true +}; + +PieceFilter +pf_eo = { + .eofb = true, + .eorl = true, + .eoud = true +}; + +PieceFilter +pf_co = { + .cofb = true, + .corl = true, + .coud = true +}; diff --git a/src/pf.h b/src/pf.h new file mode 100644 index 0000000..85ee1eb --- /dev/null +++ b/src/pf.h @@ -0,0 +1,18 @@ +#ifndef PF_H +#define PF_H + +#include "cubetypes.h" + +extern PieceFilter pf_all; +extern PieceFilter pf_4val; +extern PieceFilter pf_epcp; +extern PieceFilter pf_cpos; +extern PieceFilter pf_cp; +extern PieceFilter pf_ep; +extern PieceFilter pf_e; +extern PieceFilter pf_s; +extern PieceFilter pf_m; +extern PieceFilter pf_eo; +extern PieceFilter pf_co; + +#endif diff --git a/src/pruning.c b/src/pruning.c new file mode 100644 index 0000000..bc9dd59 --- /dev/null +++ b/src/pruning.c @@ -0,0 +1,271 @@ +#include "pruning.h" + +static void genptable_bfs(PruneData *pd, int d, Move *ms); +static void genptable_branch(PruneData *pd, uint64_t i, int d, Move *m); +static void ptable_update(PruneData *pd, Cube cube, int m); +static void ptable_update_index(PruneData *pd, uint64_t ind, int m); +static int ptableval_index(PruneData *pd, uint64_t ind); +static bool read_ptable_file(PruneData *pd); +static bool write_ptable_file(PruneData *pd); + +PruneData +pd_eofb_HTM = { + .filename = "pt_eofb_HTM", + .coord = &coord_eofb, + .moveset = moveset_HTM, +}; + +PruneData +pd_coud_HTM = { + .filename = "pt_coud_HTM", + .coord = &coord_coud, + .moveset = moveset_HTM, +}; + +PruneData +pd_cornershtr_HTM = { + .filename = "pt_cornershtr_HTM", + .coord = &coord_cornershtr, + .moveset = moveset_HTM, +}; + +PruneData +pd_corners_HTM = { + .filename = "pt_corners_HTM", + .coord = &coord_corners, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_sym16_HTM = { + .filename = "pt_drud_sym16_HTM", + .coord = &coord_drud_sym16, + .moveset = moveset_HTM, +}; + +PruneData +pd_drud_eofb = { + .filename = "pt_drud_eofb", + .coord = &coord_drud_eofb, + .moveset = moveset_eofb, +}; + +PruneData +pd_drudfin_noE_sym16_drud = { + .filename = "pt_drudfin_noE_sym16_drud", + .coord = &coord_drudfin_noE_sym16, + .moveset = moveset_drud, +}; + +PruneData +pd_htr_drud = { + .filename = "pt_htr_drud", + .coord = &coord_htr_drud, + .moveset = moveset_drud, +}; + +PruneData +pd_htrfin_htr = { + .filename = "pt_htrfin_htr", + .coord = &coord_htrfin, + .moveset = moveset_htr, +}; + +PruneData +pd_khuge_HTM = { + .filename = "pt_khuge_HTM", + .coord = &coord_khuge, + .moveset = moveset_HTM, +}; + +void +genptable(PruneData *pd) +{ + Move ms[NMOVES]; + int d; + uint64_t j, oldn; + + if (pd->generated) + return; + + /* TODO: check if memory is enough, otherwise maybe exit gracefully? */ + pd->ptable = malloc(ptablesize(pd) * sizeof(uint8_t)); + + if (read_ptable_file(pd)) { + pd->generated = true; + return; + } + pd->generated = true; + + fprintf(stderr, "Cannot load %s, generating it\n", pd->filename); + + moveset_to_list(pd->moveset, ms); + + /* We use 4 bits per value, so any distance >= 15 is set to 15 */ + for (j = 0; j < pd->coord->max; j++) + ptable_update_index(pd, j, 15); + + for (j = 0; j < pd->coord->max; j++) + if (ptableval_index(pd, j) != 15) { + printf("Error, non-max value at index %lu!\n", j); + break; + } + printf("Table set, ready to start\n"); + + ptable_update(pd, (Cube){0}, 0); + pd->n = 1; + oldn = 0; + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + 0, pd->n - oldn, pd->n, pd->coord->max); + oldn = 1; + for (d = 0; d < 15 && pd->n < pd->coord->max; d++) { + genptable_bfs(pd, d, ms); + fprintf(stderr, "Depth %d done, generated %lu\t(%lu/%lu)\n", + d+1, pd->n - oldn, pd->n, pd->coord->max); + oldn = pd->n; + } + + if (!write_ptable_file(pd)) + fprintf(stderr, "Error writing ptable file\n"); +} + +static void +genptable_bfs(PruneData *pd, int d, Move *ms) +{ + uint64_t i; + + for (i = 0; i < pd->coord->max; i++) + if (ptableval_index(pd, i) == d) + genptable_branch(pd, i, d, ms); +} + +static void +genptable_branch(PruneData *pd, uint64_t ind, int d, Move *ms) +{ + int i, j; + Cube ci, cc, c; + + /* + * This is the only line of the whole program where we REALLY need an + * anti-indexer function. We could get rid of it if only we could save + * a cube object for each index value as we go, but then we would need + * an incredible amount of memory to generate each ptable: assuming + * fields in struct cube are 32 bit ints that would take 88 times the + * memory of the table to be generated, more than 120Gb for + * ptable_khuge for example! + */ + ci = pd->coord->cube(ind); + + for (i = 0; i < pd->coord->ntrans; i++) { + /* For simplicity trans[] is NULL when ntrans = 1 */ + c = i == 0 ? ci : + apply_trans(pd->coord->trans[i], ci); + for (j = 0; ms[j] != NULLMOVE; j++) { + cc = apply_move(ms[j], c); + if (ptableval(pd, cc) > d+1) + ptable_update(pd, cc, d+1); + } + } +} + +void +print_ptable(PruneData *pd) +{ + uint64_t i, a[16]; + + for (i = 0; i < 16; i++) + a[i] = 0; + + if (!pd->generated) + genptable(pd); + + for (i = 0; i < pd->coord->max; i++) + a[ptableval_index(pd, i)]++; + + fprintf(stderr, "Values for table %s\n", pd->filename); + for (i = 0; i < 16; i++) + printf("%2lu\t%10lu\n", i, a[i]); +} + +uint64_t +ptablesize(PruneData *pd) +{ + return (pd->coord->max + 1) / 2; +} + +static void +ptable_update(PruneData *pd, Cube cube, int n) +{ + uint64_t ind = pd->coord->index(cube); + ptable_update_index(pd, ind, n); +} + +static void +ptable_update_index(PruneData *pd, uint64_t ind, int n) +{ + uint8_t oldval2 = pd->ptable[ind/2]; + int other = (ind % 2) ? oldval2 % 16 : oldval2 / 16; + + pd->ptable[ind/2] = (ind % 2) ? 16*n + other : 16*other + n; + pd->n++; +} + +int +ptableval(PruneData *pd, Cube cube) +{ + return ptableval_index(pd, pd->coord->index(cube)); +} + +static int +ptableval_index(PruneData *pd, uint64_t ind) +{ + if (!pd->generated) + genptable(pd); + + return (ind % 2) ? pd->ptable[ind/2] / 16 : pd->ptable[ind/2] % 16; +} + +static bool +read_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t r; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = fread(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return r == ptablesize(pd); +} + +static bool +write_ptable_file(PruneData *pd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t written; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, pd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + written = fwrite(pd->ptable, sizeof(uint8_t), ptablesize(pd), f); + fclose(f); + + return written == ptablesize(pd); +} + diff --git a/src/pruning.h b/src/pruning.h new file mode 100644 index 0000000..631ee3a --- /dev/null +++ b/src/pruning.h @@ -0,0 +1,23 @@ +#ifndef PRUNING_H +#define PRUNING_H + +#include "symcoord.h" + +extern PruneData pd_eofb_HTM; +extern PruneData pd_coud_HTM; +extern PruneData pd_corners_HTM; +extern PruneData pd_cornershtr_HTM; +extern PruneData pd_drud_sym16_HTM; +extern PruneData pd_drud_eofb; +extern PruneData pd_drudfin_noE_sym16_drud; +extern PruneData pd_htr_drud; +extern PruneData pd_htrfin_htr; +extern PruneData pd_khuge_HTM; + +void genptable(PruneData *pd); +void print_ptable(PruneData *pd); +uint64_t ptablesize(PruneData *pd); +int ptableval(PruneData *pd, Cube cube); + +#endif + diff --git a/src/pruning_tables.c b/src/pruning_tables.c deleted file mode 100644 index 8936b99..0000000 --- a/src/pruning_tables.c +++ /dev/null @@ -1,483 +0,0 @@ -#include -#include "pruning_tables.h" -#include "moves.h" - -/* The data contained in e.g. eofb pruning table is the same that is contained - * in eolr pruning table and so on. For small tables the memory wasted is not - * too much and it makes things easier. I may change this when I implement - * bigger tables. */ -int eofb_pruning_table[pow2to11]; -int eorl_pruning_table[pow2to11]; -int eoud_pruning_table[pow2to11]; -int coud_pruning_table[pow3to7]; -int cofb_pruning_table[pow3to7]; -int corl_pruning_table[pow3to7]; -int cp_pruning_table[factorial8]; - -int eorl_from_eofb_pruning_table[pow2to11]; -int eoud_from_eofb_pruning_table[pow2to11]; -int eoud_from_eorl_pruning_table[pow2to11]; -int eofb_from_eorl_pruning_table[pow2to11]; -int eofb_from_eoud_pruning_table[pow2to11]; -int eorl_from_eoud_pruning_table[pow2to11]; - -int coud_from_eofb_pruning_table[pow3to7]; -int coud_from_eorl_pruning_table[pow3to7]; -int cofb_from_eorl_pruning_table[pow3to7]; -int cofb_from_eoud_pruning_table[pow3to7]; -int corl_from_eoud_pruning_table[pow3to7]; -int corl_from_eofb_pruning_table[pow3to7]; - -int cp_drud_pruning_table[factorial8]; -int cp_drfb_pruning_table[factorial8]; -int cp_drrl_pruning_table[factorial8]; -int epud_pruning_table[factorial8]; -int epfb_pruning_table[factorial8]; -int eprl_pruning_table[factorial8]; - -int cp_htr_pruning_table[factorial8]; - -int cpud_to_htr_pruning_table[factorial8]; -int cpfb_to_htr_pruning_table[factorial8]; -int cprl_to_htr_pruning_table[factorial8]; - - -/* About 1Mb each */ -int8_t eofb_epose_pruning_table[pow2to11][binom12on4]; -int8_t eorl_eposs_pruning_table[pow2to11][binom12on4]; -int8_t eoud_eposm_pruning_table[pow2to11][binom12on4]; - -/* About 4.5Mb each */ -int8_t eofb_coud_pruning_table[pow2to11][pow3to7]; -int8_t eofb_corl_pruning_table[pow2to11][pow3to7]; -int8_t eorl_coud_pruning_table[pow2to11][pow3to7]; -int8_t eorl_cofb_pruning_table[pow2to11][pow3to7]; -int8_t eoud_cofb_pruning_table[pow2to11][pow3to7]; -int8_t eoud_corl_pruning_table[pow2to11][pow3to7]; - -/* About 1Mb each */ -int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4]; -int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4]; -int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4]; -int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4]; -int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4]; -int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4]; - - -/* Firs one is 88Mb, second one is 71Mb */ -int8_t cp_co_pruning_table[factorial8][pow3to7]; -int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4]; - -int initialized_small = 0; -int initialized_directdr = 0; -int initialized_drfromeo = 0; -int initialized_huge = 0; - -void init_single_table(int n, int t_tab[][19], int p_tab[n], int mask) { - int state[n]; - state[0] = 0; /* 0 should always be the solved state. */ - p_tab[0] = 0; - int state_count = 1; - for (int i = 0; i < state_count; i++) { - for (int m = 1; m < 19; m++) { - int next = t_tab[state[i]][m]; - if (mask & (1< p_tab[state[i]] + 1) && next) { - p_tab[next] = p_tab[state[i]] + 1; - state[state_count++] = next; - } - } - } - } -} - -void init_double_table(int n1, int n2, - int t_table1[n1][19], int t_table2[n2][19], - int8_t p_table[n1][n2], int mask) { - static int state1[factorial8*pow3to7], state2[factorial8*pow3to7]; - state1[0] = 0; - state2[0] = 0; - p_table[0][0] = 0; - int state_count = 1; - for (int i = 0; i < state_count; i++) { - for (int m = 1; m < 19; m++) { - int next1 = t_table1[state1[i]][m]; - int next2 = t_table2[state2[i]][m]; - if (mask & (1< -#include "utils.h" - -extern int eofb_pruning_table[pow2to11]; -extern int eorl_pruning_table[pow2to11]; -extern int eoud_pruning_table[pow2to11]; -extern int coud_pruning_table[pow3to7]; -extern int cofb_pruning_table[pow3to7]; -extern int corl_pruning_table[pow3to7]; -extern int cp_pruning_table[factorial8]; - -extern int eorl_from_eofb_pruning_table[pow2to11]; -extern int eoud_from_eofb_pruning_table[pow2to11]; -extern int eoud_from_eorl_pruning_table[pow2to11]; -extern int eofb_from_eorl_pruning_table[pow2to11]; -extern int eofb_from_eoud_pruning_table[pow2to11]; -extern int eorl_from_eoud_pruning_table[pow2to11]; - -extern int coud_from_eofb_pruning_table[pow3to7]; -extern int coud_from_eorl_pruning_table[pow3to7]; -extern int cofb_from_eorl_pruning_table[pow3to7]; -extern int cofb_from_eoud_pruning_table[pow3to7]; -extern int corl_from_eoud_pruning_table[pow3to7]; -extern int corl_from_eofb_pruning_table[pow3to7]; - -extern int cp_drud_pruning_table[factorial8]; -extern int cp_drfb_pruning_table[factorial8]; -extern int cp_drrl_pruning_table[factorial8]; -extern int epud_pruning_table[factorial8]; -extern int epfb_pruning_table[factorial8]; -extern int eprl_pruning_table[factorial8]; - -extern int cp_htr_pruning_table[factorial8]; -extern int cpud_to_htr_pruning_table[factorial8]; -extern int cpfb_to_htr_pruning_table[factorial8]; -extern int cprl_to_htr_pruning_table[factorial8]; - -/* About 1Mb each */ -extern int8_t eofb_epose_pruning_table[pow2to11][binom12on4]; -extern int8_t eorl_eposs_pruning_table[pow2to11][binom12on4]; -extern int8_t eoud_eposm_pruning_table[pow2to11][binom12on4]; - -/* About 4.5Mb each */ -extern int8_t eofb_coud_pruning_table[pow2to11][pow3to7]; -extern int8_t eofb_corl_pruning_table[pow2to11][pow3to7]; -extern int8_t eorl_coud_pruning_table[pow2to11][pow3to7]; -extern int8_t eorl_cofb_pruning_table[pow2to11][pow3to7]; -extern int8_t eoud_cofb_pruning_table[pow2to11][pow3to7]; -extern int8_t eoud_corl_pruning_table[pow2to11][pow3to7]; - -/* About 1Mb each */ -extern int8_t coud_epose_from_eofb_pruning_table[pow3to7][binom12on4]; -extern int8_t cofb_eposs_from_eorl_pruning_table[pow3to7][binom12on4]; -extern int8_t corl_eposm_from_eoud_pruning_table[pow3to7][binom12on4]; -extern int8_t coud_epose_from_eorl_pruning_table[pow3to7][binom12on4]; -extern int8_t cofb_eposs_from_eoud_pruning_table[pow3to7][binom12on4]; -extern int8_t corl_eposm_from_eofb_pruning_table[pow3to7][binom12on4]; - -/* First one 88Mb, second one 21Mb */ -extern int8_t cp_co_pruning_table[factorial8][pow3to7]; -extern int8_t triple_eo_pruning_table[pow2to11][binom12on4*binom8on4]; - -void init_small_pruning_tables(); -void init_directdr_pruning_tables(); -void init_drfromeo_pruning_tables(); -void init_huge_pruning_tables(); diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 0000000..0880f8a --- /dev/null +++ b/src/shell.c @@ -0,0 +1,93 @@ +#include "shell.h" + +static void cleanwhitespaces(char *line); +static int parseline(char *line, char **v); + +static void +cleanwhitespaces(char *line) +{ + char *i; + + for (i = line; *i != 0; i++) + if (*i == '\t' || *i == '\n') + *i = ' '; +} + +/* This function assumes that **v is large enough */ +static int +parseline(char *line, char **v) +{ + char *t; + int n = 0; + + cleanwhitespaces(line); + + for (t = strtok(line, " "); t != NULL; t = strtok(NULL, " ")) + strcpy(v[n++], t); + + return n; +} + +void +exec_args(int c, char **v) +{ + int i; + Command *cmd = NULL; + CommandArgs *args; + + for (i = 0; i < NCOMMANDS; i++) + if (commands[i] != NULL && !strcmp(v[0], commands[i]->name)) + cmd = commands[i]; + + if (cmd == NULL) { + fprintf(stderr, "%s: command not found\n", v[0]); + return; + } + + args = cmd->parse_args(c-1, &v[1]); + if (!args->success) { + fprintf(stderr, "usage: %s\n", cmd->usage); + return; + } + + cmd->exec(args); + free_args(args); +} + +void +launch() +{ + int i, shell_argc; + char line[MAXLINELEN], **shell_argv; + + shell_argv = malloc(MAXNTOKENS * sizeof(char *)); + for (i = 0; i < MAXNTOKENS; i++) + shell_argv[i] = malloc((MAXTOKENLEN+1) * sizeof(char)); + + fprintf(stderr, "Welcome to Nissy "VERSION".\n"); + fprintf(stderr, "Type 'help' for a list.\n"); + + while (true) { + fprintf(stderr, "nissy-# "); + if (fgets(line, MAXLINELEN, stdin) == NULL) + break; + shell_argc = parseline(line, shell_argv); + if (shell_argc > 0) + exec_args(shell_argc, shell_argv); + } + + for (i = 0; i < MAXNTOKENS; i++) + free(shell_argv[i]); + free(shell_argv); +} + +int +main(int argc, char *argv[]) +{ + if (argc > 1) + exec_args(argc-1, &argv[1]); + else + launch(); + + return 0; +} diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000..a72d136 --- /dev/null +++ b/src/shell.h @@ -0,0 +1,13 @@ +#ifndef SHELL_H +#define SHELL_H + +#include "commands.h" + +#define MAXLINELEN 10000 +#define MAXTOKENLEN 255 +#define MAXNTOKENS 255 + +void exec_args(int c, char **v); +void launch(); + +#endif diff --git a/src/solve.c b/src/solve.c new file mode 100644 index 0000000..1f4a155 --- /dev/null +++ b/src/solve.c @@ -0,0 +1,173 @@ +#include "solve.h" + +/* Local functions ***********************************************************/ + +static bool allowed_next(Move move, DfsData *dd); +static void dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_branch(Cube c, Step *s, SolveOptions *os, DfsData *dd); +static bool dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd); +static void dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd); +static bool dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd); + +/* Local functions ***********************************************************/ + +static bool +allowed_next(Move move, DfsData *dd) +{ + if (!possible_next(dd->last2, dd->last1, move)) + return false; + + if (commute(dd->last1, move)) + return dd->move_position[dd->last1] < dd->move_position[move]; + + return true; +} + +static void +dfs(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dfs_stop(c, s, opts, dd)) + return; + + if (dfs_check_solved(s, opts, dd)) + return; + + dfs_branch(c, s, opts, dd); + + if (opts->can_niss && !dd->niss) + dfs_niss(c, s, opts, dd); +} + +static void +dfs_branch(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move m, l1 = dd->last1, l2 = dd->last2, *moves = dd->sorted_moves; + + int i, maxnsol = opts->max_solutions; + + for (i = 0; moves[i] != NULLMOVE && dd->sols->len < maxnsol; i++) { + m = moves[i]; + if (allowed_next(m, dd)) { + dd->last2 = dd->last1; + dd->last1 = m; + append_move(dd->current_alg, m, dd->niss); + + dfs(apply_move(m, c), s, opts, dd); + + dd->current_alg->len--; + dd->last2 = l2; + dd->last1 = l1; + } + } +} + +static bool +dfs_check_solved(Step *s, SolveOptions *opts, DfsData *dd) +{ + if (dd->lb != 0) + return false; + + if (dd->current_alg->len == dd->d) { + if (s->is_valid(dd->current_alg) || opts->all) + append_alg(dd->sols, dd->current_alg); + + if (opts->verbose) + print_alg(dd->current_alg, false); + } + + return true; +} + +static void +dfs_niss(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + Move l1 = dd->last1, l2 = dd->last2; + CubeTarget ct; + + ct.cube = apply_move(inverse_move(l1), (Cube){0}); + ct.target = 1; + + if (dd->current_alg->len == 0 || s->estimate(ct)) { + dd->niss = true; + dd->last1 = NULLMOVE; + dd->last2 = NULLMOVE; + + dfs(inverse_cube(c), s, opts, dd); + + dd->last1 = l1; + dd->last2 = l2; + dd->niss = false; + } +} + +static bool +dfs_stop(Cube c, Step *s, SolveOptions *opts, DfsData *dd) +{ + CubeTarget ct = { + .cube = c, + .target = dd->d - dd->current_alg->len + }; + + if (dd->sols->len >= opts->max_solutions) + return true; + + dd->lb = s->estimate(ct); + if (opts->can_niss && !dd->niss) + dd->lb = MIN(1, dd->lb); + + if (dd->current_alg->len + dd->lb > dd->d) + return true; + + return false; +} + +/* Public functions **********************************************************/ + +AlgList * +solve(Cube cube, Step *step, SolveOptions *opts) +{ + AlgListNode *node; + AlgList *sols = new_alglist(); + Cube c; + + if (step->detect != NULL) + step->pre_trans = step->detect(cube); + c = apply_trans(step->pre_trans, cube); + + DfsData dd = { + .m = 0, + .niss = false, + .lb = -1, + .last1 = NULLMOVE, + .last2 = NULLMOVE, + .sols = sols, + .current_alg = new_alg("") + }; + + if (step->ready != NULL && !step->ready(c)) { + fprintf(stderr, "Cube not ready for solving step: "); + fprintf(stderr, "%s\n", step->ready_msg); + return sols; + } + + moveset_to_list(step->moveset, dd.sorted_moves); + movelist_to_position(dd.sorted_moves, dd.move_position); + + for (dd.d = opts->min_moves; + dd.d <= opts->max_moves && + !(sols->len && opts->optimal_only) && + sols->len < opts->max_solutions; + dd.d++) { + if (opts->verbose) + fprintf(stderr, + "Found %d solutions, searching depth %d...\n", + sols->len, dd.d); + dfs(c, step, opts, &dd); + } + + for (node = sols->first; node != NULL; node = node->next) + transform_alg(inverse_trans(step->pre_trans), node->alg); + + free_alg(dd.current_alg); + return sols; +} diff --git a/src/solve.h b/src/solve.h new file mode 100644 index 0000000..ff84e7e --- /dev/null +++ b/src/solve.h @@ -0,0 +1,9 @@ +#ifndef SOLVE_H +#define SOLVE_H + +#include "moves.h" +#include "trans.h" + +AlgList * solve(Cube cube, Step *step, SolveOptions *opts); + +#endif diff --git a/src/solver.c b/src/solver.c deleted file mode 100644 index 48e845f..0000000 --- a/src/solver.c +++ /dev/null @@ -1,1058 +0,0 @@ -#include -#include - -#include "utils.h" -#include "coordinates.h" -#include "moves.h" -#include "io.h" -#include "pruning_tables.h" - -/* Applies inverse of moves, inverse of prev_moves and then inverse of scramble - * and returns a coordinate determined by t_table. */ -int premoves_inverse(int moves[30], int scramble[], int prev_moves[30], - int t_table[][19]) { - int nprevmoves, nmoves, nscramble, coord = 0; - - for (nmoves = 0; moves[nmoves]; nmoves++); - for (nprevmoves = 0; prev_moves[nprevmoves]; nprevmoves++); - for (nscramble = 0; scramble[nscramble]; nscramble++); - - for (int i = nmoves - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[moves[i]]]; - for (int i = nprevmoves - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[prev_moves[i]]]; - for (int i = nscramble - 1; i >= 0; i--) - coord = t_table[coord][inverse_move[scramble[i]]]; - - return coord; -} - - -/******/ -/* EO */ -/******/ -void niss_eo_dfs(int eo, int scramble[], int eo_list[][30], int *eo_count, - int t_table[pow2to11][19], int p_table[pow2to11], - int last1, int last2, int moves, int m, int d, int niss, - int can_use_niss, int hide) { - - - if (*eo_count >= m || moves > d || - ((!can_use_niss || niss) && moves + p_table[eo] > d)) - return; - - eo_list[*eo_count][moves] = 0; - - if (eo == 0) { - /* If an early EO is found, or if "case F2 B", or if hide is on. */ - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*eo_count < m - 1) - copy_moves(eo_list[*eo_count], eo_list[(*eo_count)+1]); - (*eo_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - eo_list[*eo_count][moves] = niss ? -i : i; - niss_eo_dfs(t_table[eo][i], scramble, eo_list, eo_count, t_table, - p_table, i, last1, moves+1, m, d, niss, - can_use_niss, hide); - } - } - - if (*eo_count >= m) - return; - eo_list[*eo_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc, and if I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) { - int aux[] = {0,0}; - niss_eo_dfs(premoves_inverse(eo_list[*eo_count], scramble, aux, t_table), - scramble, eo_list, eo_count, t_table, p_table, - 0, 0, moves, m, d, 1, can_use_niss, hide); - } -} - -int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h) { - - init_small_pruning_tables(); - - int n = 0, eofb = 0, eorl = 0, eoud = 0; - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - } - for (int i = 0; i <= b; i++) { - if (fb) - niss_eo_dfs(eofb, scram, eo_list, &n, eofb_transition_table, - eofb_pruning_table, 0, 0, 0, m, i, 0, niss, h); - if (rl) - niss_eo_dfs(eorl, scram, eo_list, &n, eorl_transition_table, - eorl_pruning_table, 0, 0, 0, m, i, 0, niss, h); - if (ud) - niss_eo_dfs(eoud, scram, eo_list, &n, eoud_transition_table, - eoud_pruning_table, 0, 0, 0, m, i, 0, niss, h); - } - return n; -} - - -/******/ -/* CO */ -/******/ -void niss_co_dfs(int co, int scramble[], int co_list[][30], int *co_count, - int t_table[pow3to7][19], int p_table[pow3to7], - int last1, int last2, int moves, int m, int d, int niss, - int can_use_niss, int hide, int ignore) { - - - if (*co_count >= m || moves > d || - ((!can_use_niss || niss) && ((!ignore && moves + p_table[co] > d) || - ( ignore && moves + p_table[co] - 2 > d)))) - return; - - co_list[*co_count][moves] = 0; - - if (co == 0 || (ignore && ( t_table[t_table[co][F]][B] == 0 || - t_table[t_table[co][R]][L] == 0 || - t_table[t_table[co][U]][D] == 0 ))) { - /* If an early CO is found, or if "case F2 B", or if hide is on. */ - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*co_count < m - 1) - copy_moves(co_list[*co_count], co_list[(*co_count)+1]); - (*co_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - co_list[*co_count][moves] = niss ? -i : i; - niss_co_dfs(t_table[co][i], scramble, co_list, co_count, t_table, - p_table, i, last1, moves+1, m, d, niss, - can_use_niss, hide, ignore); - } - } - - if (*co_count >= m) - return; - co_list[*co_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc, and if I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) { - int aux[] = {0,0}; - niss_co_dfs(premoves_inverse(co_list[*co_count], scramble, aux, t_table), - scramble, co_list, co_count, t_table, p_table, - 0, 0, moves, m, d, 1, can_use_niss, hide, ignore); - } -} - -int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h, int ignore) { - - init_small_pruning_tables(); - - int n = 0, cofb = 0, corl = 0, coud = 0; - for (int i = 0; scram[i]; i++) { - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - } - for (int i = 0; i <= b; i++) { - if (fb) - niss_co_dfs(cofb, scram, co_list, &n, cofb_transition_table, - cofb_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - if (rl) - niss_co_dfs(corl, scram, co_list, &n, corl_transition_table, - corl_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - if (ud) - niss_co_dfs(coud, scram, co_list, &n, coud_transition_table, - coud_pruning_table, 0, 0, 0, m, i, 0, niss, h, ignore); - } - return n; -} - - -/**************/ -/* DR from EO */ -/**************/ - - -/* Scramble includes premoves for previous EO */ -void niss_dr_from_eo_dfs(int co, int epos, int scramble[], int eo_moves[30], - int dr_list[][30], int *dr_count, - int co_t_table[pow3to7][19], - int epos_t_table[binom12on4][19], - int8_t p_table[pow3to7][binom12on4], int mask, - int last1, int last2, int last1_inv, int last2_inv, - int moves, int m, int d, int niss, - int can_use_niss, int hide) { - - if (*dr_count >= m || moves > d || - ((!can_use_niss || niss) && moves + p_table[co][epos] > d)) - return; - - dr_list[*dr_count][moves] = 0; - - if (co == 0 && epos == 0) { - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*dr_count < m - 1) - copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]); - (*dr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - dr_list[*dr_count][moves] = niss ? -i : i; - niss_dr_from_eo_dfs(co_t_table[co][i], epos_t_table[epos][i], - scramble, eo_moves, dr_list, dr_count, - co_t_table, epos_t_table, p_table, mask, - i, last1, last1_inv, last2_inv, - moves+1, m, d, niss, can_use_niss, hide); - } - } - - if (*dr_count >= m) - return; - dr_list[*dr_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was F/F' etc and I am allowed to niss, try niss! */ - if (!niss && (last1 == 0 || co_t_table[0][last1] != 0) && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - niss_dr_from_eo_dfs(premoves_inverse(dr_list[*dr_count], scramble, - eo_moves, co_t_table), - premoves_inverse(dr_list[*dr_count], scramble, - eo_moves, epos_t_table), - scramble, eo_moves, dr_list, dr_count, - co_t_table, epos_t_table, p_table, - mask, last1_inv, last2_inv, 0, 0, - moves, m, d, 1, can_use_niss, hide); -} - -int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb, - int rl, int ud, int m, int b, int niss, int hide) { - - init_drfromeo_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int epose = 0, eposm = 0, eposs = 0; - int coud = 0, corl = 0, cofb = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - } - - int fake_eom[2] = {0, 0}; /* Fake EO moves */ - - if (from == 1) { - if (eofb) - return -1; - for (int i = 0; i <= b; i++) { - if (ud) - niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n, - coud_transition_table, epose_transition_table, - coud_epose_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (rl) - niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if (from == 2) { - if (eorl) - return -1; - for (int i = 0; i <= b; i++) { - if (fb) - niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eorl_pruning_table, move_mask_eorl, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (ud) - niss_dr_from_eo_dfs(coud, epose, scram, fake_eom, dr_list, &n, - coud_transition_table, epose_transition_table, - coud_epose_from_eorl_pruning_table, move_mask_eorl, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if (from == 3) { - if (eoud) - return -1; - for (int i = 0; i <= b; i++) { - if (rl) - niss_dr_from_eo_dfs(corl, eposm, scram, fake_eom, dr_list, &n, - corl_transition_table, eposm_transition_table, - corl_eposm_from_eoud_pruning_table, move_mask_eoud, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - if (fb) - niss_dr_from_eo_dfs(cofb, eposs, scram, fake_eom, dr_list, &n, - cofb_transition_table, eposs_transition_table, - cofb_eposs_from_eoud_pruning_table, move_mask_eoud, - 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else { - return -1; - } - return n; -} - - -/***************/ -/* HTR from DR */ -/***************/ - -/* Scramble includes premoves for previous DR */ -void niss_htr_from_dr_dfs(int cp, int eo3, int scramble[], int eodr_moves[30], - int htr_list[][30], int *htr_count, - int eo3_t_table[pow2to11][19], - int cp_to_htr_pruning_table[factorial8], - int cp_htr_pruning_table[factorial8], - int cp_finish_pruning_table[factorial8], - int mask, int last1, int last2, - int last1_inv, int last2_inv, int moves, - int m, int d, int niss, - int can_use_niss, int hide) { - - if (*htr_count >= m || moves > d || - ((!can_use_niss || niss) && moves + cp_to_htr_pruning_table[cp] > d) || - moves + cp_finish_pruning_table[cp] - 4 > d) - return; - - htr_list[*htr_count][moves] = 0; - - if ((cp == 0 || cp_htr_pruning_table[cp]) && eo3 == 0) { - if (moves != d || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*htr_count < m - 1) - copy_moves(htr_list[*htr_count], htr_list[(*htr_count)+1]); - (*htr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - htr_list[*htr_count][moves] = niss ? -i : i; - niss_htr_from_dr_dfs(cp_transition_table[cp][i], eo3_t_table[eo3][i], - scramble, eodr_moves, htr_list, htr_count, - eo3_t_table, cp_to_htr_pruning_table, - cp_htr_pruning_table, cp_finish_pruning_table, - mask, i, last1, last1_inv, last2_inv, - moves+1, m, d, niss, can_use_niss, hide); - } - } - - if (*htr_count >= m) - return; - htr_list[*htr_count][moves] = 0; - - /* If not nissing already and we either have not done any move yet or - * the last move was a quarter turn and I am allowed to niss, try niss! */ - if (!niss && last1 % 3 != 2 && can_use_niss && - !(hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - niss_htr_from_dr_dfs(premoves_inverse(htr_list[*htr_count], scramble, - eodr_moves, cp_transition_table), - premoves_inverse(htr_list[*htr_count], scramble, - eodr_moves, eo3_t_table), - scramble, eodr_moves, htr_list, htr_count, - eo3_t_table, cp_to_htr_pruning_table, - cp_htr_pruning_table, cp_finish_pruning_table, - mask, last1_inv, last2_inv, - 0, 0, moves, m, d, 1, can_use_niss, hide); -} - -int htr_scram_spam(int scram[], int htr_list[][30], int from, - int m, int b, int niss, int hide) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - } - - int fake_drm[2] = {0, 0}; /* Fake DR moves */ - - if ((from == 1 || from == 0) && (!eofb && !eorl && !coud)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eoud, scram, fake_drm, htr_list, &n, - eoud_transition_table, cpud_to_htr_pruning_table, - cp_htr_pruning_table, cp_drud_pruning_table, - move_mask_drud, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eofb, scram, fake_drm, htr_list, &n, - eofb_transition_table, cpfb_to_htr_pruning_table, - cp_htr_pruning_table, cp_drfb_pruning_table, - move_mask_drfb, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else if ((from == 3 || from == 0) && (!eoud && !eofb && !corl)) { - for (int i = 0; i <= b; i++) { - niss_htr_from_dr_dfs(cp, eorl, scram, fake_drm, htr_list, &n, - eorl_transition_table, cprl_to_htr_pruning_table, - cp_htr_pruning_table, cp_drrl_pruning_table, - move_mask_drrl, 0, 0, 0, 0, 0, m, i, 0, niss, hide); - } - } else { - return -1; - } - return n; -} - - -/***********************/ -/* Direct DR (no NISS) */ -/***********************/ -void dr_dfs(int eo, int eo2, int eslice, int co, - int dr_list[][30], int *dr_count, - int eo_t_table[pow2to11][19], int eo2_t_table[pow2to11][19], - int eslice_t_table[binom12on4][19], int co_t_table[pow3to7][19], - int8_t eo_eslice_p_table[pow2to11][binom12on4], - int8_t eo_co_p_table[pow2to11][pow3to7], - int8_t eo2_co_p_table[pow2to11][pow3to7], - int last1, int last2, int moves, int max_sol, - int depth, int hide) { - if (*dr_count >= max_sol || moves + eo_eslice_p_table[eo][eslice] > depth || - moves + eo_co_p_table[eo][co] > depth || - moves + eo2_co_p_table[eo2][co] > depth) - return; - - dr_list[*dr_count][moves] = 0; - - if (eo == 0 && eslice == 0 && co == 0) { - /* If an early DR is found, or if "case R2 L". */ - if (moves != depth || (parallel(last1, last2) && last2 % 3 == 2) || - (hide && moves > 0 && - (last1 % 3 == 0 || (parallel(last1, last2) && last2 % 3 == 0)))) - return; - /* Copy moves for the next solution */ - if (*dr_count < max_sol - 1) - copy_moves(dr_list[*dr_count], dr_list[(*dr_count)+1]); - (*dr_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - dr_list[*dr_count][moves] = i; - dr_dfs(eo_t_table[eo][i], eo2_t_table[eo2][i], - eslice_t_table[eslice][i], co_t_table[co][i], - dr_list, dr_count, - eo_t_table, eo2_t_table, eslice_t_table, co_t_table, - eo_eslice_p_table, eo_co_p_table, eo2_co_p_table, - i, last1, moves+1, max_sol, depth, hide); - } - } -} - -int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud, - int m, int b, int h) { - - init_directdr_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int epose = 0, eposm = 0, eposs = 0; - int coud = 0, corl = 0, cofb = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - } - - for (int i = 0; i <= b; i++) { - if (ud) - dr_dfs(eofb, eorl, epose, coud, dr_list, &n, - eofb_transition_table, eorl_transition_table, - epose_transition_table, coud_transition_table, - eofb_epose_pruning_table, eofb_coud_pruning_table, - eorl_coud_pruning_table, 0, 0, 0, m, i, h); - if (fb) - dr_dfs(eorl, eoud, eposs, cofb, dr_list, &n, - eorl_transition_table, eoud_transition_table, - eposs_transition_table, cofb_transition_table, - eorl_eposs_pruning_table, eorl_cofb_pruning_table, - eoud_cofb_pruning_table, 0, 0, 0, m, i, h); - if (rl) - dr_dfs(eoud, eofb, eposm, corl, dr_list, &n, - eoud_transition_table, eofb_transition_table, - eposm_transition_table, corl_transition_table, - eoud_eposm_pruning_table, eoud_corl_pruning_table, - eofb_corl_pruning_table, 0, 0, 0, m, i, h); - } - return n; -} - - -/*************/ -/* DR finish */ -/*************/ -void dr_finish_dfs(int cp, int ep8, int ep4, int sol[][30], int *sol_count, - int ep8_t_table[factorial8][19], - int ep4_t_table[factorial4][19], - int cp_p_table[factorial8], - int ep8_p_table[factorial8], - int mask, int last1, int last2, int moves, int m, int d) { - - - if (*sol_count >= m || moves + cp_p_table[cp] > d || - moves + ep8_p_table[ep8] > d) - return; - - sol[*sol_count][moves] = 0; - - if (cp == 0 && ep8 == 0 && ep4 == 0) { - if (moves != d) - return; - /* Copy moves for the next solution */ - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - sol[*sol_count][moves] = i; - dr_finish_dfs(cp_transition_table[cp][i], ep8_t_table[ep8][i], - ep4_t_table[ep4][i], sol, sol_count, - ep8_t_table, ep4_t_table, - cp_p_table, ep8_p_table, - mask, i, last1, moves+1, m, d); - } - } - - return; -} - -int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - int ep[12]; - ep_int_to_array(0, ep); - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - apply_move_ep_array(scram[i], ep); - } - - if ((from == 1 && (eofb || eorl || coud)) || - (from == 2 && (eorl || eoud || cofb)) || - (from == 3 && (eoud || eofb || corl)) || - ((eofb || eorl || coud) && (eorl || eoud ||cofb) && (eoud ||eofb || corl))) - return -1; - - for (int i = 0; i <= b; i++) { - if ((from == 1 || from == 0) && (!eofb && !eorl && !coud)) - dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep), - sol, &n, epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, - move_mask_drud, 0, 0, 0, m, i); - if ((from == 2 || from == 0) && (!eorl && !eoud && !cofb)) - dr_finish_dfs(cp, epfb_array_to_int(ep), eps_array_to_int(ep), - sol, &n, epfb_transition_table, eps_transition_table, - cp_drfb_pruning_table, epfb_pruning_table, - move_mask_drfb, 0, 0, 0, m, i); - if ((from == 3 || from == 0) && (!eoud && !eofb && !corl)) - dr_finish_dfs(cp, eprl_array_to_int(ep), epm_array_to_int(ep), - sol, &n, eprl_transition_table, epm_transition_table, - cp_drrl_pruning_table, eprl_pruning_table, - move_mask_drrl, 0, 0, 0, m, i); - } - - return n; -} - -int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, cp = 0; - int ep[12]; - ep_int_to_array(0, ep); - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - apply_move_ep_array(scram[i], ep); - } - - if (eofb || eorl || eoud || coud || cpud_to_htr_pruning_table[cp] != 0) - return -1; - - for (int i = 0; i <= b; i++) - dr_finish_dfs(cp, epud_array_to_int(ep), epe_array_to_int(ep), - sol, &n, epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, - move_mask_htr, 0, 0, 0, m, i); - - return n; -} - - -/**************/ -/* DR corners */ -/**************/ -void dr_corners_dfs(int cp, int sol[][30], int *sol_count, - int cp_p_table[factorial8], int mask, int last1, int last2, - int moves, int m, int d, int ignore) { - - if (*sol_count >= m || (!ignore && moves + cp_p_table[cp] > d) || - (ignore && moves + cp_p_table[cp] - 2 > d)) - return; - - - sol[*sol_count][moves] = 0; - - if (cp == 0 || - (ignore && mask == move_mask_drud && - (cp_transition_table[cp_transition_table[cp][U]][D3] == 0 || - cp_transition_table[cp_transition_table[cp][U2]][D2] == 0 || - cp_transition_table[cp_transition_table[cp][U3]][D] == 0 )) || - (ignore && mask == move_mask_drfb && - (cp_transition_table[cp_transition_table[cp][F]][B3] == 0 || - cp_transition_table[cp_transition_table[cp][F2]][B2] == 0 || - cp_transition_table[cp_transition_table[cp][F3]][B] == 0 )) || - (ignore && mask == move_mask_drrl && - (cp_transition_table[cp_transition_table[cp][R]][L3] == 0 || - cp_transition_table[cp_transition_table[cp][R2]][L2] == 0 || - cp_transition_table[cp_transition_table[cp][R3]][L] == 0 )) - ) { - if (moves != d) - return; - /* Copy moves for the next solution */ - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i) & mask) { - sol[*sol_count][moves] = i; - dr_corners_dfs(cp_transition_table[cp][i], sol, sol_count, - cp_p_table, mask, i, last1, moves+1, m, d, ignore); - } - } -} - -int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b, - int ignore) { - - init_small_pruning_tables(); - - int n = 0; - int eofb = 0, eorl = 0, eoud = 0; - int coud = 0, corl = 0, cofb = 0; - int cp = 0; - - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - coud = coud_transition_table[coud][scram[i]]; - - cp = cp_transition_table[cp][scram[i]]; - } - - if ((from == 1 && coud) || (from == 2 && cofb) || (from == 3 && corl) || - (coud && cofb && corl)) - return -1; - - for (int i = 0; i <= b; i++) { - if ((from == 1 || from == 0) && !coud) - dr_corners_dfs(cp, sol, &n, cp_drud_pruning_table, move_mask_drud, - 0, 0, 0, m, i, ignore); - if ((from == 2 || from == 0) && !cofb) - dr_corners_dfs(cp, sol, &n, cp_drfb_pruning_table, move_mask_drfb, - 0, 0, 0, m, i, ignore); - if ((from == 3 || from == 0) && !corl) - dr_corners_dfs(cp, sol, &n, cp_drrl_pruning_table, move_mask_drrl, - 0, 0, 0, m, i, ignore); - } - - return n; -} - -/***************/ -/* Full solver */ -/***************/ - -int is_ep_solved(int ep, int moves[30]) { - int ep_arr[12]; - ep_int_to_array(ep, ep_arr); - for (int i = 0; moves[i]; i++) - apply_move_ep_array(moves[i], ep_arr); - return !ep_array_to_int(ep_arr); -} - -/* Solves directly using only small tables. Suitable for short solutions. */ -void small_optimal_dfs(int eofb, int eorl, int eoud, int ep, - int coud, int cofb, int corl, int cp, - int sol[][30], int *sol_count, int last1, int last2, - int moves, int m, int d) { - if (moves + eofb_pruning_table[eofb] > d || - moves + eorl_pruning_table[eorl] > d || - moves + eoud_pruning_table[eoud] > d || - moves + coud_pruning_table[coud] > d || - moves + cofb_pruning_table[cofb] > d || - moves + corl_pruning_table[corl] > d || - moves + cp_pruning_table[cp] > d || - *sol_count >= m) - return; - - sol[*sol_count][moves] = 0; - - if (eofb == 0 && coud == 0 && cp == 0) { - if (is_ep_solved(ep, sol[*sol_count])) { - if (moves != d) - return; - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[*sol_count][moves] = i; - small_optimal_dfs(eofb_transition_table[eofb][i], - eorl_transition_table[eorl][i], - eoud_transition_table[eoud][i], ep, - coud_transition_table[coud][i], - cofb_transition_table[cofb][i], - corl_transition_table[corl][i], - cp_transition_table[cp][i], - sol, sol_count, i, last1, moves+1, m, d); - } - } -} - -/* Solves directly using only medium tables. Suitable for short solutions. -void medium_optimal_dfs(int eofb, int eorl, int eoud, - int epose, int eposs, int eposm, int ep, - int coud, int cofb, int corl, int cp, - int sol[][30], int *sol_count, int last1, int last2, - int moves, int m, int d) { - if (moves + eofb_epose_pruning_table[eofb][epose] > d || - moves + eorl_eposs_pruning_table[eorl][eposs] > d || - moves + eoud_eposm_pruning_table[eoud][eposm] > d || - moves + eofb_coud_pruning_table[eofb][coud] > d || - moves + eofb_corl_pruning_table[eofb][corl] > d || - moves + eorl_coud_pruning_table[eorl][coud] > d || - moves + eorl_cofb_pruning_table[eorl][cofb] > d || - moves + eoud_cofb_pruning_table[eoud][cofb] > d || - moves + eoud_corl_pruning_table[eoud][corl] > d || - moves + cp_pruning_table[cp] > d || - *sol_count >= m) - return; - - sol[*sol_count][moves] = 0; - - if (eofb == 0 && coud == 0 && cp == 0) { - if (is_ep_solved(ep, sol[*sol_count])) { - if (moves != d) - return; - if (*sol_count < m - 1) - copy_moves(sol[*sol_count], sol[(*sol_count)+1]); - (*sol_count)++; - return; - } - } - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[*sol_count][moves] = i; - medium_optimal_dfs(eofb_transition_table[eofb][i], - eorl_transition_table[eorl][i], - eoud_transition_table[eoud][i], - epose_transition_table[epose][i], - eposs_transition_table[eposs][i], - eposm_transition_table[eposm][i], ep, - coud_transition_table[coud][i], - cofb_transition_table[cofb][i], - corl_transition_table[corl][i], - cp_transition_table[cp][i], - sol, sol_count, i, last1, moves+1, m, d); - } - } -} -*/ - -/* Uses huge tables */ -int optimal_dfs(int ep, int cp, int eo, int co, int emslices, - int sol[][30], int last1, int last2, int moves, int d) { - if (moves + cp_co_pruning_table[cp][co] > d || - moves + triple_eo_pruning_table[eo][emslices] > d) - return 0; - - sol[0][moves] = 0; - - /* If solved, no need to check the depth */ - if (cp == 0 && co == 0 && eo == 0 && emslices == 0) - if (is_ep_solved(ep, sol[0])) - return 1; - - for (int i = 1; i < 19; i++) { - if (possible_next[last1][last2] & (1 << i)) { - sol[0][moves] = i; - if (optimal_dfs(ep, cp_transition_table[cp][i], - eofb_transition_table[eo][i], - coud_transition_table[co][i], - emslices_transition_table[emslices][i], - sol, i, last1, moves+1, d)) - return 1; - } - } - return 0; -} - -int solve_scram(int scram[], int sol[][30], int m, int b, int optimal) { - - /* Initialize pieces. */ - int eofb = 0, eorl = 0, eoud = 0, ep = 0; - int epose = 0, eposs = 0, eposm = 0; - int coud = 0, cofb = 0, corl = 0, cp = 0; - int emslices = 0; - for (int i = 0; scram[i]; i++) { - eofb = eofb_transition_table[eofb][scram[i]]; - eorl = eorl_transition_table[eorl][scram[i]]; - eoud = eoud_transition_table[eoud][scram[i]]; - - epose = epose_transition_table[epose][scram[i]]; - eposs = eposs_transition_table[eposs][scram[i]]; - eposm = eposm_transition_table[eposm][scram[i]]; - - ep = apply_move_ep_int(scram[i], ep); - - coud = coud_transition_table[coud][scram[i]]; - cofb = cofb_transition_table[cofb][scram[i]]; - corl = corl_transition_table[corl][scram[i]]; - cp = cp_transition_table[cp][scram[i]]; - - emslices = emslices_transition_table[emslices][scram[i]]; - } - - /* First we check if there are solutions of up to max_small moves. */ - int max_small = 10; - int n = 0; - init_small_pruning_tables(); - for (int i = 0; i <= min(b, max_small); i++) { - small_optimal_dfs(eofb, eorl, eoud, ep, coud, cofb, corl, cp, - sol, &n, 0, 0, 0, m, i); - if (n > 0 && optimal) - b = min(b, len(sol[0])); - } - - - if (n >= m || b <= 10) - return n; - - - /* If we found at least a solution, we return */ - if (n > 0) - return n; - - /* Then we try a 2-step solver */ - int max_step1 = 100; - int db = 12; - int step1[max_step1+10][30]; - int ss[300], step2[2][30]; - int best = b+1; - - /* TODO maybe: for now, multiple solutions can be found only using the - * short solver. */ - - int n_step1 = dr_scram_spam(scram, step1, 1, 1, 1, max_step1, min(b, db), 0); - for (int i = 0; i < n_step1; i++) { - copy_moves(scram, ss); - append_moves(step1[i], ss); - if (dr_finish_scram_spam(ss, step2, 0, 1, min(best-1,b) - len(step1[i]))) { - copy_moves(step1[i], sol[0]); - append_moves(step2[0], sol[0]); - best = len(sol[0]); - } - } - - /* If optimal solving was not required, or we have already found an optimal - * solution, we return. */ - if (best <= len(step1[n_step1-1]) || !optimal) - return best > b ? 0 : 1; - - /* Otherwise, we go on with the optimal solver. */ - int searched = len(step1[n_step1-1])-1; - - printf("Searched up to %d moves, no solution found.\n", searched); - printf("Using huge pruning tables, if not loaded it might take a while.\n"); - init_huge_pruning_tables(); - - for (int i = searched+1; i <= min(b, best-1); i++) { - if (i >= 10) - printf("Searching at depth %d.\n", i); - if (optimal_dfs(ep, cp, eofb, coud, emslices, sol, 0, 0, 0, i)) { - return 1; - } - } - return best > b ? 0 : 1; -} - -/* Given eofb, coud, ep and cp it finds a scramble that reaches that state. - * It uses a simple 3-step solver to find a preliminary "solution", and then - * gives this solutions as a scramble to a better solver (see above). */ -int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]) { - - int fake_count = 0, fake_scram[30]; - int eo_list[2][30], dr_list[2][30], finish_list[2][30]; - - /* Convert ep to array */ - int ep_arr[12]; - ep_int_to_array(ep, ep_arr); - - /* Find EO */ - init_small_pruning_tables(); - for (int d = 0; d < 10; d++) { - niss_eo_dfs(eofb, fake_scram, eo_list, &fake_count, eofb_transition_table, - eofb_pruning_table, 0, 0, 0, 1, d, 0, 0, 0); - if (fake_count) { - fake_count = 0; - break; - } - } - - /* Apply moves found, find epose */ - for (int i = 0; eo_list[0][i]; i++) { - coud = coud_transition_table[coud][eo_list[0][i]]; - cp = cp_transition_table[cp][eo_list[0][i]]; - apply_move_ep_array(eo_list[0][i], ep_arr); - } - int epose = epose_array_to_int(ep_arr); - - /* Find DR */ - init_drfromeo_pruning_tables(); - for (int d = 0; d < 16; d++) { - niss_dr_from_eo_dfs(coud, epose, fake_scram, fake_scram, dr_list, - &fake_count, coud_transition_table, - epose_transition_table, - coud_epose_from_eofb_pruning_table, move_mask_eofb, - 0, 0, 0, 0, 0, 1, d, 0, 0, 0); - if (fake_count) { - fake_count = 0; - break; - } - } - - /* Apply moves found, find epud and epe */ - for (int i = 0; dr_list[0][i]; i++) { - cp = cp_transition_table[cp][dr_list[0][i]]; - apply_move_ep_array(dr_list[0][i], ep_arr); - } - int epud = epud_array_to_int(ep_arr); - int epe = epe_array_to_int(ep_arr); - - /* Find finish */ - init_small_pruning_tables(); - for (int d = 0; d < 16; d++) { - dr_finish_dfs(cp, epud, epe, finish_list, &fake_count, - epud_transition_table, epe_transition_table, - cp_drud_pruning_table, epud_pruning_table, move_mask_drud, - 0, 0, 0, 1, d); - if (fake_count) { - fake_count = 0; - break; - } - } - - int scram[50]; - - /* Debug */ - /*print_moves(eo_list[0]); printf("\n"); - print_moves(dr_list[0]); printf("\n"); - print_moves(finish_list[0]); printf("\n");*/ - - copy_moves(eo_list[0], scram); - append_moves(dr_list[0], scram); - append_moves(finish_list[0], scram); - return solve_scram(scram, sol, 1, 25, 0); -} diff --git a/src/solver.h b/src/solver.h deleted file mode 100644 index abb55d3..0000000 --- a/src/solver.h +++ /dev/null @@ -1,16 +0,0 @@ -int eo_scram_spam(int scram[], int eo_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h); -int co_scram_spam(int scram[], int co_list[][30], int fb, int rl, int ud, - int m, int b, int niss, int h, int i); -int dr_scram_spam(int scram[], int dr_list[][30], int fb, int rl, int ud, - int m, int b, int h); -int drfrom_scram_spam(int scram[], int dr_list[][30], int from, int fb, - int rl, int ud, int m, int b, int niss, int hide); -int htr_scram_spam(int scram[], int htr_list[][30], int from, - int m, int b, int niss, int hide); -int dr_corners_scram_spam(int scram[], int sol[][30], int from, int m, int b, - int ignore); -int dr_finish_scram_spam(int scram[], int sol[][30], int from, int m, int b); -int htr_finish_scram_spam(int scram[], int sol[][30], int m, int b); -int solve_scram(int scram[], int sol[][30], int m, int b, int optimal); -int reach_state(int eofb, int coud, int ep, int cp, int sol[][30]); diff --git a/src/steps.c b/src/steps.c new file mode 100644 index 0000000..56f7369 --- /dev/null +++ b/src/steps.c @@ -0,0 +1,941 @@ +#include "steps.h" + +/* Checkers, estimators and validators ***************************************/ + +static bool check_centers(Cube cube); +static bool check_eofb(Cube cube); +static bool check_drud(Cube cube); +static bool check_htr(Cube cube); + +static int estimate_eoany_HTM(CubeTarget ct); +static int estimate_eofb_HTM(CubeTarget ct); +static int estimate_coany_HTM(CubeTarget ct); +static int estimate_coud_HTM(CubeTarget ct); +static int estimate_coany_URF(CubeTarget ct); +static int estimate_coud_URF(CubeTarget ct); +static int estimate_corners_HTM(CubeTarget ct); +static int estimate_cornershtr_HTM(CubeTarget ct); +static int estimate_corners_URF(CubeTarget ct); +static int estimate_cornershtr_URF(CubeTarget ct); +static int estimate_drany_HTM(CubeTarget ct); +static int estimate_drud_HTM(CubeTarget ct); +static int estimate_drud_eofb(CubeTarget ct); +static int estimate_dr_eofb(CubeTarget ct); +static int estimate_drudfin_drud(CubeTarget ct); +static int estimate_htr_drud(CubeTarget ct); +static int estimate_htrfin_htr(CubeTarget ct); +static int estimate_optimal_HTM(CubeTarget ct); + +static bool always_valid(Alg *alg); +static bool validate_singlecw_ending(Alg *alg); + +/* Pre-transformation detectors **********************************************/ + +static Trans detect_pretrans_eofb(Cube cube); +static Trans detect_pretrans_drud(Cube cube); + +/* Messages for when cube is not ready ***************************************/ + +static char check_centers_msg[100] = "cube must be oriented (centers solved)"; +static char check_eo_msg[100] = "EO must be solved on given axis"; +static char check_dr_msg[100] = "DR must be solved on given axis"; +static char check_htr_msg[100] = "HTR must be solved"; +static char check_drany_msg[100] = "DR must be solved on at least one axis"; + +/* Steps *********************************************************************/ + +Step +optimal_HTM = { + .shortname = "optimal", + .name = "Optimal solve (in HTM)", + + .estimate = estimate_optimal_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +/* EO steps **************************/ +Step +eoany_HTM = { + .shortname = "eo", + .name = "EO on any axis", + + .estimate = estimate_eoany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eofb_HTM = { + .shortname = "eofb", + .name = "EO on F/B", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +eorl_HTM = { + .shortname = "eorl", + .name = "EO on R/L", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = ur, +}; + +Step +eoud_HTM = { + .shortname = "eoud", + .name = "EO on U/D", + + .estimate = estimate_eofb_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* CO steps **************************/ +Step +coany_HTM = { + .shortname = "co", + .name = "CO on any axis", + + .estimate = estimate_coany_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +coud_HTM = { + .shortname = "coud", + .name = "CO on U/D", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corl_HTM = { + .shortname = "corl", + .name = "CO on R/L", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +cofb_HTM = { + .shortname = "cofb", + .name = "CO on F/B", + + .estimate = estimate_coud_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +Step +coany_URF = { + .shortname = "co-URF", + .name = "CO any axis (URF moveset)", + + .estimate = estimate_coany_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +coud_URF = { + .shortname = "coud-URF", + .name = "CO on U/D (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corl_URF = { + .shortname = "corl-URF", + .name = "CO on R/L (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = rf, +}; + +Step +cofb_URF = { + .shortname = "cofb-URF", + .name = "CO on F/B (URF moveset)", + + .estimate = estimate_coud_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = fd, +}; + +/* Misc corner steps *****************/ +Step +cornershtr_HTM = { + .shortname = "chtr", + .name = "Solve corners to HTR state", + + .estimate = estimate_cornershtr_HTM, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +cornershtr_URF = { + .shortname = "chtr-URF", + .name = "Solve corners to HTR state (URF moveset)", + + .estimate = estimate_cornershtr_URF, + .ready = NULL, + .is_valid = validate_singlecw_ending, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +Step +corners_HTM = { + .shortname = "corners", + .name = "Solve corners", + + .estimate = estimate_corners_HTM, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +corners_URF = { + .shortname = "corners-URF", + .name = "Solve corners (URF moveset)", + + .estimate = estimate_corners_URF, + .ready = NULL, + .is_valid = always_valid, + .moveset = moveset_URF, + + .pre_trans = uf, +}; + +/* DR steps **************************/ +Step +drany_HTM = { + .shortname = "dr", + .name = "DR on any axis", + + .estimate = estimate_drany_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drud_HTM = { + .shortname = "drud", + .name = "DR on U/D", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = uf, +}; + +Step +drrl_HTM = { + .shortname = "drrl", + .name = "DR on R/L", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = rf, +}; + +Step +drfb_HTM = { + .shortname = "drfb", + .name = "DR on F/B", + + .estimate = estimate_drud_HTM, + .ready = check_centers, + .ready_msg = check_centers_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_HTM, + + .pre_trans = fd, +}; + +/* DR from EO */ +Step +dr_eo = { + .shortname = "dr-eo", + .name = "DR without breaking EO (automatically detected)", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .detect = detect_pretrans_eofb, +}; + +Step +dr_eofb = { + .shortname = "dr-eofb", + .name = "DR on U/D or R/L without breaking EO on F/B", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +dr_eorl = { + .shortname = "dr-eorl", + .name = "DR on U/D or F/B without breaking EO on R/L", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +dr_eoud = { + .shortname = "dr-eoud", + .name = "DR on R/L or F/B without breaking EO on U/R", + + .estimate = estimate_dr_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drud_eofb = { + .shortname = "drud-eofb", + .name = "DR on U/D without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = uf, +}; + +Step +drrl_eofb = { + .shortname = "drrl-eofb", + .name = "DR on R/L without breaking EO on F/B", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rf, +}; + +Step +drud_eorl = { + .shortname = "drud-eorl", + .name = "DR on U/D without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = ur, +}; + +Step +drfb_eorl = { + .shortname = "drfb-eorl", + .name = "DR on F/B without breaking EO on R/L", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fr, +}; + +Step +drfb_eoud = { + .shortname = "drfb-eoud", + .name = "DR on F/B without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = fd, +}; + +Step +drrl_eoud = { + .shortname = "drrl-eoud", + .name = "DR on R/L without breaking EO on U/D", + + .estimate = estimate_drud_eofb, + .ready = check_eofb, + .ready_msg = check_eo_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_eofb, + + .pre_trans = rd, +}; + +/* DR finish steps */ +Step +dranyfin_DR = { + .shortname = "drfin", + .name = "DR finish on any axis without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +drudfin_drud = { + .shortname = "drudfin", + .name = "DR finish on U/D without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +drrlfin_drrl = { + .shortname = "drrlfin", + .name = "DR finish on R/L without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +drfbfin_drfb = { + .shortname = "drfbfin", + .name = "DR finish on F/B without breaking DR", + + .estimate = estimate_drudfin_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = always_valid, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR from DR */ +Step +htr_any = { + .shortname = "htr", + .name = "HTR from DR", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_drany_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .detect = detect_pretrans_drud, +}; + +Step +htr_drud = { + .shortname = "htr-drud", + .name = "HTR from DR on U/D", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = uf, +}; + +Step +htr_drrl = { + .shortname = "htr-drrl", + .name = "HTR from DR on R/L", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = rf, +}; + +Step +htr_drfb = { + .shortname = "htr-drfb", + .name = "HTR from DR on F/B", + + .estimate = estimate_htr_drud, + .ready = check_drud, + .ready_msg = check_dr_msg, + .is_valid = validate_singlecw_ending, + .moveset = moveset_drud, + + .pre_trans = fd, +}; + +/* HTR finish */ +Step +htrfin_htr = { + .shortname = "htrfin", + .name = "HTR finish without breaking HTR", + + .estimate = estimate_htrfin_htr, + .ready = check_htr, + .ready_msg = check_htr_msg, + .is_valid = always_valid, + .moveset = moveset_htr, + + .pre_trans = uf, +}; + +Step *steps[NSTEPS] = { + &optimal_HTM, /* first is default */ + + &eoany_HTM, + &eofb_HTM, + &eorl_HTM, + &eoud_HTM, + + &coany_HTM, + &coud_HTM, + &corl_HTM, + &cofb_HTM, + + &coany_URF, + &coud_URF, + &corl_URF, + &cofb_URF, + + &drany_HTM, + &drud_HTM, + &drrl_HTM, + &drfb_HTM, + + &dr_eo, + &dr_eofb, + &dr_eorl, + &dr_eoud, + &drud_eofb, + &drrl_eofb, + &drud_eorl, + &drfb_eorl, + &drfb_eoud, + &drrl_eoud, + + &dranyfin_DR, + &drudfin_drud, + &drrlfin_drrl, + &drfbfin_drfb, + + &htr_any, + &htr_drud, + &htr_drrl, + &htr_drfb, + + &htrfin_htr, + + &cornershtr_HTM, + &cornershtr_URF, + &corners_HTM, + &corners_URF, +}; + +/* Checkers, estimators and validators ***************************************/ + +static bool +check_centers(Cube cube) +{ + return cube.cpos == 0; +} + +static bool +check_eofb(Cube cube) +{ + return cube.eofb == 0; +} + +static bool +check_drud(Cube cube) +{ + return cube.eofb == 0 && cube.eorl == 0 && cube.coud == 0; +} + +static bool +check_htr(Cube cube) +{ + return check_drud(cube) && coord_htr_drud.index(cube) == 0; +} + +static int +estimate_eoany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_eofb_HTM, ct.cube); + r2 = ptableval(&pd_eofb_HTM, apply_trans(ur, ct.cube)); + r3 = ptableval(&pd_eofb_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_eofb_HTM(CubeTarget ct) +{ + return ptableval(&pd_eofb_HTM, ct.cube); +} + +static int +estimate_coany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_coud_HTM, ct.cube); + r2 = ptableval(&pd_coud_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_coud_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_HTM(CubeTarget ct) +{ + return ptableval(&pd_coud_HTM, ct.cube); +} + +static int +estimate_coany_URF(CubeTarget ct) +{ + int r1, r2, r3; + CubeTarget ct2, ct3; + + ct2.cube = apply_trans(rf, ct.cube); + ct2.target = ct.target; + + ct3.cube = apply_trans(fd, ct.cube); + ct3.target = ct.target; + + r1 = estimate_coud_URF(ct); + r2 = estimate_coud_URF(ct2); + r3 = estimate_coud_URF(ct3); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_coud_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the orientation of + * the corner in DBL and use that as a reference */ + + CubeTarget ct2 = {.cube = apply_move(z, ct.cube), .target = ct.target}; + CubeTarget ct3 = {.cube = apply_move(x, ct.cube), .target = ct.target}; + + int ud = estimate_coud_HTM(ct); + int rl = estimate_coud_HTM(ct2); + int fb = estimate_coud_HTM(ct3); + + return MIN(ud, MIN(rl, fb)); +} + +static int +estimate_corners_HTM(CubeTarget ct) +{ + return ptableval(&pd_corners_HTM, ct.cube); +} + +static int +estimate_cornershtr_HTM(CubeTarget ct) +{ + return ptableval(&pd_cornershtr_HTM, ct.cube); +} + +static int +estimate_cornershtr_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_cornershtr_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_corners_URF(CubeTarget ct) +{ + /* TODO: I can improve this by checking first the corner in DBL + * and use that as a reference */ + + int c, ret = 15; + Trans i; + + for (i = 0; i < NROTATIONS; i++) { + ct.cube = apply_alg(rotation_alg(i), ct.cube); + c = estimate_corners_HTM(ct); + ret = MIN(ret, c); + } + + return ret; +} + +static int +estimate_drany_HTM(CubeTarget ct) +{ + int r1, r2, r3; + + r1 = ptableval(&pd_drud_sym16_HTM, ct.cube); + r2 = ptableval(&pd_drud_sym16_HTM, apply_trans(rf, ct.cube)); + r3 = ptableval(&pd_drud_sym16_HTM, apply_trans(fd, ct.cube)); + + return MIN(r1, MIN(r2, r3)); +} + +static int +estimate_drud_HTM(CubeTarget ct) +{ + return ptableval(&pd_drud_sym16_HTM, ct.cube); +} + +static int +estimate_drud_eofb(CubeTarget ct) +{ + return ptableval(&pd_drud_eofb, ct.cube); +} + +static int +estimate_dr_eofb(CubeTarget ct) +{ + int r1, r2; + + r1 = ptableval(&pd_drud_eofb, ct.cube); + r2 = ptableval(&pd_drud_eofb, apply_trans(rf, ct.cube)); + + return MIN(r1, r2); +} + +static int +estimate_drudfin_drud(CubeTarget ct) +{ + int val = ptableval(&pd_drudfin_noE_sym16_drud, ct.cube); + + if (val != 0) + return val; + + return ct.cube.epose % 24 == 0 ? 0 : 1; +} + +static int +estimate_htr_drud(CubeTarget ct) +{ + return ptableval(&pd_htr_drud, ct.cube); +} + +static int +estimate_htrfin_htr(CubeTarget ct) +{ + return ptableval(&pd_htrfin_htr, ct.cube); +} + +static int +estimate_optimal_HTM(CubeTarget ct) +{ + int dr1, dr2, dr3, cor, ret; + Cube cube = ct.cube; + + dr1 = ptableval(&pd_khuge_HTM, cube); + cor = estimate_corners_HTM(ct); + ret = MAX(dr1, cor); + + if (ret > ct.target) + return ret; + + cube = apply_trans(rf, ct.cube); + dr2 = ptableval(&pd_khuge_HTM, cube); + ret = MAX(ret, dr2); + + if (ret > ct.target) + return ret; + + cube = apply_trans(fd, ct.cube); + dr3 = ptableval(&pd_khuge_HTM, cube); + + /* Michiel de Bondt's trick */ + if (dr1 == dr2 && dr2 == dr3 && dr1 != 0) + dr3++; + + return MAX(ret, dr3); +} + +static bool +always_valid(Alg *alg) +{ + return true; +} + +static bool +validate_singlecw_ending(Alg *alg) +{ + int i; + bool nor, inv; + Move l2 = NULLMOVE, l1 = NULLMOVE, l2i = NULLMOVE, l1i = NULLMOVE; + + for (i = 0; i < alg->len; i++) { + if (alg->inv[i]) { + l2i = l1i; + l1i = alg->move[i]; + } else { + l2 = l1; + l1 = alg->move[i]; + } + } + + nor = l1 ==base_move(l1) && (!commute(l1, l2) ||l2 ==base_move(l2)); + inv = l1i==base_move(l1i) && (!commute(l1i,l2i)||l2i==base_move(l2i)); + + return nor && inv; +} + +/* Pre-transformation detectors **********************************************/ + +static Trans +detect_pretrans_eofb(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_eofb(apply_trans(i, cube))) + return i; + + return 0; +} + +static Trans +detect_pretrans_drud(Cube cube) +{ + Trans i; + + for (i = 0; i < NROTATIONS; i++) + if (check_drud(apply_trans(i, cube))) + return i; + + return 0; +} diff --git a/src/steps.h b/src/steps.h new file mode 100644 index 0000000..aa3178c --- /dev/null +++ b/src/steps.h @@ -0,0 +1,10 @@ +#ifndef STEPS_H +#define STEPS_H + +#include "pruning.h" + +#define NSTEPS 50 + +extern Step * steps[NSTEPS]; + +#endif diff --git a/src/symcoord.c b/src/symcoord.c new file mode 100644 index 0000000..97a8d9a --- /dev/null +++ b/src/symcoord.c @@ -0,0 +1,355 @@ +#include "symcoord.h" + +static Cube antindex_coud_sym16(uint64_t ind); +static Cube antindex_cp_sym16(uint64_t ind); +static Cube antindex_eofbepos_sym16(uint64_t ind); +static Cube antindex_drud_sym16(uint64_t ind); +static Cube antindex_drudfin_noE_sym16(uint64_t ind); +static Cube antindex_khuge(uint64_t ind); + +static uint64_t index_coud_sym16(Cube cube); +static uint64_t index_cp_sym16(Cube cube); +static uint64_t index_eofbepos_sym16(Cube cube); +static uint64_t index_drud_sym16(Cube cube); +static uint64_t index_drudfin_noE_sym16(Cube cube); +static uint64_t index_khuge(Cube cube); + +static void gensym(SymData *sd); +static bool read_symdata_file(SymData *sd); +static bool write_symdata_file(SymData *sd); + +/* Transformation groups and symmetry data ***********************************/ + +static Trans +trans_group_udfix[16] = { + uf, ur, ub, ul, + df, dr, db, dl, + uf_mirror, ur_mirror, ub_mirror, ul_mirror, + df_mirror, dr_mirror, db_mirror, dl_mirror, +}; + +static SymData +sd_coud_16 = { + .filename = "sd_coud_16", + .coord = &coord_coud, + .sym_coord = &coord_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_cp_16 = { + .filename = "sd_cp_16", + .coord = &coord_cp, + .sym_coord = &coord_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static SymData +sd_eofbepos_16 = { + .filename = "sd_eofbepos_16", + .coord = &coord_eofbepos, + .sym_coord = &coord_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix +}; + +static int nsymdata = 3; +static SymData * all_sd[] = { + &sd_coud_16, + &sd_cp_16, + &sd_eofbepos_16, +}; + + +/* Coordinates and their implementation **************************************/ + +Coordinate +coord_eofbepos_sym16 = { + .index = index_eofbepos_sym16, + .cube = antindex_eofbepos_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_coud_sym16 = { + .index = index_coud_sym16, + .cube = antindex_coud_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_cp_sym16 = { + .index = index_cp_sym16, + .cube = antindex_cp_sym16, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drud_sym16 = { + .index = index_drud_sym16, + .cube = antindex_drud_sym16, + .max = POW3TO7 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_drudfin_noE_sym16 = { + .index = index_drudfin_noE_sym16, + .cube = antindex_drudfin_noE_sym16, + .max = FACTORIAL8 * 2768, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +Coordinate +coord_khuge = { + .index = index_khuge, + .cube = antindex_khuge, + .max = POW3TO7 * FACTORIAL4 * 64430, + .ntrans = 16, + .trans = trans_group_udfix, +}; + +/* Functions *****************************************************************/ + +static Cube +antindex_coud_sym16(uint64_t ind) +{ + return sd_coud_16.rep[ind]; +} + +static Cube +antindex_cp_sym16(uint64_t ind) +{ + return sd_cp_16.rep[ind]; +} + +static Cube +antindex_eofbepos_sym16(uint64_t ind) +{ + return sd_eofbepos_16.rep[ind]; +} + +static Cube +antindex_drud_sym16(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/POW3TO7); + c.coud = ind % POW3TO7; + c.cofb = c.coud; + c.corl = c.coud; + + return c; +} + +static Cube +antindex_drudfin_noE_sym16(uint64_t ind) +{ + Cube c1, c2; + + c1 = coord_epud.cube(ind % FACTORIAL8); + c2 = antindex_cp_sym16(ind/FACTORIAL8); + c1.cp = c2.cp; + + return c1; +} + +static Cube +antindex_khuge(uint64_t ind) +{ + Cube c; + + c = antindex_eofbepos_sym16(ind/(FACTORIAL4*POW3TO7)); + c.epose = ((c.epose / 24) * 24) + ((ind/POW3TO7) % 24); + c.coud = ind % POW3TO7; + + return c; +} + +static uint64_t +index_coud_sym16(Cube cube) +{ + return sd_coud_16.class[coord_coud.index(cube)]; +} + +static uint64_t +index_cp_sym16(Cube cube) +{ + return sd_cp_16.class[coord_cp.index(cube)]; +} + +static uint64_t +index_drud_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + + return index_eofbepos_sym16(c) * POW3TO7 + c.coud; +} + +static uint64_t +index_drudfin_noE_sym16(Cube cube) +{ + Trans t; + Cube c; + + t = sd_cp_16.transtorep[coord_cp.index(cube)]; + c = apply_trans(t, cube); + + return index_cp_sym16(c) * FACTORIAL8 + coord_epud.index(c); +} + +static uint64_t +index_eofbepos_sym16(Cube cube) +{ + return sd_eofbepos_16.class[coord_eofbepos.index(cube)]; +} + +static uint64_t +index_khuge(Cube cube) +{ + Trans t; + Cube c; + uint64_t a; + + t = sd_eofbepos_16.transtorep[coord_eofbepos.index(cube)]; + c = apply_trans(t, cube); + a = (index_eofbepos_sym16(c) * 24) + (c.epose % 24); + + return a * POW3TO7 + c.coud; +} + +/* Other functions ***********************************************************/ + +static void +gensym(SymData *sd) +{ + uint64_t i, in, nreps = 0; + int j; + Cube c, d; + + if (sd->generated) + return; + + sd->class = malloc(sd->coord->max * sizeof(uint64_t)); + sd->rep = malloc(sd->coord->max * sizeof(Cube)); + sd->transtorep = malloc(sd->coord->max * sizeof(Trans)); + + if (read_symdata_file(sd)) { + sd->generated = true; + return; + } + + fprintf(stderr, "Cannot load %s, generating it\n", sd->filename); + + for (i = 0; i < sd->coord->max; i++) + sd->class[i] = sd->coord->max + 1; + + for (i = 0; i < sd->coord->max; i++) { + if (sd->class[i] == sd->coord->max + 1) { + c = sd->coord->cube(i); + sd->rep[nreps] = c; + for (j = 0; j < sd->ntrans; j++) { + d = apply_trans(sd->trans[j], c); + in = sd->coord->index(d); + + if (sd->class[in] == sd->coord->max + 1) { + sd->class[in] = nreps; + sd->transtorep[in] = + inverse_trans(sd->trans[j]); + } + } + nreps++; + } + } + + sd->sym_coord->max = nreps; + sd->rep = realloc(sd->rep, nreps * sizeof(Cube)); + sd->generated = true; + + fprintf(stderr, "Found %lu classes\n", nreps); + + if (!write_symdata_file(sd)) + fprintf(stderr, "Error writing SymData file\n"); + + return; +} + +static bool +read_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + r = r && fread(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fread(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fread(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fread(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +static bool +write_symdata_file(SymData *sd) +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+100]; + uint64_t n = sd->coord->max, *sn = &sd->sym_coord->max; + bool r = true; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, sd->filename); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + r = r && fwrite(&sd->sym_coord->max, sizeof(uint64_t), 1, f) == 1; + r = r && fwrite(sd->rep, sizeof(Cube), *sn, f) == *sn; + r = r && fwrite(sd->class, sizeof(uint64_t), n, f) == n; + r = r && fwrite(sd->transtorep, sizeof(Trans), n, f) == n; + + fclose(f); + return r; +} + +void +init_symcoord() +{ + int i; + + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_coord(); + + for (i = 0; i < nsymdata; i++) + gensym(all_sd[i]); +} + diff --git a/src/symcoord.h b/src/symcoord.h new file mode 100644 index 0000000..f231c92 --- /dev/null +++ b/src/symcoord.h @@ -0,0 +1,15 @@ +#ifndef SYMCOORD_H +#define SYMCOORD_H + +#include "coord.h" + +extern Coordinate coord_coud_sym16; +extern Coordinate coord_cp_sym16; +extern Coordinate coord_eofbepos_sym16; +extern Coordinate coord_drud_sym16; +extern Coordinate coord_drudfin_noE_sym16; +extern Coordinate coord_khuge; + +void init_symcoord(); + +#endif diff --git a/src/trans.c b/src/trans.c new file mode 100644 index 0000000..2946c58 --- /dev/null +++ b/src/trans.c @@ -0,0 +1,375 @@ +#include "trans.h" + +/* Local functions ***********************************************************/ + +static bool read_ttables_file(); +static Cube rotate_via_compose(Trans r, Cube c, PieceFilter f); +static bool write_ttables_file(); + +/* Tables and other data *****************************************************/ + +static int ep_mirror[12] = { + [UF] = UF, [UL] = UR, [UB] = UB, [UR] = UL, + [DF] = DF, [DL] = DR, [DB] = DB, [DR] = DL, + [FR] = FL, [FL] = FR, [BL] = BR, [BR] = BL +}; + +static int cp_mirror[8] = { + [UFR] = UFL, [UFL] = UFR, [UBL] = UBR, [UBR] = UBL, + [DFR] = DFL, [DFL] = DFR, [DBL] = DBR, [DBR] = DBL +}; + +static int cpos_mirror[6] = { + [U_center] = U_center, [D_center] = D_center, + [R_center] = L_center, [L_center] = R_center, + [F_center] = F_center, [B_center] = B_center +}; + +/* TODO Is there a more elegant way? */ +static char rotation_alg_string[100][NROTATIONS] = { + [uf] = "", [ur] = "y", [ub] = "y2", [ul] = "y3", + [df] = "z2", [dr] = "y z2", [db] = "x2", [dl] = "y3 z2", + [rf] = "z3", [rd] = "z3 y", [rb] = "z3 y2", [ru] = "z3 y3", + [lf] = "z", [ld] = "z y3", [lb] = "z y2", [lu] = "z y", + [fu] = "x y2", [fr] = "x y", [fd] = "x", [fl] = "x y3", + [bu] = "x3", [br] = "x3 y", [bd] = "x3 y2", [bl] = "x3 y3", +}; + +static int epose_source[NTRANS]; /* 0=epose, 1=eposs, 2=eposm */ +static int eposs_source[NTRANS]; +static int eposm_source[NTRANS]; +static int eofb_source[NTRANS]; /* 0=eoud, 1=eorl, 2=eofb */ +static int eorl_source[NTRANS]; +static int eoud_source[NTRANS]; +static int coud_source[NTRANS]; /* 0=coud, 1=corl, 2=cofb */ +static int cofb_source[NTRANS]; +static int corl_source[NTRANS]; + +static int epose_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposs_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eposm_ttable[NTRANS][FACTORIAL12/FACTORIAL8]; +static int eo_ttable[NTRANS][POW2TO11]; +static int cp_ttable[NTRANS][FACTORIAL8]; +static int co_ttable[NTRANS][POW3TO7]; +static int cpos_ttable[NTRANS][FACTORIAL6]; +static Move moves_ttable[NTRANS][NMOVES]; + +/* Local functions implementation ********************************************/ + +static bool +read_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + int b = sizeof(int); + bool r = true; + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/"); + strcat(fname, "ttables"); + + if ((f = fopen(fname, "rb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fread(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fread(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fread(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fread(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fread(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fread(co_ttable[m], b, me[5], f) == me[5]; + r = r && fread(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fread(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +static Cube +rotate_via_compose(Trans r, Cube c, PieceFilter f) +{ + static int zero12[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static int zero8[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + static CubeArray ma = { + .ep = ep_mirror, + .eofb = zero12, + .eorl = zero12, + .eoud = zero12, + .cp = cp_mirror, + .coud = zero8, + .corl = zero8, + .cofb = zero8, + .cpos = cpos_mirror + }; + + Alg *inv = inverse_alg(rotation_alg(r)); + Cube ret = {0}; + + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + ret = apply_alg_generic(inv, ret, f, true); + + ret = compose_filtered(c, ret, f); + + ret = apply_alg_generic(rotation_alg(r), ret, f, true); + if (r >= NROTATIONS) + ret = move_via_arrays(&ma, ret, f); + + free_alg(inv); + return ret; +} + +static bool +write_ttables_file() +{ + init_env(); + + FILE *f; + char fname[strlen(tabledir)+20]; + bool r = true; + int b = sizeof(int); + Move m; + + /* Table sizes, used for reading and writing files */ + uint64_t me[11] = { + [0] = FACTORIAL12/FACTORIAL8, + [1] = FACTORIAL12/FACTORIAL8, + [2] = FACTORIAL12/FACTORIAL8, + [3] = POW2TO11, + [4] = FACTORIAL8, + [5] = POW3TO7, + [6] = FACTORIAL6, + [7] = NMOVES + }; + + strcpy(fname, tabledir); + strcat(fname, "/ttables"); + + if ((f = fopen(fname, "wb")) == NULL) + return false; + + for (m = 0; m < NTRANS; m++) { + r = r && fwrite(epose_ttable[m], b, me[0], f) == me[0]; + r = r && fwrite(eposs_ttable[m], b, me[1], f) == me[1]; + r = r && fwrite(eposm_ttable[m], b, me[2], f) == me[2]; + r = r && fwrite(eo_ttable[m], b, me[3], f) == me[3]; + r = r && fwrite(cp_ttable[m], b, me[4], f) == me[4]; + r = r && fwrite(co_ttable[m], b, me[5], f) == me[5]; + r = r && fwrite(cpos_ttable[m], b, me[6], f) == me[6]; + r = r && fwrite(moves_ttable[m], b, me[7], f) == me[7]; + } + + fclose(f); + return r; +} + +/* Public functions **********************************************************/ + +Cube +apply_trans(Trans t, Cube cube) +{ + /*init_trans();*/ + + int aux_epos[3] = { cube.epose, cube.eposs, cube.eposm }; + int aux_eo[3] = { cube.eoud, cube.eorl, cube.eofb }; + int aux_co[3] = { cube.coud, cube.corl, cube.cofb }; + + return (Cube) { + .epose = epose_ttable[t][aux_epos[epose_source[t]]], + .eposs = eposs_ttable[t][aux_epos[eposs_source[t]]], + .eposm = eposm_ttable[t][aux_epos[eposm_source[t]]], + .eofb = eo_ttable[t][aux_eo[eofb_source[t]]], + .eorl = eo_ttable[t][aux_eo[eorl_source[t]]], + .eoud = eo_ttable[t][aux_eo[eoud_source[t]]], + .coud = co_ttable[t][aux_co[coud_source[t]]], + .corl = co_ttable[t][aux_co[corl_source[t]]], + .cofb = co_ttable[t][aux_co[cofb_source[t]]], + .cp = cp_ttable[t][cube.cp], + .cpos = cpos_ttable[t][cube.cpos] + }; +} + +Trans +inverse_trans(Trans t) +{ + /* TODO is there a more elegant way? */ + static Trans inverse_trans_aux[NTRANS] = { + [uf] = uf, [ur] = ul, [ul] = ur, [ub] = ub, + [df] = df, [dr] = dr, [dl] = dl, [db] = db, + [rf] = lf, [rd] = bl, [rb] = rb, [ru] = fr, + [lf] = rf, [ld] = br, [lb] = lb, [lu] = fl, + [fu] = fu, [fr] = ru, [fd] = bu, [fl] = lu, + [bu] = fd, [br] = ld, [bd] = bd, [bl] = rd, + + [uf_mirror] = uf_mirror, [ur_mirror] = ur_mirror, + [ul_mirror] = ul_mirror, [ub_mirror] = ub_mirror, + [df_mirror] = df_mirror, [dr_mirror] = dl_mirror, + [dl_mirror] = dr_mirror, [db_mirror] = db_mirror, + [rf_mirror] = rf_mirror, [rd_mirror] = br_mirror, + [rb_mirror] = lb_mirror, [ru_mirror] = fl_mirror, + [lf_mirror] = lf_mirror, [ld_mirror] = bl_mirror, + [lb_mirror] = rb_mirror, [lu_mirror] = fr_mirror, + [fu_mirror] = fu_mirror, [fr_mirror] = lu_mirror, + [fd_mirror] = bu_mirror, [fl_mirror] = ru_mirror, + [bu_mirror] = fd_mirror, [br_mirror] = rd_mirror, + [bd_mirror] = bd_mirror, [bl_mirror] = ld_mirror + }; + + return inverse_trans_aux[t]; +} + +Alg * +rotation_alg(Trans t) +{ + int i; + + static Alg *rotation_alg_arr[NROTATIONS]; + static bool initialized = false; + + if (!initialized) { + for (i = 0; i < NROTATIONS; i++) + rotation_alg_arr[i] = new_alg(rotation_alg_string[i]); + + initialized = true; + } + + return rotation_alg_arr[t % NROTATIONS]; +} + +void +transform_alg(Trans t, Alg *alg) +{ + int i; + + /*init_trans();*/ + + for (i = 0; i < alg->len; i++) + alg->move[i] = moves_ttable[t][alg->move[i]]; +} + +void +init_trans() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + init_moves(); + + Cube aux, cube, c[3]; + CubeArray epcp; + int i, eparr[12], eoarr[12], cparr[8], coarr[8]; + unsigned int ui; + Move mi, move; + Trans m; + + /* Compute sources */ + for (i = 0; i < NTRANS; i++) { + cube = apply_alg(rotation_alg(i), (Cube){0}); + + epose_source[i] = edge_slice(what_edge_at(cube, FR)); + eposs_source[i] = edge_slice(what_edge_at(cube, UR)); + eposm_source[i] = edge_slice(what_edge_at(cube, UF)); + eofb_source[i] = what_center_at(cube, F_center)/2; + eorl_source[i] = what_center_at(cube, R_center)/2; + eoud_source[i] = what_center_at(cube, U_center)/2; + coud_source[i] = what_center_at(cube, U_center)/2; + cofb_source[i] = what_center_at(cube, F_center)/2; + corl_source[i] = what_center_at(cube, R_center)/2; + } + + if (read_ttables_file()) + return; + + fprintf(stderr, "Cannot load %s, generating it\n", "ttables"); + + /* Initialize tables */ + for (m = 0; m < NTRANS; m++) { + epcp = (CubeArray){ .ep = eparr, .cp = cparr }; + cube = apply_alg(rotation_alg(m), (Cube){0}); + cube_to_arrays(cube, &epcp, pf_epcp); + if (m >= NROTATIONS) { + apply_permutation(ep_mirror, eparr, 12); + apply_permutation(cp_mirror, cparr, 8); + } + + for (ui = 0; ui < FACTORIAL12/FACTORIAL8; ui++) { + c[0] = admissible_ep((Cube){ .epose = ui }, pf_e); + c[1] = admissible_ep((Cube){ .eposs = ui }, pf_s); + c[2] = admissible_ep((Cube){ .eposm = ui }, pf_m); + + cube = rotate_via_compose(m,c[epose_source[m]],pf_ep); + epose_ttable[m][ui] = cube.epose; + + cube = rotate_via_compose(m,c[eposs_source[m]],pf_ep); + eposs_ttable[m][ui] = cube.eposs; + + cube = rotate_via_compose(m,c[eposm_source[m]],pf_ep); + eposm_ttable[m][ui] = cube.eposm; + } + for (ui = 0; ui < POW2TO11; ui++ ) { + int_to_sum_zero_array(ui, 2, 12, eoarr); + apply_permutation(eparr, eoarr, 12); + eo_ttable[m][ui] = digit_array_to_int(eoarr, 11, 2); + } + for (ui = 0; ui < POW3TO7; ui++) { + int_to_sum_zero_array(ui, 3, 8, coarr); + apply_permutation(cparr, coarr, 8); + co_ttable[m][ui] = digit_array_to_int(coarr, 7, 3); + if (m >= NROTATIONS) + co_ttable[m][ui] = + invert_digits(co_ttable[m][ui], 3, 7); + } + for (ui = 0; ui < FACTORIAL8; ui++) { + cube = (Cube){ .cp = ui }; + cube = rotate_via_compose(m, cube, pf_cp); + cp_ttable[m][ui] = cube.cp; + } + for (ui = 0; ui < FACTORIAL6; ui++) { + cube = (Cube){ .cpos = ui }; + cube = rotate_via_compose(m, cube, pf_cpos); + cpos_ttable[m][ui] = cube.cpos; + } + for (mi = 0; mi < NMOVES; mi++) { + /* Old version: + * + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + mirr = apply_trans(uf_mirror, cube); + if (is_solved(cube) || is_solved(mirr)) + moves_ttable[m][mi] = move; + } + */ + + aux = apply_trans(m, apply_move(mi, (Cube){0})); + for (move = 0; move < NMOVES; move++) { + cube = apply_move(inverse_move(move), aux); + if (is_solved(cube)) { + moves_ttable[m][mi] = move; + break; + } + } + } + } + + if (!write_ttables_file()) + fprintf(stderr, "Error writing ttables\n"); +} + diff --git a/src/trans.h b/src/trans.h new file mode 100644 index 0000000..2eda568 --- /dev/null +++ b/src/trans.h @@ -0,0 +1,13 @@ +#ifndef TRANS_H +#define TRANS_H + +#include "moves.h" + +Cube apply_trans(Trans t, Cube cube); +Trans inverse_trans(Trans t); +Alg * rotation_alg(Trans i); +void transform_alg(Trans i, Alg *alg); + +void init_trans(); + +#endif diff --git a/src/utils.c b/src/utils.c index 5b7df06..f72a00e 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,132 +1,274 @@ #include "utils.h" -/* Hardcoded factorial of small numbers (n<=12). */ -int factorial[13] = { - 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600 -}; - -/* swaps two integers */ -void swap(int *a, int *b) { - int aux = *a; - *a = *b; - *b = aux; -} - -/* Converts the integer a to its representation in base b (first n digits - * only) and saves the result in r. */ -void int_to_digit_array(int a, int b, int n, int *r) { - for (int i = 0; i < n; i++) { - r[i] = a % b; - a /= b; - } -} - -/* Converts the array of n digits a to a integer using base b. */ -int digit_array_to_int(int *a, int n, int b) { - int ret = 0, p = 1; - for (int i = 0; i < n; i++) { - ret += a[i] * p; - p *= b; - } - return ret; -} - -/* Converts a permutation on [0..(n-1)] into the integer i which is the index - * of the permutation in the sorted list of all n! such permutations. - * Only works for n<=12. */ -int perm_to_index(int *a, int n) { - int ret = 0; - for (int i = 0; i < n; i++) { - int c = 0; - for (int j = i+1; j < n; j++) - if (a[i] > a[j]) - c++; - ret += factorial[n-i-1] * c; - } - return ret; -} - -/* Converts a permutation index to the actual permutation as an array - * (see perm_to_index) and saves the result to r. */ -void index_to_perm(int p, int n, int *r) { - int a[n]; - for (int j = 0; j < n; j++) - a[j] = 0; /* picked elements */ - for (int i = 0; i < n; i++) { - int c = 0, j = 0; - while (c <= p / factorial[n-i-1]) { - if (!a[j]) - c++; - j++; - } - r[i] = j-1; - a[j-1] = 1; - p %= factorial[n-i-1]; - } -} - - -int perm_sign_array(int a[], int n) { - int ret = 0; - for (int i = 0; i < n; i++) - for (int j = i+1; j < n; j++) - if (a[i]>a[j]) - ret++; - return ret % 2; -} - -int perm_sign_int(int p, int n) { - int a[n]; - index_to_perm(p, n, a); - return perm_sign_array(a, n); -} - - -/* Converts a k-element subset of a set with an element from an array of n - * elements, of which k are 1 (or just non-zero) and n-k are 0, to its index - * in the sorted list of all such subsets. - * Works only for n <= 12. */ -int subset_to_index(int *a, int n, int k) { - int ret = 0; - for (int i = 0; i < n; i++) { - if (k == n-i) - return ret; - if (a[i]) { - ret += factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]); - k--; - } - } - return ret; -} - -/* Inverse of the above */ -void index_to_subset(int s, int n, int k, int *r) { - for (int i = 0; i < n; i++) { - if (k == n-i) { - for (int j = i; j < n; j++) - r[j] = 1; - return; - } - int v = factorial[n-i-1] / (factorial[k] * factorial[n-i-1-k]); - if (s >= v) { - r[i] = 1; - k--; - s -= v; - } else { - r[i] = 0; - } - } -} - -/* Converts the first n-1 digits of a number to an array a of digits in base b; - * then adds one element to the array, so that the sum of the elements of a is - * zero modulo b. - * This is used for determing the edge orientation from an 11-bits integer or - * the corner orientation from a 7-trits integer. */ -void int_to_sum_zero_array(int x, int b, int n, int *a) { - int_to_digit_array(x, b, n-1, a); - int s = 0; - for (int i = 0; i < n - 1; i++) s = (s + a[i]) % b; - a[n-1] = (b - s) % b; +void +apply_permutation(int *perm, int *set, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + if (!is_perm(perm, n)) + return; + + for (i = 0; i < n; i++) + aux[i] = set[perm[i]]; + + memcpy(set, aux, n * sizeof(int)); + free(aux); +} + +int +binomial(int n, int k) +{ + if (n < 0 || k < 0 || k > n) + return 0; + + return factorial(n) / (factorial(k) * factorial(n-k)); +} + +int +digit_array_to_int(int *a, int n, int b) +{ + int i, ret = 0, p = 1; + + for (i = 0; i < n; i++, p *= b) + ret += a[i] * p; + + return ret; +} + +int +factorial(int n) +{ + int i, ret = 1; + + if (n < 0) + return 0; + + for (i = 1; i <= n; i++) + ret *= i; + + return ret; +} + +void +index_to_perm(int p, int n, int *r) +{ + int *a = malloc(n * sizeof(int)); + int i, j, c; + + for (i = 0; i < n; i++) + a[i] = 0; + + if (p < 0 || p >= factorial(n)) + for (i = 0; i < n; i++) + r[i] = -1; + + for (i = 0; i < n; i++) { + c = 0; + j = 0; + while (c <= p / factorial(n-i-1)) + c += a[j++] ? 0 : 1; + r[i] = j-1; + a[j-1] = 1; + p %= factorial(n-i-1); + } + + free(a); +} + +void +index_to_subset(int s, int n, int k, int *r) +{ + int i, j, v; + + if (s < 0 || s >= binomial(n, k)) { + for (i = 0; i < n; i++) + r[i] = -1; + return; + } + + for (i = 0; i < n; i++) { + if (k == n-i) { + for (j = i; j < n; j++) + r[j] = 1; + return; + } + + if (k == 0) { + for (j = i; j < n; j++) + r[j] = 0; + return; + } + + v = binomial(n-i-1, k); + if (s >= v) { + r[i] = 1; + k--; + s -= v; + } else { + r[i] = 0; + } + } +} + +void +int_to_digit_array(int a, int b, int n, int *r) +{ + int i; + + if (b <= 1) + for (i = 0; i < n; i++) + r[i] = 0; + else + for (i = 0; i < n; i++, a /= b) + r[i] = a % b; +} + +void +int_to_sum_zero_array(int x, int b, int n, int *a) +{ + int i, s = 0; + + if (b <= 1) { + for (i = 0; i < n; i++) + a[i] = 0; + } else { + int_to_digit_array(x, b, n-1, a); + for (i = 0; i < n - 1; i++) + s = (s + a[i]) % b; + a[n-1] = (b - s) % b; + } +} + +int +invert_digits(int a, int b, int n) +{ + int i, ret, *r = malloc(n * sizeof(int)); + + int_to_digit_array(a, b, n, r); + for (i = 0; i < n; i++) + r[i] = (b-r[i]) % b; + + ret = digit_array_to_int(r, n, b); + free(r); + return ret; +} + +bool +is_perm(int *a, int n) +{ + int *aux = malloc(n * sizeof(int)); + int i; + + for (i = 0; i < n; i++) + if (a[i] < 0 || a[i] >= n) + return false; + else + aux[a[i]] = 1; + + for (i = 0; i < n; i++) + if (!aux[i]) + return false; + + free(aux); + + return true; +} + +bool +is_subset(int *a, int n, int k) +{ + int i, sum = 0; + + for (i = 0; i < n; i++) + sum += a[i] ? 1 : 0; + + return sum == k; +} + +int +perm_sign(int *a, int n) +{ + int i, j, ret = 0; + + if (!is_perm(a,n)) + return -1; + + for (i = 0; i < n; i++) + for (j = i+1; j < n; j++) + ret += (a[i] > a[j]) ? 1 : 0; + + return ret % 2; +} + +int +perm_to_index(int *a, int n) +{ + int i, j, c, ret = 0; + + if (!is_perm(a, n)) + return -1; + + for (i = 0; i < n; i++) { + c = 0; + for (j = i+1; j < n; j++) + c += (a[i] > a[j]) ? 1 : 0; + ret += factorial(n-i-1) * c; + } + + return ret; +} + +int +powint(int a, int b) +{ + if (b < 0) + return 0; + if (b == 0) + return 1; + + if (b % 2) + return a * powint(a, b-1); + else + return powint(a*a, b/2); +} + +int +subset_to_index(int *a, int n, int k) +{ + int i, ret = 0; + + if (!is_subset(a, n, k)) + return binomial(n, k); + + for (i = 0; i < n; i++) { + if (k == n-i) + return ret; + if (a[i]) { + ret += binomial(n-i-1, k); + k--; + } + } + + return ret; +} + +void +sum_arrays_mod(int *src, int *dst, int n, int m) +{ + int i; + + for (i = 0; i < n; i++) + dst[i] = (m <= 0) ? 0 : (src[i] + dst[i]) % m; +} + +void +swap(int *a, int *b) +{ + int aux; + + aux = *a; + *a = *b; + *b = aux; } diff --git a/src/utils.h b/src/utils.h index 45a6302..80c33ae 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,58 +1,41 @@ -#define min(a,b) (((a) < (b)) ? (a) : (b)) -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#define abs(a) (((a) > 0) ? (a) : (-(a))) - -/* Some useful constants */ -#define pow2to11 2048 -#define pow2to12 4096 -#define pow3to7 2187 -#define pow3to8 6561 -#define pow12to4 20736 -#define factorial4 24 -#define factorial6 720 -#define factorial8 40320 -#define factorial12 479001600 -#define binom12on4 495 -#define binom8on4 70 - -void swap(int *a, int *b); - -/* Hardcoded factorial of small numbers (n<=12). */ -extern int factorial[13]; - -/* Converts the integer a to its representation in base b (first n digits - * only) and saves the result in r. */ -void int_to_digit_array(int a, int b, int n, int *r); - -/* Converts the array of n digits a to a integer using base b. */ -int digit_array_to_int(int *a, int n, int b); - -/* Converts a permutation on [0..(n-1)] into the integer i which is the index - * of the permutation in the sorted list of all n! such permutations. - * Only works for n<=12. */ -int perm_to_index(int *a, int n); - -/* Converts a permutation index to the actual permutation as an array - * (see perm_to_index) and saves the result to r. */ -void index_to_perm(int p, int n, int *r); - -/* Determine the sign of a permutation, either in integer or array format. */ -int perm_sign_array(int a[], int n); -int perm_sign_int(int p, int n); - -/* Converts a k-element subset of a set with an element from an array of n - * elements, of which k are 1 and n-k are 0, to its index in the sorted list - * of all such subsets. - * Works only for n <= 12. */ -int subset_to_index(int *a, int n, int k); - -/* Inverse of the above */ -void index_to_subset(int s, int n, int k, int *r); - -/* Converts the first n-1 digits of a number to an array a of digits in base b; - * then adds one element to the array, so that the sum of the elements of a is - * zero modulo b. - * This is used for determing the edge orientation from an 11-bits integer or - * the corner orientation from a 7-trits integer. */ -void int_to_sum_zero_array(int x, int b, int n, int *a); - +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include + +#define POW2TO6 64ULL +#define POW2TO11 2048ULL +#define POW2TO12 4096ULL +#define POW3TO7 2187ULL +#define POW3TO8 6561ULL +#define FACTORIAL4 24ULL +#define FACTORIAL6 720ULL +#define FACTORIAL7 5040ULL +#define FACTORIAL8 40320ULL +#define FACTORIAL12 479001600ULL +#define BINOM12ON4 495ULL +#define BINOM8ON4 70ULL +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +void apply_permutation(int *perm, int *set, int n); +int binomial(int n, int k); +int digit_array_to_int(int *a, int n, int b); +int factorial(int n); +void index_to_perm(int p, int n, int *r); +void index_to_subset(int s, int n, int k, int *r); +void int_to_digit_array(int a, int b, int n, int *r); +void int_to_sum_zero_array(int x, int b, int n, int *a); +int invert_digits(int a, int b, int n); +bool is_perm(int *a, int n); +bool is_subset(int *a, int n, int k); +int perm_sign(int *a, int n); +int perm_to_index(int *a, int n); +int powint(int a, int b); +int subset_to_index(int *a, int n, int k); +void sum_arrays_mod(int *src, int *dst, int n, int m); +void swap(int *a, int *b); + +#endif -- cgit v1.3