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


      1 #define MOVE(M, c) compose(c, MOVE_CUBE_ ## M)
      2 #define PREMOVE(M, c) compose(MOVE_CUBE_ ## M, c)
      3 
      4 STATIC uint8_t readmove(char);
      5 STATIC int64_t readmoves(const char *, size_t n, uint8_t [n]);
      6 STATIC int64_t countmoves(const char *);
      7 STATIC uint8_t readmodifier(char);
      8 STATIC int64_t writemoves(size_t n, const uint8_t [n], size_t m, char [m]);
      9 
     10 STATIC_INLINE bool allowednextmove(uint8_t, uint8_t);
     11 STATIC bool allowedmoves(size_t n, const uint8_t [n]);
     12 
     13 STATIC_INLINE uint8_t movebase(uint8_t);
     14 STATIC_INLINE uint8_t moveaxis(uint8_t);
     15 STATIC_INLINE bool isbase(uint8_t);
     16 STATIC_INLINE bool parallel(uint8_t, uint8_t);
     17 STATIC_INLINE uint8_t moveopposite(uint8_t);
     18 STATIC_INLINE uint8_t reorient_move(uint8_t, uint8_t);
     19 STATIC_INLINE uint8_t inverse_reorient_move(uint8_t, uint8_t);
     20 STATIC_INLINE uint8_t movefollow(uint8_t);
     21 STATIC uint8_t transform_move(uint8_t, uint8_t);
     22 
     23 STATIC cube_t move(cube_t, uint8_t);
     24 STATIC oriented_cube_t move_extended(oriented_cube_t, uint8_t);
     25 STATIC cube_t premove(cube_t, uint8_t);
     26 STATIC uint8_t inverse_move(uint8_t);
     27 STATIC void sortparallel_moves(size_t n, uint8_t [n]);
     28 STATIC bool are_lastmoves_singlecw(size_t n, const uint8_t [n]);
     29 
     30 STATIC oriented_cube_t applymoves(oriented_cube_t, const char *);
     31 
     32 #define FOREACH_READMOVE(ARG_BUF, ARG_MOVE, ARG_C, ARG_MAX, \
     33 	RET_ERROR, ARG_ACTION) \
     34 	const char *VAR_B; \
     35 	uint8_t VAR_MOVE_NOMOD, VAR_MOD; \
     36 	bool VAR_IN_PARENTHESES = false; \
     37 	for (VAR_B = ARG_BUF, ARG_C = 0; *VAR_B != '\0'; VAR_B++, ARG_C++) { \
     38 		while (*VAR_B == ' ' || *VAR_B == '\t' || *VAR_B == '\n') \
     39 			VAR_B++; \
     40 		if (*VAR_B == '\0' || ARG_C == ARG_MAX) \
     41 			break; \
     42 		if (*VAR_B == '(') { \
     43 			if (VAR_IN_PARENTHESES) { \
     44 				LOG("Nested parentheses in move sequence\n"); \
     45 				return RET_ERROR; \
     46 			} \
     47 			VAR_IN_PARENTHESES = true; \
     48 			continue; \
     49 		} \
     50 		if (*VAR_B == ')') { \
     51 			if (!VAR_IN_PARENTHESES) { \
     52 				LOG("Mismatched ')' in move sequence\n"); \
     53 				return RET_ERROR; \
     54 			} \
     55 			VAR_IN_PARENTHESES = false; \
     56 			continue; \
     57 		} \
     58 		if ((VAR_MOVE_NOMOD = readmove(*VAR_B)) == UINT8_ERROR) { \
     59 			LOG("Unknown move: %c\n", *VAR_B); \
     60 			return RET_ERROR; \
     61 		} \
     62 		if (*(VAR_B+1) == 'w') { \
     63 			VAR_MOVE_NOMOD += 18; \
     64 			VAR_B++; \
     65 		} \
     66 		if ((VAR_MOD = readmodifier(*(VAR_B+1))) != 0) \
     67 			VAR_B++; \
     68 		ARG_MOVE = VAR_MOVE_NOMOD + VAR_MOD; \
     69 		ARG_ACTION \
     70 	}
     71 
     72 STATIC uint8_t
     73 readmove(char c)
     74 {
     75 	switch (c) {
     76 	case 'U':
     77 		return MOVE_U;
     78 	case 'D':
     79 		return MOVE_D;
     80 	case 'R':
     81 		return MOVE_R;
     82 	case 'L':
     83 		return MOVE_L;
     84 	case 'F':
     85 		return MOVE_F;
     86 	case 'B':
     87 		return MOVE_B;
     88 	case 'M':
     89 		return MOVE_M;
     90 	case 'S':
     91 		return MOVE_S;
     92 	case 'E':
     93 		return MOVE_E;
     94 	case 'x':
     95 		return MOVE_x;
     96 	case 'y':
     97 		return MOVE_y;
     98 	case 'z':
     99 		return MOVE_z;
    100 	default:
    101 		return UINT8_ERROR;
    102 	}
    103 }
    104 
    105 STATIC uint8_t
    106 readmodifier(char c)
    107 {
    108 	switch (c) {
    109 	case '1': /* Fallthrough */
    110 	case '2': /* Fallthrough */
    111 	case '3':
    112 		return c - '0' - 1;
    113 	case '\'':
    114 		return 2;
    115 	default:
    116 		return 0;
    117 	}
    118 }
    119 
    120 STATIC int64_t
    121 readmoves(const char *buf, size_t n, uint8_t ret[n])
    122 {
    123 // TODO: modify to accept NISS
    124 	uint8_t m;
    125 	uint64_t c;
    126 
    127 	FOREACH_READMOVE(buf, m, c, n, NISSY_ERROR_INVALID_MOVES,
    128 		ret[c] = m;
    129 	)
    130 
    131 	return (int64_t)c;
    132 }
    133 
    134 STATIC int64_t
    135 countmoves(const char *buf)
    136 {
    137 	uint8_t m;
    138 	uint64_t c;
    139 	int64_t count;
    140 
    141 	count = 0;
    142 	FOREACH_READMOVE(buf, m, c, INT_MAX, NISSY_ERROR_INVALID_MOVES,
    143 		count += m <= MOVE_Bw3 ? 1 : (m <= MOVE_E3 ? 2 : 0);
    144 	)
    145 
    146 	return count;
    147 }
    148 
    149 STATIC int64_t
    150 writemoves(
    151 	size_t nmoves,
    152 	const uint8_t m[nmoves],
    153 	size_t buf_size,
    154 	char buf[buf_size]
    155 )
    156 {
    157 	size_t i, len, w;
    158 	const char *s;
    159 
    160 	if (buf_size == 0) {
    161 		LOG("Error: cannot write moves to buffer of size 0.\n");
    162 		return NISSY_ERROR_BUFFER_SIZE;
    163 	}
    164 
    165 	for (i = 0, w = 0; i < nmoves; i++, w++) {
    166 		s = movestr[m[i]];
    167 		len = strlen(s);
    168 		if (len + w >= buf_size) {
    169 			LOG("Error: the given buffer is too small for "
    170 			     "writing the given moves.\n");
    171 			goto writemoves_error;
    172 		}
    173 		memcpy(buf+w, s, len);
    174 		w += len;
    175 		buf[w] = ' ';
    176 	}
    177 
    178 	if (w > 0) w--; /* Remove last space */
    179 	buf[w] = '\0';
    180 
    181 	return (int64_t)w;
    182 
    183 writemoves_error:
    184 	*buf = '\0';
    185 	return NISSY_ERROR_BUFFER_SIZE;
    186 }
    187 
    188 STATIC_INLINE bool
    189 allowednextmove(uint8_t m1, uint8_t m2)
    190 {
    191 // TODO: adjust allowedmask
    192 // TODO: movemask is now 64 bits
    193 	return allowedmask[movebase(m1)] & (UINT32_C(1) << m2);
    194 }
    195 
    196 STATIC bool
    197 allowedmoves(size_t n, const uint8_t m[n])
    198 {
    199 	uint8_t j;
    200 
    201 	for (j = 1; j < n; j++)
    202 		if (!allowednextmove(m[j-1], m[j]))
    203 			return false;
    204 
    205 	return true;
    206 }
    207 
    208 STATIC_INLINE uint8_t
    209 movebase(uint8_t move)
    210 {
    211 	return move / 3;
    212 }
    213 
    214 STATIC_INLINE uint8_t
    215 moveaxis(uint8_t move)
    216 {
    217 	return move / 6;
    218 }
    219 
    220 STATIC_INLINE bool
    221 isbase(uint8_t move)
    222 {
    223 	return move == 3 * movebase(move);
    224 }
    225 
    226 STATIC_INLINE bool
    227 parallel(uint8_t m1, uint8_t m2)
    228 {
    229 // TODO add unit tests
    230 //TODO fix the logic (maybe use moveaxis(movefollow)), then remove comment
    231 	return moveaxis(m1) == moveaxis(m2);
    232 }
    233 
    234 STATIC_INLINE uint8_t
    235 moveopposite(uint8_t move)
    236 {
    237 	return movebase(move) == 2 * moveaxis(move) ? move + 3 : move - 3;
    238 }
    239 
    240 STATIC_INLINE uint8_t
    241 reorient_move(uint8_t m, uint8_t or)
    242 {
    243 	return transform_move(m, orientation_trans[or]);
    244 }
    245 
    246 STATIC_INLINE uint8_t
    247 inverse_reorient_move(uint8_t m, uint8_t or)
    248 {
    249 	return transform_move(m, inverse_trans_table[orientation_trans[or]]);
    250 }
    251 
    252 /* This is currently unused, but it may turn out to be useful at some point */
    253 STATIC_INLINE uint8_t
    254 movefollow(uint8_t move)
    255 {
    256 	uint8_t b, m;
    257 
    258 	if (move <= MOVE_B3)
    259 		return move;
    260 
    261 	if (move <= MOVE_Bw3)
    262 		return move - MOVE_Uw;
    263 
    264 	b = UINT8_C(3) * (move / UINT8_C(3));
    265 	m = move - b;
    266 	switch (b) {
    267 	case MOVE_M:
    268 		return MOVE_L + m;
    269 	case MOVE_S:
    270 		return MOVE_F + m;
    271 	case MOVE_E:
    272 		return MOVE_D + m;
    273 	case MOVE_x:
    274 		return MOVE_R + m;
    275 	case MOVE_y:
    276 		return MOVE_U + m;
    277 	case MOVE_z:
    278 		return MOVE_F + m;
    279 	default:
    280 		return UINT8_ERROR;
    281 	}
    282 }
    283 
    284 STATIC cube_t
    285 move(cube_t c, uint8_t m)
    286 {
    287 	switch (m) {
    288 	case MOVE_U:
    289 		return MOVE(U, c);
    290 	case MOVE_U2:
    291 		return MOVE(U2, c);
    292 	case MOVE_U3:
    293 		return MOVE(U3, c);
    294 	case MOVE_D:
    295 		return MOVE(D, c);
    296 	case MOVE_D2:
    297 		return MOVE(D2, c);
    298 	case MOVE_D3:
    299 		return MOVE(D3, c);
    300 	case MOVE_R:
    301 		return MOVE(R, c);
    302 	case MOVE_R2:
    303 		return MOVE(R2, c);
    304 	case MOVE_R3:
    305 		return MOVE(R3, c);
    306 	case MOVE_L:
    307 		return MOVE(L, c);
    308 	case MOVE_L2:
    309 		return MOVE(L2, c);
    310 	case MOVE_L3:
    311 		return MOVE(L3, c);
    312 	case MOVE_F:
    313 		return MOVE(F, c);
    314 	case MOVE_F2:
    315 		return MOVE(F2, c);
    316 	case MOVE_F3:
    317 		return MOVE(F3, c);
    318 	case MOVE_B:
    319 		return MOVE(B, c);
    320 	case MOVE_B2:
    321 		return MOVE(B2, c);
    322 	case MOVE_B3:
    323 		return MOVE(B3, c);
    324 	default:
    325 		LOG("move error: %" PRIu8 " is not a basic move\n", m);
    326 		return ZERO_CUBE;
    327 	}
    328 }
    329 
    330 STATIC uint8_t
    331 transform_move(uint8_t m, uint8_t t)
    332 {
    333 	uint8_t a, base, modifier;
    334 
    335 	if (m > MOVE_B3) {
    336 		LOG("transform_move: attempting to transform %s, but "
    337 		    "transofrmations are only supported for basic moves\n",
    338 		    movestr[m]);
    339 		return UINT8_ERROR;
    340 	}
    341 
    342 	a = moveaxis(m);
    343 	base = trans_move_table[t][a];
    344 	if (movebase(m) != 2 * a)
    345 		base = moveopposite(base);
    346 
    347 	modifier = m % 3;
    348 	if (t >= TRANS_UFm)
    349 		modifier = 2 - modifier;
    350 
    351 	return base + modifier;
    352 }
    353 
    354 STATIC oriented_cube_t
    355 move_extended(oriented_cube_t c, uint8_t m)
    356 {
    357 	int i;
    358 	equivalent_moves_t eqm;
    359 	oriented_cube_t ret;
    360 
    361 	eqm = equivalent_moves_table[m];
    362 	ret = c;
    363 
    364 	for (i = 0; eqm.move[i] != UINT8_MAX; i++)
    365 		ret.cube = move(
    366 		    ret.cube, reorient_move(eqm.move[i], ret.orientation));
    367 
    368 	for (i = 0; eqm.rotation[i] != UINT8_MAX; i++)
    369 		ret.orientation = orientation_transition_table[
    370 		    ret.orientation][eqm.rotation[i]];
    371 
    372 	return ret;
    373 }
    374 
    375 /* Applies the INVERSE of m BEFORE the scramble corresponding to c */
    376 STATIC cube_t
    377 premove(cube_t c, uint8_t m)
    378 {
    379 	switch (m) {
    380 	case MOVE_U:
    381 		return PREMOVE(U3, c);
    382 	case MOVE_U2:
    383 		return PREMOVE(U2, c);
    384 	case MOVE_U3:
    385 		return PREMOVE(U, c);
    386 	case MOVE_D:
    387 		return PREMOVE(D3, c);
    388 	case MOVE_D2:
    389 		return PREMOVE(D2, c);
    390 	case MOVE_D3:
    391 		return PREMOVE(D, c);
    392 	case MOVE_R:
    393 		return PREMOVE(R3, c);
    394 	case MOVE_R2:
    395 		return PREMOVE(R2, c);
    396 	case MOVE_R3:
    397 		return PREMOVE(R, c);
    398 	case MOVE_L:
    399 		return PREMOVE(L3, c);
    400 	case MOVE_L2:
    401 		return PREMOVE(L2, c);
    402 	case MOVE_L3:
    403 		return PREMOVE(L, c);
    404 	case MOVE_F:
    405 		return PREMOVE(F3, c);
    406 	case MOVE_F2:
    407 		return PREMOVE(F2, c);
    408 	case MOVE_F3:
    409 		return PREMOVE(F, c);
    410 	case MOVE_B:
    411 		return PREMOVE(B3, c);
    412 	case MOVE_B2:
    413 		return PREMOVE(B2, c);
    414 	case MOVE_B3:
    415 		return PREMOVE(B, c);
    416 	default:
    417 		LOG("premove error: unknown move %" PRIu8 "\n", m);
    418 		return ZERO_CUBE;
    419 	}
    420 }
    421 
    422 STATIC uint8_t
    423 inverse_move(uint8_t m)
    424 {
    425 	return m - 2 * (m % 3) + 2;	
    426 }
    427 
    428 STATIC void
    429 sortparallel_moves(size_t n, uint8_t moves[n])
    430 {
    431 // TODO: fix for wide moves...
    432 	uint8_t i;
    433 
    434 	if (n < 2)
    435 		return;
    436 
    437 	for (i = 0; i < n-1; i++)
    438 		if (moveaxis(moves[i]) == moveaxis(moves[i+1]) &&
    439 		    movebase(moves[i]) == movebase(moves[i+1]) + 1)
    440 			SWAP(moves[i], moves[i+1]);
    441 }
    442 
    443 STATIC bool
    444 are_lastmoves_singlecw(size_t n, const uint8_t moves[n])
    445 {
    446 	bool two;
    447 
    448 	if (n == 0)
    449 		return true;
    450 
    451 	two = n > 1 && parallel(moves[n-1], moves[n-2]);
    452 
    453 	return isbase(moves[n-1]) && (!two || isbase(moves[n-2]));
    454 }
    455 
    456 STATIC oriented_cube_t
    457 applymoves(oriented_cube_t cube, const char *buf)
    458 {
    459 	int c;
    460 	uint8_t m;
    461 
    462 	DBG_ASSERT(isconsistent(cube), ZERO_ORIENTED_CUBE,
    463 	    "move error: inconsistent cube\n");
    464 
    465 	FOREACH_READMOVE(buf, m, c, -1, ZERO_ORIENTED_CUBE,
    466 		cube = move_extended(cube, m);
    467 	)
    468 
    469 	return cube;
    470 }