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

moves.h (5589B)


      1 #define MOVE(M, c) compose(c, MOVE_CUBE_ ## M)
      2 #define PREMOVE(M, c) compose(MOVE_CUBE_ ## M, c)
      3 
      4 STATIC_INLINE bool allowednextmove(uint8_t *, uint8_t);
      5 
      6 STATIC_INLINE uint8_t inverse_trans(uint8_t);
      7 STATIC_INLINE uint8_t movebase(uint8_t);
      8 STATIC_INLINE uint8_t moveaxis(uint8_t);
      9 STATIC_INLINE uint32_t disable_moves(uint32_t, uint8_t);
     10 
     11 STATIC cube_t move(cube_t, uint8_t);
     12 STATIC cube_t premove(cube_t, uint8_t);
     13 STATIC uint8_t inverse_move(uint8_t);
     14 STATIC void invertmoves(uint8_t *, uint8_t, uint8_t *);
     15 
     16 STATIC int readmoves(const char *, int, uint8_t *);
     17 STATIC cube_t applymoves(cube_t, const char *);
     18 STATIC cube_t frommoves(const char *);
     19 
     20 #define FOREACH_READMOVE(ARG_BUF, ARG_MOVE, ARG_C, ARG_MAX, \
     21 	LABEL_ERROR, ARG_ACTION) \
     22 	const char *VAR_B; \
     23 	uint8_t VAR_MOVE_NOMOD, VAR_MOD; \
     24 	for (VAR_B = ARG_BUF, ARG_C = 0; *VAR_B != '\0'; VAR_B++, ARG_C++) { \
     25 		while (*VAR_B == ' ' || *VAR_B == '\t' || *VAR_B == '\n') \
     26 			VAR_B++; \
     27 		if (*VAR_B == '\0' || ARG_C == ARG_MAX) \
     28 			break; \
     29 		if ((VAR_MOVE_NOMOD = readmove(*VAR_B)) == UINT8_ERROR) \
     30 			goto LABEL_ERROR; \
     31 		if ((VAR_MOD = readmodifier(*(VAR_B+1))) != 0) \
     32 			VAR_B++; \
     33 		ARG_MOVE = VAR_MOVE_NOMOD + VAR_MOD; \
     34 		ARG_ACTION \
     35 	}
     36 
     37 STATIC bool
     38 allowednextmove(uint8_t *moves, uint8_t n)
     39 {
     40 	uint8_t base[3], axis[3];
     41 
     42 	if (n < 2)
     43 		return true;
     44 
     45 	base[0] = movebase(moves[n-1]);
     46 	axis[0] = moveaxis(moves[n-1]);
     47 	base[1] = movebase(moves[n-2]);
     48 	axis[1] = moveaxis(moves[n-2]);
     49 
     50 	if (base[0] == base[1] || (axis[0] == axis[1] && base[0] < base[1]))
     51 		return false;
     52 
     53 	if (n == 2)
     54 		return true;
     55 
     56 	base[2] = movebase(moves[n-3]);
     57 	axis[2] = moveaxis(moves[n-3]);
     58 
     59 	return axis[1] != axis[2] || base[0] != base[2];
     60 }
     61 
     62 STATIC_INLINE uint32_t 
     63 disable_moves(uint32_t current_result, uint8_t base_index)
     64 {
     65 	return current_result & ~(7 << base_index);
     66 }
     67 
     68 STATIC_INLINE uint8_t
     69 inverse_trans(uint8_t t)
     70 {
     71 	return inverse_trans_table[t];
     72 }
     73 
     74 STATIC_INLINE uint8_t
     75 movebase(uint8_t move)
     76 {
     77 	return move / 3;
     78 }
     79 
     80 STATIC_INLINE uint8_t
     81 moveaxis(uint8_t move)
     82 {
     83 	return move / 6;
     84 }
     85 
     86 STATIC cube_t
     87 move(cube_t c, uint8_t m)
     88 {
     89 	switch (m) {
     90 	case MOVE_U:
     91 		return MOVE(U, c);
     92 	case MOVE_U2:
     93 		return MOVE(U2, c);
     94 	case MOVE_U3:
     95 		return MOVE(U3, c);
     96 	case MOVE_D:
     97 		return MOVE(D, c);
     98 	case MOVE_D2:
     99 		return MOVE(D2, c);
    100 	case MOVE_D3:
    101 		return MOVE(D3, c);
    102 	case MOVE_R:
    103 		return MOVE(R, c);
    104 	case MOVE_R2:
    105 		return MOVE(R2, c);
    106 	case MOVE_R3:
    107 		return MOVE(R3, c);
    108 	case MOVE_L:
    109 		return MOVE(L, c);
    110 	case MOVE_L2:
    111 		return MOVE(L2, c);
    112 	case MOVE_L3:
    113 		return MOVE(L3, c);
    114 	case MOVE_F:
    115 		return MOVE(F, c);
    116 	case MOVE_F2:
    117 		return MOVE(F2, c);
    118 	case MOVE_F3:
    119 		return MOVE(F3, c);
    120 	case MOVE_B:
    121 		return MOVE(B, c);
    122 	case MOVE_B2:
    123 		return MOVE(B2, c);
    124 	case MOVE_B3:
    125 		return MOVE(B3, c);
    126 	default:
    127 		LOG("move error, unknown move\n");
    128 		return ZERO_CUBE;
    129 	}
    130 }
    131 
    132 /* Applies the INVERSE of m BEFORE the scramble corresponding to c */
    133 STATIC cube_t
    134 premove(cube_t c, uint8_t m)
    135 {
    136 	switch (m) {
    137 	case MOVE_U:
    138 		return PREMOVE(U3, c);
    139 	case MOVE_U2:
    140 		return PREMOVE(U2, c);
    141 	case MOVE_U3:
    142 		return PREMOVE(U, c);
    143 	case MOVE_D:
    144 		return PREMOVE(D3, c);
    145 	case MOVE_D2:
    146 		return PREMOVE(D2, c);
    147 	case MOVE_D3:
    148 		return PREMOVE(D, c);
    149 	case MOVE_R:
    150 		return PREMOVE(R3, c);
    151 	case MOVE_R2:
    152 		return PREMOVE(R2, c);
    153 	case MOVE_R3:
    154 		return PREMOVE(R, c);
    155 	case MOVE_L:
    156 		return PREMOVE(L3, c);
    157 	case MOVE_L2:
    158 		return PREMOVE(L2, c);
    159 	case MOVE_L3:
    160 		return PREMOVE(L, c);
    161 	case MOVE_F:
    162 		return PREMOVE(F3, c);
    163 	case MOVE_F2:
    164 		return PREMOVE(F2, c);
    165 	case MOVE_F3:
    166 		return PREMOVE(F, c);
    167 	case MOVE_B:
    168 		return PREMOVE(B3, c);
    169 	case MOVE_B2:
    170 		return PREMOVE(B2, c);
    171 	case MOVE_B3:
    172 		return PREMOVE(B, c);
    173 	default:
    174 		LOG("move error, unknown move\n");
    175 		return ZERO_CUBE;
    176 	}
    177 }
    178 
    179 STATIC uint8_t
    180 inverse_move(uint8_t m)
    181 {
    182 	return m - 2 * (m % 3) + 2;	
    183 }
    184 
    185 /*
    186 GCC has issues when -Wstringop-overflow is used together with O3. It produces
    187 warnings like the following:
    188 
    189 In function 'invertmoves',
    190     inlined from 'solve_h48_appendsolution' at src/solvers/h48/solve.h:81:3,
    191     inlined from 'solve_h48_dfs.isra' at src/solvers/h48/solve.h:139:3:
    192 warning: writing 32 bytes into a region of size 0 [-Wstringop-overflow=]
    193   197 |                 ret[i] = inverse_move(moves[nmoves - i - 1]);
    194       |                 ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    195 In function 'solve_h48_dfs.isra':
    196 note: at offset 192 into destination object 'invertedpremoves' of size 20
    197    71 |         uint8_t invertedpremoves[MAXLEN];
    198 
    199 Clang does not give any warning.
    200 Someone else complained here: https://access.redhat.com/solutions/6755371
    201 
    202 To solve this issue temporarily, we use a lower optimization setting for
    203 this function only.
    204 
    205 TODO check if the issue is resolved
    206 */
    207 #pragma GCC push_options
    208 #pragma GCC optimize ("O2")
    209 STATIC void
    210 invertmoves(uint8_t *moves, uint8_t nmoves, uint8_t *ret)
    211 {
    212 	uint8_t i;
    213 
    214 	for (i = 0; i < nmoves; i++)
    215 		ret[i] = inverse_move(moves[nmoves - i - 1]);
    216 }
    217 #pragma GCC pop_options
    218 
    219 STATIC int
    220 readmoves(const char *buf, int max, uint8_t *ret)
    221 {
    222 	uint8_t m;
    223 	int c;
    224 
    225 	FOREACH_READMOVE(buf, m, c, max, readmoves_error,
    226 		ret[c] = m;
    227 	)
    228 
    229 	return c;
    230 
    231 readmoves_error:
    232 	LOG("readmoves error\n");
    233 	return -1;
    234 }
    235 
    236 STATIC cube_t
    237 applymoves(cube_t cube, const char *buf)
    238 {
    239 	int c;
    240 	uint8_t m;
    241 
    242 	DBG_ASSERT(isconsistent(cube), ZERO_CUBE,
    243 	    "move error: inconsistent cube\n");
    244 
    245 	FOREACH_READMOVE(buf, m, c, -1, applymoves_error,
    246 		cube = move(cube, m);
    247 	)
    248 
    249 	return cube;
    250 
    251 applymoves_error:
    252 	LOG("applymoves error\n");
    253 	return ZERO_CUBE;
    254 }
    255 
    256 STATIC cube_t
    257 frommoves(const char *buf)
    258 {
    259 	return applymoves(SOLVED_CUBE, buf);
    260 }