aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cube.c888
-rw-r--r--src/cube.h27
-rw-r--r--src/cube.sync-conflict-20230524-181836-JOKKFPA.c614
-rw-r--r--src/cube.sync-conflict-20230524-181836-JOKKFPA.h27
-rw-r--r--src/cube.sync-conflict-20230524-182143-JOKKFPA.c614
-rw-r--r--src/cube.sync-conflict-20230524-182143-JOKKFPA.h27
-rw-r--r--src/cube.sync-conflict-20230524-182146-JOKKFPA.c614
-rw-r--r--src/cube.sync-conflict-20230524-182146-JOKKFPA.h27
8 files changed, 2838 insertions, 0 deletions
diff --git a/src/cube.c b/src/cube.c
new file mode 100644
index 0000000..4372b5e
--- /dev/null
+++ b/src/cube.c
@@ -0,0 +1,888 @@
1/*
2# Cube representation, moves, transformations and (tentatively) indexing
3
4## Textual description
5
6The functions readcube() and writecube() use the following format.
7Each edge is represented by two letters denoting the sides it belongs to
8and one number denoting its orientation (0 oriented, 1 mis-oriented).
9Similarly, each corner is represented by three letters and a number
10(0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise).
11Edge orientation is relative to the F / B axis, corner orientation is
12relative to the U / D axis.
13
14The correct order of the pieces is the same as that defined in the
15section "Internal cube representation", except that pieces are read
16left-to-right. Pieces are divided by slices, so the ordering is not the
17most intuitive, but it is more convenient for the internal representation.
18
19Whitespaces between pieces are ignored when reading the cube, and a
20single whitespace character is added between pieces when writing.
21
22For example, the solved cube looks like this:
23
24UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 \ (no newline)
25UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0
26
27The cube after the moves R'U'F looks like this:
28
29FL1 BR0 DB0 UR1 UF0 UB0 DL0 FR0 UL1 DF1 BL0 DR0 \ (no newline)
30UBL1 DBR1 UFR2 DFR2 DFL2 UBL2 UFL2 DBL0
31
32More formats might be supported in the future.
33
34## Internal cube representation
35
36The cube_t data structure implemented in this file is designed to
37efficiently perform common operations on a 3x3x3 Rubik's cube when
38solving it with an iterative-deepening DFS search. It is not the most
39general, complete, easy to read or compact one. Since the cube can
40be trivially reoriented before the search, we only encode permutations
41of the cube that keep the center pieces in a fixed position (that is,
42we do not encode the position of the centers).
43
44The cube state is encoded in two 64-bit integers, one for edges and one
45for centers. We explain how edges are encoded first, and the highlight
46the few differences with corners afterwards.
47
48For encoding edges, only the 60 least-significant bits are used. Each
49edge described by 5 bits. The position of a 5-bit block in the 64-bit
50integer determine the position of the edge piece in the cube, according
51to the following table (least-significant bits on the right):
52
5355-59 50-54 45-49 40-44 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
54 BR BL FL FR DR DL UL UR DF DB UB UF
55ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee
56
57For each edge, the 4 least-significant bits ('ssee' in the table)
58determine the piece. The two bits marked with 'ss' determine the internal
59slice the piece belongs to, i.e. they are either '00' for M, '01' for
60S or '10' for E. The other two bits (marked with 'ee') determine the
61actual edge piece among the 4 in the same slice, and they are assigned
62somewhat arbitarily. Using this representation and the ordering defined
63in the table above, the edges are correctly permuted when these 4 bits
64for each represent the numbers 0 to 11 in the correct order.
65
66The last bit determines the orientation. The orientation of an edge
67depends on its position, and it is defined being 0 if the edge can be
68moved to its place in the solved orientation by permutations in the
69subgroup <U, D, R, L, F2, B2>.
70
71Corners are encoded in the 48 least-significant bits, and are described
72by 6 bits each, their position being defined by the following table:
73
74 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
75 DBL DFL UBR UFL DBR DFL UBL UFR
76oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc
77
78The bit marked with an 'x' describes the axis the corner belongs to.
79The 0 axis consists of the corners UFR, UBL, DFL and DBR, and the other
80four corners form the axis marked with 1. Then two bits are needed to
81identify the corner among the four of the same axis. The last three bits
82determine the orientation, where one corner is defined to be oriented
83(marked with '000') if its top or bottom sticker faces the top or bottom
84side. A corner a clockwise turn away from being oriented, thus requiring
85a counter-clockwise turn to be oriented correctly, is marked with '001',
86and a corner a counter-clockwise turn away is marked with '010'. The most
87significant bit is not used to determine the corner orientation, but it
88must always be set to '0' to simplify the moving operations (see below).
89
90## Basic moves
91
92The 18 basic moves of the cube could be performed by applying a suitable
93general permutation (see below), but they have instead been manually
94implemented with a few simple operations each, to improve performance.
95
96For each move we first permute the pieces. This amounts to shifting
97around 4 blocks of bits for edges and 4 for corners. Since in some cases
98adjacent pieces on the cube are also adjacent in the bit representation we
99use, we can save some operations by shifting multiple blocks together.
100
101There are some moves that change the orientation of the pieces. Namely,
102the moves F, F', B and B' change the orientation of the edges and those
103moves as well as R, R', L and L' change the orientation of the corners.
104Edge orientation is easy to address: we simply xor the edge representation
105by a bit mask with zeroes everywhere except for the 4 edges that need
106to be flipped (i.e. the ones on the twisted face).
107
108Corner orientation is harder to reproduce efficiently working only with
109bitwise operations, as it involves performing operations modulo 3.
110However, with the help of the extra bit we reserved, we are able to
111do this using only two additions and 3 bitwise operations, without
112any multiplication, division or modulo operation. The trick is
113to use the following formula to sum two numbers x, y in {0,1,2}:
114
115 ((x+y) + (x+y+1)/4) % 4
116
117The thrid bit is needed because x+y and x+y+1 can exceed 3. We can
118apply this operation to multiple pairs of bits representing ternary
119digits at once using binary operations. See the function coapply()
120below for the details.
121
122## Inverting the cube
123
124TODO
125
126## Transformations (conjugations by full-cube rotations)
127
128TODO
129
130## Indexing (tentative)
131
132TODO - subgroup description etc
133
134*/
135
136#include <stdbool.h>
137#include <stdint.h>
138#include <string.h>
139
140#ifdef DEBUG
141#include <stdio.h>
142#endif
143
144#include "cube.h"
145
146#define _error 0xFFFFFFFF
147
148#define _eoblock 0x10ULL /* 10000 */
149#define _epblock 0x0FULL /* 01111 */
150#define _eblock 0x1FULL /* 11111 */
151#define _eblock2 0x3FFULL /* 1111111111 */
152
153#define _coblock 0x18ULL /* 011000 */
154#define _cpblock 0x07ULL /* 000111 */
155#define _cblock 0x3FULL /* 111111 */
156#define _cblock2 0xFFFULL /* 111111111111 */
157
158#define _eomask 0x842108421084210ULL /* 10000 repeated 12 times */
159#define _comask 0x618618618618ULL /* 011000 repeated 8 times */
160#define _coonemask 0x208208208208ULL /* 001000 repeated 8 times */
161#define _coextramask 0x820820820820ULL /* 100000 repeated 8 times */
162
163#define _emask_u (_eblock2 | _eblock2 << 20ULL)
164#define _emask_d (_eblock2 << 10ULL | _eblock2 << 30ULL)
165#define _emask_r (_eblock << 20ULL | _eblock2 << 35ULL | _eblock << 55ULL)
166#define _emask_l (_eblock2 << 45ULL | _eblock2 << 25ULL)
167#define _emask_f (_eblock | _eblock << 15ULL | _eblock2 << 40ULL)
168#define _emask_b (_eblock2 << 5ULL | _eblock2 << 50ULL)
169
170#define _cmask_u (_cblock2 | _cblock2 << 24ULL)
171#define _cmask_d (_cblock2 << 12ULL | _cblock2 << 36ULL)
172#define _cmask_r (_cblock | _cblock << 18ULL | _cblock2 << 30ULL)
173#define _cmask_l (_cblock2 << 6ULL | _cblock << 24ULL | _cblock << 42ULL)
174#define _cmask_f (_cblock | _cblock << 12ULL | _cblock << 24ULL | _cblock << 36ULL)
175#define _cmask_b (_cblock << 6ULL | _cblock << 18ULL | _cblock << 30ULL | _cblock << 42ULL)
176
177#define _eomask_f (1ULL << 4ULL | 1ULL << 19ULL | 1ULL << 44ULL | 1ULL << 49ULL)
178#define _eomask_b (1ULL << 9ULL | 1ULL << 14ULL | 1ULL << 54ULL | 1ULL << 59ULL)
179
180#define _comask_r (2ULL << 3ULL | 1ULL << 33ULL | 2ULL << 21ULL | 1ULL << 39ULL)
181#define _comask_l (1ULL << 27ULL | 2ULL << 9ULL | 2ULL << 15ULL | 1ULL << 45ULL)
182#define _comask_f (1ULL << 3ULL | 1ULL << 15ULL | 2ULL << 27ULL | 2ULL << 39ULL)
183#define _comask_b (1ULL << 9ULL | 1ULL << 21ULL | 2ULL << 33ULL | 2ULL << 45ULL)
184
185static inline uint64_t coapply(uint64_t, uint64_t);
186static uint64_t permsign(uint64_t *, int);
187static uint64_t readep(char *);
188static uint64_t readeo(char *);
189static uint64_t readcp(char *);
190static uint64_t readco(char *);
191static uint64_t readmove(char);
192static uint64_t readmodifier(char);
193
194static char *edgestr[] = {
195 "UF", "UB", "DB", "DF",
196 "UR", "UL", "DL", "DR",
197 "FR", "FL", "BL", "BR"
198};
199static char *cornerstr[] = {
200 "UFR", "UBL", "DFL", "DBR",
201 "UFL", "UBR", "DFR", "DBL"
202};
203static char *cornerstralt[] = {
204 "URF", "ULB", "DLF", "DRB",
205 "ULF", "URB", "DRF", "DLB"
206};
207static char *movestr[] = {
208 [U] = "U", [U2] = "U2", [U3] = "U'",
209 [D] = "D", [D2] = "D2", [D3] = "D'",
210 [R] = "R", [R2] = "R2", [R3] = "R'",
211 [L] = "L", [L2] = "L2", [L3] = "L'",
212 [F] = "F", [F2] = "F2", [F3] = "F'",
213 [B] = "B", [B2] = "B2", [B3] = "B'",
214};
215
216cube_t solvedcube = { .e = 0x5A928398A418820ULL, .c = 0x1C61440C2040ULL };
217cube_t errorcube = { .e = _error, .c = _error };
218
219
220static uint64_t
221permsign(uint64_t *a, int n)
222{
223 int i, j;
224 uint64_t ret;
225
226 ret = 0;
227
228 for (i = 0; i < n; i++)
229 for (j = i+1; j < n; j++)
230 ret += a[i] > a[j] ? 1 : 0;
231
232 return ret % 2;
233}
234
235bool
236isconsistent(cube_t cube)
237{
238 uint64_t x, p[12], sum, co;
239 bool found[12];
240 int i;
241
242 sum = 0;
243
244 /* Check for EP consistency */
245 for (i = 0; i < 12; i++)
246 found[i] = false;
247 for (i = 0, x = cube.e; i < 12; i++, x >>= 5ULL) {
248 p[i] = x & _epblock;
249 if (p[i] >= 12)
250 goto inconsistent_ep;
251 found[p[i]] = true;
252 }
253 for (i = 0; i < 12; i++)
254 if (!found[i])
255 goto inconsistent_ep;
256 sum = permsign(p, 12);
257
258 /* Check for CP consistency */
259 for (i = 0; i < 8; i++)
260 found[i] = false;
261 for (i = 0, x = cube.c; i < 8; i++, x >>= 6ULL) {
262 p[i] = x & _cpblock;
263 if (p[i] >= 8)
264 goto inconsistent_cp;
265 found[p[i]] = true;
266 }
267 for (i = 0; i < 8; i++)
268 if (!found[i])
269 goto inconsistent_cp;
270 sum += permsign(p, 8);
271
272 /* Check permutation parity */
273 if (sum % 2 != 0)
274 goto inconsistent_parity;
275
276 /* Check for EO parity */
277 for (i = 0, sum = 0, x = cube.e; i < 12; i++, x >>= 5ULL)
278 sum += (x & _eoblock) >> 4ULL;
279 if (sum % 2 != 0)
280 goto inconsistent_eo;
281
282 /* Check for CO parity */
283 for (i = 0, sum = 0, x = cube.c; i < 8; i++, x >>= 6ULL) {
284 co = (x & _coblock) >> 3ULL;
285 if (co > 2)
286 goto inconsistent_co3;
287 sum += co;
288 }
289 if (sum % 3 != 0)
290 goto inconsistent_co;
291
292 /* Check that CO extra bit is zero */
293 if (cube.c & _coextramask)
294 goto inconsistent_coextra;
295
296 return true;
297
298inconsistent_ep:
299#ifdef DEBUG
300 fprintf(stderr, "Inconsistent EP\n");
301#endif
302 goto inconsistent_return;
303inconsistent_cp:
304#ifdef DEBUG
305 fprintf(stderr, "Inconsistent CP\n");
306#endif
307 goto inconsistent_return;
308inconsistent_parity:
309#ifdef DEBUG
310 fprintf(stderr, "Inconsistent parity\n");
311#endif
312 goto inconsistent_return;
313inconsistent_eo:
314#ifdef DEBUG
315 fprintf(stderr, "Inconsistent EO\n");
316#endif
317 goto inconsistent_return;
318inconsistent_co3:
319#ifdef DEBUG
320 fprintf(stderr, "Inconsistent CO=3\n");
321#endif
322 goto inconsistent_return;
323inconsistent_co:
324#ifdef DEBUG
325 fprintf(stderr, "Inconsistent CO\n");
326#endif
327 goto inconsistent_return;
328inconsistent_coextra:
329#ifdef DEBUG
330 fprintf(stderr, "Inconsistent extra bit for CO\n");
331#endif
332 goto inconsistent_return;
333inconsistent_return:
334 return false;
335}
336
337bool
338issolved(cube_t cube)
339{
340 return cube.c == solvedcube.c && cube.e == solvedcube.e;
341}
342
343
344static uint64_t
345readep(char *str)
346{
347 uint64_t e;
348
349 for (e = 0; e < 12; e++)
350 if (!strncmp(str, edgestr[e], 2))
351 return e;
352
353#ifdef DEBUG
354 fprintf(stderr, "Error reading EP\n");
355#endif
356 return _error;
357}
358
359static uint64_t
360readeo(char *str)
361{
362 if (*str == '0')
363 return 0ULL;
364 if (*str == '1')
365 return 1ULL;
366
367#ifdef DEBUG
368 fprintf(stderr, "Error reading EO\n");
369#endif
370 return _error;
371}
372
373static uint64_t
374readcp(char *str)
375{
376 uint64_t c;
377
378 for (c = 0; c < 8; c++)
379 if (!strncmp(str, cornerstr[c], 3) ||
380 !strncmp(str, cornerstralt[c], 3))
381 return c;
382
383#ifdef DEBUG
384 fprintf(stderr, "Error reading CP\n");
385#endif
386 return _error;
387}
388
389static uint64_t
390readco(char *str)
391{
392 if (*str == '0')
393 return 0ULL;
394 if (*str == '1')
395 return 1ULL;
396 if (*str == '2')
397 return 2ULL;
398
399#ifdef DEBUG
400 fprintf(stderr, "Error reading CO\n");
401#endif
402 return _error;
403}
404
405cube_t
406readcube(char *buf)
407{
408 int i;
409 uint64_t piece, orient;
410 cube_t ret = {0};
411 char *b = buf;
412
413 for (i = 0; i < 12; i++) {
414 while (*b == ' ' || *b == '\t' || *b == '\n')
415 b++;
416 if ((piece = readep(b)) == _error)
417 goto readcube_error;
418 b += 2;
419 if ((orient = readeo(b)) == _error)
420 goto readcube_error;
421 b++;
422 ret.e |= (piece << (i * 5ULL)) | (orient << (i * 5ULL + 4ULL));
423 }
424 for (i = 0; i < 8; i++) {
425 while (*b == ' ' || *b == '\t' || *b == '\n')
426 b++;
427 if ((piece = readcp(b)) == _error)
428 goto readcube_error;
429 b += 3;
430 if ((orient = readco(b)) == _error)
431 goto readcube_error;
432 b++;
433 ret.c |= (piece << (i * 6ULL)) | (orient << (i * 6ULL + 3ULL));
434 }
435
436 return ret;
437
438readcube_error:
439#ifdef DEBUG
440 fprintf(stderr, "readcube error\n");
441#endif
442 return errorcube;
443}
444
445void
446writecube(cube_t cube, char *buf)
447{
448 char *errormsg;
449 uint64_t piece, orien, x;
450 size_t len;
451 int i;
452
453 if (!isconsistent(cube)) {
454 errormsg = "ERROR: cannot write inconsistent cube";
455 goto writecube_error;
456 }
457
458 for (i = 0, x = cube.e; i < 12; i++, x >>= 5ULL) {
459 piece = x & _epblock;
460 orien = (x & _eoblock) >> 4ULL;
461 buf[4*i ] = edgestr[piece][0];
462 buf[4*i + 1] = edgestr[piece][1];
463 buf[4*i + 2] = orien + '0';
464 buf[4*i + 3] = ' ';
465 }
466 for (i = 0, x = cube.c; i < 8; i++, x >>= 6ULL) {
467 piece = x & _cpblock;
468 orien = (x & _coblock) >> 3ULL;
469 buf[48 + 5*i ] = cornerstr[piece][0];
470 buf[48 + 5*i + 1] = cornerstr[piece][1];
471 buf[48 + 5*i + 2] = cornerstr[piece][2];
472 buf[48 + 5*i + 3] = orien + '0';
473 buf[48 + 5*i + 4] = ' ';
474 }
475
476 buf[48+39] = '\0';
477
478 return;
479
480writecube_error:
481#ifdef DEBUG
482 fprintf(stderr, "writecube error, see stdout for details\n");
483#endif
484 len = strlen(errormsg);
485 memcpy(buf, errormsg, len);
486 buf[len] = '\n';
487 buf[len+1] = '\0';
488}
489
490
491static uint64_t
492readmove(char c)
493{
494 switch (c) {
495 case 'U':
496 return U;
497 case 'D':
498 return D;
499 case 'R':
500 return R;
501 case 'L':
502 return L;
503 case 'F':
504 return F;
505 case 'B':
506 return B;
507 default:
508 return _error;
509 }
510}
511
512static uint64_t
513readmodifier(char c)
514{
515 switch (c) {
516 case '1': /* Fallthrough */
517 case '2': /* Fallthrough */
518 case '3':
519 return c - '0' - 1;
520 case '\'':
521 return 2;
522 default:
523 return 0;
524 }
525}
526
527int
528readmoves(char *buf, move_t *m)
529{
530 int n;
531 uint64_t r;
532 char *b;
533
534 for (b = buf, n = 0; *b != '\0'; b++) {
535 while (*b == ' ' || *b == '\t' || *b == '\n')
536 b++;
537 if (*b == '\0')
538 return n;
539 if ((r = readmove(*b)) == _error)
540 goto readmoves_error;
541 m[n] = (move_t)r;
542 if ((r = readmodifier(*(b+1))) != 0) {
543 b++;
544 m[n] += r;
545 }
546 n++;
547 }
548
549 return n;
550
551readmoves_error:
552#ifdef DEBUG
553 fprintf(stderr, "readmoves error\n");
554#endif
555 return -1;
556}
557
558void
559writemoves(move_t *m, int n, char *buf)
560{
561 int i;
562 size_t len;
563 char *b, *s;
564
565 for (i = 0, b = buf; i < n; i++, b++) {
566 s = movestr[m[i]];
567 len = strlen(s);
568 memcpy(b, s, len);
569 b += len;
570 *b = ' ';
571 }
572 *b = '\0';
573}
574
575
576static inline uint64_t
577coapply(uint64_t c, uint64_t m)
578{
579 uint64_t z, b;
580
581 z = c + m;
582 b = ((z + _coonemask) & _coextramask) >> 2ULL;
583
584 return (z + b) & ~_coextramask;
585}
586
587cube_t
588move(cube_t c, move_t m)
589{
590 cube_t ret = {0};
591
592#ifdef DEBUG
593 if (!isconsistent(c)) {
594 fprintf(stderr, "move error, inconsistent cube\n");
595 goto move_error;
596 }
597#endif
598
599 switch (m) {
600 case U:
601 ret.e = c.e & ~_emask_u;
602 ret.e |=
603 (c.e & _eblock) << 25ULL |
604 (c.e & _eblock << 5ULL) << 15ULL |
605 (c.e & _eblock2 << 20ULL) >> 20ULL;
606
607 ret.c = c.c & ~_cmask_u;
608 ret.c |=
609 (c.c & _cblock2) << 24ULL |
610 (c.c & _cblock << 24ULL) >> 18ULL |
611 (c.c & _cblock << 30ULL) >> 30ULL;
612
613 return ret;
614 case U2:
615 ret.e = c.e & ~_emask_u;
616 ret.e |=
617 (c.e & (_eblock | _eblock << 20ULL)) << 5ULL |
618 (c.e & (_eblock << 5ULL | _eblock << 25ULL)) >> 5ULL;
619
620 ret.c = c.c & ~_cmask_u;
621 ret.c |=
622 (c.c & (_cblock | _cblock << 24ULL)) << 6ULL |
623 (c.c & (_cblock << 6ULL | _cblock << 30ULL)) >> 6ULL;
624
625 return ret;
626 case U3:
627 ret.e = c.e & ~_emask_u;
628 ret.e |=
629 (c.e & _eblock2) << 20ULL |
630 (c.e & _eblock << 25ULL) >> 25ULL |
631 (c.e & _eblock << 20ULL) >> 15ULL;
632
633 ret.c = c.c & ~_cmask_u;
634 ret.c |=
635 (c.c & _cblock) << 30ULL |
636 (c.c & _cblock << 6ULL) << 18ULL |
637 (c.c & _cblock2 << 24ULL) >> 24ULL;
638
639 return ret;
640 case D:
641 ret.e = c.e & ~_emask_d;
642 ret.e |=
643 (c.e & _eblock2 << 10ULL) << 20ULL |
644 (c.e & _eblock << 30ULL) >> 15ULL |
645 (c.e & _eblock << 35ULL) >> 25ULL;
646
647 ret.c = c.c & ~_cmask_d;
648 ret.c |=
649 (c.c & _cblock2 << 12ULL) << 24ULL |
650 (c.c & _cblock << 36ULL) >> 18ULL |
651 (c.c & _cblock << 42ULL) >> 30ULL;
652
653 return ret;
654 case D2:
655 ret.e = c.e & ~_emask_d;
656 ret.e |=
657 (c.e & (_eblock << 10ULL | _eblock << 30ULL)) << 5ULL |
658 (c.e & (_eblock << 15ULL | _eblock << 35ULL)) >> 5ULL;
659
660 ret.c = c.c & ~_cmask_d;
661 ret.c |=
662 (c.c & (_cblock << 12ULL | _cblock << 36ULL)) << 6ULL |
663 (c.c & (_cblock << 18ULL | _cblock << 42ULL)) >> 6ULL;
664
665 return ret;
666 case D3:
667 ret.e = c.e & ~_emask_d;
668 ret.e |=
669 (c.e & _eblock << 10ULL) << 25ULL |
670 (c.e & _eblock << 15ULL) << 15ULL |
671 (c.e & _eblock2 << 30ULL) >> 20ULL;
672
673 ret.c = c.c & ~_cmask_d;
674 ret.c |=
675 (c.c & _cblock << 12ULL) << 30ULL |
676 (c.c & _cblock << 18ULL) << 18ULL |
677 (c.c & _cblock2 << 36ULL) >> 24ULL;
678
679 return ret;
680 case R:
681 ret.e = c.e & ~_emask_r;
682 ret.e |=
683 (c.e & _eblock << 20ULL) << 35ULL |
684 (c.e & _eblock << 55ULL) >> 20ULL |
685 (c.e & _eblock << 35ULL) << 5ULL |
686 (c.e & _eblock << 40ULL) >> 20ULL;
687
688 ret.c = c.c & ~_cmask_r;
689 ret.c |=
690 (c.c & _cblock) << 30ULL |
691 (c.c & _cblock << 30ULL) >> 12ULL |
692 (c.c & _cblock << 18ULL) << 18ULL |
693 (c.c & _cblock << 36ULL) >> 36ULL;
694
695 ret.c = coapply(ret.c, _comask_r);
696
697 return ret;
698 case R2:
699 ret.e = c.e & ~_emask_r;
700 ret.e |=
701 (c.e & (_eblock << 20ULL | _eblock << 40ULL)) << 15ULL |
702 (c.e & (_eblock << 35ULL | _eblock << 55ULL)) >> 15ULL;
703
704 ret.c = c.c & ~_cmask_r;
705 ret.c |=
706 (c.c & _cblock) << 18ULL |
707 (c.c & _cblock << 18ULL) >> 18ULL |
708 (c.c & _cblock << 30ULL) << 6ULL |
709 (c.c & _cblock << 36ULL) >> 6ULL;
710
711 return ret;
712 case R3:
713 ret.e = c.e & ~_emask_r;
714 ret.e |=
715 (c.e & (_eblock << 20ULL | _eblock << 35ULL)) << 20ULL |
716 (c.e & _eblock << 55ULL) >> 35ULL |
717 (c.e & _eblock << 40ULL) >> 5ULL;
718
719 ret.c = c.c & ~_cmask_r;
720 ret.c |=
721 (c.c & _cblock) << 36ULL |
722 (c.c & _cblock << 30ULL) >> 30ULL |
723 (c.c & _cblock << 18ULL) << 12ULL |
724 (c.c & _cblock << 36ULL) >> 18ULL;
725
726 ret.c = coapply(ret.c, _comask_r);
727
728 return ret;
729 case L:
730 ret.e = c.e & ~_emask_l;
731 ret.e |=
732 (c.e & _eblock2 << 25ULL) << 20ULL |
733 (c.e & _eblock << 45ULL) >> 15ULL |
734 (c.e & _eblock << 50ULL) >> 25ULL;
735
736 ret.c = c.c & ~_cmask_l;
737 ret.c |=
738 (c.c & _cblock << 6ULL) << 18ULL |
739 (c.c & _cblock << 12ULL) << 30ULL |
740 (c.c & _cblock << 24ULL) >> 12ULL |
741 (c.c & _cblock << 42ULL) >> 36ULL;
742
743 ret.c = coapply(ret.c, _comask_l);
744
745 return ret;
746 case L2:
747 ret.e = c.e & ~_emask_l;
748 ret.e |=
749 (c.e & (_eblock << 25ULL | _eblock << 45ULL)) << 5ULL |
750 (c.e & (_eblock << 30ULL | _eblock << 50ULL)) >> 5ULL;
751
752 ret.c = c.c & ~_cmask_l;
753 ret.c |=
754 (c.c & _cblock << 6ULL) << 6ULL |
755 (c.c & _cblock << 12ULL) >> 6ULL |
756 (c.c & _cblock << 24ULL) << 18ULL |
757 (c.c & _cblock << 42ULL) >> 18ULL;
758
759 return ret;
760 case L3:
761 ret.e = c.e & ~_emask_l;
762 ret.e |=
763 (c.e & _eblock << 25ULL) << 25ULL |
764 (c.e & _eblock << 30ULL) << 15ULL |
765 (c.e & _eblock2 << 45ULL) >> 20ULL;
766
767 ret.c = c.c & ~_cmask_l;
768 ret.c |=
769 (c.c & _cblock << 6ULL) << 36ULL |
770 (c.c & _cblock << 12ULL) << 12ULL |
771 (c.c & _cblock << 24ULL) >> 18ULL |
772 (c.c & _cblock << 42ULL) >> 30ULL;
773
774 ret.c = coapply(ret.c, _comask_l);
775
776 return ret;
777 case F:
778 ret.e = c.e & ~_emask_f;
779 ret.e |=
780 (c.e & _eblock) << 40ULL |
781 (c.e & _eblock << 15ULL) << 30ULL |
782 (c.e & _eblock << 40ULL) >> 25ULL |
783 (c.e & _eblock << 45ULL) >> 45ULL;
784
785 ret.e ^= _eomask_f;
786
787 ret.c = c.c & ~_cmask_f;
788 ret.c |=
789 (c.c & _cblock) << 36ULL |
790 (c.c & (_cblock << 24ULL | _cblock << 36ULL)) >> 24ULL |
791 (c.c & _cblock << 12ULL) << 12ULL;
792
793 ret.c = coapply(ret.c, _comask_f);
794
795 return ret;
796 case F2:
797 ret.e = c.e & ~_emask_f;
798 ret.e |=
799 (c.e & _eblock) << 15ULL |
800 (c.e & _eblock << 15ULL) >> 15ULL |
801 (c.e & _eblock << 40ULL) << 5ULL |
802 (c.e & _eblock << 45ULL) >> 5ULL;
803
804 ret.c = c.c & ~_cmask_f;
805 ret.c |=
806 (c.c & (_cblock | _cblock << 24ULL)) << 12ULL |
807 (c.c & (_cblock << 12ULL | _cblock << 36ULL)) >> 12ULL;
808
809 return ret;
810 case F3:
811 ret.e = c.e & ~_emask_f;
812 ret.e |=
813 (c.e & _eblock) << 45ULL |
814 (c.e & _eblock << 15ULL) << 25ULL |
815 (c.e & _eblock << 40ULL) >> 40ULL |
816 (c.e & _eblock << 45ULL) >> 30ULL;
817
818 ret.e ^= _eomask_f;
819
820 ret.c = c.c & ~_cmask_f;
821 ret.c |=
822 (c.c & (_cblock | _cblock << 12ULL)) << 24ULL |
823 (c.c & _cblock << 24ULL) >> 12ULL |
824 (c.c & _cblock << 36ULL) >> 36ULL;
825
826 ret.c = coapply(ret.c, _comask_f);
827
828 return ret;
829 case B:
830 ret.e = c.e & ~_emask_b;
831 ret.e |=
832 (c.e & _eblock2 << 5ULL) << 45ULL |
833 (c.e & _eblock << 50ULL) >> 40ULL |
834 (c.e & _eblock << 55ULL) >> 50ULL;
835
836 ret.e ^= _eomask_b;
837
838 ret.c = c.c & ~_cmask_b;
839 ret.c |=
840 (c.c & _cblock << 6ULL) << 36ULL |
841 (c.c & _cblock << 18ULL) << 12ULL |
842 (c.c & (_cblock << 30ULL | _cblock << 42ULL)) >> 24ULL;
843
844 ret.c = coapply(ret.c, _comask_b);
845
846 return ret;
847 case B2:
848 ret.e = c.e & ~_emask_b;
849 ret.e |=
850 (c.e & (_eblock << 5ULL | _eblock << 50ULL)) << 5ULL |
851 (c.e & (_eblock << 10ULL | _eblock << 55ULL)) >> 5ULL;
852
853 ret.c = c.c & ~_cmask_b;
854 ret.c |=
855 (c.c & (_cblock << 6ULL | _cblock << 30ULL)) << 12ULL |
856 (c.c & (_cblock << 18ULL | _cblock << 42ULL)) >> 12ULL;
857
858 return ret;
859 case B3:
860 ret.e = c.e & ~_emask_b;
861 ret.e |=
862 (c.e & _eblock << 5ULL) << 50ULL |
863 (c.e & _eblock << 10ULL) << 40ULL |
864 (c.e & _eblock2 << 50ULL) >> 45ULL;
865
866 ret.e ^= _eomask_b;
867
868 ret.c = c.c & ~_cmask_b;
869 ret.c |=
870 (c.c & (_cblock << 6ULL | _cblock << 18ULL)) << 24ULL |
871 (c.c & _cblock << 30ULL) >> 12ULL |
872 (c.c & _cblock << 42ULL) >> 36ULL;
873
874 ret.c = coapply(ret.c, _comask_b);
875
876 return ret;
877 default:
878 goto move_unknown;
879 }
880
881move_unknown:
882#ifdef DEBUG
883 fprintf(stderr, "move error, unknow move\n");
884#endif
885 goto move_error;
886move_error:
887 return errorcube;
888}
diff --git a/src/cube.h b/src/cube.h
new file mode 100644
index 0000000..b916f96
--- /dev/null
+++ b/src/cube.h
@@ -0,0 +1,27 @@
1typedef enum {
2 U, U2, U3, D, D2, D3,
3 R, R2, R3, L, L2, L3,
4 F, F2, F3, B, B2, B3
5} move_t;
6typedef struct {
7 uint64_t e;
8 uint64_t c;
9} cube_t;
10
11extern cube_t solvedcube;
12extern cube_t errorcube;
13
14bool isconsistent(cube_t);
15bool issolved(cube_t);
16
17cube_t readcube(char *);
18void writecube(cube_t, char *);
19
20int readmoves(char *, move_t *);
21void writemoves(move_t *, int, char *);
22
23cube_t move(cube_t, move_t);
24
25/*
26cube_t inverse(cube_t);
27*/
diff --git a/src/cube.sync-conflict-20230524-181836-JOKKFPA.c b/src/cube.sync-conflict-20230524-181836-JOKKFPA.c
new file mode 100644
index 0000000..e64acca
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-181836-JOKKFPA.c
@@ -0,0 +1,614 @@
1/*
2# Cube representation, moves, transformations and indexing
3
4## String description
5
6The functions readcube() and writecube() use the following format.
7Each edge is represented by two letters denoting the sides it belongs to
8and one number denoting its orientation (0 oriented, 1 mis-oriented).
9Similarly, each corner is represented by three letters and a number
10(0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise).
11Edge orientation is relative to the F / B axis, corner orientation is
12relative to the U / D axis.
13
14The correct order of the pieces is the same as that defined in the
15section "Internal cube representation", except that pieces are read
16left-to-right. Pieces are divided by slices, so the ordering is not the
17most intuitive, but it is more convenient for the internal representation.
18
19Whitespaces between pieces are ignored when reading the cube, and a
20single whitespace character is added between pieces when writing.
21
22For example, the solved cube looks like this:
23
24UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0
25
26The cube after the moves R'U'F looks like this:
27
28FL1 BR0 DB0 UR1 UF0 UB0 DL0 FR0 UL1 DF1 BL0 DR0 UBL1 DBR1 UFR2 DFR2 DFL2 UBL2 UFL2 DBL0
29
30More formats might be supported in the future.
31
32## Internal cube representation
33
34The cube_t data structure implemented in this file is designed to
35efficiently perform common operations on a 3x3x3 Rubik's cube when
36solving it with an iterative-deepening DFS search. It is not the most
37general, complete, easy to read or compact one. Since the cube can
38be trivially reoriented before the search, we only encode permutations
39of the cube that keep the center pieces in a fixed position (that is,
40we do not encode the position of the centers).
41
42The cube state is encoded in two 64-bit integers, one for edges and one
43for centers. We explain how edges are encoded first, and the highlight
44the few differences with corners afterwards.
45
46For encoding edges, only the 60 least-significant bits are used. Each
47edge described by 5 bits. The position of a 5-bit block in the 64-bit
48integer determine the position of the edge piece in the cube, according
49to the following table (least-significant bits on the right):
50
5155-59 50-54 45-49 40-44 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
52 BR BL FL FR DR DL UL UR DF DB UB UF
53ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee
54
55For each edge, the 4 least-significant bits ('ssee' in the table)
56determine the piece. The two bits marked with 'ss' determine the internal
57slice the piece belongs to, i.e. they are either '00' for M, '01' for
58S or '10' for E. The other two bits (marked with 'ee') determine the
59actual edge piece among the 4 in the same slice, and they are assigned
60somewhat arbitarily. Using this representation and the ordering defined
61in the table above, the edges are correctly permuted when these 4 bits
62for each represent the numbers 0 to 11 in the correct order.
63
64The last bit determines the orientation. The orientation of an edge
65depends on its position, and it is defined being 0 if the edge can be
66moved to its place in the solved orientation by permutations in the
67subgroup <U, D, R, L, F2, B2>.
68
69Corners are encoded in the 48 least-significant bits, and are described
70by 6 bits each, their position being defined by the following table:
71
72 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
73 DBL DFL UBR UFL DBR DFL UBL UFR
74oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc
75
76The bit marked with an 'x' describes the axis the corner belongs to.
77The 0 axis consists of the corners UFR, UBL, DFL and DBR, and the other
78four corners form the axis marked with 1. Then two bits are needed to
79identify the corner among the four of the same axis. The last three bits
80determine the orientation, where one corner is defined to be oriented
81(marked with '000') if its top or bottom sticker faces the top or bottom
82side. A corner a clockwise turn away from being oriented, thus requiring
83a counter-clockwise turn to be oriented correctly, is marked with '001',
84and a corner a counter-clockwise turn away is marked with '010'. The most
85significant bit is not used to determine the corner orientation, but it
86must always be set to '0' to simplify the moving operations (see below).
87
88## Basic moves
89
90The 18 basic moves of the cube could be performed by applying a suitable
91general permutation (see below), but they have instead been manually
92implemented with a few simple operations each, to improve performance.
93
94For each move we first permute the pieces. This amounts to shifting
95around 4 blocks of bits for edges and 4 for corners. Since in some cases
96adjacent pieces on the cube are also adjacent in the bit representation we
97use, we can save some operations by shifting multiple blocks together.
98For example, for the move U for edges we shift a block of 15 bits 5
99positions to the left and a block of 5 bits 15 positions to the right.
100
101There are some moves that change the orientation of the pieces. Namely,
102the moves F, F', B and B' change the orientation of the edges and those
103moves as well as R, R', L and L' change the orientation of the corners.
104Edge orientation is easy to address: we simply xor the edge representation
105by a bit mask with zeroes everywhere except for the 4 edges that need
106to be flipped (i.e. the ones on the twisted face).
107
108Corner orientation is harder to reproduce efficiently working only with
109bitwise operations, as it involves performing operations modulo 3.
110However, with the help of the extra bit we reserved, we are able to
111do this using only two additions and 3 bitwise operations, without
112any multiplication, division or modulo operation. The trick is
113to use the following formula to sum two numbers x, y in {0,1,2}:
114
115 ((x+y) + (x+y+1)/4) % 4
116
117The thrid bit is needed because x+y and x+y+1 can exceed 3.
118See below (in the code) for the details.
119
120## Inverting the cube
121
122TODO
123
124## Transformations (conjugations by full-cube rotations)
125
126TODO
127
128## Indexing
129
130TODO - subgroup description etc
131
132Ideas for pruning (for another file?):
133- Use corner separation + CO as main coordinate (~150k states)
134 - for huge tables, htr corners can be used (6 times larger)
135- Symmetry table, one entry or each main coordinate value with the
136 following info:
137 - index of the corresponding main symcoord (13 bits)
138 - transtorep (6 bits)
139 - base value for pruning table (5 bits, probably 4)
140 - pruning value for only main coord, i.e. fallback (4 bits)
141- To get a full coord for the cube:
142 - get first coord c, get the transtorep
143 - transform edges with transtorep
144 - get second coordinate e
145 - return c * MAXE + e
146 - This is still too big, so divide by a power of 2 to get the hashed index
147 - second coordinate: ep always, + varible number of eo bit (0 to 11)
148- Generate table:
149 - first probe for base value:
150 - solve coord using fallback table for pruning for 10k random states or so
151 - loop over all possible values (even if going for smaller table)
152 - with inverse-index strategy or what?
153 - use 1 bit per entry (more than base value or not)
154*/
155
156#include <stdbool.h>
157#include <stdint.h>
158#include <string.h>
159
160#ifdef DEBUG
161#include <stdio.h>
162#endif
163
164#include "cube.h"
165
166#define _error 0xFFFFFFFF
167
168#define _esize 5ULL
169#define _eoblock 0x10ULL /* 10000 */
170#define _epblock 0x0FULL /* 01111 */
171#define _eblock 0x1FULL /* 11111 */
172
173#define _csize 6ULL
174#define _coblock 0x18ULL /* 011000 */
175#define _cpblock 0x07ULL /* 000111 */
176#define _cblock 0x3FULL /* 111111 */
177
178#define _edge_uf 0ULL /* 00 00 */
179#define _edge_ub 1ULL /* 00 01 */
180#define _edge_db 2ULL /* 00 10 */
181#define _edge_df 3ULL /* 00 11 */
182#define _edge_ur 4ULL /* 01 00 */
183#define _edge_ul 5ULL /* 01 01 */
184#define _edge_dl 6ULL /* 01 10 */
185#define _edge_dr 7ULL /* 01 11 */
186#define _edge_fr 8ULL /* 10 00 */
187#define _edge_fl 9ULL /* 10 01 */
188#define _edge_bl 10ULL /* 10 10 */
189#define _edge_br 11ULL /* 10 11 */
190
191#define _corner_ufr 0ULL /* 0 00 */
192#define _corner_ubl 1ULL /* 0 01 */
193#define _corner_dfl 2ULL /* 0 10 */
194#define _corner_dbr 3ULL /* 0 11 */
195#define _corner_ufl 4ULL /* 1 00 */
196#define _corner_ubr 5ULL /* 1 01 */
197#define _corner_dfr 6ULL /* 1 10 */
198#define _corner_dbl 7ULL /* 1 11 */
199
200#define ESHIFT(i) ((i) * _esize)
201#define EOSHIFT(i) (4ULL + (i) * _esize)
202#define EMASK(i) (_eblock << ESHIFT(i))
203#define EOMASK(i) (_eoblock << ESHIFT(i))
204#define EPMASK(i) (_epblock << ESHIFT(i))
205#define ESOLVED(ee) (_edge_##ee << (_edge_##ee * _esize))
206#define EDGEAT(e, i) (((e) & EMASK(i)) >> ESHIFT(i))
207#define EOAT(e, i) (((e) & EOMASK(i)) >> EOSHIFT(i))
208#define EPAT(e, i) (((e) & EPMASK(i)) >> ESHIFT(i))
209
210#define CSHIFT(i) ((i) * _csize)
211#define COSHIFT(i) (3ULL + (i) * _csize)
212#define CMASK(i) (_cblock << CSHIFT(i))
213#define COMASK(i) (_coblock << CSHIFT(i))
214#define CPMASK(i) (_cpblock << CSHIFT(i))
215#define CSOLVED(ccc) (_corner_##ccc << (_corner_##ccc * _csize))
216#define CORNERAT(c, i) (((c) & CMASK(i)) >> CSHIFT(i))
217#define COAT(c, i) (((c) & COMASK(i)) >> COSHIFT(i))
218#define CPAT(c, i) (((c) & CPMASK(i)) >> CSHIFT(i))
219
220#define _emask_u (EMASK(uf) | EMASK(ul) | EMASK(ub) | EMASK(ur))
221#define _emask_d (EMASK(df) | EMASK(dl) | EMASK(db) | EMASK(dr))
222#define _emask_r (EMASK(ur) | EMASK(dr) | EMASK(fr) | EMASK(br))
223#define _emask_l (EMASK(ul) | EMASK(dl) | EMASK(fl) | EMASK(bl))
224#define _emask_f (EMASK(uf) | EMASK(df) | EMASK(fr) | EMASK(fl))
225#define _emask_b (EMASK(ub) | EMASK(db) | EMASK(br) | EMASK(bl))
226
227#define _cmask_u (CMASK(ufr) | CMASK(ufl) | CMASK(ubl) | CMASK(ubr))
228#define _cmask_d (CMASK(dfr) | CMASK(dfl) | CMASK(dbl) | CMASK(dbr))
229#define _cmask_r (CMASK(ufr) | CMASK(dfr) | CMASK(ubr) | CMASK(dbr))
230#define _cmask_l (CMASK(ufl) | CMASK(dfl) | CMASK(ubl) | CMASK(dbl))
231#define _cmask_f (CMASK(ufr) | CMASK(ufl) | CMASK(dfr) | CMASK(dfl))
232#define _cmask_b (CMASK(ubr) | CMASK(ubl) | CMASK(dbr) | CMASK(dbl))
233
234#define _eomask (EOMASK(uf) | EOMASK(ul) | EOMASK(ub) | EOMASK(ur) \
235 EOMASK(df) | EOMASK(dl) | EOMASK(db) | EOMASK(dr) \
236 EOMASK(fr) | EOMASK(fl) | EOMASK(bl) | EOMASK(br))
237#define _comask (COMASK(ufr) | COMASK(ufl) | COMASK(ubl) | COMASK(ubr) \
238 COMASK(dfr) | COMASK(dfl) | COMASK(dbl) | COMASK(dbr))
239
240static uint64_t permsign(uint64_t *, int);
241static uint64_t readep(char *);
242static uint64_t readeo(char *);
243static uint64_t readcp(char *);
244static uint64_t readco(char *);
245static uint64_t readmove(char);
246static uint64_t readmodifier(char);
247
248static char *edgestr[] = {
249 [_edge_uf] = "UF",
250 [_edge_ub] = "UB",
251 [_edge_db] = "DB",
252 [_edge_df] = "DF",
253 [_edge_ur] = "UR",
254 [_edge_ul] = "UL",
255 [_edge_dl] = "DL",
256 [_edge_dr] = "DR",
257 [_edge_fr] = "FR",
258 [_edge_fl] = "FL",
259 [_edge_bl] = "BL",
260 [_edge_br] = "BR"
261};
262static char *cornerstr[] = {
263 [_corner_ufr] = "UFR",
264 [_corner_ubl] = "UBL",
265 [_corner_dfl] = "DFL",
266 [_corner_dbr] = "DBR",
267 [_corner_ufl] = "UFL",
268 [_corner_ubr] = "UBR",
269 [_corner_dfr] = "DFR",
270 [_corner_dbl] = "DBL"
271};
272static char *movestr[] = {
273 [U] = "U", [U2] = "U2", [U3] = "U'",
274 [D] = "D", [D2] = "D2", [D3] = "D'",
275 [R] = "R", [R2] = "R2", [R3] = "R'",
276 [L] = "L", [L2] = "L2", [L3] = "L'",
277 [F] = "F", [F2] = "F2", [F3] = "F'",
278 [B] = "B", [B2] = "B2", [B3] = "B'",
279};
280
281cube_t solvedcube = {
282 .e = ESOLVED(uf) | ESOLVED(ul) | ESOLVED(ub) | ESOLVED(ur) |
283 ESOLVED(df) | ESOLVED(dl) | ESOLVED(db) | ESOLVED(dr) |
284 ESOLVED(fr) | ESOLVED(fl) | ESOLVED(bl) | ESOLVED(br),
285 .c = CSOLVED(ufr) | CSOLVED(ufl) | CSOLVED(ubl) | CSOLVED(ubr) |
286 CSOLVED(dfr) | CSOLVED(dfl) | CSOLVED(dbl) | CSOLVED(dbr),
287};
288cube_t errorcube = { .e = _error, .c = _error };
289
290
291static uint64_t
292permsign(uint64_t *a, int n)
293{
294 int i, j;
295 uint64_t ret;
296
297 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
306bool
307isconsistent(cube_t cube)
308{
309 uint64_t p[12], sum;
310 bool found[12];
311 int i;
312
313 sum = 0;
314
315 /* Check for EP consistency */
316 for (i = 0; i < 12; i++)
317 found[i] = false;
318 for (i = 0; i < 12; i++) {
319 p[i] = EPAT(cube.e, i);
320 found[p[i]] = true;
321 }
322 for (i = 0; i < 12; i++)
323 if (!found[i])
324 return false;
325 sum = permsign(p, 12);
326
327 /* Check for CP consistency */
328 for (i = 0; i < 8; i++)
329 found[i] = false;
330 for (i = 0; i < 8; i++) {
331 p[i] = CPAT(cube.c, i);
332 found[p[i]] = true;
333 }
334 for (i = 0; i < 8; i++)
335 if (!found[i])
336 return false;
337 sum += permsign(p, 8);
338
339 /* Check permutation parity */
340 if (sum % 2 != 0)
341 return false;
342
343 /* Check for EO parity */
344 for (i = 0, sum = 0; i < 12; i++)
345 sum += EOAT(cube.e, i);
346 if (sum % 2 != 0)
347 return false;
348
349 /* Check for CO parity */
350 for (i = 0, sum = 0; i < 8; i++)
351 sum += COAT(cube.c, i);
352 if (sum % 3 != 0)
353 return false;
354
355 /* Check that CO extra bit is zero */
356 for (i = 0; i < 8; i++)
357 if (cube.c & (1ULL << (5 + i * _csize)))
358 return false;
359
360 return true;
361}
362
363bool
364issolved(cube_t cube)
365{
366 return cube.c == solvedcube.c && cube.e == solvedcube.e;
367}
368
369
370static uint64_t
371readep(char *str)
372{
373 if (!strncmp(str, "UF", 2))
374 return _edge_uf;
375 if (!strncmp(str, "UL", 2))
376 return _edge_ul;
377 if (!strncmp(str, "UB", 2))
378 return _edge_ub;
379 if (!strncmp(str, "UR", 2))
380 return _edge_ur;
381 if (!strncmp(str, "DF", 2))
382 return _edge_df;
383 if (!strncmp(str, "DL", 2))
384 return _edge_dl;
385 if (!strncmp(str, "DB", 2))
386 return _edge_db;
387 if (!strncmp(str, "DR", 2))
388 return _edge_dr;
389 if (!strncmp(str, "FR", 2))
390 return _edge_fr;
391 if (!strncmp(str, "FL", 2))
392 return _edge_fl;
393 if (!strncmp(str, "BL", 2))
394 return _edge_bl;
395 if (!strncmp(str, "BR", 2))
396 return _edge_br;
397
398 return _error;
399}
400
401static uint64_t
402readeo(char *str)
403{
404 if (*str == '0')
405 return 0ULL;
406 if (*str == '1')
407 return 1ULL;
408
409 return _error;
410}
411
412static uint64_t
413readcp(char *str)
414{
415 if (!strncmp(str, "UFR", 3) || !strncmp(str, "URF", 3))
416 return _corner_ufr;
417 if (!strncmp(str, "UFL", 3) || !strncmp(str, "ULF", 3))
418 return _corner_ufl;
419 if (!strncmp(str, "UBL", 3) || !strncmp(str, "ULB", 3))
420 return _corner_ubl;
421 if (!strncmp(str, "UBR", 3) || !strncmp(str, "URB", 3))
422 return _corner_ubr;
423 if (!strncmp(str, "DFR", 3) || !strncmp(str, "DRF", 3))
424 return _corner_dfr;
425 if (!strncmp(str, "DFL", 3) || !strncmp(str, "DLF", 3))
426 return _corner_dfl;
427 if (!strncmp(str, "DBL", 3) || !strncmp(str, "DLB", 3))
428 return _corner_dbl;
429 if (!strncmp(str, "DBR", 3) || !strncmp(str, "DRB", 3))
430 return _corner_dbr;
431
432 return _error;
433}
434
435static uint64_t
436readco(char *str)
437{
438 if (*str == '0')
439 return 0ULL;
440 if (*str == '1')
441 return 1ULL;
442 if (*str == '2')
443 return 2ULL;
444
445 return _error;
446}
447
448cube_t
449readcube(char *buf)
450{
451 int i;
452 uint64_t piece, orient;
453 cube_t ret = {0};
454 char *b = buf;
455
456 for (i = 0; i < 12; i++) {
457 while (*b == ' ' || *b == '\t' || *b == '\n')
458 b++;
459 if ((piece = readep(b)) == _error)
460 goto readcube_error;
461 b += 2;
462 if ((orient = readeo(b)) == _error)
463 goto readcube_error;
464 b++;
465 ret.e |= (piece << ESHIFT(i)) | (orient << EOSHIFT(i));
466 }
467 for (i = 0; i < 8; i++) {
468 while (*b == ' ' || *b == '\t' || *b == '\n')
469 b++;
470 if ((piece = readcp(b)) == _error)
471 goto readcube_error;
472 b += 3;
473 if ((orient = readco(b)) == _error)
474 goto readcube_error;
475 b++;
476 ret.c |= (piece << CSHIFT(i)) | (orient << COSHIFT(i));
477 }
478
479 return ret;
480
481readcube_error:
482 return errorcube;
483}
484
485void
486writecube(cube_t cube, char *buf)
487{
488 char *errormsg;
489 uint64_t piece;
490 size_t len;
491 int i;
492
493 if (!isconsistent(cube)) {
494 errormsg = "ERROR: cannot write inconsistent cube";
495 goto writecube_error;
496 }
497
498 for (i = 0; i < 12; i++) {
499 piece = EPAT(cube.e, i);
500 buf[4*i ] = edgestr[piece][0];
501 buf[4*i + 1] = edgestr[piece][1];
502 buf[4*i + 2] = EOAT(cube.e, i) + '0';
503 buf[4*i + 3] = ' ';
504 }
505 for (i = 0; i < 8; i++) {
506 piece = CPAT(cube.c, i);
507 buf[48 + 5*i ] = cornerstr[piece][0];
508 buf[48 + 5*i + 1] = cornerstr[piece][1];
509 buf[48 + 5*i + 2] = cornerstr[piece][2];
510 buf[48 + 5*i + 3] = COAT(cube.c, i) + '0';
511 buf[48 + 5*i + 4] = ' ';
512 }
513
514 buf[48+39] = '\0';
515
516 return;
517
518writecube_error:
519 len = strlen(errormsg);
520 strcpy(buf, errormsg);
521 buf[len] = '\n';
522 buf[len+1] = '\0';
523}
524
525
526static uint64_t
527readmove(char c)
528{
529 switch (c) {
530 case 'U':
531 return U;
532 case 'D':
533 return D;
534 case 'R':
535 return R;
536 case 'L':
537 return L;
538 case 'F':
539 return F;
540 case 'B':
541 return B;
542 default:
543 return _error;
544 }
545}
546
547static uint64_t
548readmodifier(char c)
549{
550 switch (c) {
551 case '1': /* Fallthrough */
552 case '2': /* Fallthrough */
553 case '3':
554 return c - '0' - 1;
555 case '\'':
556 return 2;
557 default:
558 return 0;
559 }
560}
561
562int
563readmoves(char *buf, move_t *m)
564{
565 int n;
566 uint64_t r;
567 char *b;
568
569 for (b = buf, n = 0; *b != '\0'; b++) {
570 while (*b == ' ' || *b == '\t' || *b == '\n')
571 b++;
572 if ((r = readmove(*b)) == _error)
573 return -1;
574 m[n] = (move_t)r;
575 if ((r = readmodifier(*(b+1))) != 0) {
576 b++;
577 m[n] += r;
578 }
579 n++;
580 }
581
582 return n;
583}
584
585void
586writemoves(move_t *m, int n, char *buf)
587{
588 int i;
589 char *b, *s;
590
591 for (i = 0, b = buf; i < n; i++, b++) {
592 s = movestr[m[i]];
593 strcpy(b, s);
594 b += strlen(s);
595 *b = ' ';
596 }
597 *b = '\0';
598}
599
600
601cube_t
602move(move_t m, cube_t c)
603{
604 /* TODO - not implemented yet */
605
606 cube_t ret = {0};
607
608 switch (m) {
609 case U:
610 return ret;
611 default:
612 return ret;
613 }
614}
diff --git a/src/cube.sync-conflict-20230524-181836-JOKKFPA.h b/src/cube.sync-conflict-20230524-181836-JOKKFPA.h
new file mode 100644
index 0000000..2763d16
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-181836-JOKKFPA.h
@@ -0,0 +1,27 @@
1typedef enum {
2 U, U2, U3, D, D2, D3,
3 R, R2, R3, L, L2, L3,
4 F, F2, F3, B, B2, B3
5} move_t;
6typedef struct {
7 uint64_t e;
8 uint64_t c;
9} cube_t;
10
11extern cube_t solvedcube;
12extern cube_t errorcube;
13
14bool isconsistent(cube_t);
15bool issolved(cube_t);
16
17cube_t readcube(char *);
18void writecube(cube_t, char *);
19
20int readmoves(char *, move_t *);
21void writemoves(move_t *, int, char *);
22
23/*
24cube_t move(move_t, cube_t);
25cube_t inverse(cube_t);
26
27*/
diff --git a/src/cube.sync-conflict-20230524-182143-JOKKFPA.c b/src/cube.sync-conflict-20230524-182143-JOKKFPA.c
new file mode 100644
index 0000000..e64acca
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-182143-JOKKFPA.c
@@ -0,0 +1,614 @@
1/*
2# Cube representation, moves, transformations and indexing
3
4## String description
5
6The functions readcube() and writecube() use the following format.
7Each edge is represented by two letters denoting the sides it belongs to
8and one number denoting its orientation (0 oriented, 1 mis-oriented).
9Similarly, each corner is represented by three letters and a number
10(0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise).
11Edge orientation is relative to the F / B axis, corner orientation is
12relative to the U / D axis.
13
14The correct order of the pieces is the same as that defined in the
15section "Internal cube representation", except that pieces are read
16left-to-right. Pieces are divided by slices, so the ordering is not the
17most intuitive, but it is more convenient for the internal representation.
18
19Whitespaces between pieces are ignored when reading the cube, and a
20single whitespace character is added between pieces when writing.
21
22For example, the solved cube looks like this:
23
24UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0
25
26The cube after the moves R'U'F looks like this:
27
28FL1 BR0 DB0 UR1 UF0 UB0 DL0 FR0 UL1 DF1 BL0 DR0 UBL1 DBR1 UFR2 DFR2 DFL2 UBL2 UFL2 DBL0
29
30More formats might be supported in the future.
31
32## Internal cube representation
33
34The cube_t data structure implemented in this file is designed to
35efficiently perform common operations on a 3x3x3 Rubik's cube when
36solving it with an iterative-deepening DFS search. It is not the most
37general, complete, easy to read or compact one. Since the cube can
38be trivially reoriented before the search, we only encode permutations
39of the cube that keep the center pieces in a fixed position (that is,
40we do not encode the position of the centers).
41
42The cube state is encoded in two 64-bit integers, one for edges and one
43for centers. We explain how edges are encoded first, and the highlight
44the few differences with corners afterwards.
45
46For encoding edges, only the 60 least-significant bits are used. Each
47edge described by 5 bits. The position of a 5-bit block in the 64-bit
48integer determine the position of the edge piece in the cube, according
49to the following table (least-significant bits on the right):
50
5155-59 50-54 45-49 40-44 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
52 BR BL FL FR DR DL UL UR DF DB UB UF
53ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee
54
55For each edge, the 4 least-significant bits ('ssee' in the table)
56determine the piece. The two bits marked with 'ss' determine the internal
57slice the piece belongs to, i.e. they are either '00' for M, '01' for
58S or '10' for E. The other two bits (marked with 'ee') determine the
59actual edge piece among the 4 in the same slice, and they are assigned
60somewhat arbitarily. Using this representation and the ordering defined
61in the table above, the edges are correctly permuted when these 4 bits
62for each represent the numbers 0 to 11 in the correct order.
63
64The last bit determines the orientation. The orientation of an edge
65depends on its position, and it is defined being 0 if the edge can be
66moved to its place in the solved orientation by permutations in the
67subgroup <U, D, R, L, F2, B2>.
68
69Corners are encoded in the 48 least-significant bits, and are described
70by 6 bits each, their position being defined by the following table:
71
72 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
73 DBL DFL UBR UFL DBR DFL UBL UFR
74oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc
75
76The bit marked with an 'x' describes the axis the corner belongs to.
77The 0 axis consists of the corners UFR, UBL, DFL and DBR, and the other
78four corners form the axis marked with 1. Then two bits are needed to
79identify the corner among the four of the same axis. The last three bits
80determine the orientation, where one corner is defined to be oriented
81(marked with '000') if its top or bottom sticker faces the top or bottom
82side. A corner a clockwise turn away from being oriented, thus requiring
83a counter-clockwise turn to be oriented correctly, is marked with '001',
84and a corner a counter-clockwise turn away is marked with '010'. The most
85significant bit is not used to determine the corner orientation, but it
86must always be set to '0' to simplify the moving operations (see below).
87
88## Basic moves
89
90The 18 basic moves of the cube could be performed by applying a suitable
91general permutation (see below), but they have instead been manually
92implemented with a few simple operations each, to improve performance.
93
94For each move we first permute the pieces. This amounts to shifting
95around 4 blocks of bits for edges and 4 for corners. Since in some cases
96adjacent pieces on the cube are also adjacent in the bit representation we
97use, we can save some operations by shifting multiple blocks together.
98For example, for the move U for edges we shift a block of 15 bits 5
99positions to the left and a block of 5 bits 15 positions to the right.
100
101There are some moves that change the orientation of the pieces. Namely,
102the moves F, F', B and B' change the orientation of the edges and those
103moves as well as R, R', L and L' change the orientation of the corners.
104Edge orientation is easy to address: we simply xor the edge representation
105by a bit mask with zeroes everywhere except for the 4 edges that need
106to be flipped (i.e. the ones on the twisted face).
107
108Corner orientation is harder to reproduce efficiently working only with
109bitwise operations, as it involves performing operations modulo 3.
110However, with the help of the extra bit we reserved, we are able to
111do this using only two additions and 3 bitwise operations, without
112any multiplication, division or modulo operation. The trick is
113to use the following formula to sum two numbers x, y in {0,1,2}:
114
115 ((x+y) + (x+y+1)/4) % 4
116
117The thrid bit is needed because x+y and x+y+1 can exceed 3.
118See below (in the code) for the details.
119
120## Inverting the cube
121
122TODO
123
124## Transformations (conjugations by full-cube rotations)
125
126TODO
127
128## Indexing
129
130TODO - subgroup description etc
131
132Ideas for pruning (for another file?):
133- Use corner separation + CO as main coordinate (~150k states)
134 - for huge tables, htr corners can be used (6 times larger)
135- Symmetry table, one entry or each main coordinate value with the
136 following info:
137 - index of the corresponding main symcoord (13 bits)
138 - transtorep (6 bits)
139 - base value for pruning table (5 bits, probably 4)
140 - pruning value for only main coord, i.e. fallback (4 bits)
141- To get a full coord for the cube:
142 - get first coord c, get the transtorep
143 - transform edges with transtorep
144 - get second coordinate e
145 - return c * MAXE + e
146 - This is still too big, so divide by a power of 2 to get the hashed index
147 - second coordinate: ep always, + varible number of eo bit (0 to 11)
148- Generate table:
149 - first probe for base value:
150 - solve coord using fallback table for pruning for 10k random states or so
151 - loop over all possible values (even if going for smaller table)
152 - with inverse-index strategy or what?
153 - use 1 bit per entry (more than base value or not)
154*/
155
156#include <stdbool.h>
157#include <stdint.h>
158#include <string.h>
159
160#ifdef DEBUG
161#include <stdio.h>
162#endif
163
164#include "cube.h"
165
166#define _error 0xFFFFFFFF
167
168#define _esize 5ULL
169#define _eoblock 0x10ULL /* 10000 */
170#define _epblock 0x0FULL /* 01111 */
171#define _eblock 0x1FULL /* 11111 */
172
173#define _csize 6ULL
174#define _coblock 0x18ULL /* 011000 */
175#define _cpblock 0x07ULL /* 000111 */
176#define _cblock 0x3FULL /* 111111 */
177
178#define _edge_uf 0ULL /* 00 00 */
179#define _edge_ub 1ULL /* 00 01 */
180#define _edge_db 2ULL /* 00 10 */
181#define _edge_df 3ULL /* 00 11 */
182#define _edge_ur 4ULL /* 01 00 */
183#define _edge_ul 5ULL /* 01 01 */
184#define _edge_dl 6ULL /* 01 10 */
185#define _edge_dr 7ULL /* 01 11 */
186#define _edge_fr 8ULL /* 10 00 */
187#define _edge_fl 9ULL /* 10 01 */
188#define _edge_bl 10ULL /* 10 10 */
189#define _edge_br 11ULL /* 10 11 */
190
191#define _corner_ufr 0ULL /* 0 00 */
192#define _corner_ubl 1ULL /* 0 01 */
193#define _corner_dfl 2ULL /* 0 10 */
194#define _corner_dbr 3ULL /* 0 11 */
195#define _corner_ufl 4ULL /* 1 00 */
196#define _corner_ubr 5ULL /* 1 01 */
197#define _corner_dfr 6ULL /* 1 10 */
198#define _corner_dbl 7ULL /* 1 11 */
199
200#define ESHIFT(i) ((i) * _esize)
201#define EOSHIFT(i) (4ULL + (i) * _esize)
202#define EMASK(i) (_eblock << ESHIFT(i))
203#define EOMASK(i) (_eoblock << ESHIFT(i))
204#define EPMASK(i) (_epblock << ESHIFT(i))
205#define ESOLVED(ee) (_edge_##ee << (_edge_##ee * _esize))
206#define EDGEAT(e, i) (((e) & EMASK(i)) >> ESHIFT(i))
207#define EOAT(e, i) (((e) & EOMASK(i)) >> EOSHIFT(i))
208#define EPAT(e, i) (((e) & EPMASK(i)) >> ESHIFT(i))
209
210#define CSHIFT(i) ((i) * _csize)
211#define COSHIFT(i) (3ULL + (i) * _csize)
212#define CMASK(i) (_cblock << CSHIFT(i))
213#define COMASK(i) (_coblock << CSHIFT(i))
214#define CPMASK(i) (_cpblock << CSHIFT(i))
215#define CSOLVED(ccc) (_corner_##ccc << (_corner_##ccc * _csize))
216#define CORNERAT(c, i) (((c) & CMASK(i)) >> CSHIFT(i))
217#define COAT(c, i) (((c) & COMASK(i)) >> COSHIFT(i))
218#define CPAT(c, i) (((c) & CPMASK(i)) >> CSHIFT(i))
219
220#define _emask_u (EMASK(uf) | EMASK(ul) | EMASK(ub) | EMASK(ur))
221#define _emask_d (EMASK(df) | EMASK(dl) | EMASK(db) | EMASK(dr))
222#define _emask_r (EMASK(ur) | EMASK(dr) | EMASK(fr) | EMASK(br))
223#define _emask_l (EMASK(ul) | EMASK(dl) | EMASK(fl) | EMASK(bl))
224#define _emask_f (EMASK(uf) | EMASK(df) | EMASK(fr) | EMASK(fl))
225#define _emask_b (EMASK(ub) | EMASK(db) | EMASK(br) | EMASK(bl))
226
227#define _cmask_u (CMASK(ufr) | CMASK(ufl) | CMASK(ubl) | CMASK(ubr))
228#define _cmask_d (CMASK(dfr) | CMASK(dfl) | CMASK(dbl) | CMASK(dbr))
229#define _cmask_r (CMASK(ufr) | CMASK(dfr) | CMASK(ubr) | CMASK(dbr))
230#define _cmask_l (CMASK(ufl) | CMASK(dfl) | CMASK(ubl) | CMASK(dbl))
231#define _cmask_f (CMASK(ufr) | CMASK(ufl) | CMASK(dfr) | CMASK(dfl))
232#define _cmask_b (CMASK(ubr) | CMASK(ubl) | CMASK(dbr) | CMASK(dbl))
233
234#define _eomask (EOMASK(uf) | EOMASK(ul) | EOMASK(ub) | EOMASK(ur) \
235 EOMASK(df) | EOMASK(dl) | EOMASK(db) | EOMASK(dr) \
236 EOMASK(fr) | EOMASK(fl) | EOMASK(bl) | EOMASK(br))
237#define _comask (COMASK(ufr) | COMASK(ufl) | COMASK(ubl) | COMASK(ubr) \
238 COMASK(dfr) | COMASK(dfl) | COMASK(dbl) | COMASK(dbr))
239
240static uint64_t permsign(uint64_t *, int);
241static uint64_t readep(char *);
242static uint64_t readeo(char *);
243static uint64_t readcp(char *);
244static uint64_t readco(char *);
245static uint64_t readmove(char);
246static uint64_t readmodifier(char);
247
248static char *edgestr[] = {
249 [_edge_uf] = "UF",
250 [_edge_ub] = "UB",
251 [_edge_db] = "DB",
252 [_edge_df] = "DF",
253 [_edge_ur] = "UR",
254 [_edge_ul] = "UL",
255 [_edge_dl] = "DL",
256 [_edge_dr] = "DR",
257 [_edge_fr] = "FR",
258 [_edge_fl] = "FL",
259 [_edge_bl] = "BL",
260 [_edge_br] = "BR"
261};
262static char *cornerstr[] = {
263 [_corner_ufr] = "UFR",
264 [_corner_ubl] = "UBL",
265 [_corner_dfl] = "DFL",
266 [_corner_dbr] = "DBR",
267 [_corner_ufl] = "UFL",
268 [_corner_ubr] = "UBR",
269 [_corner_dfr] = "DFR",
270 [_corner_dbl] = "DBL"
271};
272static char *movestr[] = {
273 [U] = "U", [U2] = "U2", [U3] = "U'",
274 [D] = "D", [D2] = "D2", [D3] = "D'",
275 [R] = "R", [R2] = "R2", [R3] = "R'",
276 [L] = "L", [L2] = "L2", [L3] = "L'",
277 [F] = "F", [F2] = "F2", [F3] = "F'",
278 [B] = "B", [B2] = "B2", [B3] = "B'",
279};
280
281cube_t solvedcube = {
282 .e = ESOLVED(uf) | ESOLVED(ul) | ESOLVED(ub) | ESOLVED(ur) |
283 ESOLVED(df) | ESOLVED(dl) | ESOLVED(db) | ESOLVED(dr) |
284 ESOLVED(fr) | ESOLVED(fl) | ESOLVED(bl) | ESOLVED(br),
285 .c = CSOLVED(ufr) | CSOLVED(ufl) | CSOLVED(ubl) | CSOLVED(ubr) |
286 CSOLVED(dfr) | CSOLVED(dfl) | CSOLVED(dbl) | CSOLVED(dbr),
287};
288cube_t errorcube = { .e = _error, .c = _error };
289
290
291static uint64_t
292permsign(uint64_t *a, int n)
293{
294 int i, j;
295 uint64_t ret;
296
297 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
306bool
307isconsistent(cube_t cube)
308{
309 uint64_t p[12], sum;
310 bool found[12];
311 int i;
312
313 sum = 0;
314
315 /* Check for EP consistency */
316 for (i = 0; i < 12; i++)
317 found[i] = false;
318 for (i = 0; i < 12; i++) {
319 p[i] = EPAT(cube.e, i);
320 found[p[i]] = true;
321 }
322 for (i = 0; i < 12; i++)
323 if (!found[i])
324 return false;
325 sum = permsign(p, 12);
326
327 /* Check for CP consistency */
328 for (i = 0; i < 8; i++)
329 found[i] = false;
330 for (i = 0; i < 8; i++) {
331 p[i] = CPAT(cube.c, i);
332 found[p[i]] = true;
333 }
334 for (i = 0; i < 8; i++)
335 if (!found[i])
336 return false;
337 sum += permsign(p, 8);
338
339 /* Check permutation parity */
340 if (sum % 2 != 0)
341 return false;
342
343 /* Check for EO parity */
344 for (i = 0, sum = 0; i < 12; i++)
345 sum += EOAT(cube.e, i);
346 if (sum % 2 != 0)
347 return false;
348
349 /* Check for CO parity */
350 for (i = 0, sum = 0; i < 8; i++)
351 sum += COAT(cube.c, i);
352 if (sum % 3 != 0)
353 return false;
354
355 /* Check that CO extra bit is zero */
356 for (i = 0; i < 8; i++)
357 if (cube.c & (1ULL << (5 + i * _csize)))
358 return false;
359
360 return true;
361}
362
363bool
364issolved(cube_t cube)
365{
366 return cube.c == solvedcube.c && cube.e == solvedcube.e;
367}
368
369
370static uint64_t
371readep(char *str)
372{
373 if (!strncmp(str, "UF", 2))
374 return _edge_uf;
375 if (!strncmp(str, "UL", 2))
376 return _edge_ul;
377 if (!strncmp(str, "UB", 2))
378 return _edge_ub;
379 if (!strncmp(str, "UR", 2))
380 return _edge_ur;
381 if (!strncmp(str, "DF", 2))
382 return _edge_df;
383 if (!strncmp(str, "DL", 2))
384 return _edge_dl;
385 if (!strncmp(str, "DB", 2))
386 return _edge_db;
387 if (!strncmp(str, "DR", 2))
388 return _edge_dr;
389 if (!strncmp(str, "FR", 2))
390 return _edge_fr;
391 if (!strncmp(str, "FL", 2))
392 return _edge_fl;
393 if (!strncmp(str, "BL", 2))
394 return _edge_bl;
395 if (!strncmp(str, "BR", 2))
396 return _edge_br;
397
398 return _error;
399}
400
401static uint64_t
402readeo(char *str)
403{
404 if (*str == '0')
405 return 0ULL;
406 if (*str == '1')
407 return 1ULL;
408
409 return _error;
410}
411
412static uint64_t
413readcp(char *str)
414{
415 if (!strncmp(str, "UFR", 3) || !strncmp(str, "URF", 3))
416 return _corner_ufr;
417 if (!strncmp(str, "UFL", 3) || !strncmp(str, "ULF", 3))
418 return _corner_ufl;
419 if (!strncmp(str, "UBL", 3) || !strncmp(str, "ULB", 3))
420 return _corner_ubl;
421 if (!strncmp(str, "UBR", 3) || !strncmp(str, "URB", 3))
422 return _corner_ubr;
423 if (!strncmp(str, "DFR", 3) || !strncmp(str, "DRF", 3))
424 return _corner_dfr;
425 if (!strncmp(str, "DFL", 3) || !strncmp(str, "DLF", 3))
426 return _corner_dfl;
427 if (!strncmp(str, "DBL", 3) || !strncmp(str, "DLB", 3))
428 return _corner_dbl;
429 if (!strncmp(str, "DBR", 3) || !strncmp(str, "DRB", 3))
430 return _corner_dbr;
431
432 return _error;
433}
434
435static uint64_t
436readco(char *str)
437{
438 if (*str == '0')
439 return 0ULL;
440 if (*str == '1')
441 return 1ULL;
442 if (*str == '2')
443 return 2ULL;
444
445 return _error;
446}
447
448cube_t
449readcube(char *buf)
450{
451 int i;
452 uint64_t piece, orient;
453 cube_t ret = {0};
454 char *b = buf;
455
456 for (i = 0; i < 12; i++) {
457 while (*b == ' ' || *b == '\t' || *b == '\n')
458 b++;
459 if ((piece = readep(b)) == _error)
460 goto readcube_error;
461 b += 2;
462 if ((orient = readeo(b)) == _error)
463 goto readcube_error;
464 b++;
465 ret.e |= (piece << ESHIFT(i)) | (orient << EOSHIFT(i));
466 }
467 for (i = 0; i < 8; i++) {
468 while (*b == ' ' || *b == '\t' || *b == '\n')
469 b++;
470 if ((piece = readcp(b)) == _error)
471 goto readcube_error;
472 b += 3;
473 if ((orient = readco(b)) == _error)
474 goto readcube_error;
475 b++;
476 ret.c |= (piece << CSHIFT(i)) | (orient << COSHIFT(i));
477 }
478
479 return ret;
480
481readcube_error:
482 return errorcube;
483}
484
485void
486writecube(cube_t cube, char *buf)
487{
488 char *errormsg;
489 uint64_t piece;
490 size_t len;
491 int i;
492
493 if (!isconsistent(cube)) {
494 errormsg = "ERROR: cannot write inconsistent cube";
495 goto writecube_error;
496 }
497
498 for (i = 0; i < 12; i++) {
499 piece = EPAT(cube.e, i);
500 buf[4*i ] = edgestr[piece][0];
501 buf[4*i + 1] = edgestr[piece][1];
502 buf[4*i + 2] = EOAT(cube.e, i) + '0';
503 buf[4*i + 3] = ' ';
504 }
505 for (i = 0; i < 8; i++) {
506 piece = CPAT(cube.c, i);
507 buf[48 + 5*i ] = cornerstr[piece][0];
508 buf[48 + 5*i + 1] = cornerstr[piece][1];
509 buf[48 + 5*i + 2] = cornerstr[piece][2];
510 buf[48 + 5*i + 3] = COAT(cube.c, i) + '0';
511 buf[48 + 5*i + 4] = ' ';
512 }
513
514 buf[48+39] = '\0';
515
516 return;
517
518writecube_error:
519 len = strlen(errormsg);
520 strcpy(buf, errormsg);
521 buf[len] = '\n';
522 buf[len+1] = '\0';
523}
524
525
526static uint64_t
527readmove(char c)
528{
529 switch (c) {
530 case 'U':
531 return U;
532 case 'D':
533 return D;
534 case 'R':
535 return R;
536 case 'L':
537 return L;
538 case 'F':
539 return F;
540 case 'B':
541 return B;
542 default:
543 return _error;
544 }
545}
546
547static uint64_t
548readmodifier(char c)
549{
550 switch (c) {
551 case '1': /* Fallthrough */
552 case '2': /* Fallthrough */
553 case '3':
554 return c - '0' - 1;
555 case '\'':
556 return 2;
557 default:
558 return 0;
559 }
560}
561
562int
563readmoves(char *buf, move_t *m)
564{
565 int n;
566 uint64_t r;
567 char *b;
568
569 for (b = buf, n = 0; *b != '\0'; b++) {
570 while (*b == ' ' || *b == '\t' || *b == '\n')
571 b++;
572 if ((r = readmove(*b)) == _error)
573 return -1;
574 m[n] = (move_t)r;
575 if ((r = readmodifier(*(b+1))) != 0) {
576 b++;
577 m[n] += r;
578 }
579 n++;
580 }
581
582 return n;
583}
584
585void
586writemoves(move_t *m, int n, char *buf)
587{
588 int i;
589 char *b, *s;
590
591 for (i = 0, b = buf; i < n; i++, b++) {
592 s = movestr[m[i]];
593 strcpy(b, s);
594 b += strlen(s);
595 *b = ' ';
596 }
597 *b = '\0';
598}
599
600
601cube_t
602move(move_t m, cube_t c)
603{
604 /* TODO - not implemented yet */
605
606 cube_t ret = {0};
607
608 switch (m) {
609 case U:
610 return ret;
611 default:
612 return ret;
613 }
614}
diff --git a/src/cube.sync-conflict-20230524-182143-JOKKFPA.h b/src/cube.sync-conflict-20230524-182143-JOKKFPA.h
new file mode 100644
index 0000000..2763d16
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-182143-JOKKFPA.h
@@ -0,0 +1,27 @@
1typedef enum {
2 U, U2, U3, D, D2, D3,
3 R, R2, R3, L, L2, L3,
4 F, F2, F3, B, B2, B3
5} move_t;
6typedef struct {
7 uint64_t e;
8 uint64_t c;
9} cube_t;
10
11extern cube_t solvedcube;
12extern cube_t errorcube;
13
14bool isconsistent(cube_t);
15bool issolved(cube_t);
16
17cube_t readcube(char *);
18void writecube(cube_t, char *);
19
20int readmoves(char *, move_t *);
21void writemoves(move_t *, int, char *);
22
23/*
24cube_t move(move_t, cube_t);
25cube_t inverse(cube_t);
26
27*/
diff --git a/src/cube.sync-conflict-20230524-182146-JOKKFPA.c b/src/cube.sync-conflict-20230524-182146-JOKKFPA.c
new file mode 100644
index 0000000..e64acca
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-182146-JOKKFPA.c
@@ -0,0 +1,614 @@
1/*
2# Cube representation, moves, transformations and indexing
3
4## String description
5
6The functions readcube() and writecube() use the following format.
7Each edge is represented by two letters denoting the sides it belongs to
8and one number denoting its orientation (0 oriented, 1 mis-oriented).
9Similarly, each corner is represented by three letters and a number
10(0 oriented, 1 twisted clockwise, 2 twisted counter-clockwise).
11Edge orientation is relative to the F / B axis, corner orientation is
12relative to the U / D axis.
13
14The correct order of the pieces is the same as that defined in the
15section "Internal cube representation", except that pieces are read
16left-to-right. Pieces are divided by slices, so the ordering is not the
17most intuitive, but it is more convenient for the internal representation.
18
19Whitespaces between pieces are ignored when reading the cube, and a
20single whitespace character is added between pieces when writing.
21
22For example, the solved cube looks like this:
23
24UF0 UB0 DB0 DF0 UR0 UL0 DL0 DR0 FR0 FL0 BL0 BR0 UFR0 UBL0 DFL0 DBR0 UFL0 UBR0 DFR0 DBL0
25
26The cube after the moves R'U'F looks like this:
27
28FL1 BR0 DB0 UR1 UF0 UB0 DL0 FR0 UL1 DF1 BL0 DR0 UBL1 DBR1 UFR2 DFR2 DFL2 UBL2 UFL2 DBL0
29
30More formats might be supported in the future.
31
32## Internal cube representation
33
34The cube_t data structure implemented in this file is designed to
35efficiently perform common operations on a 3x3x3 Rubik's cube when
36solving it with an iterative-deepening DFS search. It is not the most
37general, complete, easy to read or compact one. Since the cube can
38be trivially reoriented before the search, we only encode permutations
39of the cube that keep the center pieces in a fixed position (that is,
40we do not encode the position of the centers).
41
42The cube state is encoded in two 64-bit integers, one for edges and one
43for centers. We explain how edges are encoded first, and the highlight
44the few differences with corners afterwards.
45
46For encoding edges, only the 60 least-significant bits are used. Each
47edge described by 5 bits. The position of a 5-bit block in the 64-bit
48integer determine the position of the edge piece in the cube, according
49to the following table (least-significant bits on the right):
50
5155-59 50-54 45-49 40-44 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
52 BR BL FL FR DR DL UL UR DF DB UB UF
53ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee ossee
54
55For each edge, the 4 least-significant bits ('ssee' in the table)
56determine the piece. The two bits marked with 'ss' determine the internal
57slice the piece belongs to, i.e. they are either '00' for M, '01' for
58S or '10' for E. The other two bits (marked with 'ee') determine the
59actual edge piece among the 4 in the same slice, and they are assigned
60somewhat arbitarily. Using this representation and the ordering defined
61in the table above, the edges are correctly permuted when these 4 bits
62for each represent the numbers 0 to 11 in the correct order.
63
64The last bit determines the orientation. The orientation of an edge
65depends on its position, and it is defined being 0 if the edge can be
66moved to its place in the solved orientation by permutations in the
67subgroup <U, D, R, L, F2, B2>.
68
69Corners are encoded in the 48 least-significant bits, and are described
70by 6 bits each, their position being defined by the following table:
71
72 35-39 30-34 25-29 20-24 15-19 10-14 5-9 0-4
73 DBL DFL UBR UFL DBR DFL UBL UFR
74oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc oooxcc
75
76The bit marked with an 'x' describes the axis the corner belongs to.
77The 0 axis consists of the corners UFR, UBL, DFL and DBR, and the other
78four corners form the axis marked with 1. Then two bits are needed to
79identify the corner among the four of the same axis. The last three bits
80determine the orientation, where one corner is defined to be oriented
81(marked with '000') if its top or bottom sticker faces the top or bottom
82side. A corner a clockwise turn away from being oriented, thus requiring
83a counter-clockwise turn to be oriented correctly, is marked with '001',
84and a corner a counter-clockwise turn away is marked with '010'. The most
85significant bit is not used to determine the corner orientation, but it
86must always be set to '0' to simplify the moving operations (see below).
87
88## Basic moves
89
90The 18 basic moves of the cube could be performed by applying a suitable
91general permutation (see below), but they have instead been manually
92implemented with a few simple operations each, to improve performance.
93
94For each move we first permute the pieces. This amounts to shifting
95around 4 blocks of bits for edges and 4 for corners. Since in some cases
96adjacent pieces on the cube are also adjacent in the bit representation we
97use, we can save some operations by shifting multiple blocks together.
98For example, for the move U for edges we shift a block of 15 bits 5
99positions to the left and a block of 5 bits 15 positions to the right.
100
101There are some moves that change the orientation of the pieces. Namely,
102the moves F, F', B and B' change the orientation of the edges and those
103moves as well as R, R', L and L' change the orientation of the corners.
104Edge orientation is easy to address: we simply xor the edge representation
105by a bit mask with zeroes everywhere except for the 4 edges that need
106to be flipped (i.e. the ones on the twisted face).
107
108Corner orientation is harder to reproduce efficiently working only with
109bitwise operations, as it involves performing operations modulo 3.
110However, with the help of the extra bit we reserved, we are able to
111do this using only two additions and 3 bitwise operations, without
112any multiplication, division or modulo operation. The trick is
113to use the following formula to sum two numbers x, y in {0,1,2}:
114
115 ((x+y) + (x+y+1)/4) % 4
116
117The thrid bit is needed because x+y and x+y+1 can exceed 3.
118See below (in the code) for the details.
119
120## Inverting the cube
121
122TODO
123
124## Transformations (conjugations by full-cube rotations)
125
126TODO
127
128## Indexing
129
130TODO - subgroup description etc
131
132Ideas for pruning (for another file?):
133- Use corner separation + CO as main coordinate (~150k states)
134 - for huge tables, htr corners can be used (6 times larger)
135- Symmetry table, one entry or each main coordinate value with the
136 following info:
137 - index of the corresponding main symcoord (13 bits)
138 - transtorep (6 bits)
139 - base value for pruning table (5 bits, probably 4)
140 - pruning value for only main coord, i.e. fallback (4 bits)
141- To get a full coord for the cube:
142 - get first coord c, get the transtorep
143 - transform edges with transtorep
144 - get second coordinate e
145 - return c * MAXE + e
146 - This is still too big, so divide by a power of 2 to get the hashed index
147 - second coordinate: ep always, + varible number of eo bit (0 to 11)
148- Generate table:
149 - first probe for base value:
150 - solve coord using fallback table for pruning for 10k random states or so
151 - loop over all possible values (even if going for smaller table)
152 - with inverse-index strategy or what?
153 - use 1 bit per entry (more than base value or not)
154*/
155
156#include <stdbool.h>
157#include <stdint.h>
158#include <string.h>
159
160#ifdef DEBUG
161#include <stdio.h>
162#endif
163
164#include "cube.h"
165
166#define _error 0xFFFFFFFF
167
168#define _esize 5ULL
169#define _eoblock 0x10ULL /* 10000 */
170#define _epblock 0x0FULL /* 01111 */
171#define _eblock 0x1FULL /* 11111 */
172
173#define _csize 6ULL
174#define _coblock 0x18ULL /* 011000 */
175#define _cpblock 0x07ULL /* 000111 */
176#define _cblock 0x3FULL /* 111111 */
177
178#define _edge_uf 0ULL /* 00 00 */
179#define _edge_ub 1ULL /* 00 01 */
180#define _edge_db 2ULL /* 00 10 */
181#define _edge_df 3ULL /* 00 11 */
182#define _edge_ur 4ULL /* 01 00 */
183#define _edge_ul 5ULL /* 01 01 */
184#define _edge_dl 6ULL /* 01 10 */
185#define _edge_dr 7ULL /* 01 11 */
186#define _edge_fr 8ULL /* 10 00 */
187#define _edge_fl 9ULL /* 10 01 */
188#define _edge_bl 10ULL /* 10 10 */
189#define _edge_br 11ULL /* 10 11 */
190
191#define _corner_ufr 0ULL /* 0 00 */
192#define _corner_ubl 1ULL /* 0 01 */
193#define _corner_dfl 2ULL /* 0 10 */
194#define _corner_dbr 3ULL /* 0 11 */
195#define _corner_ufl 4ULL /* 1 00 */
196#define _corner_ubr 5ULL /* 1 01 */
197#define _corner_dfr 6ULL /* 1 10 */
198#define _corner_dbl 7ULL /* 1 11 */
199
200#define ESHIFT(i) ((i) * _esize)
201#define EOSHIFT(i) (4ULL + (i) * _esize)
202#define EMASK(i) (_eblock << ESHIFT(i))
203#define EOMASK(i) (_eoblock << ESHIFT(i))
204#define EPMASK(i) (_epblock << ESHIFT(i))
205#define ESOLVED(ee) (_edge_##ee << (_edge_##ee * _esize))
206#define EDGEAT(e, i) (((e) & EMASK(i)) >> ESHIFT(i))
207#define EOAT(e, i) (((e) & EOMASK(i)) >> EOSHIFT(i))
208#define EPAT(e, i) (((e) & EPMASK(i)) >> ESHIFT(i))
209
210#define CSHIFT(i) ((i) * _csize)
211#define COSHIFT(i) (3ULL + (i) * _csize)
212#define CMASK(i) (_cblock << CSHIFT(i))
213#define COMASK(i) (_coblock << CSHIFT(i))
214#define CPMASK(i) (_cpblock << CSHIFT(i))
215#define CSOLVED(ccc) (_corner_##ccc << (_corner_##ccc * _csize))
216#define CORNERAT(c, i) (((c) & CMASK(i)) >> CSHIFT(i))
217#define COAT(c, i) (((c) & COMASK(i)) >> COSHIFT(i))
218#define CPAT(c, i) (((c) & CPMASK(i)) >> CSHIFT(i))
219
220#define _emask_u (EMASK(uf) | EMASK(ul) | EMASK(ub) | EMASK(ur))
221#define _emask_d (EMASK(df) | EMASK(dl) | EMASK(db) | EMASK(dr))
222#define _emask_r (EMASK(ur) | EMASK(dr) | EMASK(fr) | EMASK(br))
223#define _emask_l (EMASK(ul) | EMASK(dl) | EMASK(fl) | EMASK(bl))
224#define _emask_f (EMASK(uf) | EMASK(df) | EMASK(fr) | EMASK(fl))
225#define _emask_b (EMASK(ub) | EMASK(db) | EMASK(br) | EMASK(bl))
226
227#define _cmask_u (CMASK(ufr) | CMASK(ufl) | CMASK(ubl) | CMASK(ubr))
228#define _cmask_d (CMASK(dfr) | CMASK(dfl) | CMASK(dbl) | CMASK(dbr))
229#define _cmask_r (CMASK(ufr) | CMASK(dfr) | CMASK(ubr) | CMASK(dbr))
230#define _cmask_l (CMASK(ufl) | CMASK(dfl) | CMASK(ubl) | CMASK(dbl))
231#define _cmask_f (CMASK(ufr) | CMASK(ufl) | CMASK(dfr) | CMASK(dfl))
232#define _cmask_b (CMASK(ubr) | CMASK(ubl) | CMASK(dbr) | CMASK(dbl))
233
234#define _eomask (EOMASK(uf) | EOMASK(ul) | EOMASK(ub) | EOMASK(ur) \
235 EOMASK(df) | EOMASK(dl) | EOMASK(db) | EOMASK(dr) \
236 EOMASK(fr) | EOMASK(fl) | EOMASK(bl) | EOMASK(br))
237#define _comask (COMASK(ufr) | COMASK(ufl) | COMASK(ubl) | COMASK(ubr) \
238 COMASK(dfr) | COMASK(dfl) | COMASK(dbl) | COMASK(dbr))
239
240static uint64_t permsign(uint64_t *, int);
241static uint64_t readep(char *);
242static uint64_t readeo(char *);
243static uint64_t readcp(char *);
244static uint64_t readco(char *);
245static uint64_t readmove(char);
246static uint64_t readmodifier(char);
247
248static char *edgestr[] = {
249 [_edge_uf] = "UF",
250 [_edge_ub] = "UB",
251 [_edge_db] = "DB",
252 [_edge_df] = "DF",
253 [_edge_ur] = "UR",
254 [_edge_ul] = "UL",
255 [_edge_dl] = "DL",
256 [_edge_dr] = "DR",
257 [_edge_fr] = "FR",
258 [_edge_fl] = "FL",
259 [_edge_bl] = "BL",
260 [_edge_br] = "BR"
261};
262static char *cornerstr[] = {
263 [_corner_ufr] = "UFR",
264 [_corner_ubl] = "UBL",
265 [_corner_dfl] = "DFL",
266 [_corner_dbr] = "DBR",
267 [_corner_ufl] = "UFL",
268 [_corner_ubr] = "UBR",
269 [_corner_dfr] = "DFR",
270 [_corner_dbl] = "DBL"
271};
272static char *movestr[] = {
273 [U] = "U", [U2] = "U2", [U3] = "U'",
274 [D] = "D", [D2] = "D2", [D3] = "D'",
275 [R] = "R", [R2] = "R2", [R3] = "R'",
276 [L] = "L", [L2] = "L2", [L3] = "L'",
277 [F] = "F", [F2] = "F2", [F3] = "F'",
278 [B] = "B", [B2] = "B2", [B3] = "B'",
279};
280
281cube_t solvedcube = {
282 .e = ESOLVED(uf) | ESOLVED(ul) | ESOLVED(ub) | ESOLVED(ur) |
283 ESOLVED(df) | ESOLVED(dl) | ESOLVED(db) | ESOLVED(dr) |
284 ESOLVED(fr) | ESOLVED(fl) | ESOLVED(bl) | ESOLVED(br),
285 .c = CSOLVED(ufr) | CSOLVED(ufl) | CSOLVED(ubl) | CSOLVED(ubr) |
286 CSOLVED(dfr) | CSOLVED(dfl) | CSOLVED(dbl) | CSOLVED(dbr),
287};
288cube_t errorcube = { .e = _error, .c = _error };
289
290
291static uint64_t
292permsign(uint64_t *a, int n)
293{
294 int i, j;
295 uint64_t ret;
296
297 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
306bool
307isconsistent(cube_t cube)
308{
309 uint64_t p[12], sum;
310 bool found[12];
311 int i;
312
313 sum = 0;
314
315 /* Check for EP consistency */
316 for (i = 0; i < 12; i++)
317 found[i] = false;
318 for (i = 0; i < 12; i++) {
319 p[i] = EPAT(cube.e, i);
320 found[p[i]] = true;
321 }
322 for (i = 0; i < 12; i++)
323 if (!found[i])
324 return false;
325 sum = permsign(p, 12);
326
327 /* Check for CP consistency */
328 for (i = 0; i < 8; i++)
329 found[i] = false;
330 for (i = 0; i < 8; i++) {
331 p[i] = CPAT(cube.c, i);
332 found[p[i]] = true;
333 }
334 for (i = 0; i < 8; i++)
335 if (!found[i])
336 return false;
337 sum += permsign(p, 8);
338
339 /* Check permutation parity */
340 if (sum % 2 != 0)
341 return false;
342
343 /* Check for EO parity */
344 for (i = 0, sum = 0; i < 12; i++)
345 sum += EOAT(cube.e, i);
346 if (sum % 2 != 0)
347 return false;
348
349 /* Check for CO parity */
350 for (i = 0, sum = 0; i < 8; i++)
351 sum += COAT(cube.c, i);
352 if (sum % 3 != 0)
353 return false;
354
355 /* Check that CO extra bit is zero */
356 for (i = 0; i < 8; i++)
357 if (cube.c & (1ULL << (5 + i * _csize)))
358 return false;
359
360 return true;
361}
362
363bool
364issolved(cube_t cube)
365{
366 return cube.c == solvedcube.c && cube.e == solvedcube.e;
367}
368
369
370static uint64_t
371readep(char *str)
372{
373 if (!strncmp(str, "UF", 2))
374 return _edge_uf;
375 if (!strncmp(str, "UL", 2))
376 return _edge_ul;
377 if (!strncmp(str, "UB", 2))
378 return _edge_ub;
379 if (!strncmp(str, "UR", 2))
380 return _edge_ur;
381 if (!strncmp(str, "DF", 2))
382 return _edge_df;
383 if (!strncmp(str, "DL", 2))
384 return _edge_dl;
385 if (!strncmp(str, "DB", 2))
386 return _edge_db;
387 if (!strncmp(str, "DR", 2))
388 return _edge_dr;
389 if (!strncmp(str, "FR", 2))
390 return _edge_fr;
391 if (!strncmp(str, "FL", 2))
392 return _edge_fl;
393 if (!strncmp(str, "BL", 2))
394 return _edge_bl;
395 if (!strncmp(str, "BR", 2))
396 return _edge_br;
397
398 return _error;
399}
400
401static uint64_t
402readeo(char *str)
403{
404 if (*str == '0')
405 return 0ULL;
406 if (*str == '1')
407 return 1ULL;
408
409 return _error;
410}
411
412static uint64_t
413readcp(char *str)
414{
415 if (!strncmp(str, "UFR", 3) || !strncmp(str, "URF", 3))
416 return _corner_ufr;
417 if (!strncmp(str, "UFL", 3) || !strncmp(str, "ULF", 3))
418 return _corner_ufl;
419 if (!strncmp(str, "UBL", 3) || !strncmp(str, "ULB", 3))
420 return _corner_ubl;
421 if (!strncmp(str, "UBR", 3) || !strncmp(str, "URB", 3))
422 return _corner_ubr;
423 if (!strncmp(str, "DFR", 3) || !strncmp(str, "DRF", 3))
424 return _corner_dfr;
425 if (!strncmp(str, "DFL", 3) || !strncmp(str, "DLF", 3))
426 return _corner_dfl;
427 if (!strncmp(str, "DBL", 3) || !strncmp(str, "DLB", 3))
428 return _corner_dbl;
429 if (!strncmp(str, "DBR", 3) || !strncmp(str, "DRB", 3))
430 return _corner_dbr;
431
432 return _error;
433}
434
435static uint64_t
436readco(char *str)
437{
438 if (*str == '0')
439 return 0ULL;
440 if (*str == '1')
441 return 1ULL;
442 if (*str == '2')
443 return 2ULL;
444
445 return _error;
446}
447
448cube_t
449readcube(char *buf)
450{
451 int i;
452 uint64_t piece, orient;
453 cube_t ret = {0};
454 char *b = buf;
455
456 for (i = 0; i < 12; i++) {
457 while (*b == ' ' || *b == '\t' || *b == '\n')
458 b++;
459 if ((piece = readep(b)) == _error)
460 goto readcube_error;
461 b += 2;
462 if ((orient = readeo(b)) == _error)
463 goto readcube_error;
464 b++;
465 ret.e |= (piece << ESHIFT(i)) | (orient << EOSHIFT(i));
466 }
467 for (i = 0; i < 8; i++) {
468 while (*b == ' ' || *b == '\t' || *b == '\n')
469 b++;
470 if ((piece = readcp(b)) == _error)
471 goto readcube_error;
472 b += 3;
473 if ((orient = readco(b)) == _error)
474 goto readcube_error;
475 b++;
476 ret.c |= (piece << CSHIFT(i)) | (orient << COSHIFT(i));
477 }
478
479 return ret;
480
481readcube_error:
482 return errorcube;
483}
484
485void
486writecube(cube_t cube, char *buf)
487{
488 char *errormsg;
489 uint64_t piece;
490 size_t len;
491 int i;
492
493 if (!isconsistent(cube)) {
494 errormsg = "ERROR: cannot write inconsistent cube";
495 goto writecube_error;
496 }
497
498 for (i = 0; i < 12; i++) {
499 piece = EPAT(cube.e, i);
500 buf[4*i ] = edgestr[piece][0];
501 buf[4*i + 1] = edgestr[piece][1];
502 buf[4*i + 2] = EOAT(cube.e, i) + '0';
503 buf[4*i + 3] = ' ';
504 }
505 for (i = 0; i < 8; i++) {
506 piece = CPAT(cube.c, i);
507 buf[48 + 5*i ] = cornerstr[piece][0];
508 buf[48 + 5*i + 1] = cornerstr[piece][1];
509 buf[48 + 5*i + 2] = cornerstr[piece][2];
510 buf[48 + 5*i + 3] = COAT(cube.c, i) + '0';
511 buf[48 + 5*i + 4] = ' ';
512 }
513
514 buf[48+39] = '\0';
515
516 return;
517
518writecube_error:
519 len = strlen(errormsg);
520 strcpy(buf, errormsg);
521 buf[len] = '\n';
522 buf[len+1] = '\0';
523}
524
525
526static uint64_t
527readmove(char c)
528{
529 switch (c) {
530 case 'U':
531 return U;
532 case 'D':
533 return D;
534 case 'R':
535 return R;
536 case 'L':
537 return L;
538 case 'F':
539 return F;
540 case 'B':
541 return B;
542 default:
543 return _error;
544 }
545}
546
547static uint64_t
548readmodifier(char c)
549{
550 switch (c) {
551 case '1': /* Fallthrough */
552 case '2': /* Fallthrough */
553 case '3':
554 return c - '0' - 1;
555 case '\'':
556 return 2;
557 default:
558 return 0;
559 }
560}
561
562int
563readmoves(char *buf, move_t *m)
564{
565 int n;
566 uint64_t r;
567 char *b;
568
569 for (b = buf, n = 0; *b != '\0'; b++) {
570 while (*b == ' ' || *b == '\t' || *b == '\n')
571 b++;
572 if ((r = readmove(*b)) == _error)
573 return -1;
574 m[n] = (move_t)r;
575 if ((r = readmodifier(*(b+1))) != 0) {
576 b++;
577 m[n] += r;
578 }
579 n++;
580 }
581
582 return n;
583}
584
585void
586writemoves(move_t *m, int n, char *buf)
587{
588 int i;
589 char *b, *s;
590
591 for (i = 0, b = buf; i < n; i++, b++) {
592 s = movestr[m[i]];
593 strcpy(b, s);
594 b += strlen(s);
595 *b = ' ';
596 }
597 *b = '\0';
598}
599
600
601cube_t
602move(move_t m, cube_t c)
603{
604 /* TODO - not implemented yet */
605
606 cube_t ret = {0};
607
608 switch (m) {
609 case U:
610 return ret;
611 default:
612 return ret;
613 }
614}
diff --git a/src/cube.sync-conflict-20230524-182146-JOKKFPA.h b/src/cube.sync-conflict-20230524-182146-JOKKFPA.h
new file mode 100644
index 0000000..2763d16
--- /dev/null
+++ b/src/cube.sync-conflict-20230524-182146-JOKKFPA.h
@@ -0,0 +1,27 @@
1typedef enum {
2 U, U2, U3, D, D2, D3,
3 R, R2, R3, L, L2, L3,
4 F, F2, F3, B, B2, B3
5} move_t;
6typedef struct {
7 uint64_t e;
8 uint64_t c;
9} cube_t;
10
11extern cube_t solvedcube;
12extern cube_t errorcube;
13
14bool isconsistent(cube_t);
15bool issolved(cube_t);
16
17cube_t readcube(char *);
18void writecube(cube_t, char *);
19
20int readmoves(char *, move_t *);
21void writemoves(move_t *, int, char *);
22
23/*
24cube_t move(move_t, cube_t);
25cube_t inverse(cube_t);
26
27*/

Generated with cgit - Back to sebastiano.tronto.net