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


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