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 }