twisty_puzzles 0.0.2 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -1
  3. data/README.md +6 -1
  4. data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
  5. data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
  6. data/ext/twisty_puzzles/native/cube_average.c +184 -0
  7. data/ext/twisty_puzzles/native/cube_average.h +5 -0
  8. data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
  9. data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
  10. data/ext/twisty_puzzles/native/cube_state.c +264 -0
  11. data/ext/twisty_puzzles/native/cube_state.h +31 -0
  12. data/ext/twisty_puzzles/native/extconf.rb +1 -1
  13. data/ext/twisty_puzzles/native/face_symbols.c +67 -0
  14. data/ext/twisty_puzzles/native/face_symbols.h +34 -0
  15. data/ext/twisty_puzzles/native/native.c +28 -0
  16. data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
  17. data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
  18. data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
  19. data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
  20. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
  21. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
  22. data/ext/twisty_puzzles/native/skewb_state.c +214 -0
  23. data/ext/twisty_puzzles/native/skewb_state.h +23 -0
  24. data/ext/twisty_puzzles/native/utils.c +76 -0
  25. data/ext/twisty_puzzles/native/utils.h +28 -0
  26. data/lib/twisty_puzzles.rb +1 -0
  27. data/lib/twisty_puzzles/abstract_move.rb +1 -2
  28. data/lib/twisty_puzzles/abstract_move_parser.rb +1 -1
  29. data/lib/twisty_puzzles/algorithm.rb +1 -1
  30. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +1 -0
  31. data/lib/twisty_puzzles/cancellation_helper.rb +1 -1
  32. data/lib/twisty_puzzles/color_scheme.rb +1 -1
  33. data/lib/twisty_puzzles/commutator.rb +3 -0
  34. data/lib/twisty_puzzles/compiled_algorithm.rb +4 -4
  35. data/lib/twisty_puzzles/coordinate.rb +4 -6
  36. data/lib/twisty_puzzles/cube.rb +3 -3
  37. data/lib/twisty_puzzles/cube_move.rb +0 -4
  38. data/lib/twisty_puzzles/cube_move_parser.rb +23 -23
  39. data/lib/twisty_puzzles/cube_print_helper.rb +4 -3
  40. data/lib/twisty_puzzles/parser.rb +3 -3
  41. data/lib/twisty_puzzles/rotation.rb +2 -0
  42. data/lib/twisty_puzzles/skewb_move.rb +1 -0
  43. data/lib/twisty_puzzles/skewb_move_parser.rb +1 -0
  44. data/lib/twisty_puzzles/skewb_notation.rb +7 -1
  45. data/lib/twisty_puzzles/version.rb +1 -1
  46. metadata +32 -12
@@ -0,0 +1,5 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ void init_skewb_algorithm_class_under(VALUE module);
@@ -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
+ }