twisty_puzzles 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
- data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
- data/ext/twisty_puzzles/native/cube_average.c +184 -0
- data/ext/twisty_puzzles/native/cube_average.h +5 -0
- data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
- data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
- data/ext/twisty_puzzles/native/cube_state.c +264 -0
- data/ext/twisty_puzzles/native/cube_state.h +31 -0
- data/ext/twisty_puzzles/native/face_symbols.c +67 -0
- data/ext/twisty_puzzles/native/face_symbols.h +34 -0
- data/ext/twisty_puzzles/native/native.c +28 -0
- data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
- data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
- data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
- data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
- data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
- data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
- data/ext/twisty_puzzles/native/skewb_state.c +214 -0
- data/ext/twisty_puzzles/native/skewb_state.h +23 -0
- data/ext/twisty_puzzles/native/utils.c +76 -0
- data/ext/twisty_puzzles/native/utils.h +31 -0
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +22 -1
@@ -0,0 +1,237 @@
|
|
1
|
+
#include "skewb_coordinate.h"
|
2
|
+
|
3
|
+
#include "utils.h"
|
4
|
+
#include "cube_coordinate.h"
|
5
|
+
|
6
|
+
static VALUE SkewbCoordinateClass = Qnil;
|
7
|
+
static ID center_part_type_id = 0;
|
8
|
+
static ID corner_part_type_id = 0;
|
9
|
+
|
10
|
+
typedef struct {
|
11
|
+
face_index_t on_face_index;
|
12
|
+
SkewbPartType part_type;
|
13
|
+
size_t within_face_index;
|
14
|
+
} SkewbCoordinateData;
|
15
|
+
|
16
|
+
static size_t SkewbCoordinateData_size(const void* const ptr) {
|
17
|
+
return sizeof(SkewbCoordinateData);
|
18
|
+
}
|
19
|
+
|
20
|
+
static const rb_data_type_t SkewbCoordinateData_type = {
|
21
|
+
"SkewbTrainer::Native::SkewbCoordinateData",
|
22
|
+
{NULL, NULL, SkewbCoordinateData_size, NULL},
|
23
|
+
NULL, NULL,
|
24
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
25
|
+
};
|
26
|
+
|
27
|
+
Corner rotated_corner(const Corner corner, const int rotation) {
|
28
|
+
Corner result;
|
29
|
+
for (size_t i = 0; i < 3; ++i) {
|
30
|
+
result.face_indices[i] = corner.face_indices[(i + rotation) % 3];
|
31
|
+
}
|
32
|
+
return result;
|
33
|
+
}
|
34
|
+
|
35
|
+
static size_t skewb_corner_index_component(const face_index_t face_index) {
|
36
|
+
return face_index / 3;
|
37
|
+
}
|
38
|
+
|
39
|
+
static size_t corner_within_face_index(const Corner corner) {
|
40
|
+
size_t less_significant_index, more_significant_index;
|
41
|
+
if (switch_axes(corner.face_indices[1], corner.face_indices[2])) {
|
42
|
+
less_significant_index = corner.face_indices[2];
|
43
|
+
more_significant_index = corner.face_indices[1];
|
44
|
+
} else {
|
45
|
+
less_significant_index = corner.face_indices[1];
|
46
|
+
more_significant_index = corner.face_indices[2];
|
47
|
+
}
|
48
|
+
return 1 + skewb_corner_index_component(more_significant_index) * 2 + skewb_corner_index_component(less_significant_index);
|
49
|
+
}
|
50
|
+
|
51
|
+
size_t corner_sticker_index(const Corner corner) {
|
52
|
+
return center_sticker_index(corner.face_indices[0]) + corner_within_face_index(corner);
|
53
|
+
}
|
54
|
+
|
55
|
+
size_t center_sticker_index(const face_index_t on_face_index) {
|
56
|
+
return on_face_index * skewb_stickers_per_face;
|
57
|
+
}
|
58
|
+
|
59
|
+
static VALUE part_type_from_symbol(const VALUE part_type_symbol) {
|
60
|
+
Check_Type(face_symbol, T_SYMBOL);
|
61
|
+
if (SYM2ID(part_type_symbol) == center_part_type_id) {
|
62
|
+
return CENTER;
|
63
|
+
} else if (SYM2ID(part_type_symbol) == corner_part_type_id) {
|
64
|
+
return CORNER;
|
65
|
+
} else {
|
66
|
+
rb_raise(rb_eArgError, "Invalid part type symbol %+"PRIsVALUE"", part_type_symbol);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
static VALUE part_type_to_symbol(const SkewbPartType part_type) {
|
71
|
+
// Caching these keys isn't easy because the garbage collector will get them.
|
72
|
+
switch (part_type) {
|
73
|
+
case CENTER:
|
74
|
+
return ID2SYM(center_part_type_id);
|
75
|
+
case CORNER:
|
76
|
+
return ID2SYM(corner_part_type_id);
|
77
|
+
default:
|
78
|
+
rb_raise(rb_eRuntimeError, "invalid skewb part type");
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
Corner extract_corner(const VALUE face_symbols) {
|
83
|
+
if (RARRAY_LEN(face_symbols) != 3) {
|
84
|
+
rb_raise(rb_eArgError, "A corner of a skewb must have 3 faces.");
|
85
|
+
}
|
86
|
+
Corner corner;
|
87
|
+
for (size_t i = 0; i < 3; ++i) {
|
88
|
+
corner.face_indices[i] = face_index(rb_ary_entry(face_symbols, i));
|
89
|
+
}
|
90
|
+
for (size_t i = 0; i < 3; ++i) {
|
91
|
+
if (axis_index(corner.face_indices[i]) == axis_index(corner.face_indices[(i + 1) % 3])) {
|
92
|
+
rb_raise(rb_eArgError, "A corner of a skewb must have 3 faces on different axis.");
|
93
|
+
}
|
94
|
+
}
|
95
|
+
// Check chirality
|
96
|
+
if (neighbor_face_index(corner.face_indices[0], neighbor_index(corner.face_indices[0], corner.face_indices[1]) + 1) != corner.face_indices[2]) {
|
97
|
+
rb_raise(rb_eArgError, "Corner of a skewb must have a valid chirality.");
|
98
|
+
}
|
99
|
+
return corner;
|
100
|
+
}
|
101
|
+
|
102
|
+
FaceCorners get_face_corners(const face_index_t face_index) {
|
103
|
+
FaceCorners result;
|
104
|
+
for (size_t i = 0; i < 4; ++i) {
|
105
|
+
result.corners[i].face_indices[0] = face_index;
|
106
|
+
result.corners[i].face_indices[1] = neighbor_face_index(face_index, i);
|
107
|
+
result.corners[i].face_indices[2] = neighbor_face_index(face_index, i + 1);
|
108
|
+
}
|
109
|
+
return result;
|
110
|
+
}
|
111
|
+
|
112
|
+
#define GetSkewbCoordinateData(obj, data) \
|
113
|
+
do { \
|
114
|
+
TypedData_Get_Struct((obj), SkewbCoordinateData, &SkewbCoordinateData_type, (data)); \
|
115
|
+
} while (0)
|
116
|
+
|
117
|
+
size_t SkewbCoordinate_sticker_index(const VALUE self) {
|
118
|
+
SkewbCoordinateData* data;
|
119
|
+
GetSkewbCoordinateData(self, data);
|
120
|
+
return center_sticker_index(data->on_face_index) + data->within_face_index;
|
121
|
+
}
|
122
|
+
|
123
|
+
static VALUE SkewbCoordinate_alloc(const VALUE klass) {
|
124
|
+
SkewbCoordinateData* data;
|
125
|
+
const VALUE object = TypedData_Make_Struct(klass, SkewbCoordinateData, &SkewbCoordinateData_type, data);
|
126
|
+
data->on_face_index = 0;
|
127
|
+
data->part_type = CENTER;
|
128
|
+
data->within_face_index = 0;
|
129
|
+
return object;
|
130
|
+
}
|
131
|
+
|
132
|
+
static VALUE SkewbCoordinate_for_center(const VALUE klass, const VALUE face_symbol) {
|
133
|
+
SkewbCoordinateData* data;
|
134
|
+
const VALUE object = TypedData_Make_Struct(klass, SkewbCoordinateData, &SkewbCoordinateData_type, data);
|
135
|
+
data->on_face_index = face_index(face_symbol);
|
136
|
+
data->part_type = CENTER;
|
137
|
+
data->within_face_index = 0;
|
138
|
+
return object;
|
139
|
+
}
|
140
|
+
|
141
|
+
static VALUE SkewbCoordinate_for_corner(const VALUE klass, const VALUE face_symbols) {
|
142
|
+
Corner corner = extract_corner(face_symbols);
|
143
|
+
SkewbCoordinateData* data;
|
144
|
+
const VALUE object = TypedData_Make_Struct(klass, SkewbCoordinateData, &SkewbCoordinateData_type, data);
|
145
|
+
data->on_face_index = face_index(rb_ary_entry(face_symbols, 0));
|
146
|
+
data->part_type = CORNER;
|
147
|
+
data->within_face_index = corner_within_face_index(corner);
|
148
|
+
return object;
|
149
|
+
}
|
150
|
+
|
151
|
+
static VALUE SkewbCoordinate_hash(const VALUE self) {
|
152
|
+
const SkewbCoordinateData* data;
|
153
|
+
GetSkewbCoordinateData(self, data);
|
154
|
+
|
155
|
+
st_index_t hash = rb_hash_start((st_index_t)SkewbCoordinate_hash);
|
156
|
+
hash = rb_hash_uint(hash, data->on_face_index);
|
157
|
+
hash = rb_hash_uint(hash, data->part_type);
|
158
|
+
hash = rb_hash_uint(hash, data->within_face_index);
|
159
|
+
return ST2FIX(rb_hash_end(hash));
|
160
|
+
}
|
161
|
+
|
162
|
+
static VALUE SkewbCoordinate_eql(const VALUE self, const VALUE other) {
|
163
|
+
if (self == other) {
|
164
|
+
return Qtrue;
|
165
|
+
}
|
166
|
+
if (rb_obj_class(self) != rb_obj_class(other)) {
|
167
|
+
return Qfalse;
|
168
|
+
}
|
169
|
+
const SkewbCoordinateData* self_data;
|
170
|
+
GetSkewbCoordinateData(self, self_data);
|
171
|
+
const SkewbCoordinateData* other_data;
|
172
|
+
GetSkewbCoordinateData(other, other_data);
|
173
|
+
if (self_data->on_face_index == other_data->on_face_index &&
|
174
|
+
self_data->part_type == other_data->part_type &&
|
175
|
+
self_data->within_face_index == other_data->within_face_index) {
|
176
|
+
return Qtrue;
|
177
|
+
} else {
|
178
|
+
return Qfalse;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
static VALUE SkewbCoordinate_spaceship(const VALUE self, const VALUE other) {
|
184
|
+
if (self == other) {
|
185
|
+
return INT2NUM(0);
|
186
|
+
}
|
187
|
+
if (rb_obj_class(self) != rb_obj_class(other)) {
|
188
|
+
rb_raise(rb_eTypeError, "Cannot compare two incompatible types.");
|
189
|
+
}
|
190
|
+
const SkewbCoordinateData* self_data;
|
191
|
+
GetSkewbCoordinateData(self, self_data);
|
192
|
+
const SkewbCoordinateData* other_data;
|
193
|
+
GetSkewbCoordinateData(other, other_data);
|
194
|
+
#define cmp(a, b) \
|
195
|
+
do { \
|
196
|
+
if ((a) != (b)) { \
|
197
|
+
if ((a) < (b)) { \
|
198
|
+
return INT2NUM(-1); \
|
199
|
+
} else { \
|
200
|
+
return INT2NUM(1); \
|
201
|
+
} \
|
202
|
+
} \
|
203
|
+
} while (0)
|
204
|
+
cmp(self_data->on_face_index, other_data->on_face_index);
|
205
|
+
cmp(self_data->part_type, other_data->part_type);
|
206
|
+
cmp(self_data->within_face_index, other_data->within_face_index);
|
207
|
+
#undef cmp
|
208
|
+
return 0;
|
209
|
+
}
|
210
|
+
|
211
|
+
VALUE SkewbCoordinate_face(const VALUE self) {
|
212
|
+
SkewbCoordinateData* data;
|
213
|
+
GetSkewbCoordinateData(self, data);
|
214
|
+
return face_symbol(data->on_face_index);
|
215
|
+
}
|
216
|
+
|
217
|
+
VALUE SkewbCoordinate_part_type(const VALUE self) {
|
218
|
+
SkewbCoordinateData* data;
|
219
|
+
GetSkewbCoordinateData(self, data);
|
220
|
+
return part_type_to_symbol(data->part_type);
|
221
|
+
}
|
222
|
+
|
223
|
+
void init_skewb_coordinate_class_under(const VALUE module) {
|
224
|
+
center_part_type_id = rb_intern("center");
|
225
|
+
corner_part_type_id = rb_intern("corner");
|
226
|
+
SkewbCoordinateClass = rb_define_class_under(module, "SkewbCoordinate", rb_cObject);
|
227
|
+
rb_define_alloc_func(SkewbCoordinateClass, SkewbCoordinate_alloc);
|
228
|
+
rb_define_method(SkewbCoordinateClass, "hash", SkewbCoordinate_hash, 0);
|
229
|
+
rb_define_method(SkewbCoordinateClass, "eql?", SkewbCoordinate_eql, 1);
|
230
|
+
rb_define_alias(SkewbCoordinateClass, "==", "eql?");
|
231
|
+
rb_define_method(SkewbCoordinateClass, "<=>", SkewbCoordinate_spaceship, 1);
|
232
|
+
rb_define_method(SkewbCoordinateClass, "face", SkewbCoordinate_face, 0);
|
233
|
+
rb_define_method(SkewbCoordinateClass, "part_type", SkewbCoordinate_part_type, 0);
|
234
|
+
rb_define_singleton_method(SkewbCoordinateClass, "for_center", SkewbCoordinate_for_center, 1);
|
235
|
+
rb_define_singleton_method(SkewbCoordinateClass, "for_corner", SkewbCoordinate_for_corner, 1);
|
236
|
+
}
|
237
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
#include "face_symbols.h"
|
6
|
+
|
7
|
+
#define skewb_faces cube_faces
|
8
|
+
#define skewb_stickers_per_face 5
|
9
|
+
#define total_skewb_stickers skewb_stickers_per_face * skewb_faces
|
10
|
+
|
11
|
+
typedef enum {
|
12
|
+
CENTER,
|
13
|
+
CORNER,
|
14
|
+
} SkewbPartType;
|
15
|
+
|
16
|
+
typedef struct {
|
17
|
+
face_index_t face_indices[3];
|
18
|
+
} Corner;
|
19
|
+
|
20
|
+
Corner rotated_corner(Corner corner, int rotation);
|
21
|
+
|
22
|
+
size_t corner_sticker_index(Corner corner);
|
23
|
+
|
24
|
+
size_t center_sticker_index(face_index_t on_face_index);
|
25
|
+
|
26
|
+
size_t SkewbCoordinate_sticker_index(VALUE self);
|
27
|
+
|
28
|
+
Corner extract_corner(VALUE face_symbols);
|
29
|
+
|
30
|
+
typedef struct {
|
31
|
+
Corner corners[4];
|
32
|
+
} FaceCorners;
|
33
|
+
|
34
|
+
FaceCorners get_face_corners(face_index_t face_index);
|
35
|
+
|
36
|
+
void init_skewb_coordinate_class_under(VALUE module);
|
@@ -0,0 +1,271 @@
|
|
1
|
+
#include "skewb_layer_fingerprint.h"
|
2
|
+
|
3
|
+
#include <stdint.h>
|
4
|
+
|
5
|
+
#include "skewb_state.h"
|
6
|
+
#include "skewb_coordinate.h"
|
7
|
+
#include "utils.h"
|
8
|
+
|
9
|
+
typedef enum {
|
10
|
+
BOTH_MISSING,
|
11
|
+
ONE_ORIENTED_ONE_MISSING,
|
12
|
+
ONE_TWISTED_ONE_MISSING,
|
13
|
+
BOTH_ORIENTED_AND_ADJACENT,
|
14
|
+
BOTH_ORIENTED_AND_OPPOSITE,
|
15
|
+
ONE_ORIENTED_ONE_TWISTED_AND_ADJACENT,
|
16
|
+
ONE_ORIENTED_ONE_TWISTED_AND_OPPOSITE,
|
17
|
+
BOTH_TWISTED_SAME_WAY_AND_ADJACENT,
|
18
|
+
BOTH_TWISTED_SAME_WAY_AND_OPPOSITE,
|
19
|
+
BOTH_TWISTED_OPPOSITE_WAY_AND_ADJACENT,
|
20
|
+
BOTH_TWISTED_OPPOSITE_WAY_AND_OPPOSITE,
|
21
|
+
NUM_CORNER_PAIR_TYPES,
|
22
|
+
} CornerPairType;
|
23
|
+
|
24
|
+
typedef struct {
|
25
|
+
VALUE stickers[3];
|
26
|
+
} ActualCornerStickers;
|
27
|
+
|
28
|
+
static ID plus = 868;
|
29
|
+
static ID times = 848;
|
30
|
+
|
31
|
+
#define max_corner_pair_group_size 8
|
32
|
+
|
33
|
+
typedef struct {
|
34
|
+
Corner corner_pairs[max_corner_pair_group_size][2];
|
35
|
+
int num_corner_pairs;
|
36
|
+
// Number of different group fingerprints. This can be used to merge several group fingerprints.
|
37
|
+
int num_group_fingerprints;
|
38
|
+
} CornerPairGroup;
|
39
|
+
|
40
|
+
#define num_corner_pair_groups 7
|
41
|
+
|
42
|
+
static CornerPairGroup corner_pair_groups[cube_faces][num_corner_pair_groups];
|
43
|
+
|
44
|
+
static ActualCornerStickers get_actual_corner_stickers(const SkewbStateData* const skewb_state,
|
45
|
+
const Corner corner) {
|
46
|
+
ActualCornerStickers result;
|
47
|
+
for (size_t i = 0; i < 3; ++i) {
|
48
|
+
result.stickers[i] = skewb_state->stickers[corner_sticker_index(rotated_corner(corner, i))];
|
49
|
+
}
|
50
|
+
return result;
|
51
|
+
}
|
52
|
+
|
53
|
+
static bool has_color_at(const ActualCornerStickers actual, const size_t index, const VALUE color) {
|
54
|
+
return color_eq(actual.stickers[index], color);
|
55
|
+
}
|
56
|
+
|
57
|
+
typedef struct {
|
58
|
+
int layer_index;
|
59
|
+
bool is_oriented;
|
60
|
+
bool is_present;
|
61
|
+
} ActualCornerStickersInfo;
|
62
|
+
|
63
|
+
static ActualCornerStickersInfo get_info(const ActualCornerStickers actual, const VALUE layer_color) {
|
64
|
+
ActualCornerStickersInfo info = {-1, 0, 0};
|
65
|
+
for (size_t i = 0; i < 3; ++i) {
|
66
|
+
if (has_color_at(actual, i, layer_color)) {
|
67
|
+
info.layer_index = i;
|
68
|
+
info.is_present = 1;
|
69
|
+
if (i == 0) {
|
70
|
+
info.is_oriented = 1;
|
71
|
+
}
|
72
|
+
break;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return info;
|
76
|
+
}
|
77
|
+
|
78
|
+
typedef struct {
|
79
|
+
ActualCornerStickers actual;
|
80
|
+
ActualCornerStickersInfo info;
|
81
|
+
} AnnotatedActualCornerStickers;
|
82
|
+
|
83
|
+
static AnnotatedActualCornerStickers get_annotated(const SkewbStateData* const skewb_state,
|
84
|
+
const Corner corner,
|
85
|
+
const VALUE layer_color) {
|
86
|
+
AnnotatedActualCornerStickers annotated;
|
87
|
+
annotated.actual = get_actual_corner_stickers(skewb_state, corner);
|
88
|
+
annotated.info = get_info(annotated.actual, layer_color);
|
89
|
+
return annotated;
|
90
|
+
}
|
91
|
+
|
92
|
+
static bool is_adjacent(const AnnotatedActualCornerStickers annotated_a, const AnnotatedActualCornerStickers annotated_b) {
|
93
|
+
for (size_t i = 0; i < 3; ++i) {
|
94
|
+
if (i == annotated_a.info.layer_index) {
|
95
|
+
continue;
|
96
|
+
}
|
97
|
+
const VALUE color_a = annotated_a.actual.stickers[i];
|
98
|
+
for (size_t j = 0; j < 3; ++j) {
|
99
|
+
if (j == annotated_b.info.layer_index) {
|
100
|
+
continue;
|
101
|
+
}
|
102
|
+
const VALUE color_b = annotated_b.actual.stickers[j];
|
103
|
+
if (color_eq(color_a, color_b)) {
|
104
|
+
return TRUE;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
return FALSE;
|
109
|
+
}
|
110
|
+
|
111
|
+
// Returns the basic type (i.e. without taking into account whether its adjacent or opposite corners) for two present corners.
|
112
|
+
static CornerPairType get_basic_type(const AnnotatedActualCornerStickers annotated_a, const AnnotatedActualCornerStickers annotated_b, const int num_oriented) {
|
113
|
+
// Note that we always use the adjacent variations.
|
114
|
+
switch (num_oriented) {
|
115
|
+
case 2:
|
116
|
+
return BOTH_ORIENTED_AND_ADJACENT;
|
117
|
+
case 1:
|
118
|
+
return ONE_ORIENTED_ONE_TWISTED_AND_ADJACENT;
|
119
|
+
case 0: {
|
120
|
+
if (annotated_a.info.layer_index == annotated_b.info.layer_index) {
|
121
|
+
return BOTH_TWISTED_SAME_WAY_AND_ADJACENT;
|
122
|
+
} else {
|
123
|
+
return BOTH_TWISTED_OPPOSITE_WAY_AND_ADJACENT;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
default: rb_raise(rb_eRuntimeError, "invalid num oriented");
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
static CornerPairType corner_pair_type(const SkewbStateData* const skewb_state,
|
131
|
+
const Corner corner_a,
|
132
|
+
const Corner corner_b,
|
133
|
+
const VALUE layer_color) {
|
134
|
+
const AnnotatedActualCornerStickers annotated_a = get_annotated(skewb_state, corner_a, layer_color);
|
135
|
+
const AnnotatedActualCornerStickers annotated_b = get_annotated(skewb_state, corner_b, layer_color);
|
136
|
+
const int num_oriented = annotated_a.info.is_oriented + annotated_b.info.is_oriented;
|
137
|
+
const int num_present = annotated_a.info.is_present + annotated_b.info.is_present;
|
138
|
+
switch (num_present) {
|
139
|
+
case 0: return BOTH_MISSING;
|
140
|
+
case 1:
|
141
|
+
switch (num_oriented) {
|
142
|
+
case 0: return ONE_TWISTED_ONE_MISSING;
|
143
|
+
case 1: return ONE_ORIENTED_ONE_MISSING;
|
144
|
+
default: rb_raise(rb_eRuntimeError, "invalid num oriented");
|
145
|
+
}
|
146
|
+
case 2: break; // Continue function execution.
|
147
|
+
default: rb_raise(rb_eRuntimeError, "invalid num present");
|
148
|
+
}
|
149
|
+
const CornerPairType basic_type = get_basic_type(annotated_a, annotated_b, num_oriented);
|
150
|
+
return is_adjacent(annotated_a, annotated_b) ? basic_type : basic_type + 1;
|
151
|
+
}
|
152
|
+
|
153
|
+
static uint64_t corner_pair_group_fingerprint(const SkewbStateData* const skewb_state, const CornerPairGroup group, const VALUE layer_color) {
|
154
|
+
int corner_pair_type_counts[NUM_CORNER_PAIR_TYPES];
|
155
|
+
memset(corner_pair_type_counts, 0, NUM_CORNER_PAIR_TYPES * sizeof(int));
|
156
|
+
for (size_t i = 0; i < group.num_corner_pairs; ++i) {
|
157
|
+
++corner_pair_type_counts[corner_pair_type(skewb_state, group.corner_pairs[i][0], group.corner_pairs[i][1], layer_color)];
|
158
|
+
}
|
159
|
+
uint64_t fingerprint = 0;
|
160
|
+
for (size_t i = 0; i < NUM_CORNER_PAIR_TYPES; ++i) {
|
161
|
+
fingerprint *= (group.num_corner_pairs + 1);
|
162
|
+
fingerprint |= corner_pair_type_counts[i];
|
163
|
+
}
|
164
|
+
return fingerprint;
|
165
|
+
}
|
166
|
+
|
167
|
+
static VALUE skewb_layer_fingerprint(const VALUE module, const VALUE skewb_state, const VALUE face_symbol) {
|
168
|
+
const SkewbStateData* data;
|
169
|
+
GetSkewbStateData(skewb_state, data);
|
170
|
+
const face_index_t layer_face_index = face_index(face_symbol);
|
171
|
+
const VALUE layer_color = data->stickers[center_sticker_index(layer_face_index)];
|
172
|
+
// We use a Ruby integer for accumulation because the result will overflow.
|
173
|
+
VALUE rb_fingerprint = INT2NUM(0);
|
174
|
+
int64_t last_multiplier = 0;
|
175
|
+
for (size_t i = 0; i < num_corner_pair_groups; ++i) {
|
176
|
+
if (last_multiplier > 0) {
|
177
|
+
rb_fingerprint = rb_funcall(rb_fingerprint, times, 1, INT2NUM(last_multiplier));
|
178
|
+
}
|
179
|
+
const CornerPairGroup group = corner_pair_groups[layer_face_index][i];
|
180
|
+
const uint64_t group_fingerprint = corner_pair_group_fingerprint(data, group, layer_color);
|
181
|
+
const VALUE rb_group_fingerprint = LONG2NUM(group_fingerprint);
|
182
|
+
rb_fingerprint = rb_funcall(rb_fingerprint, plus, 1, rb_group_fingerprint);
|
183
|
+
last_multiplier = group.num_group_fingerprints;
|
184
|
+
}
|
185
|
+
return rb_fingerprint;
|
186
|
+
}
|
187
|
+
|
188
|
+
static CornerPairGroup adjacent_corner_pairs_group(const FaceCorners corners) {
|
189
|
+
CornerPairGroup result;
|
190
|
+
result.num_corner_pairs = 4;
|
191
|
+
for (size_t i = 0; i < 4; ++i) {
|
192
|
+
result.corner_pairs[i][0] = corners.corners[i];
|
193
|
+
result.corner_pairs[i][1] = corners.corners[(i + 1) % 4];
|
194
|
+
}
|
195
|
+
return result;
|
196
|
+
}
|
197
|
+
|
198
|
+
static CornerPairGroup opposite_corner_pairs_group(const FaceCorners corners) {
|
199
|
+
CornerPairGroup result;
|
200
|
+
result.num_corner_pairs = 2;
|
201
|
+
for (size_t i = 0; i < 2; ++i) {
|
202
|
+
result.corner_pairs[i][0] = corners.corners[i];
|
203
|
+
result.corner_pairs[i][1] = corners.corners[i + 2];
|
204
|
+
}
|
205
|
+
return result;
|
206
|
+
}
|
207
|
+
|
208
|
+
static CornerPairGroup stacked_corner_pairs_group(const FaceCorners top_corners, const FaceCorners bottom_corners) {
|
209
|
+
CornerPairGroup result;
|
210
|
+
result.num_corner_pairs = 4;
|
211
|
+
for (size_t i = 0; i < 4; ++i) {
|
212
|
+
result.corner_pairs[i][0] = top_corners.corners[i];
|
213
|
+
result.corner_pairs[i][1] = bottom_corners.corners[3 - i];
|
214
|
+
}
|
215
|
+
return result;
|
216
|
+
}
|
217
|
+
|
218
|
+
static CornerPairGroup short_diagonal_corner_pairs_group(const FaceCorners top_corners, const FaceCorners bottom_corners) {
|
219
|
+
CornerPairGroup result;
|
220
|
+
result.num_corner_pairs = 8;
|
221
|
+
for (size_t i = 0; i < 8; ++i) {
|
222
|
+
const size_t top_index = i / 2;
|
223
|
+
const size_t bottom_index = (top_index + 1 + (i % 2) * 2) % 4;
|
224
|
+
result.corner_pairs[i][0] = top_corners.corners[top_index];
|
225
|
+
result.corner_pairs[i][1] = bottom_corners.corners[bottom_index];
|
226
|
+
}
|
227
|
+
return result;
|
228
|
+
}
|
229
|
+
|
230
|
+
static CornerPairGroup long_diagonal_corner_pairs_group(const FaceCorners top_corners, const FaceCorners bottom_corners) {
|
231
|
+
CornerPairGroup result;
|
232
|
+
result.num_corner_pairs = 4;
|
233
|
+
for (size_t i = 0; i < 4; ++i) {
|
234
|
+
result.corner_pairs[i][0] = top_corners.corners[i];
|
235
|
+
result.corner_pairs[i][1] = bottom_corners.corners[(3 - i + 2) % 4];
|
236
|
+
}
|
237
|
+
return result;
|
238
|
+
}
|
239
|
+
|
240
|
+
static void init_corner_pair_groups_for_face_index(const face_index_t layer_face_index) {
|
241
|
+
const FaceCorners top_corners = get_face_corners(layer_face_index);
|
242
|
+
const FaceCorners bottom_corners = get_face_corners(opposite_face_index(layer_face_index));
|
243
|
+
if (top_corners.corners[0].face_indices[1] == top_corners.corners[0].face_indices[2] &&
|
244
|
+
top_corners.corners[0].face_indices[2] == top_corners.corners[0].face_indices[1]) {
|
245
|
+
rb_raise(rb_eRuntimeError, "Failed initialization due to wrong order of corners.");
|
246
|
+
}
|
247
|
+
corner_pair_groups[layer_face_index][0] = adjacent_corner_pairs_group(top_corners);
|
248
|
+
corner_pair_groups[layer_face_index][1] = opposite_corner_pairs_group(top_corners);
|
249
|
+
corner_pair_groups[layer_face_index][2] = adjacent_corner_pairs_group(bottom_corners);
|
250
|
+
corner_pair_groups[layer_face_index][3] = opposite_corner_pairs_group(bottom_corners);
|
251
|
+
corner_pair_groups[layer_face_index][4] = stacked_corner_pairs_group(top_corners, bottom_corners);
|
252
|
+
corner_pair_groups[layer_face_index][5] = short_diagonal_corner_pairs_group(top_corners, bottom_corners);
|
253
|
+
corner_pair_groups[layer_face_index][6] = long_diagonal_corner_pairs_group(top_corners, bottom_corners);
|
254
|
+
|
255
|
+
for (int i = 0; i < num_corner_pair_groups; ++i) {
|
256
|
+
CornerPairGroup* const group = &corner_pair_groups[layer_face_index][i];
|
257
|
+
group->num_group_fingerprints = iexp(group->num_corner_pairs + 1, NUM_CORNER_PAIR_TYPES);
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
static void init_corner_pair_groups() {
|
262
|
+
for (face_index_t layer_face_index = 0; layer_face_index < cube_faces; ++layer_face_index) {
|
263
|
+
init_corner_pair_groups_for_face_index(layer_face_index);
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
void init_skewb_layer_fingerprint_method_under(const VALUE module) {
|
268
|
+
plus = rb_intern("+");
|
269
|
+
times = rb_intern("*");
|
270
|
+
rb_define_singleton_method(module, "skewb_layer_fingerprint", skewb_layer_fingerprint, 2);
|
271
|
+
}
|