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 c4f64cf2556c0f8597e1fbc19d7c664b8da94612
parent 4668156da5915c8d7a7b40edaf6617b004547bd7
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Fri, 11 Oct 2024 19:24:15 +0200

Added buffer sizes in API args

Diffstat:
Mshell.c | 15++++++++-------
Msrc/nissy.c | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/nissy.h | 99+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mtools/000_gendata/gendata.c | 6+++---
Mtools/001_derive_h48/derive_h48.c | 3+--
Mtools/100_checkdata/checkdata.c | 8++++----
Mtools/200_stats_tables_h48/stats_tables_h48.c | 12++++++++----
Mtools/300_solve_small/solve_small.c | 24+++++++++++++-----------
Mtools/tool.h | 7++++---
9 files changed, 180 insertions(+), 93 deletions(-)

diff --git a/shell.c b/shell.c @@ -9,9 +9,9 @@ #include "src/nissy.h" -#define PRINTCUBE_BUFFER_SIZE 1024 /* Should be enough */ -#define SOLUTIONS_BUFFER_SIZE 500000 /* Should be enough */ -#define MAX_PATH_LENGTH 10000 /* Should be enough */ +#define PRINTCUBE_BUFFER_SIZE UINT64_C(1024) +#define SOLUTIONS_BUFFER_SIZE UINT64_C(500000) +#define MAX_PATH_LENGTH UINT64_C(10000) #define FLAG_CUBE "-cube" #define FLAG_PERM "-perm" @@ -308,8 +308,8 @@ convert_exec(args_t *args) char result[PRINTCUBE_BUFFER_SIZE]; int64_t ret; - ret = nissy_convert( - args->str_format_in, args->str_format_out, args->str_cube, result); + ret = nissy_convert(args->str_format_in, args->str_format_out, + args->str_cube, PRINTCUBE_BUFFER_SIZE, result); if (ret == NISSY_OK || ret == NISSY_WARNING_UNSOLVABLE) printf("%s\n", result); @@ -382,7 +382,7 @@ gendata_exec(args_t *args) buf = malloc(size); - ret = nissy_gendata(args->str_solver, buf); + ret = nissy_gendata(args->str_solver, size, buf); if (ret < 0) { fprintf(stderr, "Unknown error in generating data\n"); fclose(file); @@ -473,7 +473,8 @@ solve_exec(args_t *args) ret = nissy_solve( args->cube, args->str_solver, nissflag, args->minmoves, - args->maxmoves, args->maxsolutions, args->optimal, buf, solutions); + args->maxmoves, args->maxsolutions, args->optimal, + size, buf, SOLUTIONS_BUFFER_SIZE, solutions); free(buf); diff --git a/src/nissy.c b/src/nissy.c @@ -16,7 +16,7 @@ int parse_h48_solver(const char *, uint8_t [static 1], uint8_t [static 1]); STATIC int64_t write_result(cube_t, char [static 22]); STATIC bool distribution_equal(const uint64_t [static INFO_DISTRIBUTION_LEN], const uint64_t [static INFO_DISTRIBUTION_LEN], uint8_t); -STATIC bool checkdata(const void *, const tableinfo_t *); +STATIC bool checkdata(const char *, const tableinfo_t *); #define GETCUBE_OPTIONS(S, F) { .option = S, .fix = F } struct { @@ -67,7 +67,7 @@ parse_h48_solver_error: } STATIC bool -checkdata(const void *buf, const tableinfo_t *info) +checkdata(const char *buf, const tableinfo_t *info) { uint64_t distr[INFO_DISTRIBUTION_LEN]; @@ -202,6 +202,12 @@ nissy_applymoves( cube_t c, res; int64_t err; + if (moves == NULL) { + LOG("Error: 'moves' argument is NULL\n"); + err = NISSY_ERROR_NULL_POINTER; + goto nissy_applymoves_error; + } + c = readcube("B32", cube); if (!isconsistent(c)) { @@ -228,7 +234,7 @@ nissy_applymoves_error: int64_t nissy_applytrans( const char cube[static 22], - const char *transformation, + const char transformation[static 12], char result[static 22] ) { @@ -267,6 +273,12 @@ nissy_frommoves( cube_t res; int64_t err; + if (moves == NULL) { + LOG("Error: 'moves' argument is NULL\n"); + err = NISSY_ERROR_NULL_POINTER; + goto nissy_frommoves_error; + } + res = applymoves(SOLVED_CUBE, moves); if (!isconsistent(res)) { @@ -287,13 +299,32 @@ nissy_convert( const char *format_in, const char *format_out, const char *cube_string, - char *result + size_t result_size, + char result[result_size] ) { cube_t c; int ret; int64_t err; + if (format_in == NULL) { + LOG("Error: 'format_in' argument is NULL\n"); + err = NISSY_ERROR_NULL_POINTER; + goto nissy_convert_error; + } + + if (format_out == NULL) { + LOG("Error: 'format_out' argument is NULL\n"); + err = NISSY_ERROR_NULL_POINTER; + goto nissy_convert_error; + } + + if (cube_string == NULL) { + LOG("Error: 'cube_string' argument is NULL\n"); + err = NISSY_ERROR_NULL_POINTER; + goto nissy_convert_error; + } + c = readcube(format_in, cube_string); if (iserror(c)) { @@ -334,6 +365,11 @@ nissy_getcube( int i; cube_t c; + if (options == NULL) { + LOG("Error: 'options' argument is NULL\n"); + return NISSY_ERROR_NULL_POINTER; + } + for (i = 0; getcube_options[i].option != NULL; i++) if (!strcmp(options, getcube_options[i].option)) getcube_options[i].fix(&ep, &eo, &cp, &co); @@ -355,13 +391,19 @@ nissy_datasize( const char *solver ) { + if (solver == NULL) { + LOG("Error: 'solver' argument is NULL\n"); + return NISSY_ERROR_NULL_POINTER; + } + /* gendata() handles a NULL *data as a "dryrun" request */ - return nissy_gendata(solver, NULL); + return nissy_gendata(solver, 0, NULL); } int64_t nissy_datainfo( - const void *data, + size_t data_size, + const char data[data_size], void (*write)(const char *, ...) ) { @@ -396,7 +438,8 @@ nissy_datainfo( } if (info.next != 0) - return nissy_datainfo((char *)data + info.next, write); + return nissy_datainfo( + data_size - info.next, (char *)data + info.next, write); write("\n---------\n"); @@ -406,12 +449,18 @@ nissy_datainfo( int64_t nissy_gendata( const char *solver, - void *data + size_t data_size, + char data[data_size] ) { int p; gendata_h48_arg_t arg; + if (solver == NULL) { + LOG("Error: 'solver' argument is NULL\n"); + return NISSY_ERROR_NULL_POINTER; + } + arg.buf = data; if (!strncmp(solver, "h48", 3)) { p = parse_h48_solver(solver, &arg.h, &arg.k); @@ -427,8 +476,8 @@ nissy_gendata( int64_t nissy_checkdata( - const char *solver, - const void *data + uint64_t data_size, + const char data[data_size] ) { char *buf; @@ -454,16 +503,23 @@ nissy_solve( uint8_t nissflag, int8_t minmoves, int8_t maxmoves, - int64_t maxsolutions, + int64_t maxsols, int8_t optimal, - const void *data, - char *solutions + size_t data_size, + const char data[data_size], + size_t sols_size, + char sols[sols_size] ) { cube_t c; int p; uint8_t h, k; + if (solver == NULL) { + LOG("Error: 'solver' argument is NULL\n"); + return NISSY_ERROR_NULL_POINTER; + } + c = readcube_B32(cube); if (!isconsistent(c)) { @@ -486,24 +542,19 @@ nissy_solve( maxmoves = 20; } - if (maxsolutions < 0) { + if (maxsols < 0) { LOG("solve: 'maxsols' is negative, stopping\n"); return NISSY_ERROR_OPTIONS; } - if (maxsolutions == 0) { + if (maxsols == 0) { LOG("solve: 'maxsols' is 0, returning no solution\n"); return 0; } - if (solutions == NULL) { - LOG("solve: return parameter 'solutions' is NULL, stopping\n"); - return NISSY_ERROR_NULL_POINTER; - } - if (!strncmp(solver, "h48", 3)) { if (!strcmp(solver, "h48stats")) - return solve_h48stats(c, maxmoves, data, solutions); + return solve_h48stats(c, maxmoves, data, sols); p = parse_h48_solver(solver, &h, &k); if (p != 0) { @@ -512,13 +563,13 @@ nissy_solve( } else { return THREADS > 1 ? solve_h48_multithread(c, minmoves, - maxmoves, maxsolutions, data, solutions) : + maxmoves, maxsols, data, sols) : solve_h48(c, minmoves, - maxmoves, maxsolutions, data, solutions); + maxmoves, maxsols, data, sols); } } else if (!strcmp(solver, "simple")) { return solve_simple( - c, minmoves, maxmoves, maxsolutions, optimal, solutions); + c, minmoves, maxmoves, maxsols, optimal, sols); } else { LOG("solve: unknown solver '%s'\n", solver); return NISSY_ERROR_INVALID_SOLVER; diff --git a/src/nissy.h b/src/nissy.h @@ -98,7 +98,7 @@ Apply the given sequence of moves on the given cube. Parameters: cube - The cube to move, in B32 format. - moves - The moves to apply to the cube. + moves - The moves to apply to the cube. Must be a NULL-terminated string. result - The return parameter for the resulting cube, in B32 format. Return values: @@ -108,6 +108,7 @@ Return values: or due to an unknown internal error. NISSY_ERROR_INVALID_CUBE - The given cube is invalid. NISSY_ERROR_INVALID_MOVES - The given moves are invalid. + NISSY_ERROR_NULL_POINTER - The 'moves' argument is NULL. */ int64_t nissy_applymoves( const char cube[static 22], @@ -132,7 +133,7 @@ Return values: */ int64_t nissy_applytrans( const char cube[static 22], - const char *transformation, + const char transformation[static 12], char result[static 22] ); @@ -140,7 +141,8 @@ int64_t nissy_applytrans( Apply the given moves to the solved cube. Parameters: - moves - The moves to be applied to the solved cube. + moves - The moves to be applied to the solved cube. Must be a + NULL-terminated string. result - Return parameter for the resulting cube, in B32 format. Return values: @@ -148,6 +150,7 @@ Return values: NISSY_WARNING_UNSOLVABLE - The resulting cube is not solvable. This is probably due to an unknown internal error. NISSY_ERROR_INVALID_MOVES - The given moves are invalid. + NISSY_ERROR_NULL_POINTER - The 'moves' argument is NULL. */ int64_t nissy_frommoves( const char *moves, @@ -158,23 +161,26 @@ int64_t nissy_frommoves( Convert the given cube between the two given formats. Parameters: - format_in - The input format. - format_out - The output format. - string - The cube, in format_in format. - result - Return parameter for the cube in format_out format. Must be - large enough to contains the cube in this format. + format_in - The input format. + format_out - The output format. + cube_string - The cube, in format_in format. + retult_size - The allocated size of the result array. + result - Return parameter for the cube in format_out format. Return values: NISSY_OK - The conversion was performed succesfully. NISSY_ERROR_INVALID_CUBE - The given cube is invalid. NISSY_ERROR_INVALID_FORMAT - At least one of the given formats is invalid. NISSY_ERROR_UNKNOWN - An unknown error occurred. + NISSY_ERROR_NULL_POINTER - At least one of 'format_in', 'format_out' or + 'cube_string' arguments is NULL. */ int64_t nissy_convert( const char *format_in, const char *format_out, - const char *cube, - char *result + const char *cube_string, + uint64_t result_size, + char result[result_size] ); /* @@ -214,6 +220,7 @@ Parameters: Return values: NISSY_ERROR_INVALID_SOLVER - The given solver is not known. + NISSY_ERROR_NULL_POINTER - The 'solver' argument is null. NISSY_ERROR_UNKNOWN - An unknown error occurred. Any value >= 0 - The size of the data, in bytes. */ @@ -225,55 +232,75 @@ int64_t nissy_datasize( Compute the data for the given solver and store it in generated_data. Parameters: - solver - The name of the solver. - data - The return parameter for the generated data. Must be large enoguh - to contain the whole data. It is advised to use nissy_datasize to - check how much memory is needed. + solver - The name of the solver. + data_size - The size of the data buffer. It is advised to use nissy_datasize + to check how much memory is needed. + data - The return parameter for the generated data. Return values: NISSY_ERROR_INVALID_SOLVER - The given solver is not known. + NISSY_ERROR_NULL_POINTER - The 'solver' argument is null. NISSY_ERROR_UNKNOWN - An error occurred while generating the data. Any value >= 0 - The size of the data, in bytes. */ int64_t nissy_gendata( const char *solver, - void *data + uint64_t data_size, + char data[data_size] ); /* Print information on a data table via the provided callback writer. Parameters: - data - The data - write - A callback writer with the same signature as printf(3). + data_size - The size of the data buffer. + data - The data to be read. + write - A callback writer with the same signature as printf(3). Return values: NISSY_OK - The data is correct. NISSY_ERROR_DATA - The data contains errors. */ int64_t nissy_datainfo( - const void *data, + uint64_t data_size, + const char data[data_size], void (*write)(const char *, ...) ); /* +Check that the data is a valid data table for a solver. + +Parameters: + data_size - The size of the data buffer. + data - The data for the solver. Can be computed with gendata. + +Return values: + NISSY_OK - The data is valid. + NISSY_ERROR_DATA - The data is invalid. +*/ +int64_t nissy_checkdata( + uint64_t data_size, + const char data[data_size] +); + +/* 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. - 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. - maxsolutions - The maximum number of solutions. - optimal - If set to a non-negative value, the maximum number of moves - above the optimal solution length. - data - The data for the solver. Can be computed with gendata. - solutions - The return parameter for the solutions. Must be large enough - to store all found solutions. The solutions are separated by - a '\n' (newline) and a '\0' (NULL character) terminates the - list. - TODO: replace with callback writer. + cube - The cube to solver, in B32 format. + solver - The name of the solver. + 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. + maxsols - The maximum number of solutions. + optimal - If set to a non-negative value, the maximum number of moves + above the optimal solution length. + data_size - The size of the data buffer. + data - The data for the solver. Can be computed with gendata. + sols_size - The size of the solutions buffer. + sols - The return parameter for the solutions. The solutions are + separated by a '\n' (newline) and a '\0' (NULL character) + terminates the list. Return values: NISSY_OK - Cube solved succesfully. @@ -281,8 +308,8 @@ Return values: NISSY_ERROR_UNSOLVABLE_CUBE - The given cube is valid, but not solvable with the given solver. NISSY_ERROR_OPTIONS - One or more of the given options are invalid. - NISSY_ERROR_NULL_POINTER - One of the provided pointers is null. NISSY_ERROR_INVALID_SOLVER - The given solver is not known. + NISSY_ERROR_NULL_POINTER - The 'solver' argument is null. Any value >= 0 - The number of solutions found. */ int64_t nissy_solve( @@ -293,8 +320,10 @@ int64_t nissy_solve( int8_t maxmoves, int64_t maxsolutions, int8_t optimal, - const void *data, - char *solutions + uint64_t data_size, + const char data[data_size], + uint64_t sols_size, + char sols[sols_size] ); /* diff --git a/tools/000_gendata/gendata.c b/tools/000_gendata/gendata.c @@ -17,8 +17,8 @@ run(void) { case -2: goto gendata_run_finish; default: - nissy_datainfo(buf, write_stdout); - consistent = nissy_checkdata(solver, buf) == 0; + nissy_datainfo(size, buf, write_stdout); + consistent = nissy_checkdata(size, buf) == 0; expected = check_distribution(solver, buf); if (consistent && expected) { printf("\n"); @@ -42,7 +42,7 @@ int main(int argc, char **argv) { uint8_t h, k; if (argc < 2) { - fprintf(stderr, "Error: not enough arguments. " + printf("Error: not enough arguments. " "A solver must be given.\n"); return 1; } diff --git a/tools/001_derive_h48/derive_h48.c b/tools/001_derive_h48/derive_h48.c @@ -19,8 +19,7 @@ void run(void) { int main(int argc, char **argv) { if (argc < 5) { - fprintf(stderr, - "Error: not enough arguments. Required:\n" + printf("Error: not enough arguments. Required:\n" "1. Solver name for large table\n" "2. Solver name for derived table\n" "3. Filename containing large table\n" diff --git a/tools/100_checkdata/checkdata.c b/tools/100_checkdata/checkdata.c @@ -12,19 +12,19 @@ run(void) { size = nissy_datasize(solver); if (size <= 0) { - fprintf(stderr, "Error in datasize\n"); + printf("Error in datasize\n"); return; } if ((f = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "Error reading file %s\n", filename); + printf("Error reading file %s\n", filename); return; } buf = malloc(size); fread(buf, size, 1, f); fclose(f); - result = nissy_checkdata(solver, buf); + result = nissy_checkdata(size, buf); free(buf); printf("checkdata %s\n", result == 0 ? "succeeded" : "failed"); @@ -34,7 +34,7 @@ run(void) { int main(int argc, char **argv) { if (argc < 3) { - fprintf(stderr, "Error: not enough arguments. " + printf("Error: not enough arguments. " "A solver and a file name must be given.\n"); return 1; } diff --git a/tools/200_stats_tables_h48/stats_tables_h48.c b/tools/200_stats_tables_h48/stats_tables_h48.c @@ -6,6 +6,7 @@ #define LOG_EVERY (NCUBES_PER_THREAD / 10) int NCUBES_PER_THREAD; +int64_t size = 0; const char *solver = "h48stats"; const char *filename = "tables/h48h0k4"; char *buf; @@ -40,11 +41,12 @@ run_thread(void *arg) cp = rand64(); co = rand64(); nissy_getcube(ep, eo, cp, co, "fix", cube); - nissy_solve(cube, "h48stats", "", 0, MAXMOVES, 1, -1, buf, s); + nissy_solve(cube, "h48stats", NISSY_NISSFLAG_NORMAL, + 0, MAXMOVES, 1, -1, size, buf, 12, s); for (j = 0; j < 12; j++) a->v[j][(int)s[j]]++; if ((i+1) % LOG_EVERY == 0) - fprintf(stderr, "[thread %d] %d cubes solved...\n", + printf("[thread %d] %d cubes solved...\n", a->thread_id, i+1); } @@ -57,6 +59,8 @@ void run(void) { pthread_t thread[THREADS]; thread_arg_t arg[THREADS]; + size = nissy_datasize(solver); + for (i = 0; i < THREADS; i++) { arg[i] = (thread_arg_t) { .thread_id = i, @@ -88,7 +92,7 @@ int main(int argc, char **argv) { nissy_setlogger(log_stderr); if (argc < 2) { - fprintf(stderr, "Error: not enough arguments. " + printf("Error: not enough arguments. " "Number of cubes per thread must be provided.\n"); return 1; } @@ -96,7 +100,7 @@ int main(int argc, char **argv) { NCUBES_PER_THREAD = atoi(argv[1]); if (NCUBES_PER_THREAD < 1 || NCUBES_PER_THREAD > 1000000) { - fprintf(stderr, "Invalid number of cubes: must be > 0 and " + printf("Invalid number of cubes: must be > 0 and " "< 1000000, but %d was given.\n", NCUBES_PER_THREAD); return 1; } diff --git a/tools/300_solve_small/solve_small.c b/tools/300_solve_small/solve_small.c @@ -1,6 +1,9 @@ #include "../tool.h" +#define SOL_BUFFER_LEN 1000 + const char *solver = "h48h0k4"; +int64_t size = 0; char *buf; char *scrambles[] = { @@ -14,25 +17,22 @@ char *scrambles[] = { void run(void) { int i; int64_t n; - char sol[100], cube[22]; + char sol[SOL_BUFFER_LEN], cube[22]; printf("Solved the following scrambles:\n\n"); for (i = 0; scrambles[i] != NULL; i++) { printf("%d. %s\n", i+1, scrambles[i]); - fprintf(stderr, "Solving scramble %s\n", scrambles[i]); + printf("Solving scramble %s\n", scrambles[i]); if (nissy_frommoves(scrambles[i], cube) == -1) { - fprintf(stderr, "Invalid scramble\n"); - printf("Invalid\n"); + printf("Invalid scramble\n"); continue; } - n = nissy_solve( - cube, solver, "", 0, 20, 1, -1, buf, sol); - if (n == 0) { - printf("No solution\n"); - fprintf(stderr, "No solution found\n"); - } else { + n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL, + 0, 20, 1, -1, size, buf, SOL_BUFFER_LEN, sol); + if (n == 0) + printf("No solution found\n"); + else printf("Solutions:\n%s\n", sol); - } } } @@ -46,6 +46,8 @@ int main(void) { if (getdata(solver, &buf, filename) != 0) return 1; + size = nissy_datasize(solver); + timerun(run); free(buf); diff --git a/tools/tool.h b/tools/tool.h @@ -96,7 +96,7 @@ generatetable(const char *solver, char **buf) } *buf = malloc(size); - gensize = nissy_gendata(solver, *buf); + gensize = nissy_gendata(solver, size, *buf); if (gensize != size) { printf("Error generating table"); @@ -212,7 +212,7 @@ gendata_run( case -2: goto gendata_run_finish; default: - nissy_datainfo(buf, write_stdout); + nissy_datainfo(size, buf, write_stdout); printf("\n"); printf("Succesfully generated %" PRId64 " bytes. " "See above for details on the tables.\n", size); @@ -237,6 +237,7 @@ derivedata_run( int64_t size; char *buf; + buf = NULL; size = derivetable(solver_large, solver_small, filename_large, &buf); switch (size) { case -1: @@ -244,7 +245,7 @@ derivedata_run( case -2: goto derivedata_run_finish; default: - nissy_datainfo(buf, write_stdout); + nissy_datainfo(size, buf, write_stdout); printf("\n"); printf("Succesfully generated %" PRId64 " bytes. " "See above for details on the tables.\n", size);