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 5e291466fbbc45aed74f67a1b2e555d1f0c44d8f
parent d4fadc24ee1993104bac7fd854ed6ca83f60ee66
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Thu, 10 Oct 2024 19:49:48 +0200

Improved error messages and add some comments in nissy.h

Diffstat:
Mshell.c | 56+++++++++++++++-----------------------------------------
Msrc/core/cube.h | 14++------------
Msrc/core/io_cube.h | 62+++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/core/io_trans.h | 1-
Msrc/core/moves.h | 24+++++++++---------------
Msrc/core/transform.h | 6++++--
Msrc/nissy.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/nissy.h | 120++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mtools/nissy_extra.h | 1+
9 files changed, 228 insertions(+), 119 deletions(-)

diff --git a/shell.c b/shell.c @@ -56,9 +56,6 @@ typedef struct { int64_t maxsolutions; } args_t; -static void print_cube_result(int64_t, char [static 22]); -static void print_str_result(int64_t, char *); - static int64_t compose_exec(args_t *); static int64_t inverse_exec(args_t *); static int64_t applymoves_exec(args_t *); @@ -240,36 +237,6 @@ rand64(void) return ret; } -static void -print_cube_result(int64_t ret, char result[static 22]) -{ - switch (ret) { - case 0: - break; - case 1: - fprintf(stderr, "Warning: resulting cube not solvable\n"); - break; - case 2: /* Fallthrough */ - default: - fprintf(stderr, "Unknown error (result is inconsistent)\n"); - } - - printf("%s\n", result); -} - -static void -print_str_result(int64_t ret, char *result) -{ - switch (ret) { - case 0: - break; - default: - fprintf(stderr, "Unknown error\n"); - } - - printf("%s\n", result); -} - static int64_t compose_exec(args_t *args) { @@ -277,7 +244,8 @@ compose_exec(args_t *args) int64_t ret; ret = nissy_compose(args->cube, args->cube_perm, result); - print_cube_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -289,7 +257,8 @@ inverse_exec(args_t *args) int64_t ret; ret = nissy_inverse(args->cube, result); - print_cube_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -301,7 +270,8 @@ applymoves_exec(args_t *args) int64_t ret; ret = nissy_applymoves(args->cube, args->str_moves, result); - print_cube_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -313,7 +283,8 @@ applytrans_exec(args_t *args) int64_t ret; ret = nissy_applytrans(args->cube, args->str_trans, result); - print_cube_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -325,7 +296,8 @@ frommoves_exec(args_t *args) int64_t ret; ret = nissy_frommoves(args->str_moves, result); - print_cube_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -338,7 +310,8 @@ convert_exec(args_t *args) ret = nissy_convert( args->str_format_in, args->str_format_out, args->str_cube, result); - print_str_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -354,7 +327,8 @@ randomcube_exec(args_t *args) cp = rand64(); co = rand64(); ret = nissy_getcube(ep, eo, cp, co, "fix", result); - print_str_result(ret, result); + if (ret == 0) + printf("%s\n", result); return ret; } @@ -526,7 +500,7 @@ help_exec(args_t *args) printf("Available commands and usage:\n\n"); for (i = 0; commands[i].name != NULL; i++) printf("%-15s%s\n", commands[i].name, commands[i].syn); - printf("\nUse 'help COMMAND' for more information.\n"); + printf("\nUse 'help -command COMMAND' for more information.\n"); } else { for (i = 0; commands[i].name != NULL; i++) if (!strcmp(args->str_command, commands[i].name)) diff --git a/src/core/cube.h b/src/core/cube.h @@ -66,16 +66,10 @@ isconsistent(cube_t cube) return true; inconsistent_ep: - LOG("Inconsistent EP\n"); - return false; inconsistent_cp: - LOG("Inconsistent CP\n"); - return false; inconsistent_eo: - LOG("Inconsistent EO\n"); - return false; inconsistent_co: - LOG("Inconsistent CO\n"); + /* We used to do more logging here, hence the 4 different labels */ return false; } @@ -115,13 +109,9 @@ issolvable(cube_t cube) return true; issolvable_parity: - LOG("EP and CP parities are different\n"); - return false; issolvable_eo: - LOG("Odd number of flipped edges\n"); - return false; issolvable_co: - LOG("Sum of corner orientation is not multiple of 3\n"); + /* We used to do more logging here, hence the 3 different labels */ return false; } diff --git a/src/core/io_cube.h b/src/core/io_cube.h @@ -1,3 +1,6 @@ +STATIC cube_t readcube(const char *, const char *); +STATIC int writecube(const char *, cube_t, char *); +STATIC void log_available_formats(void); STATIC uint8_t readco(const char *); STATIC uint8_t readcp(const char *); STATIC uint8_t readeo(const char *); @@ -29,7 +32,7 @@ STATIC struct { { .name = "NONE", .read = NULL, .write = NULL }, }; -cube_t +STATIC cube_t readcube(const char *format, const char *buf) { int i; @@ -38,11 +41,12 @@ readcube(const char *format, const char *buf) if (!strcmp(format, ioformat[i].name)) return ioformat[i].read(buf); - LOG("Cannot read cube in the given format\n"); + LOG("Cannot read cube: unknown format '%s'\n", format); + log_available_formats(); return ZERO_CUBE; } -void +STATIC int writecube(const char *format, cube_t cube, char *buf) { char *errormsg; @@ -58,18 +62,30 @@ writecube(const char *format, cube_t cube, char *buf) for (i = 0; ioformat[i].write != NULL; i++) { if (!strcmp(format, ioformat[i].name)) { ioformat[i].write(cube, buf); - return; + return 0; } } + LOG("Cannot write cube: unknown format '%s'\n", format); + log_available_formats(); errormsg = "ERROR: format"; writecube_error: - LOG("writecube error, see stdout for details\n"); len = strlen(errormsg); memcpy(buf, errormsg, len); - buf[len] = '\n'; - buf[len+1] = '\0'; + buf[len] = '\0'; + return 1; +} + +STATIC void +log_available_formats(void) +{ + int i; + + LOG("Available formats: "); + for (i = 0; ioformat[i].read != NULL; i++) + LOG("'%s' ", ioformat[i].name); + LOG("\n"); } STATIC uint8_t @@ -133,14 +149,34 @@ readcube_B32(const char *buf) for (i = 0; i < 8; i++) { c[i] = b32tocorner(buf[i]); - DBG_ASSERT(c[i] < 255, ZERO_CUBE, - "Error reading B32 corner %d (char %d)\n", i, i); + if (c[i] == UINT8_ERROR) { + LOG("Error reading B32 corner %d ", i); + if (buf[i] == 0) { + LOG("(string terminated early)\n"); + } else { + LOG("(char '%c')\n", buf[i]); + } + return ZERO_CUBE; + } + } + + if (buf[8] != '=') { + LOG("Error reading B32 separator: a single '=' " + "must be used to separate edges and corners\n"); + return ZERO_CUBE; } for (i = 0; i < 12; i++) { e[i] = b32toedge(buf[i+9]); - DBG_ASSERT(e[i] < 255, ZERO_CUBE, - "Error reading B32 edge %d (char %d)\n", i, i+9); + if (e[i] == UINT8_ERROR) { + LOG("Error reading B32 edge %d ", i); + if (buf[i+9] == 0) { + LOG("(string terminated early)\n"); + } else { + LOG("(char '%c')\n", buf[i+9]); + } + return ZERO_CUBE; + } } return cubefromarray(c, e); @@ -314,7 +350,7 @@ STATIC uint8_t b32toedge(char c) { if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'f'))) - return 255; + return UINT8_ERROR; return c <= 'Z' ? (uint8_t)(c - 'A') : (uint8_t)(c - 'a') + 26; } @@ -324,7 +360,7 @@ b32tocorner(char c) { uint8_t val; if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'f'))) - return 255; + return UINT8_ERROR; val = c <= 'Z' ? (uint8_t)(c - 'A') : (uint8_t)(c - 'a') + 26; diff --git a/src/core/io_trans.h b/src/core/io_trans.h @@ -10,7 +10,6 @@ readtrans(const char *buf) if (!strncmp(buf, transstr[t], 11)) return t; - LOG("readtrans error\n"); return UINT8_ERROR; } diff --git a/src/core/moves.h b/src/core/moves.h @@ -18,7 +18,7 @@ STATIC cube_t applymoves(cube_t, const char *); STATIC cube_t frommoves(const char *); #define FOREACH_READMOVE(ARG_BUF, ARG_MOVE, ARG_C, ARG_MAX, \ - LABEL_ERROR, ARG_ACTION) \ + RET_ERROR, ARG_ACTION) \ const char *VAR_B; \ uint8_t VAR_MOVE_NOMOD, VAR_MOD; \ for (VAR_B = ARG_BUF, ARG_C = 0; *VAR_B != '\0'; VAR_B++, ARG_C++) { \ @@ -26,8 +26,10 @@ STATIC cube_t frommoves(const char *); VAR_B++; \ if (*VAR_B == '\0' || ARG_C == ARG_MAX) \ break; \ - if ((VAR_MOVE_NOMOD = readmove(*VAR_B)) == UINT8_ERROR) \ - goto LABEL_ERROR; \ + if ((VAR_MOVE_NOMOD = readmove(*VAR_B)) == UINT8_ERROR) { \ + LOG("Error: unknown move '%c'\n", *VAR_B); \ + return RET_ERROR; \ + } \ if ((VAR_MOD = readmodifier(*(VAR_B+1))) != 0) \ VAR_B++; \ ARG_MOVE = VAR_MOVE_NOMOD + VAR_MOD; \ @@ -124,7 +126,7 @@ move(cube_t c, uint8_t m) case MOVE_B3: return MOVE(B3, c); default: - LOG("move error, unknown move\n"); + LOG("move error: unknown move %" PRIu8 "\n", m); return ZERO_CUBE; } } @@ -171,7 +173,7 @@ premove(cube_t c, uint8_t m) case MOVE_B3: return PREMOVE(B, c); default: - LOG("move error, unknown move\n"); + LOG("premove error: unknown move %" PRIu8 "\n", m); return ZERO_CUBE; } } @@ -222,15 +224,11 @@ readmoves(const char *buf, int max, uint8_t *ret) uint8_t m; int c; - FOREACH_READMOVE(buf, m, c, max, readmoves_error, + FOREACH_READMOVE(buf, m, c, max, -1, ret[c] = m; ) return c; - -readmoves_error: - LOG("readmoves error\n"); - return -1; } STATIC cube_t @@ -242,15 +240,11 @@ applymoves(cube_t cube, const char *buf) DBG_ASSERT(isconsistent(cube), ZERO_CUBE, "move error: inconsistent cube\n"); - FOREACH_READMOVE(buf, m, c, -1, applymoves_error, + FOREACH_READMOVE(buf, m, c, -1, ZERO_CUBE, cube = move(cube, m); ) return cube; - -applymoves_error: - LOG("applymoves error\n"); - return ZERO_CUBE; } STATIC cube_t diff --git a/src/core/transform.h b/src/core/transform.h @@ -229,7 +229,7 @@ transform_corners(cube_t c, uint8_t t) case TRANS_BLm: return TRANS_CORNERS_MIRRORED(BLm, c); default: - LOG("transform error, unknown transformation %" PRIu8 "\n", t); + LOG("transform error: unknown transformation %" PRIu8 "\n", t); return ZERO_CUBE; } } @@ -335,7 +335,6 @@ transform(cube_t c, uint8_t t) case TRANS_BLm: return TRANS_MIRRORED(BLm, c); default: - LOG("transform error, unknown transformation %" PRIu8 "\n", t); return ZERO_CUBE; } } @@ -350,5 +349,8 @@ applytrans(cube_t cube, const char *buf) t = readtrans(buf); + if (t == UINT8_ERROR) + LOG("Unknown transformation '%s'\n", buf); + return transform(cube, t); } diff --git a/src/nissy.c b/src/nissy.c @@ -110,13 +110,19 @@ STATIC int64_t write_result(cube_t cube, char result[static 22]) { if (!isconsistent(cube)) { + LOG("Error: resulting cube is invalid\n"); writecube("B32", ZERO_CUBE, result); - return 2; + return 8; } writecube("B32", cube, result); - return issolvable(cube) ? 0 : 1; + if (!issolvable(cube)) { + LOG("Warning: resulting cube is not solvable\n"); + return 9; + } + + return 0; } int64_t @@ -129,7 +135,19 @@ nissy_compose( cube_t c, p, res; c = readcube("B32", cube); + + if (!isconsistent(c)) { + LOG("Error in nissy_compose: given cube is invalid\n"); + return 1; + } + p = readcube("B32", permutation); + + if (!isconsistent(p)) { + LOG("Error in nissy_compose: given permutation is invalid\n"); + return 2; + } + res = compose(c, p); return write_result(res, result); @@ -144,6 +162,12 @@ nissy_inverse( cube_t c, res; c = readcube("B32", cube); + + if (iserror(c)) { + LOG("Error in nissy_inverse: given cube is invalid\n"); + return 1; + } + res = inverse(c); return write_result(res, result); @@ -159,6 +183,12 @@ nissy_applymoves( cube_t c, res; c = readcube("B32", cube); + + if (!isconsistent(c)) { + LOG("Error in nissy_applymoves: given cube is invalid\n"); + return 1; + } + res = applymoves(c, moves); return write_result(res, result); @@ -174,6 +204,12 @@ nissy_applytrans( cube_t c, res; c = readcube("B32", cube); + + if (!isconsistent(c)) { + LOG("Error in nissy_applytrans: given cube is invalid\n"); + return 1; + } + res = applytrans(c, transformation); return write_result(res, result); @@ -189,6 +225,11 @@ nissy_frommoves( res = applymoves(SOLVED_CUBE, moves); + if (!isconsistent(res)) { + /* Moves must be invalid */ + return 1; + } + return write_result(res, result); } @@ -200,12 +241,20 @@ nissy_convert( char *result ) { + int ret; cube_t c; c = readcube(format_in, cube_string); - writecube(format_out, c, result); - return isconsistent(c) ? 0 : 2; + if (iserror(c)) + return 1; + + ret = writecube(format_out, c, result); + + if (ret != 0) + return 2; + + return isconsistent(c) ? 0 : 3; } int64_t @@ -387,8 +436,10 @@ nissy_solve( return -1; } else { return THREADS > 1 ? - solve_h48_multithread(c, minmoves, maxmoves, maxsolutions, data, solutions) : - solve_h48(c, minmoves, maxmoves, maxsolutions, data, solutions); + solve_h48_multithread(c, minmoves, + maxmoves, maxsolutions, data, solutions) : + solve_h48(c, minmoves, + maxmoves, maxsolutions, data, solutions); } } else if (!strcmp(solver, "simple")) { return solve_simple( diff --git a/src/nissy.h b/src/nissy.h @@ -1,10 +1,17 @@ /* -If you include this file, you should also include the following: +This is libnissy (temporarily also known as h48), a Rubik's cube library. -inttypes, stdarg, stdbool, string +If you include this file, you should also use the following includes: -All the functions below return 0 in case of success and a positive -number in case of error, unless otherwise specified. +#include <inttypes> +#include <stdarg> +#include <stdbool> +#include <string> + +All the functions below return 0 in case of success and a positive number +in case of error, unless otherwise specified. Errors are checked in code +order: for example if error code 1 is returned then it could that also +an error with code 2 or higher occurred. Arguments of type char [static 22] denote a cube in B32 format. Other available formats are H48 and SRC. See README.md for more info on @@ -18,40 +25,85 @@ A transformation must be given in the format for example 'rotation UF' or 'mirrored BL'. */ -/* Apply the secod argument as a permutation on the first argument */ +/* +Apply the secod argument as a permutation on the first argument. + +Return values: + 0 Valid result + 1 The given cube is invalid + 2 The given permutation is invalid + 9 The resulting cube is not solvable +*/ int64_t nissy_compose( const char cube[static 22], const char permutation[static 22], char result[static 22] ); -/* Compute the inverse of the given cube */ +/* +Compute the inverse of the given cube. + +Return values: + 0 Valid result + 1 The given cube is invalid + 9 The resulting cube is not solvable +*/ int64_t nissy_inverse( const char cube[static 22], char result[static 22] ); -/* Apply the given sequence of moves on the given cube */ +/* +Apply the given sequence of moves on the given cube. + +Return values: + 0 Valid result + 1 The given cube is invalid + 8 The given moves are invalid + 9 The resulting cube is not solvable +*/ int64_t nissy_applymoves( const char cube[static 22], const char *moves, char result[static 22] ); -/* Apply the single given transformation to the given cube */ +/* +Apply the single given transformation to the given cube. + +Return values: + 0 Valid result + 1 The given cube is invalid + 8 The given transformation is invalid + 9 The resulting cube is not solvable +*/ int64_t nissy_applytrans( const char cube[static 22], const char *transformation, char result[static 22] ); -/* Return the cube obtained by applying the given moves to the solved cube */ +/* +Apply the given moves to the solved cube. + +Return values: + 0 Valid result + 1 The given moves are invalid +*/ int64_t nissy_frommoves( const char *moves, char result[static 22] ); -/* Convert the given cube between the two given formats */ +/* +Convert the given cube between the two given formats. + +Return values: + 0 Valid result + 1 The given cube or format_in is invalid + 2 The resulting cube or format_out is invalid + 3 The resulting cube is inconsistent +*/ int64_t nissy_convert( const char *format_in, const char *format_out, @@ -70,40 +122,48 @@ int64_t nissy_getcube( ); /* -Returns the size of the data generated by nissy_gendata, when called with -the same parameters, or -1 in case of error. The returned value can be -slightly larger than the actual table size. +Compute the size of the data generated by nissy_gendata, when called with +the same parameters, or -1 in case of error. + +Return values: + -1 Error + >=0 The size of the table, in bytes */ int64_t nissy_datasize( const char *solver ); -/* Returns the number of bytes written, or -1 in case of error */ +/* +Compute the data for the given solver and store it in generated_data. + +Return values: + -1 Error + >=0 The size of the table, in bytes +*/ int64_t nissy_gendata( const char *solver, void *generated_data ); -/* Temporarily added to test h48 intermediate tables */ -int64_t nissy_derivedata( - const char *options, - const void *fulltable, - void *generated_data -); - -/* Returns 0 on positive check, 1 on error */ -int64_t nissy_checkdata( - const char *solver, - const void *data -); +/* +Print information on a data table via the provided callback writer. -/* Print information on a data table via the provided callback writer */ +Return values: + 0 No error + 1 The given data could not be read correctly +*/ int64_t nissy_datainfo( const void *table, void (*write)(const char *, ...) ); -/* Returns the number of solutions found, or -1 in case of error */ +/* +Solve the given cube using the given solver and options + +Return values: + -1 Error + >=0 The number of solutions found +*/ int64_t nissy_solve( const char cube[static 22], const char *solver, @@ -116,5 +176,7 @@ int64_t nissy_solve( char *solutions ); -/* Set a global logger function used by this library. */ +/* +Set a global logger function used by this library. +*/ void nissy_setlogger(void (*logger_function)(const char *, ...)); diff --git a/tools/nissy_extra.h b/tools/nissy_extra.h @@ -10,3 +10,4 @@ for testing purposes only. size_t gendata_h48_derive(uint8_t, const void *, void *); int parse_h48_solver(const char *, uint8_t [static 1], uint8_t [static 1]); +int64_t nissy_derivedata(const char *, const void *, void *);