h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

commit 11e6aeeaf5f08839c6effb8f68710081acf491bf
parent 110e079f81277b40ec76be0b86b074a632496109
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Sun,  9 Mar 2025 07:43:37 +0100

Working (?) version of coordinate solver

Diffstat:
MTODO_COORDINATES | 15++++++++++++++-
Mshell/shell.c | 28+++++++++++++++++++++++-----
Msrc/nissy.c | 21+++++++++++++++------
Msrc/nissy.h | 4++++
Rsrc/solvers/coord/coord_common.h -> src/solvers/coord/common.h | 0
Msrc/solvers/coord/coord.h | 4++--
Msrc/solvers/coord/gendata.h | 30+++++++++++++++---------------
Msrc/solvers/coord/solve.h | 69++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Rsrc/solvers/coord/coord_types_macros.h -> src/solvers/coord/types_macros.h | 0
Msrc/solvers/h48/solve.h | 7+++----
Mtest/120_gendata_eo/gendata_eo_tests.c | 4++--
Mtest/test.h | 2+-
12 files changed, 131 insertions(+), 53 deletions(-)

diff --git a/TODO_COORDINATES b/TODO_COORDINATES @@ -1,4 +1,16 @@ -- solver (from TODOs in appending solution) +- coord solver + - add new parameter to solve(): options (char *) + - fix all usages + x tests + x shell + - tools + - python bindings + - document what to do when changing interface? + - where do I document which solvers take which parameters? + now that I am implementing more solvers I need an extra + document in doc/ + - in nissy.c, check for minmoves, maxmoves and nissflag (must be in range) + - add a dispatcher in solvers.h? - test solver for EO - add NISS (and add tests for NISS EO) - make solve parallelized @@ -7,3 +19,4 @@ - make gendata parallelized - refactor common parts of coord and h48 solvers +- move parse_h48 to h48 module? diff --git a/shell/shell.c b/shell/shell.c @@ -17,6 +17,7 @@ #define FLAG_PERM "-perm" #define FLAG_COMMAND "-command" #define FLAG_STR_CUBE "-cubestr" +#define FLAG_SOLVE_OPTS "-options" #define FLAG_FORMAT "-format" #define FLAG_FORMAT_IN "-fin" #define FLAG_FORMAT_OUT "-fout" @@ -44,6 +45,7 @@ typedef struct { char cube_perm[22]; char *str_command; char *str_cube; + char *str_options; char *str_format; char *str_format_in; char *str_format_out; @@ -79,6 +81,7 @@ static bool set_cube(int, char **, args_t *); static bool set_cube_perm(int, char **, args_t *); static bool set_str_command(int, char **, args_t *); static bool set_str_cube(int, char **, args_t *); +static bool set_str_options(int, char **, args_t *); static bool set_str_format(int, char **, args_t *); static bool set_str_format_in(int, char **, args_t *); static bool set_str_format_out(int, char **, args_t *); @@ -105,6 +108,7 @@ struct { OPTION(FLAG_PERM, 1, set_cube_perm), OPTION(FLAG_COMMAND, 1, set_str_command), OPTION(FLAG_STR_CUBE, 1, set_str_cube), + OPTION(FLAG_SOLVE_OPTS, 1, set_str_options), OPTION(FLAG_FORMAT, 1, set_str_format), OPTION(FLAG_FORMAT_IN, 1, set_str_format_in), OPTION(FLAG_FORMAT_OUT, 1, set_str_format_out), @@ -197,6 +201,7 @@ struct { "solve", "solve " FLAG_SOLVER " SOLVER" "[" FLAG_MINMOVES " n] [" FLAG_MAXMOVES " N] " + "[" FLAG_SOLVE_OPTS " options] " FLAG_CUBE " CUBE" FLAG_THREADS " T", "Solve the given CUBE using SOLVER, " @@ -208,6 +213,7 @@ struct { "solve_scramble", "solve_scramble " FLAG_SOLVER " SOLVER" "[" FLAG_MINMOVES " n] [" FLAG_MAXMOVES " N] " + "[" FLAG_SOLVE_OPTS " options] " FLAG_MOVES " MOVES", "Solve the given SCRAMBLE using SOLVER, " "using at least n and at most N moves. " @@ -473,15 +479,15 @@ solve_exec(args_t *args) fclose(file); if (read != 1) { fprintf(stderr, "Error reading data from file: " - "fread() returned %zu instead of 1 when attempting to" + "fread() returned %zu instead of 1 when attempting to " "read %" PRId64 " bytes from file %s\n", read, size, path); - return -2; + goto solve_exec_error; } ret = nissy_solve( - args->cube, args->str_solver, nissflag, args->minmoves, - args->maxmoves, args->maxsolutions, args->optimal, args->threads, - size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats); + args->cube, args->str_solver, args->str_options, nissflag, + args->minmoves, args->maxmoves, args->maxsolutions, args->optimal, + args->threads, size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats); free(buf); @@ -491,6 +497,10 @@ solve_exec(args_t *args) printf("%s", solutions); return 0; + +solve_exec_error: + free(buf); + return -2; } static int64_t @@ -655,6 +665,14 @@ set_str_cube(int argc, char **argv, args_t *args) } static bool +set_str_options(int argc, char **argv, args_t *args) +{ + args->str_options = argv[0]; + + return true; +} + +static bool set_str_format(int argc, char **argv, args_t *args) { args->str_format = argv[0]; diff --git a/src/nissy.c b/src/nissy.c @@ -451,14 +451,16 @@ nissy_gendata_unsafe( return NISSY_ERROR_DATA; } - arg.buf_size = data_size; - arg.buf = data; if (!strncmp(solver, "h48", 3)) { + arg.buf_size = data_size; + arg.buf = data; parse_ret = parse_h48_solver(solver, &arg.h, &arg.k); arg.maxdepth = 20; if (parse_ret != NISSY_OK) return parse_ret; return gendata_h48(&arg); + } else if (!strncmp(solver, "coord_", 6)) { + return gendata_coord_dispatch(solver+6, data); } else { LOG("gendata: unknown solver %s\n", solver); return NISSY_ERROR_INVALID_SOLVER; @@ -501,6 +503,7 @@ long long nissy_solve( const char cube[static NISSY_SIZE_B32], const char *solver, + const char *options, unsigned nissflag, unsigned minmoves, unsigned maxmoves, @@ -536,6 +539,8 @@ nissy_solve( return NISSY_ERROR_UNSOLVABLE_CUBE; } +/* TODO: checks for minmoves, maxmoves, nissflag */ + if (maxsols == 0) { LOG("solve: 'maxsols' is 0, returning no solution\n"); return 0; @@ -562,11 +567,15 @@ nissy_solve( if (!strncmp(solver, "h48", 3)) { parse_ret = parse_h48_solver(solver, &h, &k); - if (parse_ret == NISSY_OK) - return solve_h48(c, minmoves, maxmoves, maxsols, opt, - t, data_size, data, sols_size, sols, stats); - else + if (parse_ret != NISSY_OK) return parse_ret; +/* TODO give warning if options is not NULL or empty? */ + return solve_h48(c, minmoves, maxmoves, maxsols, + opt, t, data_size, data, sols_size, sols, stats); + } else if (!strncmp(solver, "coord_", 6)) { + return solve_coord_dispatch(c, solver + 6, options, nissflag, + minmoves, maxmoves, maxsols, opt, t, data_size, data, + sols_size, sols); } else { LOG("solve: unknown solver '%s'\n", solver); return NISSY_ERROR_INVALID_SOLVER; diff --git a/src/nissy.h b/src/nissy.h @@ -257,6 +257,9 @@ Solve the given cube using the given solver and options. Parameters: cube - The cube to solver, in B32 format. solver - The name of the solver. + options - Extra options for the solver. Some solver require additional + parameters, some do not. For example, a coordinate solver + may require an "axis" (UD, FB, RL) as a parameter. nissflag - The flags for NISS (linear, inverse, mixed, or combinations). minmoves - The minimum number of moves for a solution. maxmoves - The maximum number of moves for a solution. @@ -290,6 +293,7 @@ long long nissy_solve( const char cube[static NISSY_SIZE_B32], const char *solver, + const char *options, unsigned nissflag, unsigned minmoves, unsigned maxmoves, diff --git a/src/solvers/coord/coord_common.h b/src/solvers/coord/common.h diff --git a/src/solvers/coord/coord.h b/src/solvers/coord/coord.h @@ -1,5 +1,5 @@ -#include "coord_types_macros.h" +#include "types_macros.h" #include "eo.h" -#include "coord_common.h" +#include "common.h" #include "gendata.h" #include "solve.h" diff --git a/src/solvers/coord/gendata.h b/src/solvers/coord/gendata.h @@ -1,26 +1,26 @@ -STATIC size_t gendata_coordinate(const coord_t *, void *); -STATIC size_t gendata_coordinate_name(const char *, void *); -STATIC tableinfo_t genptable_coordinate(const coord_t *, const void *, uint8_t *); +STATIC size_t gendata_coord(const coord_t *, void *); +STATIC int64_t gendata_coord_dispatch(const char *, void *); +STATIC tableinfo_t genptable_coord(const coord_t *, const void *, uint8_t *); STATIC uint8_t get_coord_pval(const coord_t *, const uint8_t *, uint64_t); STATIC void set_coord_pval(const coord_t *, uint8_t *, uint64_t, uint8_t); -STATIC size_t -gendata_coordinate_name(const char *name, void *buf) +STATIC int64_t +gendata_coord_dispatch(const char *coordstr, void *buf) { coord_t *coord; - coord = parse_coord(name, strlen(name)); + coord = parse_coord(coordstr, strlen(coordstr)); + if (coord == NULL) { - LOG("Cannot generate data for coordinate '%s': not found\n", - name); - return 0; + LOG("Could not parse coordinate '%s'\n", coord); + return NISSY_ERROR_INVALID_SOLVER; } - return gendata_coordinate(coord, buf); + return (int64_t)gendata_coord(coord, buf); } STATIC size_t -gendata_coordinate(const coord_t *coord, void *buf) +gendata_coord(const coord_t *coord, void *buf) { uint64_t coord_dsize, tablesize, ninfo; void *pruningbuf, *coord_data; @@ -33,7 +33,7 @@ gendata_coordinate(const coord_t *coord, void *buf) tablesize = DIV_ROUND_UP(coord->max, 2); if (buf == NULL) - goto gendata_coordinate_return_size; + goto gendata_coord_return_size; if (ninfo == 2) { coord_data_info = (tableinfo_t) { @@ -62,15 +62,15 @@ gendata_coordinate(const coord_t *coord, void *buf) } table = ((uint8_t *)pruningbuf) + INFOSIZE; - pruning_info = genptable_coordinate(coord, coord_data, table); + pruning_info = genptable_coord(coord, coord_data, table); writetableinfo(&pruning_info, INFOSIZE + tablesize, pruningbuf); -gendata_coordinate_return_size: +gendata_coord_return_size: return ninfo * INFOSIZE + coord_dsize + tablesize; } STATIC tableinfo_t -genptable_coordinate(const coord_t *coord, const void *data, uint8_t *table) +genptable_coord(const coord_t *coord, const void *data, uint8_t *table) { uint64_t tablesize, i, j, d, tot; tableinfo_t info; diff --git a/src/solvers/coord/solve.h b/src/solvers/coord/solve.h @@ -20,8 +20,9 @@ typedef struct { STATIC int64_t solve_coord(cube_t, coord_t *, uint8_t, uint8_t, uint8_t, uint8_t, uint64_t, int, int, uint64_t, const void *, uint64_t, char *); -STATIC int64_t solve_coord_dispatch(cube_t, const char *, uint8_t, uint8_t, - uint8_t, uint64_t, int, int, uint64_t, const void *, uint64_t, char *); +STATIC int64_t solve_coord_dispatch(cube_t, const char *, const char *, + uint8_t, uint8_t, uint8_t, uint64_t, int, int, uint64_t, const void *, + uint64_t, char *); STATIC bool solve_coord_appendchar(char *, uint64_t, uint64_t *, char); STATIC int64_t solve_coord_appendsolution(dfsarg_solve_coord_t *); STATIC int64_t solve_coord_dfs(dfsarg_solve_coord_t *); @@ -29,7 +30,9 @@ STATIC int64_t solve_coord_dfs(dfsarg_solve_coord_t *); STATIC int64_t solve_coord_appendsolution(dfsarg_solve_coord_t *arg) { - uint8_t i, t, tmoves[MAXLEN_COORDSOL]; + uint8_t i, t, l, tmoves[MAXLEN_COORDSOL]; + char *m; + int64_t strl; if (*arg->nsols >= arg->maxsolutions || arg->nmoves > *arg->shortest_sol + arg->optimal) @@ -41,9 +44,26 @@ solve_coord_appendsolution(dfsarg_solve_coord_t *arg) for (i = 0; i < arg->nmoves; i++) tmoves[i] = transform_move(arg->moves[i], t); - /* TODO append tmoves[] */ + l = arg->solutions_size - *arg->solutions_used; + m = *arg->solutions + *arg->solutions_used; + strl = writemoves(tmoves, arg->nmoves, l, m); + if (strl < 0) + goto solve_coord_appendsolution_error; + + *arg->solutions_used += MAX(0, strl-1); + + if (!solve_coord_appendchar( + *arg->solutions, arg->solutions_size, arg->solutions_used, '\n')) + goto solve_coord_appendsolution_error; + + (*arg->nsols)++; + *arg->shortest_sol = MIN(*arg->shortest_sol, arg->nmoves); return 1; + +solve_coord_appendsolution_error: + LOG("Could not append solution to buffer: size too small\n"); + return NISSY_ERROR_BUFFER_SIZE; } STATIC bool @@ -61,8 +81,11 @@ solve_coord_appendchar(char *s, uint64_t s_size, uint64_t *s_used, char c) STATIC int64_t solve_coord_dfs(dfsarg_solve_coord_t *arg) { + uint8_t m, pval; + uint32_t mm; uint64_t coord; - uint8_t pval; + int64_t n, ret; + cube_t backup_cube; coord = arg->coord->coord(arg->cube, arg->coord_data); @@ -76,8 +99,23 @@ solve_coord_dfs(dfsarg_solve_coord_t *arg) if (arg->nmoves + pval > arg->depth) return 0; - /* TODO recursive call */ - /* Is allowednextmove available here? */ + backup_cube = arg->cube; + + ret = 0; + mm = allowednextmove_mask(arg->moves, arg->nmoves); + arg->nmoves++; + for (m = 0; m < 18; m++) { + if (!(mm & (1 << m))) + continue; + + arg->moves[arg->nmoves-1] = m; + arg->cube = move(backup_cube, m); + n = solve_coord_dfs(arg); + if (n < 0) + return n; + ret += n; + } + arg->nmoves--; return 0; } @@ -85,7 +123,8 @@ solve_coord_dfs(dfsarg_solve_coord_t *arg) STATIC int64_t solve_coord_dispatch( cube_t cube, - const char *coord_axis, + const char *coordstr, + const char *options, uint8_t nissflag, uint8_t minmoves, uint8_t maxmoves, @@ -98,23 +137,19 @@ solve_coord_dispatch( char *sols ) { - int i, n; coord_t *coord; uint8_t axis; - n = strlen(coord_axis); - for (i = 0; coord_axis[i] != ' ' && coord_axis[i] != '\0'; i++) ; - - coord = parse_coord(coord_axis, i); - axis = parse_axis(coord_axis + i + 1, n - i - 1); + coord = parse_coord(coordstr, strlen(coordstr)); + axis = parse_axis(options, strlen(options)); if (coord == NULL) { - LOG("Could not parse coordinate '%s'\n", coord_axis); + LOG("Could not parse coordinate '%s'\n", coordstr); return NISSY_ERROR_INVALID_SOLVER; } if (axis == UINT8_ERROR) { - LOG("Could not parse axis from '%s'\n", coord_axis); + LOG("Could not parse axis from options '%s'\n", options); return NISSY_ERROR_INVALID_SOLVER; } @@ -165,8 +200,8 @@ solve_coord( nsols = 0; sols_used = 0; shortest_sol = MAXLEN_COORDSOL + 1; - c = transform(cube, t); t = coord->axistrans[axis]; + c = transform(cube, t); arg = (dfsarg_solve_coord_t) { .cube = c, diff --git a/src/solvers/coord/coord_types_macros.h b/src/solvers/coord/types_macros.h diff --git a/src/solvers/h48/solve.h b/src/solvers/h48/solve.h @@ -145,8 +145,7 @@ solve_h48_appendallsym(dfsarg_solve_h48_t *arg) goto solve_h48_appendallsym_error; (*arg->nsols)++; - *arg->shortest_sol = - MIN(*arg->shortest_sol, arg->nmoves + arg->npremoves); + *arg->shortest_sol = MIN(*arg->shortest_sol, n); ret++; } @@ -291,7 +290,7 @@ solve_h48_dfs(dfsarg_solve_h48_t *arg) if (popcount_u32(mm_normal) <= popcount_u32(mm_inverse)) { arg->nmoves++; for (m = 0; m < 18; m++) { - if (!(mm_normal & (1 << m))) + if (!(mm_normal & (UINT32_C(1) << (uint32_t)m))) continue; arg->moves[arg->nmoves-1] = m; arg->cube = move(backup_cube, m); @@ -308,7 +307,7 @@ solve_h48_dfs(dfsarg_solve_h48_t *arg) } else { arg->npremoves++; for (m = 0; m < 18; m++) { - if(!(mm_inverse & (1 << m))) + if(!(mm_inverse & (UINT32_C(1) << (uint32_t)m))) continue; arg->premoves[arg->npremoves-1] = m; arg->inverse = move(backup_inverse, m); diff --git a/test/120_gendata_eo/gendata_eo_tests.c b/test/120_gendata_eo/gendata_eo_tests.c @@ -17,7 +17,7 @@ Pruning table values (from nissy): char buf[FULLSIZE]; -size_t gendata_coordinate_name(const char *, void *); +size_t gendata_coord_dispatch(const char *, void *); bool readtableinfo(uint64_t, const char *, tableinfo_t *); void run(void) { @@ -25,7 +25,7 @@ void run(void) { size_t result; tableinfo_t info; - result = gendata_coordinate_name("EO", buf); + result = gendata_coord_dispatch("EO", buf); if (readtableinfo(FULLSIZE, buf, &info) != NISSY_OK) { printf("Error reading info from table\n"); return; diff --git a/test/test.h b/test/test.h @@ -14,7 +14,7 @@ #include "../src/solvers/h48/coordinate_macros.h" #include "../src/solvers/h48/map_types_macros.h" #include "../src/solvers/h48/gendata_types_macros.h" -#include "../src/solvers/coord/coord_types_macros.h" +#include "../src/solvers/coord/types_macros.h" #define STRLENMAX 10000