twisty_puzzles 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|