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.
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include "face_symbols.h"
6
+
7
+ typedef struct {
8
+ size_t cube_size;
9
+ VALUE* stickers;
10
+ } CubeStateData;
11
+
12
+ extern const rb_data_type_t CubeStateData_type;
13
+
14
+ #define GetCubeStateData(obj, data) \
15
+ do { \
16
+ TypedData_Get_Struct((obj), CubeStateData, &CubeStateData_type, (data)); \
17
+ } while (0)
18
+
19
+ #define GetInitializedCubeStateData(obj, data) \
20
+ do { \
21
+ GetCubeStateData((obj), (data)); \
22
+ if (data->stickers == NULL) { \
23
+ rb_raise(rb_eArgError, "Cube state isn't initialized."); \
24
+ } \
25
+ } while(0)
26
+
27
+ void rotate_slice_for_cube(face_index_t turned_face_index, size_t slice_index, direction_t direction, const CubeStateData* data);
28
+
29
+ void rotate_face_for_cube(face_index_t turned_face_index, direction_t direction, const CubeStateData* data);
30
+
31
+ void init_cube_state_class_under(VALUE module);
@@ -0,0 +1,67 @@
1
+ #include "face_symbols.h"
2
+
3
+ #include <stdlib.h>
4
+
5
+ static ID face_ids[cube_faces];
6
+
7
+ face_index_t face_index(const VALUE face_symbol) {
8
+ Check_Type(face_symbol, T_SYMBOL);
9
+ const ID face_id = SYM2ID(face_symbol);
10
+ for (size_t i = 0; i < cube_faces; ++i) {
11
+ if (face_ids[i] == face_id) {
12
+ return i;
13
+ }
14
+ }
15
+ rb_raise(rb_eArgError, "Invalid face symbol %+"PRIsVALUE"", face_symbol);
16
+ }
17
+
18
+ // TODO This is very unelegant.
19
+ static const face_index_t U_neighbors[neighbor_faces] = {F, L, B, R};
20
+ static const face_index_t F_neighbors[neighbor_faces] = {U, R, D, L};
21
+ static const face_index_t R_neighbors[neighbor_faces] = {U, B, D, F};
22
+
23
+ face_index_t neighbor_face_index(const face_index_t face_index, const size_t index) {
24
+ const size_t adjusted_index = face_index == axis_index(face_index) ? index : -index;
25
+ const size_t cropped_index = (adjusted_index % 4 + 4) % 4;
26
+ switch (axis_index(face_index)) {
27
+ case U: return U_neighbors[cropped_index];
28
+ case F: return F_neighbors[cropped_index];
29
+ case R: return R_neighbors[cropped_index];
30
+ default:
31
+ rb_raise(rb_eRuntimeError, "invalid axis index");
32
+ }
33
+ }
34
+
35
+ VALUE face_symbol(const face_index_t face_index) {
36
+ return ID2SYM(face_ids[face_index]);
37
+ }
38
+
39
+ axis_index_t axis_index(const face_index_t face_index) {
40
+ return MIN(face_index, opposite_face_index(face_index));
41
+ }
42
+
43
+ bool same_axis(const face_index_t left_face_index, const face_index_t right_face_index) {
44
+ return axis_index(left_face_index) == axis_index(right_face_index);
45
+ }
46
+
47
+ face_index_t opposite_face_index(const face_index_t face_index) {
48
+ return cube_faces - 1 - face_index;
49
+ }
50
+
51
+ size_t neighbor_index(const face_index_t base_face_index, const face_index_t other_face_index) {
52
+ for (int i = 0; i < 4; ++i) {
53
+ if (neighbor_face_index(base_face_index, i) == other_face_index) {
54
+ return i;
55
+ }
56
+ }
57
+ return -1;
58
+ }
59
+
60
+ void init_face_symbols() {
61
+ face_ids[U] = rb_intern("U");
62
+ face_ids[F] = rb_intern("F");
63
+ face_ids[R] = rb_intern("R");
64
+ face_ids[L] = rb_intern("L");
65
+ face_ids[B] = rb_intern("B");
66
+ face_ids[D] = rb_intern("D");
67
+ }
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ #include "utils.h"
6
+
7
+ typedef char face_index_t;
8
+ typedef char axis_index_t;
9
+
10
+ #define cube_faces 6
11
+ #define neighbor_faces 4
12
+
13
+ #define U 0
14
+ #define F 1
15
+ #define R 2
16
+ #define L 3
17
+ #define B 4
18
+ #define D 5
19
+
20
+ face_index_t face_index(VALUE face_symbol);
21
+
22
+ face_index_t neighbor_face_index(face_index_t face_index, size_t index);
23
+
24
+ VALUE face_symbol(face_index_t face_index);
25
+
26
+ axis_index_t axis_index(face_index_t face_index);
27
+
28
+ bool same_axis(face_index_t left_face_index, face_index_t right_face_index);
29
+
30
+ face_index_t opposite_face_index(face_index_t face_index);
31
+
32
+ size_t neighbor_index(face_index_t base_face_index, face_index_t other_face_index);
33
+
34
+ void init_face_symbols();
@@ -0,0 +1,28 @@
1
+ #include <ruby.h>
2
+
3
+ #include "cube_algorithm.h"
4
+ #include "cube_average.h"
5
+ #include "cube_coordinate.h"
6
+ #include "cube_state.h"
7
+ #include "face_symbols.h"
8
+ #include "skewb_algorithm.h"
9
+ #include "skewb_coordinate.h"
10
+ #include "skewb_layer_fingerprint.h"
11
+ #include "skewb_state.h"
12
+
13
+ VALUE TwistyPuzzlesModule = Qnil;
14
+ VALUE NativeModule = Qnil;
15
+
16
+ void Init_native() {
17
+ TwistyPuzzlesModule = rb_define_module("TwistyPuzzles");
18
+ NativeModule = rb_define_module_under(TwistyPuzzlesModule, "Native");
19
+ init_cube_algorithm_class_under(NativeModule);
20
+ init_cube_average_class_under(NativeModule);
21
+ init_cube_coordinate_class_under(NativeModule);
22
+ init_cube_state_class_under(NativeModule);
23
+ init_face_symbols();
24
+ init_skewb_algorithm_class_under(NativeModule);
25
+ init_skewb_coordinate_class_under(NativeModule);
26
+ init_skewb_layer_fingerprint_method_under(NativeModule);
27
+ init_skewb_state_class_under(NativeModule);
28
+ }
@@ -0,0 +1,331 @@
1
+ #include "skewb_algorithm.h"
2
+
3
+ #include "face_symbols.h"
4
+ #include "skewb_coordinate.h"
5
+ #include "skewb_state.h"
6
+ #include "utils.h"
7
+
8
+ static ID move_id;
9
+ static ID rotation_id;
10
+ static VALUE SkewbAlgorithmClass = Qnil;
11
+
12
+ typedef enum {
13
+ MOVE,
14
+ ROTATION,
15
+ } SkewbMoveType;
16
+
17
+ typedef union {
18
+ Corner corner;
19
+ face_index_t face_index;
20
+ } SkewbAxis;
21
+
22
+ typedef struct {
23
+ SkewbMoveType type;
24
+ SkewbAxis axis;
25
+ direction_t direction;
26
+ } SkewbMove;
27
+
28
+ typedef struct {
29
+ size_t size;
30
+ // We need this because we can't use the usual checking for 0/NULL because and empty algorithm is valid.
31
+ bool initialized;
32
+ SkewbMove* moves;
33
+ } SkewbAlgorithmData;
34
+
35
+ static void SkewbAlgorithmData_free(void* const ptr) {
36
+ const SkewbAlgorithmData* const data = ptr;
37
+ free(data->moves);
38
+ free(ptr);
39
+ }
40
+
41
+ static size_t SkewbAlgorithmData_size(const void* const ptr) {
42
+ const SkewbAlgorithmData* const data = ptr;
43
+ return sizeof(SkewbAlgorithmData) + data->size * sizeof(SkewbMove);
44
+ }
45
+
46
+ const rb_data_type_t SkewbAlgorithmData_type = {
47
+ "TwistyPuzzles::Native::SkewbAlgorithmData",
48
+ {NULL, SkewbAlgorithmData_free, SkewbAlgorithmData_size, NULL},
49
+ NULL, NULL,
50
+ RUBY_TYPED_FREE_IMMEDIATELY
51
+ };
52
+
53
+ static void check_moves(const SkewbAlgorithmData* const data, const char* const name) {
54
+ for (size_t i = 0; i < data->size; ++i) {
55
+ const SkewbMoveType type = data->moves[i].type;
56
+ if (type != MOVE && type != ROTATION) {
57
+ rb_raise(rb_eRuntimeError, "invalid move type %d in %s", type, name);
58
+ }
59
+ }
60
+ }
61
+
62
+ static SkewbMove* malloc_moves(const size_t n) {
63
+ SkewbMove* const moves = malloc(n * sizeof(SkewbMove));
64
+ if (moves == NULL) {
65
+ rb_raise(rb_eNoMemError, "Allocating skewb algorithm failed.");
66
+ }
67
+ return moves;
68
+ }
69
+
70
+ static VALUE SkewbAlgorithm_alloc(const VALUE klass) {
71
+ SkewbAlgorithmData* data;
72
+ const VALUE object = TypedData_Make_Struct(klass, SkewbAlgorithmData, &SkewbAlgorithmData_type, data);
73
+ data->size = 0;
74
+ data->initialized = FALSE;
75
+ data->moves = NULL;
76
+ return object;
77
+ }
78
+
79
+ #define GetSkewbAlgorithmData(obj, data) \
80
+ do { \
81
+ TypedData_Get_Struct((obj), SkewbAlgorithmData, &SkewbAlgorithmData_type, (data)); \
82
+ } while (0)
83
+
84
+ #define GetInitializedSkewbAlgorithmData(obj, data) \
85
+ do { \
86
+ GetSkewbAlgorithmData((obj), (data)); \
87
+ if (!data->initialized) { \
88
+ rb_raise(rb_eRuntimeError, "Skewb algorithm isn't initialized."); \
89
+ } \
90
+ } while(0)
91
+
92
+ static SkewbMoveType extract_move_type(const VALUE move_symbol) {
93
+ Check_Type(move_symbol, T_SYMBOL);
94
+ const ID move_symbol_id = SYM2ID(move_symbol);
95
+ if (move_symbol_id == move_id) {
96
+ return MOVE;
97
+ } else if (move_symbol_id == rotation_id) {
98
+ return ROTATION;
99
+ } else {
100
+ rb_raise(rb_eArgError, "Got invalid move symbol.");
101
+ }
102
+ }
103
+
104
+ static SkewbAxis extract_axis(const SkewbMoveType type, const VALUE axis) {
105
+ SkewbAxis result;
106
+ switch (type) {
107
+ case MOVE:
108
+ result.corner = extract_corner(axis);
109
+ return result;
110
+ case ROTATION:
111
+ result.face_index = face_index(axis);
112
+ return result;
113
+ default:
114
+ rb_raise(rb_eRuntimeError, "invalid move type %d in extract_axis", type);
115
+ }
116
+ }
117
+
118
+ static VALUE SkewbAlgorithm_initialize(const VALUE self, const VALUE moves) {
119
+ Check_Type(moves, T_ARRAY);
120
+ SkewbAlgorithmData* data;
121
+ GetSkewbAlgorithmData(self, data);
122
+ data->size = RARRAY_LEN(moves);
123
+ data->initialized = TRUE;
124
+ data->moves = malloc_moves(data->size);
125
+ for (size_t i = 0; i < RARRAY_LEN(moves); ++i) {
126
+ const VALUE move = rb_ary_entry(moves, i);
127
+ if (RARRAY_LEN(move) != 3) {
128
+ rb_raise(rb_eArgError, "Moves must have 3 elements. Got %ld.", RARRAY_LEN(moves));
129
+ }
130
+ const SkewbMoveType type = extract_move_type(rb_ary_entry(move, 0));
131
+ data->moves[i].type = type;
132
+ data->moves[i].axis = extract_axis(type, rb_ary_entry(move, 1));
133
+ data->moves[i].direction = NUM2INT(rb_ary_entry(move, 2));
134
+ }
135
+ return self;
136
+ }
137
+
138
+ static void apply_move_to(const SkewbMove move, SkewbStateData* const skewb_state) {
139
+ switch (move.type) {
140
+ case MOVE:
141
+ rotate_corner_for_skewb_state(move.axis.corner, move.direction, skewb_state);
142
+ break;
143
+ case ROTATION:
144
+ rotate_skewb_state(move.axis.face_index, move.direction, skewb_state);
145
+ break;
146
+ default:
147
+ rb_raise(rb_eRuntimeError, "invalid move type %d in apply_move_to", move.type);
148
+ }
149
+ }
150
+
151
+ static VALUE SkewbAlgorithm_apply_to(const VALUE self, const VALUE skewb_state) {
152
+ SkewbStateData* skewb_state_data;
153
+ GetSkewbStateData(skewb_state, skewb_state_data);
154
+ const SkewbAlgorithmData* data;
155
+ GetInitializedSkewbAlgorithmData(self, data);
156
+ for (size_t i = 0; i < data->size; ++i) {
157
+ apply_move_to(data->moves[i], skewb_state_data);
158
+ }
159
+ return Qnil;
160
+ }
161
+
162
+ // Takes either face_index or the opposite, depending which one is present on corner.
163
+ static face_index_t axis_face_on_corner(const Corner corner, const face_index_t face_index) {
164
+ for (size_t i = 0; i < 3; ++i) {
165
+ if (same_axis(corner.face_indices[i], face_index)) {
166
+ return corner.face_indices[i];
167
+ }
168
+ }
169
+ rb_raise(rb_eRuntimeError, "invalid state in axis_face_on_corner");
170
+ }
171
+
172
+ bool corners_eq(Corner left, Corner right) {
173
+ return left.face_indices[0] == right.face_indices[0] &&
174
+ (left.face_indices[1] == right.face_indices[1] && left.face_indices[2] == right.face_indices[2] ||
175
+ left.face_indices[1] == right.face_indices[2] && left.face_indices[2] == right.face_indices[1]);
176
+ }
177
+
178
+ size_t equivalent_corner_index(const FaceCorners corners, const Corner corner) {
179
+ for (size_t i = 0; i < 4; ++i) {
180
+ for (int j = 0; j < 3; ++j) {
181
+ if (corners_eq(rotated_corner(corners.corners[i], j), corner)) {
182
+ return i;
183
+ }
184
+ }
185
+ }
186
+ rb_raise(rb_eRuntimeError, "invalid state in equivalent_corner_index");
187
+ }
188
+
189
+ static SkewbMove rotate_move_by(const SkewbMove move, const face_index_t rotation_face_index, const direction_t rotation_direction) {
190
+ SkewbMove result = move;
191
+ switch (move.type) {
192
+ case MOVE: {
193
+ const face_index_t nice_rotation_face_index = axis_face_on_corner(move.axis.corner, rotation_face_index);
194
+ const face_index_t nice_rotation_direction = rotation_face_index == nice_rotation_face_index ? rotation_direction : invert_cube_direction(rotation_direction);
195
+ FaceCorners corners = get_face_corners(nice_rotation_face_index);
196
+ const size_t corner_index = equivalent_corner_index(corners, move.axis.corner);
197
+ result.axis.corner = corners.corners[(corner_index + nice_rotation_direction) % 4];
198
+ break;
199
+ }
200
+ case ROTATION: {
201
+ if (!same_axis(move.axis.face_index, rotation_face_index)) {
202
+ const size_t index = neighbor_index(rotation_face_index, move.axis.face_index);
203
+ result.axis.face_index = neighbor_face_index(rotation_face_index, index + rotation_direction);
204
+ }
205
+ break;
206
+ }
207
+ default:
208
+ rb_raise(rb_eRuntimeError, "invalid move type %d in rotate_move_by", move.type);
209
+ }
210
+ return result;
211
+ }
212
+
213
+ static VALUE SkewbAlgorithm_rotate_by(const VALUE self, const VALUE rotation_face_symbol, const VALUE direction) {
214
+ const face_index_t rotation_face_index = face_index(rotation_face_symbol);
215
+ const direction_t rotation_direction = NUM2INT(direction);
216
+ const SkewbAlgorithmData* data;
217
+ GetInitializedSkewbAlgorithmData(self, data);
218
+ SkewbAlgorithmData* rotated_data;
219
+ const VALUE rotated = TypedData_Make_Struct(SkewbAlgorithmClass, SkewbAlgorithmData, &SkewbAlgorithmData_type, rotated_data);
220
+ rotated_data->size = data->size;
221
+ rotated_data->initialized = TRUE;
222
+ rotated_data->moves = malloc_moves(rotated_data->size);
223
+ for (size_t i = 0; i < data->size; ++i) {
224
+ rotated_data->moves[i] = rotate_move_by(data->moves[i], rotation_face_index, rotation_direction);
225
+ }
226
+ return rotated;
227
+ }
228
+
229
+ static SkewbMove mirror_move(const SkewbMove move, const face_index_t normal_face_index) {
230
+ SkewbMove result = move;
231
+ switch (move.type) {
232
+ case MOVE: {
233
+ for (size_t i = 0; i < 3; ++i) {
234
+ const face_index_t current_face_index = move.axis.corner.face_indices[i];
235
+ if (same_axis(current_face_index, normal_face_index)) {
236
+ // We need to make one face into the opposite and then swap to to fix the chirality.
237
+ result.axis.corner.face_indices[(i + 1) % 3] = opposite_face_index(current_face_index);
238
+ result.axis.corner.face_indices[i] = move.axis.corner.face_indices[(i + 1) % 3];
239
+ break;
240
+ }
241
+ }
242
+ result.direction = invert_skewb_direction(move.direction);
243
+ break;
244
+ }
245
+ case ROTATION: {
246
+ if (!same_axis(move.axis.face_index, normal_face_index)) {
247
+ result.direction = invert_cube_direction(move.direction);
248
+ }
249
+ break;
250
+ }
251
+ default:
252
+ rb_raise(rb_eRuntimeError, "invalid move type %d in mirror_move", move.type);
253
+ }
254
+ return result;
255
+ }
256
+
257
+ static VALUE SkewbAlgorithm_mirror(const VALUE self, const VALUE normal_face_symbol) {
258
+ const face_index_t normal_face_index = face_index(normal_face_symbol);
259
+ const SkewbAlgorithmData* data;
260
+ GetInitializedSkewbAlgorithmData(self, data);
261
+ SkewbAlgorithmData* mirrored_data;
262
+ const VALUE mirrored = TypedData_Make_Struct(SkewbAlgorithmClass, SkewbAlgorithmData, &SkewbAlgorithmData_type, mirrored_data);
263
+ mirrored_data->size = data->size;
264
+ mirrored_data->initialized = TRUE;
265
+ mirrored_data->moves = malloc_moves(mirrored_data->size);
266
+ for (size_t i = 0; i < data->size; ++i) {
267
+ mirrored_data->moves[i] = mirror_move(data->moves[i], normal_face_index);
268
+ }
269
+ return mirrored;
270
+ }
271
+
272
+ static SkewbMove invert_move(const SkewbMove move) {
273
+ SkewbMove result = move;
274
+ switch (move.type) {
275
+ case MOVE:
276
+ result.direction = invert_skewb_direction(result.direction);
277
+ break;
278
+ case ROTATION:
279
+ result.direction = invert_cube_direction(result.direction);
280
+ break;
281
+ default:
282
+ rb_raise(rb_eRuntimeError, "invalid move type %d in invert_move", move.type);
283
+ }
284
+ return result;
285
+ }
286
+
287
+ static VALUE SkewbAlgorithm_inverse(const VALUE self) {
288
+ const SkewbAlgorithmData* data;
289
+ GetInitializedSkewbAlgorithmData(self, data);
290
+ SkewbAlgorithmData* inverted_data;
291
+ const VALUE inverted = TypedData_Make_Struct(SkewbAlgorithmClass, SkewbAlgorithmData, &SkewbAlgorithmData_type, inverted_data);
292
+ inverted_data->size = data->size;
293
+ inverted_data->initialized = TRUE;
294
+ inverted_data->moves = malloc_moves(inverted_data->size);
295
+ for (size_t i = 0; i < data->size; ++i) {
296
+ inverted_data->moves[i] = invert_move(data->moves[data->size - 1 - i]);
297
+ }
298
+ return inverted;
299
+ }
300
+
301
+ static VALUE SkewbAlgorithm_plus(const VALUE self, const VALUE other) {
302
+ const SkewbAlgorithmData* self_data;
303
+ GetInitializedSkewbAlgorithmData(self, self_data);
304
+ const SkewbAlgorithmData* other_data;
305
+ GetInitializedSkewbAlgorithmData(other, other_data);
306
+ SkewbAlgorithmData* sum_data;
307
+ const VALUE sum = TypedData_Make_Struct(SkewbAlgorithmClass, SkewbAlgorithmData, &SkewbAlgorithmData_type, sum_data);
308
+ sum_data->size = self_data->size + other_data->size;
309
+ sum_data->initialized = TRUE;
310
+ sum_data->moves = malloc_moves(sum_data->size);
311
+ for (size_t i = 0; i < self_data->size; ++i) {
312
+ sum_data->moves[i] = self_data->moves[i];
313
+ }
314
+ for (size_t i = 0; i < other_data->size; ++i) {
315
+ sum_data->moves[self_data->size + i] = other_data->moves[i];
316
+ }
317
+ return sum;
318
+ }
319
+
320
+ void init_skewb_algorithm_class_under(const VALUE module) {
321
+ move_id = rb_intern("move");
322
+ rotation_id = rb_intern("rotation");
323
+ SkewbAlgorithmClass = rb_define_class_under(module, "SkewbAlgorithm", rb_cObject);
324
+ rb_define_alloc_func(SkewbAlgorithmClass, SkewbAlgorithm_alloc);
325
+ rb_define_method(SkewbAlgorithmClass, "initialize", SkewbAlgorithm_initialize, 1);
326
+ rb_define_method(SkewbAlgorithmClass, "apply_to", SkewbAlgorithm_apply_to, 1);
327
+ rb_define_method(SkewbAlgorithmClass, "rotate_by", SkewbAlgorithm_rotate_by, 2);
328
+ rb_define_method(SkewbAlgorithmClass, "mirror", SkewbAlgorithm_mirror, 1);
329
+ rb_define_method(SkewbAlgorithmClass, "inverse", SkewbAlgorithm_inverse, 0);
330
+ rb_define_method(SkewbAlgorithmClass, "+", SkewbAlgorithm_plus, 1);
331
+ }