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 (5451B)


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