twisty_puzzles 0.0.2 → 0.0.7

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.
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,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);
@@ -2,4 +2,4 @@
2
2
 
3
3
  require 'mkmf'
4
4
 
5
- create_makefile('native')
5
+ create_makefile('twisty_puzzles/native')
@@ -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
+ }