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 c14e780551c4e7aa88d99b226b12f7cdd7b92527
parent ec564e62dcfe701b5ee3111b92c7d72ffd0dda6c
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Sat, 30 Mar 2024 18:40:02 +0100

Added cocsep data generation, but it is very slow

Diffstat:
MTODO.txt | 9++++++---
Mcube.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Atest/061_inverse_trans/00_all.in | 0
Atest/061_inverse_trans/00_all.out | 0
Atest/061_inverse_trans/inverse_trans_tests.c | 38++++++++++++++++++++++++++++++++++++++
Atest/100_gendata_cocsep/00_all.in | 0
Atest/100_gendata_cocsep/00_all.out | 1+
Atest/100_gendata_cocsep/gendata_cocsep_tests.c | 14++++++++++++++
Mtest/test.h | 1+
9 files changed, 243 insertions(+), 23 deletions(-)

diff --git a/TODO.txt b/TODO.txt @@ -1,8 +1,11 @@ -## (find better name) H48 solver, ideas - -TODO implement lookup tables +TODO optimize cocsep generation (very slow!) +TODO cocsep data: fix +TODO cocsep data: add hash checks? +TODO implement big pruning table for H48 solver TODO alternative: ARM NEON part +## H48 optimal solver + First compute co + csep. Use csep as a binary number (2^7 instead of 70, loose a factor of 1.8 but still fits in a few megabytes or less). Use co + csep as an index in a table whose entries have: 6 bits for ttrep, diff --git a/cube.c b/cube.c @@ -28,7 +28,33 @@ #endif /****************************************************************************** -Section: constants, strings and other stuff +Section: mathematical constants +******************************************************************************/ + +#define _2p11 2048U +#define _2p12 4096U +#define _3p7 2187U +#define _3p8 6561U +#define _12c4 495U +#define _8c4 70U + +_static int64_t binomial[12][12] = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 4, 6, 4, 1, 0, 0, 0, 0, 0, 0, 0}, + {1, 5, 10, 10, 5, 1, 0, 0, 0, 0, 0, 0}, + {1, 6, 15, 20, 15, 6, 1, 0, 0, 0, 0, 0}, + {1, 7, 21, 35, 35, 21, 7, 1, 0, 0, 0, 0}, + {1, 8, 28, 56, 70, 56, 28, 8, 1, 0, 0, 0}, + {1, 9, 36, 84, 126, 126, 84, 36, 9, 1, 0, 0}, + {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1, 0}, + {1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1}, +}; + +/****************************************************************************** +Section: moves definitions and tables ******************************************************************************/ #define U 0U @@ -378,7 +404,7 @@ _static cube_t solved = { #define _trans_cube_BLm_inverse fastcube( \ 38, 37, 39, 36, 67, 64, 66, 65, 23, 20, 21, 22, 24, 27, 26, 25, 3, 2, 1, 0) -static char *cornerstr[] = { +_static char *cornerstr[] = { [_c_ufr] = "UFR", [_c_ubl] = "UBL", [_c_dfl] = "DFL", @@ -389,7 +415,7 @@ static char *cornerstr[] = { [_c_dbl] = "DBL" }; -static char *cornerstralt[] = { +_static char *cornerstralt[] = { [_c_ufr] = "URF", [_c_ubl] = "ULB", [_c_dfl] = "DLF", @@ -400,7 +426,7 @@ static char *cornerstralt[] = { [_c_dbl] = "DLB" }; -static char *edgestr[] = { +_static char *edgestr[] = { [_e_uf] = "UF", [_e_ub] = "UB", [_e_db] = "DB", @@ -415,7 +441,7 @@ static char *edgestr[] = { [_e_br] = "BR" }; -static char *movestr[] = { +_static char *movestr[] = { [U] = "U", [U2] = "U2", [U3] = "U'", @@ -436,7 +462,7 @@ static char *movestr[] = { [B3] = "B'", }; -static char *transstr[] = { +_static char *transstr[] = { [UFr] = "rotation UF", [UFm] = "mirrored UF", [ULr] = "rotation UL", @@ -487,19 +513,55 @@ static char *transstr[] = { [BLm] = "mirrored BL", }; -static int64_t binomial[12][12] = { - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 4, 6, 4, 1, 0, 0, 0, 0, 0, 0, 0}, - {1, 5, 10, 10, 5, 1, 0, 0, 0, 0, 0, 0}, - {1, 6, 15, 20, 15, 6, 1, 0, 0, 0, 0, 0}, - {1, 7, 21, 35, 35, 21, 7, 1, 0, 0, 0, 0}, - {1, 8, 28, 56, 70, 56, 28, 8, 1, 0, 0, 0}, - {1, 9, 36, 84, 126, 126, 84, 36, 9, 1, 0, 0}, - {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1, 0}, - {1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1}, +static uint8_t inverse_trans_table[48] = { + [UFr] = UFr, + [UFm] = UFm, + [ULr] = URr, + [ULm] = ULm, + [UBr] = UBr, + [UBm] = UBm, + [URr] = ULr, + [URm] = URm, + [DFr] = DFr, + [DFm] = DFm, + [DLr] = DLr, + [DLm] = DRm, + [DBr] = DBr, + [DBm] = DBm, + [DRr] = DRr, + [DRm] = DLm, + [RUr] = FRr, + [RUm] = FLm, + [RFr] = LFr, + [RFm] = RFm, + [RDr] = BLr, + [RDm] = BRm, + [RBr] = RBr, + [RBm] = LBm, + [LUr] = FLr, + [LUm] = FRm, + [LFr] = RFr, + [LFm] = LFm, + [LDr] = BRr, + [LDm] = BLm, + [LBr] = LBr, + [LBm] = RBm, + [FUr] = FUr, + [FUm] = FUm, + [FRr] = RUr, + [FRm] = LUm, + [FDr] = BUr, + [FDm] = BUm, + [FLr] = LUr, + [FLm] = RUm, + [BUr] = FDr, + [BUm] = FDm, + [BRr] = LDr, + [BRm] = RDm, + [BDr] = BDr, + [BDm] = BDm, + [BLr] = RDr, + [BLm] = LDm, }; /****************************************************************************** @@ -536,6 +598,7 @@ _static_inline cube_fast_t compose_fast(cube_fast_t, cube_fast_t); _static_inline int64_t coord_fast_co(cube_fast_t); _static_inline int64_t coord_fast_csep(cube_fast_t); +_static_inline int64_t coord_fast_cocsep(cube_fast_t); _static_inline int64_t coord_fast_eo(cube_fast_t); _static_inline int64_t coord_fast_esep(cube_fast_t); @@ -694,6 +757,12 @@ coord_fast_csep(cube_fast_t c) } _static_inline int64_t +coord_fast_cocsep(cube_fast_t c) +{ + return (coord_fast_co(c) << 7) + coord_fast_csep(c); +} + +_static_inline int64_t coord_fast_eo(cube_fast_t c) { cube_fast_t eo, shifted; @@ -779,6 +848,7 @@ _static_inline cube_fast_t compose_fast(cube_fast_t, cube_fast_t); _static_inline int64_t coord_fast_co(cube_fast_t); _static_inline int64_t coord_fast_csep(cube_fast_t); +_static_inline int64_t coord_fast_cocsep(cube_fast_t); _static_inline int64_t coord_fast_eo(cube_fast_t); _static_inline int64_t coord_fast_esep(cube_fast_t); @@ -934,6 +1004,12 @@ coord_fast_csep(cube_fast_t c) } _static_inline int64_t +coord_fast_cocsep(cube_fast_t c) +{ + return (coord_fast_co(c) << 7) + coord_fast_csep(c); +} + +_static_inline int64_t coord_fast_eo(cube_fast_t c) { int i, p; @@ -1734,16 +1810,23 @@ transform(cube_fast_t c, uint8_t t) } /****************************************************************************** -Section: moves and move sequences +Section: moves, move sequences and transformations This section contains methods to work with moves and arrays of moves. They do not rely on the cube structure. ******************************************************************************/ +_static_inline uint8_t inverse_trans(uint8_t); _static_inline uint8_t movebase(uint8_t); _static_inline uint8_t moveaxis(uint8_t); _static_inline uint8_t +inverse_trans(uint8_t t) +{ + return inverse_trans_table[t]; +} + +_static_inline uint8_t movebase(uint8_t move) { return move / 3; @@ -1759,7 +1842,87 @@ moveaxis(uint8_t move) Section: auxiliary procedures for H48 optimal solver (temporary) ******************************************************************************/ +_static size_t gendata_cocsep(void *); +_static uint16_t dfs_cocsep(cube_fast_t, uint8_t, uint8_t, uint16_t *, uint32_t *); + +/* +Each element of the cocsep table is a uint32_t used as follows: + - Lowest 8-bit block: pruning value + - Second-lower 8-bit block: "ttrep" (transformation to representative) + - Top 16-bit block: symcoord value +*/ +_static size_t +gendata_cocsep(void *buf) +{ + uint32_t *buf32; + uint16_t n, cc; + uint8_t i, j; + size_t tablesize; + + tablesize = _3p7 << 7U; + + buf32 = (uint32_t *)buf; + memset(buf32, 0xFFU, 4*tablesize); + memset(buf32 + tablesize, 0, 21*4); + + for (i = 0, n = 0, cc = 0; cc != 0 || i == 0; i++) { + DBG_LOG("gendata_cocsep: generating depth %" PRIu8 "\n", i); + cc = dfs_cocsep(cubetofast(solvedcube()), 0, i, &n, buf32); + buf32[tablesize+i+1] = (uint32_t)cc; + } + buf32[tablesize] = (uint32_t)n; + i--; + + DBG_LOG("cocsep data computed, %" PRIu16 " symmetry classes\n", n); + DBG_LOG("Pruning value distribution:\n"); + for (j = 0; j < i; j++) + DBG_LOG("%" PRIu8 ":\t%" PRIu32 "\n", j, buf32[tablesize+j+1]); + + return 4*(tablesize + i + 1); +} + +_static uint16_t +dfs_cocsep( + cube_fast_t c, + uint8_t depth, + uint8_t maxdepth, + uint16_t *n, + uint32_t *buf32 +) +{ + uint8_t m, t, tinv, olddepth; + uint16_t cc; + uint32_t oldvalue; + uint64_t i; + cube_fast_t d; + + oldvalue = buf32[coord_fast_cocsep(c)]; + if (depth == maxdepth) { + if ((oldvalue & 0xFFU) != 0xFFU) + return 0; + for (t = 0, cc = 0; t < 48; t++) { + d = transform(c, t); + i = coord_fast_cocsep(d); + tinv = inverse_trans(t); + if ((buf32[i] & 0xFFU) == 0xFFU) + cc++; + buf32[i] = (*n << 16U) | (tinv << 8U) | depth; + } + (*n)++; + + return cc; + } + + olddepth = (uint8_t)(oldvalue & 0xFFU); + if (olddepth != depth) + return 0; + + for (m = 0, cc = 0; m < 18; m++) + cc += dfs_cocsep(move(c, m), depth+1, maxdepth, n, buf32); + + return cc; +} /****************************************************************************** Section: solvers diff --git a/test/061_inverse_trans/00_all.in b/test/061_inverse_trans/00_all.in diff --git a/test/061_inverse_trans/00_all.out b/test/061_inverse_trans/00_all.out diff --git a/test/061_inverse_trans/inverse_trans_tests.c b/test/061_inverse_trans/inverse_trans_tests.c @@ -0,0 +1,38 @@ +#include "../test.h" + +uint8_t readtrans(char *); +uint8_t inverse_trans(uint8_t); +cube_t applymoves(cube_t, char *); +cube_t applytrans(cube_t, char *); +extern char *transstr[]; + +int main(void) { + uint8_t t, tinv; + cube_t cube; + + for (t = 0; t < 48; t++) { + cube = solvedcube(); + cube = applymoves(cube, "R"); + cube = applymoves(cube, "U"); + cube = applymoves(cube, "F"); + + cube = applytrans(cube, transstr[t]); + tinv = inverse_trans(t); + cube = applytrans(cube, transstr[tinv]); + + if (iserror(cube)) { + printf("Error transforming cube\n"); + } else if (!issolvable(cube)) { + printf("Transformed cube is not solvable\n"); + } else { + cube = applymoves(cube, "F'"); + cube = applymoves(cube, "U'"); + cube = applymoves(cube, "R'"); + if (!issolved(cube)) + printf("%s: Error! Got %" PRIu8 "\n", + transstr[t], tinv); + } + } + + return 0; +} diff --git a/test/100_gendata_cocsep/00_all.in b/test/100_gendata_cocsep/00_all.in diff --git a/test/100_gendata_cocsep/00_all.out b/test/100_gendata_cocsep/00_all.out @@ -0,0 +1 @@ +1119788 diff --git a/test/100_gendata_cocsep/gendata_cocsep_tests.c b/test/100_gendata_cocsep/gendata_cocsep_tests.c @@ -0,0 +1,14 @@ +#include "../test.h" + +size_t gendata_cocsep(void *); + +int main(void) { + uint32_t buf[300000]; + size_t result; + + result = gendata_cocsep(buf); + + printf("%zu\n", result); + + return 0; +} diff --git a/test/test.h b/test/test.h @@ -22,5 +22,6 @@ typedef cube_t cube_fast_t; cube_t solvedcube(void); bool iserror(cube_t); bool issolvable(cube_t); +bool issolved(cube_t); cube_t readcube(char *, char *); void writecube(char *, cube_t, char *);