cube.c (11255B)
1 #include <inttypes.h> 2 #include <stdbool.h> 3 #include <string.h> 4 5 #include "cube.h" 6 7 #ifdef DEBUG 8 9 #include <stdio.h> 10 #define _static 11 #define _static_inline 12 #define DBG_LOG(...) fprintf(stderr, __VA_ARGS__) 13 #define DBG_WARN(condition, ...) if (!(condition)) DBG_LOG(__VA_ARGS__); 14 #define DBG_ASSERT(condition, retval, ...) \ 15 if (!(condition)) { \ 16 DBG_LOG(__VA_ARGS__); \ 17 return retval; \ 18 } 19 20 #else 21 22 #define _static static 23 #define _static_inline static inline 24 #define DBG_LOG(...) 25 #define DBG_WARN(condition, ...) 26 #define DBG_ASSERT(condition, retval, ...) 27 28 #endif 29 30 #include "constants.h" 31 32 _static_inline cube_t invertco(cube_t); 33 _static int permsign(uint8_t *, int); 34 _static uint8_t readco(const char *); 35 _static uint8_t readcp(const char *); 36 _static uint8_t readeo(const char *); 37 _static uint8_t readep(const char *); 38 _static cube_t read_H48(const char *); 39 _static uint8_t readpiece_LST(const char **); 40 _static cube_t read_LST(const char *); 41 _static int writepiece_LST(uint8_t, char *); 42 _static void write_H48(cube_t, char *); 43 _static void write_LST(cube_t, char *); 44 _static uint8_t readmove(char); 45 _static uint8_t readmodifier(char); 46 47 _static uint8_t 48 readco(const char *str) 49 { 50 if (*str == '0') 51 return 0; 52 if (*str == '1') 53 return _ctwist_cw; 54 if (*str == '2') 55 return _ctwist_ccw; 56 57 DBG_LOG("Error reading CO\n"); 58 return _error; 59 } 60 61 _static uint8_t 62 readcp(const char *str) 63 { 64 uint8_t c; 65 66 for (c = 0; c < 8; c++) 67 if (!strncmp(str, cornerstr[c], 3) || 68 !strncmp(str, cornerstralt[c], 3)) 69 return c; 70 71 DBG_LOG("Error reading CP\n"); 72 return _error; 73 } 74 75 _static uint8_t 76 readeo(const char *str) 77 { 78 if (*str == '0') 79 return 0; 80 if (*str == '1') 81 return _eflip; 82 83 DBG_LOG("Error reading EO\n"); 84 return _error; 85 } 86 87 _static uint8_t 88 readep(const char *str) 89 { 90 uint8_t e; 91 92 for (e = 0; e < 12; e++) 93 if (!strncmp(str, edgestr[e], 2)) 94 return e; 95 96 DBG_LOG("Error reading EP\n"); 97 return _error; 98 } 99 100 _static cube_t 101 read_H48(const char *buf) 102 { 103 int i; 104 uint8_t piece, orient; 105 cube_t ret = {0}; 106 const char *b; 107 108 b = buf; 109 110 for (i = 0; i < 12; i++) { 111 while (*b == ' ' || *b == '\t' || *b == '\n') 112 b++; 113 if ((piece = readep(b)) == _error) 114 return zero; 115 b += 2; 116 if ((orient = readeo(b)) == _error) 117 return zero; 118 b++; 119 ret.edge[i] = piece | orient; 120 } 121 for (i = 0; i < 8; i++) { 122 while (*b == ' ' || *b == '\t' || *b == '\n') 123 b++; 124 if ((piece = readcp(b)) == _error) 125 return zero; 126 b += 3; 127 if ((orient = readco(b)) == _error) 128 return zero; 129 b++; 130 ret.corner[i] = piece | orient; 131 } 132 133 return ret; 134 } 135 136 _static uint8_t 137 readpiece_LST(const char **b) 138 { 139 uint8_t ret; 140 bool read; 141 142 while (**b == ',' || **b == ' ' || **b == '\t' || **b == '\n') 143 (*b)++; 144 145 for (ret = 0, read = false; **b >= '0' && **b <= '9'; (*b)++) { 146 read = true; 147 ret = ret * 10 + (**b) - '0'; 148 } 149 150 return read ? ret : _error; 151 } 152 153 _static cube_t 154 read_LST(const char *buf) 155 { 156 int i; 157 cube_t ret = {0}; 158 159 for (i = 0; i < 8; i++) 160 ret.corner[i] = readpiece_LST(&buf); 161 162 for (i = 0; i < 12; i++) 163 ret.edge[i] = readpiece_LST(&buf); 164 165 return ret; 166 } 167 168 _static int 169 writepiece_LST(uint8_t piece, char *buf) 170 { 171 char digits[3]; 172 int i, len = 0; 173 174 while (piece != 0) { 175 digits[len++] = (piece % 10) + '0'; 176 piece /= 10; 177 } 178 179 if (len == 0) 180 digits[len++] = '0'; 181 182 for (i = 0; i < len; i++) 183 buf[i] = digits[len-i-1]; 184 185 buf[len] = ','; 186 buf[len+1] = ' '; 187 188 return len+2; 189 } 190 191 _static void 192 write_H48(cube_t cube, char *buf) 193 { 194 uint8_t piece, perm, orient; 195 int i; 196 197 for (i = 0; i < 12; i++) { 198 piece = cube.edge[i]; 199 perm = piece & _pbits; 200 orient = (piece & _eobit) >> _eoshift; 201 buf[4*i ] = edgestr[perm][0]; 202 buf[4*i + 1] = edgestr[perm][1]; 203 buf[4*i + 2] = orient + '0'; 204 buf[4*i + 3] = ' '; 205 } 206 for (i = 0; i < 8; i++) { 207 piece = cube.corner[i]; 208 perm = piece & _pbits; 209 orient = (piece & _cobits) >> _coshift; 210 buf[48 + 5*i ] = cornerstr[perm][0]; 211 buf[48 + 5*i + 1] = cornerstr[perm][1]; 212 buf[48 + 5*i + 2] = cornerstr[perm][2]; 213 buf[48 + 5*i + 3] = orient + '0'; 214 buf[48 + 5*i + 4] = ' '; 215 } 216 217 buf[48+39] = '\0'; 218 } 219 220 _static void 221 write_LST(cube_t cube, char *buf) 222 { 223 int i, ptr; 224 uint8_t piece; 225 226 ptr = 0; 227 228 for (i = 0; i < 8; i++) { 229 piece = cube.corner[i]; 230 ptr += writepiece_LST(piece, buf + ptr); 231 } 232 233 for (i = 0; i < 12; i++) { 234 piece = cube.edge[i]; 235 ptr += writepiece_LST(piece, buf + ptr); 236 } 237 238 *(buf+ptr-2) = 0; 239 } 240 241 _static uint8_t 242 readmove(char c) 243 { 244 switch (c) { 245 case 'U': 246 return U; 247 case 'D': 248 return D; 249 case 'R': 250 return R; 251 case 'L': 252 return L; 253 case 'F': 254 return F; 255 case 'B': 256 return B; 257 default: 258 return _error; 259 } 260 } 261 262 _static uint8_t 263 readmodifier(char c) 264 { 265 switch (c) { 266 case '1': /* Fallthrough */ 267 case '2': /* Fallthrough */ 268 case '3': 269 return c - '0' - 1; 270 case '\'': 271 return 2; 272 default: 273 return 0; 274 } 275 } 276 277 _static_inline cube_t 278 invertco(cube_t c) 279 { 280 uint8_t i, piece, orien; 281 cube_t ret; 282 283 ret = c; 284 for (i = 0; i < 8; i++) { 285 piece = c.corner[i]; 286 orien = ((piece << 1) | (piece >> 1)) & _cobits2; 287 ret.corner[i] = (piece & _pbits) | orien; 288 } 289 290 return ret; 291 } 292 293 _static int 294 permsign(uint8_t *a, int n) 295 { 296 int i, j; 297 uint8_t ret = 0; 298 299 for (i = 0; i < n; i++) 300 for (j = i+1; j < n; j++) 301 ret += a[i] > a[j] ? 1 : 0; 302 303 return ret % 2; 304 } 305 306 cube_t 307 cube_new(void) 308 { 309 return solved; 310 } 311 312 cube_t 313 cube_clone(cube_t c) 314 { 315 cube_t ret; 316 317 memcpy(&ret, &c, sizeof(cube_t)); 318 319 return ret; 320 } 321 322 bool 323 cube_consistent(cube_t cube) 324 { 325 uint8_t i, p, e, piece; 326 bool found[12]; 327 328 for (i = 0; i < 12; i++) 329 found[i] = false; 330 for (i = 0; i < 12; i++) { 331 piece = cube.edge[i]; 332 p = piece & _pbits; 333 e = piece & _eobit; 334 if (p >= 12) 335 goto inconsistent_ep; 336 if (e != 0 && e != _eobit) 337 goto inconsistent_eo; 338 found[p] = true; 339 } 340 for (i = 0; i < 12; i++) 341 if (!found[i]) 342 goto inconsistent_ep; 343 344 for (i = 0; i < 8; i++) 345 found[i] = false; 346 for (i = 0; i < 8; i++) { 347 piece = cube.corner[i]; 348 p = piece & _pbits; 349 e = piece & _cobits; 350 if (p >= 8) 351 goto inconsistent_cp; 352 if (e != 0 && e != _ctwist_cw && e != _ctwist_ccw) 353 goto inconsistent_co; 354 found[p] = true; 355 } 356 for (i = 0; i < 8; i++) 357 if (!found[i]) 358 goto inconsistent_co; 359 360 return true; 361 362 inconsistent_ep: 363 DBG_LOG("Inconsistent EP\n"); 364 return false; 365 inconsistent_cp: 366 DBG_LOG("Inconsistent CP\n"); 367 return false; 368 inconsistent_eo: 369 DBG_LOG("Inconsistent EO\n"); 370 return false; 371 inconsistent_co: 372 DBG_LOG("Inconsistent CO\n"); 373 return false; 374 } 375 376 bool 377 cube_solvable(cube_t cube) 378 { 379 uint8_t i, eo, co, piece, edges[12], corners[8]; 380 381 DBG_ASSERT(cube_consistent(cube), false, 382 "cube_solvable: cube is inconsistent\n"); 383 384 for (i = 0; i < 12; i++) 385 edges[i] = cube.edge[i] & _pbits; 386 for (i = 0; i < 8; i++) 387 corners[i] = cube.corner[i] & _pbits; 388 389 if (permsign(edges, 12) != permsign(corners, 8)) 390 goto solvable_parity; 391 392 eo = 0; 393 for (i = 0; i < 12; i++) { 394 piece = cube.edge[i]; 395 eo += (piece & _eobit) >> _eoshift; 396 } 397 if (eo % 2 != 0) 398 goto solvable_eo; 399 400 co = 0; 401 for (i = 0; i < 8; i++) { 402 piece = cube.corner[i]; 403 co += (piece & _cobits) >> _coshift; 404 } 405 if (co % 3 != 0) 406 goto solvable_co; 407 408 return true; 409 410 solvable_parity: 411 DBG_LOG("EP and CP parities are different\n"); 412 return false; 413 solvable_eo: 414 DBG_LOG("Odd number of flipped edges\n"); 415 return false; 416 solvable_co: 417 DBG_LOG("Sum of corner orientation is not multiple of 3\n"); 418 return false; 419 } 420 421 bool 422 cube_solved(cube_t cube) 423 { 424 return cube_equal(cube, solved); 425 } 426 427 bool 428 cube_equal(cube_t c1, cube_t c2) 429 { 430 int i; 431 bool ret; 432 433 ret = true; 434 for (i = 0; i < 8; i++) 435 ret = ret && c1.corner[i] == c2.corner[i]; 436 for (i = 0; i < 12; i++) 437 ret = ret && c1.edge[i] == c2.edge[i]; 438 439 return ret; 440 } 441 442 bool 443 cube_error(cube_t cube) 444 { 445 return cube_equal(cube, zero); 446 } 447 448 cube_t 449 cube_compose(cube_t c1, cube_t c2) 450 { 451 cube_t ret; 452 uint8_t i, piece1, piece2, p, orien, aux, auy; 453 454 DBG_ASSERT(cube_consistent(c1) && cube_consistent(c2), 455 zero, "cube_compose error: inconsistent cube\n") 456 457 ret = zero; 458 459 for (i = 0; i < 12; i++) { 460 piece2 = c2.edge[i]; 461 p = piece2 & _pbits; 462 piece1 = c1.edge[p]; 463 orien = (piece2 ^ piece1) & _eobit; 464 ret.edge[i] = (piece1 & _pbits) | orien; 465 } 466 467 for (i = 0; i < 8; i++) { 468 piece2 = c2.corner[i]; 469 p = piece2 & _pbits; 470 piece1 = c1.corner[p]; 471 aux = (piece2 & _cobits) + (piece1 & _cobits); 472 auy = (aux + _ctwist_cw) >> 2U; 473 orien = (aux + auy) & _cobits2; 474 ret.corner[i] = (piece1 & _pbits) | orien; 475 } 476 477 return ret; 478 } 479 480 cube_t 481 cube_inverse(cube_t cube) 482 { 483 cube_t ret; 484 uint8_t i, piece, orien; 485 486 DBG_ASSERT(cube_consistent(cube), zero, 487 "cube_inverse error: inconsistent cube\n"); 488 489 ret = zero; 490 491 for (i = 0; i < 12; i++) { 492 piece = cube.edge[i]; 493 orien = piece & _eobit; 494 ret.edge[piece & _pbits] = i | orien; 495 } 496 497 for (i = 0; i < 8; i++) { 498 piece = cube.corner[i]; 499 orien = ((piece << 1) | (piece >> 1)) & _cobits2; 500 ret.corner[piece & _pbits] = i | orien; 501 } 502 503 return ret; 504 } 505 506 cube_t 507 cube_move(cube_t c, move_t m) 508 { 509 return cube_compose(c, move_table[m]); 510 } 511 512 cube_t 513 cube_transform(cube_t c, trans_t t) 514 { 515 cube_t tcube, tinv; 516 517 tcube = trans_table[t][NORMAL]; 518 tinv = trans_table[t][INVERSE]; 519 520 return t < 24 ? 521 cube_compose(cube_compose(tcube, c), tinv) : 522 invertco(cube_compose(cube_compose(tcube, c), tinv)); 523 } 524 525 int64_t 526 cube_coord_co(cube_t c) 527 { 528 int i, p; 529 int64_t ret; 530 531 for (ret = 0, i = 0, p = 1; i < 7; i++, p *= 3) 532 ret += p * (c.corner[i] >> _coshift); 533 534 return ret; 535 } 536 537 int64_t 538 cube_coord_eo(cube_t c) 539 { 540 int i, p; 541 int64_t ret; 542 543 for (ret = 0, i = 1, p = 1; i < 12; i++, p *= 2) 544 ret += p * (c.edge[i] >> _eoshift); 545 546 return ret; 547 } 548 549 cube_t 550 cube_read(const char *format, const char *buf) 551 { 552 cube_t cube; 553 554 if (!strcmp(format, "H48")) { 555 cube = read_H48(buf); 556 } else if (!strcmp(format, "LST")) { 557 cube = read_LST(buf); 558 } else { 559 DBG_LOG("Cannot read cube in the given format\n"); 560 cube = zero; 561 } 562 563 return cube; 564 } 565 566 void 567 cube_write(const char *format, cube_t cube, char *buf) 568 { 569 char *errormsg; 570 size_t len; 571 572 if (!cube_consistent(cube)) { 573 errormsg = "ERROR: cannot write inconsistent cube"; 574 goto write_error; 575 } 576 577 if (!strcmp(format, "H48")) { 578 write_H48(cube, buf); 579 } else if (!strcmp(format, "LST")) { 580 write_LST(cube, buf); 581 } else { 582 errormsg = "ERROR: cannot write cube in the given format"; 583 goto write_error; 584 } 585 586 return; 587 588 write_error: 589 DBG_LOG("cube_write error, see stdout for details\n"); 590 len = strlen(errormsg); 591 memcpy(buf, errormsg, len); 592 buf[len] = '\n'; 593 buf[len+1] = '\0'; 594 } 595 596 int 597 cube_readmoves(const char *buf, move_t *ret) 598 { 599 int n; 600 move_t r, m; 601 const char *b; 602 603 for (n = 0, b = buf; *b != '\0'; b++) { 604 while (*b == ' ' || *b == '\t' || *b == '\n') 605 b++; 606 if (*b == '\0') 607 goto applymoves_finish; 608 if ((r = readmove(*b)) == _error) 609 goto applymoves_error; 610 if ((m = readmodifier(*(b+1))) != 0) 611 b++; 612 ret[n++] = m + r; 613 } 614 615 applymoves_finish: 616 return n; 617 618 applymoves_error: 619 DBG_LOG("applymoves error\n"); 620 return -1; 621 } 622 623 trans_t 624 cube_readtrans(const char *buf) 625 { 626 trans_t t; 627 628 for (t = 0; t < 48; t++) 629 if (!strncmp(buf, transstr[t], 11)) 630 return t; 631 632 return -1; 633 } 634 635 char * 636 cube_movestr(move_t m) 637 { 638 return movestr[m]; 639 } 640 641 char * 642 cube_transstr(trans_t t) 643 { 644 return transstr[t]; 645 } 646 647 move_t 648 cube_inversemove(move_t m) 649 { 650 return m - 2*(m%3) + 2; 651 } 652 653 trans_t 654 cube_inversetrans(trans_t t) 655 { 656 return inverse_trans_table[t]; 657 }