cubecore

A library of core functions for working with 3x3x3 Rubik's cubes
git clone https://git.tronto.net/cubecore
Download | Log | Files | Refs | README | LICENSE

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 }