#define _move(M, c) compose_fast(c, _move_cube_ ## M) #define _premove(M, c) compose_fast(_move_cube_ ## M, c) #define _trans_rotation(T, c) \ compose_fast(compose_fast(_trans_cube_ ## T, c), \ _trans_cube_ ## T ## _inverse) #define _trans_mirrored(T, c) \ invertco_fast(compose_fast(compose_fast(_trans_cube_ ## T, c), \ _trans_cube_ ## T ## _inverse)) _static int permsign(uint8_t *, int); _static uint8_t readco(const char *); _static uint8_t readcp(const char *); _static uint8_t readeo(const char *); _static uint8_t readep(const char *); _static cube_t readcube_H48(const char *); _static uint8_t readpiece_LST(const char **); _static cube_t readcube_LST(const char *); _static int writepiece_LST(uint8_t, char *); _static void writecube_H48(cube_t, char *); _static void writecube_LST(cube_t, char *); _static uint8_t readmove(char); _static uint8_t readmodifier(char); _static uint8_t readtrans(const char *); _static int writemoves(uint8_t *, int, char *); _static void writetrans(uint8_t, char *); _static cube_fast_t move(cube_fast_t, uint8_t); _static cube_fast_t transform(cube_fast_t, uint8_t); cube_t solvedcube(void) { return solved; } bool isconsistent(cube_t cube) { uint8_t i, p, e, piece; bool found[12]; for (i = 0; i < 12; i++) found[i] = false; for (i = 0; i < 12; i++) { piece = cube.edge[i]; p = piece & _pbits; e = piece & _eobit; if (p >= 12) goto inconsistent_ep; if (e != 0 && e != _eobit) goto inconsistent_eo; found[p] = true; } for (i = 0; i < 12; i++) if (!found[i]) goto inconsistent_ep; for (i = 0; i < 8; i++) found[i] = false; for (i = 0; i < 8; i++) { piece = cube.corner[i]; p = piece & _pbits; e = piece & _cobits; if (p >= 8) goto inconsistent_cp; if (e != 0 && e != _ctwist_cw && e != _ctwist_ccw) goto inconsistent_co; found[p] = true; } for (i = 0; i < 8; i++) if (!found[i]) goto inconsistent_co; return true; inconsistent_ep: DBG_LOG("Inconsistent EP\n"); return false; inconsistent_cp: DBG_LOG("Inconsistent CP\n"); return false; inconsistent_eo: DBG_LOG("Inconsistent EO\n"); return false; inconsistent_co: DBG_LOG("Inconsistent CO\n"); return false; } bool issolvable(cube_t cube) { uint8_t i, eo, co, piece, edges[12], corners[8]; DBG_ASSERT(isconsistent(cube), false, "issolvable: cube is inconsistent\n"); for (i = 0; i < 12; i++) edges[i] = cube.edge[i] & _pbits; for (i = 0; i < 8; i++) corners[i] = cube.corner[i] & _pbits; if (permsign(edges, 12) != permsign(corners, 8)) goto issolvable_parity; eo = 0; for (i = 0; i < 12; i++) { piece = cube.edge[i]; eo += (piece & _eobit) >> _eoshift; } if (eo % 2 != 0) goto issolvable_eo; co = 0; for (i = 0; i < 8; i++) { piece = cube.corner[i]; co += (piece & _cobits) >> _coshift; } if (co % 3 != 0) goto issolvable_co; return true; issolvable_parity: DBG_LOG("EP and CP parities are different\n"); return false; issolvable_eo: DBG_LOG("Odd number of flipped edges\n"); return false; issolvable_co: DBG_LOG("Sum of corner orientation is not multiple of 3\n"); return false; } bool issolved(cube_t cube) { return equal(cube, solved); } bool equal(cube_t c1, cube_t c2) { int i; bool ret; ret = true; for (i = 0; i < 8; i++) ret = ret && c1.corner[i] == c2.corner[i]; for (i = 0; i < 12; i++) ret = ret && c1.edge[i] == c2.edge[i]; return ret; } bool iserror(cube_t cube) { return equal(cube, zero); } cube_t compose(cube_t c1, cube_t c2) { DBG_ASSERT(isconsistent(c1) && isconsistent(c2), zero, "compose error: inconsistent cube\n") return fasttocube(compose_fast(cubetofast(c1), cubetofast(c2))); } cube_t inverse(cube_t cube) { cube_t ret; uint8_t i, piece, orien; DBG_ASSERT(isconsistent(cube), zero, "inverse error: inconsistent cube\n"); ret = zero; for (i = 0; i < 12; i++) { piece = cube.edge[i]; orien = piece & _eobit; ret.edge[piece & _pbits] = i | orien; } for (i = 0; i < 8; i++) { piece = cube.corner[i]; orien = ((piece << 1) | (piece >> 1)) & _cobits2; ret.corner[piece & _pbits] = i | orien; } return ret; } cube_t applymoves(cube_t cube, const char *buf) { cube_fast_t fast; uint8_t r, m; const char *b; DBG_ASSERT(isconsistent(cube), zero, "move error: inconsistent cube\n"); fast = cubetofast(cube); for (b = buf; *b != '\0'; b++) { while (*b == ' ' || *b == '\t' || *b == '\n') b++; if (*b == '\0') goto applymoves_finish; if ((r = readmove(*b)) == _error) goto applymoves_error; if ((m = readmodifier(*(b+1))) != 0) b++; fast = move(fast, r + m); } applymoves_finish: return fasttocube(fast); applymoves_error: DBG_LOG("applymoves error\n"); return zero; } cube_t applytrans(cube_t cube, const char *buf) { cube_fast_t fast; uint8_t t; DBG_ASSERT(isconsistent(cube), zero, "transformation error: inconsistent cube\n"); t = readtrans(buf); fast = cubetofast(cube); fast = transform(fast, t); return fasttocube(fast); } cube_t readcube(const char *format, const char *buf) { cube_t cube; if (!strcmp(format, "H48")) { cube = readcube_H48(buf); } else if (!strcmp(format, "LST")) { cube = readcube_LST(buf); } else { DBG_LOG("Cannot read cube in the given format\n"); cube = zero; } return cube; } void writecube(const char *format, cube_t cube, char *buf) { char *errormsg; size_t len; if (!isconsistent(cube)) { errormsg = "ERROR: cannot write inconsistent cube"; goto writecube_error; } if (!strcmp(format, "H48")) { writecube_H48(cube, buf); } else if (!strcmp(format, "LST")) { writecube_LST(cube, buf); } else { errormsg = "ERROR: cannot write cube in the given format"; goto writecube_error; } return; writecube_error: DBG_LOG("writecube error, see stdout for details\n"); len = strlen(errormsg); memcpy(buf, errormsg, len); buf[len] = '\n'; buf[len+1] = '\0'; } _static int permsign(uint8_t *a, int n) { int i, j; uint8_t ret = 0; for (i = 0; i < n; i++) for (j = i+1; j < n; j++) ret += a[i] > a[j] ? 1 : 0; return ret % 2; } _static uint8_t readco(const char *str) { if (*str == '0') return 0; if (*str == '1') return _ctwist_cw; if (*str == '2') return _ctwist_ccw; DBG_LOG("Error reading CO\n"); return _error; } _static uint8_t readcp(const char *str) { uint8_t c; for (c = 0; c < 8; c++) if (!strncmp(str, cornerstr[c], 3) || !strncmp(str, cornerstralt[c], 3)) return c; DBG_LOG("Error reading CP\n"); return _error; } _static uint8_t readeo(const char *str) { if (*str == '0') return 0; if (*str == '1') return _eflip; DBG_LOG("Error reading EO\n"); return _error; } _static uint8_t readep(const char *str) { uint8_t e; for (e = 0; e < 12; e++) if (!strncmp(str, edgestr[e], 2)) return e; DBG_LOG("Error reading EP\n"); return _error; } _static cube_t readcube_H48(const char *buf) { int i; uint8_t piece, orient; cube_t ret = {0}; const char *b; b = buf; for (i = 0; i < 12; i++) { while (*b == ' ' || *b == '\t' || *b == '\n') b++; if ((piece = readep(b)) == _error) return zero; b += 2; if ((orient = readeo(b)) == _error) return zero; b++; ret.edge[i] = piece | orient; } for (i = 0; i < 8; i++) { while (*b == ' ' || *b == '\t' || *b == '\n') b++; if ((piece = readcp(b)) == _error) return zero; b += 3; if ((orient = readco(b)) == _error) return zero; b++; ret.corner[i] = piece | orient; } return ret; } _static uint8_t readpiece_LST(const char **b) { uint8_t ret; bool read; while (**b == ',' || **b == ' ' || **b == '\t' || **b == '\n') (*b)++; for (ret = 0, read = false; **b >= '0' && **b <= '9'; (*b)++) { read = true; ret = ret * 10 + (**b) - '0'; } return read ? ret : _error; } _static cube_t readcube_LST(const char *buf) { int i; cube_t ret = {0}; for (i = 0; i < 8; i++) ret.corner[i] = readpiece_LST(&buf); for (i = 0; i < 12; i++) ret.edge[i] = readpiece_LST(&buf); return ret; } _static int writepiece_LST(uint8_t piece, char *buf) { char digits[3]; int i, len = 0; while (piece != 0) { digits[len++] = (piece % 10) + '0'; piece /= 10; } if (len == 0) digits[len++] = '0'; for (i = 0; i < len; i++) buf[i] = digits[len-i-1]; buf[len] = ','; buf[len+1] = ' '; return len+2; } _static void writecube_H48(cube_t cube, char *buf) { uint8_t piece, perm, orient; int i; for (i = 0; i < 12; i++) { piece = cube.edge[i]; perm = piece & _pbits; orient = (piece & _eobit) >> _eoshift; buf[4*i ] = edgestr[perm][0]; buf[4*i + 1] = edgestr[perm][1]; buf[4*i + 2] = orient + '0'; buf[4*i + 3] = ' '; } for (i = 0; i < 8; i++) { piece = cube.corner[i]; perm = piece & _pbits; orient = (piece & _cobits) >> _coshift; buf[48 + 5*i ] = cornerstr[perm][0]; buf[48 + 5*i + 1] = cornerstr[perm][1]; buf[48 + 5*i + 2] = cornerstr[perm][2]; buf[48 + 5*i + 3] = orient + '0'; buf[48 + 5*i + 4] = ' '; } buf[48+39] = '\0'; } _static void writecube_LST(cube_t cube, char *buf) { int i, ptr; uint8_t piece; ptr = 0; for (i = 0; i < 8; i++) { piece = cube.corner[i]; ptr += writepiece_LST(piece, buf + ptr); } for (i = 0; i < 12; i++) { piece = cube.edge[i]; ptr += writepiece_LST(piece, buf + ptr); } *(buf+ptr-2) = 0; } _static uint8_t readmove(char c) { switch (c) { case 'U': return _move_U; case 'D': return _move_D; case 'R': return _move_R; case 'L': return _move_L; case 'F': return _move_F; case 'B': return _move_B; default: return _error; } } _static uint8_t readmodifier(char c) { switch (c) { case '1': /* Fallthrough */ case '2': /* Fallthrough */ case '3': return c - '0' - 1; case '\'': return 2; default: return 0; } } _static uint8_t readtrans(const char *buf) { uint8_t t; for (t = 0; t < 48; t++) if (!strncmp(buf, transstr[t], 11)) return t; DBG_LOG("readtrans error\n"); return _error; } _static int writemoves(uint8_t *m, int n, char *buf) { int i; size_t len; const char *s; char *b; for (i = 0, b = buf; i < n; i++, b++) { s = movestr[m[i]]; len = strlen(s); memcpy(b, s, len); b += len; *b = ' '; } if (b != buf) b--; /* Remove last space */ *b = '\0'; return b - buf; } _static void writetrans(uint8_t t, char *buf) { if (t >= 48) memcpy(buf, "error trans", 11); else memcpy(buf, transstr[t], 11); buf[11] = '\0'; } _static cube_fast_t move(cube_fast_t c, uint8_t m) { switch (m) { case _move_U: return _move(U, c); case _move_U2: return _move(U2, c); case _move_U3: return _move(U3, c); case _move_D: return _move(D, c); case _move_D2: return _move(D2, c); case _move_D3: return _move(D3, c); case _move_R: return _move(R, c); case _move_R2: return _move(R2, c); case _move_R3: return _move(R3, c); case _move_L: return _move(L, c); case _move_L2: return _move(L2, c); case _move_L3: return _move(L3, c); case _move_F: return _move(F, c); case _move_F2: return _move(F2, c); case _move_F3: return _move(F3, c); case _move_B: return _move(B, c); case _move_B2: return _move(B2, c); case _move_B3: return _move(B3, c); default: DBG_LOG("move error, unknown move\n"); return zero_fast; } } _static cube_fast_t transform(cube_fast_t c, uint8_t t) { switch (t) { case _trans_UFr: return _trans_rotation(UFr, c); case _trans_ULr: return _trans_rotation(ULr, c); case _trans_UBr: return _trans_rotation(UBr, c); case _trans_URr: return _trans_rotation(URr, c); case _trans_DFr: return _trans_rotation(DFr, c); case _trans_DLr: return _trans_rotation(DLr, c); case _trans_DBr: return _trans_rotation(DBr, c); case _trans_DRr: return _trans_rotation(DRr, c); case _trans_RUr: return _trans_rotation(RUr, c); case _trans_RFr: return _trans_rotation(RFr, c); case _trans_RDr: return _trans_rotation(RDr, c); case _trans_RBr: return _trans_rotation(RBr, c); case _trans_LUr: return _trans_rotation(LUr, c); case _trans_LFr: return _trans_rotation(LFr, c); case _trans_LDr: return _trans_rotation(LDr, c); case _trans_LBr: return _trans_rotation(LBr, c); case _trans_FUr: return _trans_rotation(FUr, c); case _trans_FRr: return _trans_rotation(FRr, c); case _trans_FDr: return _trans_rotation(FDr, c); case _trans_FLr: return _trans_rotation(FLr, c); case _trans_BUr: return _trans_rotation(BUr, c); case _trans_BRr: return _trans_rotation(BRr, c); case _trans_BDr: return _trans_rotation(BDr, c); case _trans_BLr: return _trans_rotation(BLr, c); case _trans_UFm: return _trans_mirrored(UFm, c); case _trans_ULm: return _trans_mirrored(ULm, c); case _trans_UBm: return _trans_mirrored(UBm, c); case _trans_URm: return _trans_mirrored(URm, c); case _trans_DFm: return _trans_mirrored(DFm, c); case _trans_DLm: return _trans_mirrored(DLm, c); case _trans_DBm: return _trans_mirrored(DBm, c); case _trans_DRm: return _trans_mirrored(DRm, c); case _trans_RUm: return _trans_mirrored(RUm, c); case _trans_RFm: return _trans_mirrored(RFm, c); case _trans_RDm: return _trans_mirrored(RDm, c); case _trans_RBm: return _trans_mirrored(RBm, c); case _trans_LUm: return _trans_mirrored(LUm, c); case _trans_LFm: return _trans_mirrored(LFm, c); case _trans_LDm: return _trans_mirrored(LDm, c); case _trans_LBm: return _trans_mirrored(LBm, c); case _trans_FUm: return _trans_mirrored(FUm, c); case _trans_FRm: return _trans_mirrored(FRm, c); case _trans_FDm: return _trans_mirrored(FDm, c); case _trans_FLm: return _trans_mirrored(FLm, c); case _trans_BUm: return _trans_mirrored(BUm, c); case _trans_BRm: return _trans_mirrored(BRm, c); case _trans_BDm: return _trans_mirrored(BDm, c); case _trans_BLm: return _trans_mirrored(BLm, c); default: DBG_LOG("transform error, unknown transformation\n"); return zero_fast; } }