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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/README.md +6 -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/extconf.rb +1 -1
- 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 +28 -0
- data/lib/twisty_puzzles.rb +1 -0
- data/lib/twisty_puzzles/abstract_move.rb +1 -2
- data/lib/twisty_puzzles/abstract_move_parser.rb +1 -1
- data/lib/twisty_puzzles/algorithm.rb +1 -1
- data/lib/twisty_puzzles/axis_face_and_direction_move.rb +1 -0
- data/lib/twisty_puzzles/cancellation_helper.rb +1 -1
- data/lib/twisty_puzzles/color_scheme.rb +1 -1
- data/lib/twisty_puzzles/commutator.rb +3 -0
- data/lib/twisty_puzzles/compiled_algorithm.rb +4 -4
- data/lib/twisty_puzzles/coordinate.rb +4 -6
- data/lib/twisty_puzzles/cube.rb +3 -3
- data/lib/twisty_puzzles/cube_move.rb +0 -4
- data/lib/twisty_puzzles/cube_move_parser.rb +23 -23
- data/lib/twisty_puzzles/cube_print_helper.rb +4 -3
- data/lib/twisty_puzzles/parser.rb +3 -3
- data/lib/twisty_puzzles/rotation.rb +2 -0
- data/lib/twisty_puzzles/skewb_move.rb +1 -0
- data/lib/twisty_puzzles/skewb_move_parser.rb +1 -0
- data/lib/twisty_puzzles/skewb_notation.rb +7 -1
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +32 -12
@@ -0,0 +1,214 @@
|
|
1
|
+
#include "skewb_state.h"
|
2
|
+
|
3
|
+
#include "face_symbols.h"
|
4
|
+
|
5
|
+
static VALUE SkewbStateClass = Qnil;
|
6
|
+
|
7
|
+
static void SkewbStateData_mark(void* const ptr) {
|
8
|
+
const SkewbStateData* data = ptr;
|
9
|
+
for (size_t i = 0; i < total_skewb_stickers; ++i) {
|
10
|
+
rb_gc_mark(data->stickers[i]);
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
static size_t SkewbStateData_size(const void* const ptr) {
|
15
|
+
return sizeof(SkewbStateData);
|
16
|
+
}
|
17
|
+
|
18
|
+
const rb_data_type_t SkewbStateData_type = {
|
19
|
+
"SkewbTrainer::Native::SkewbStateData",
|
20
|
+
{SkewbStateData_mark, NULL, SkewbStateData_size, NULL},
|
21
|
+
NULL, NULL,
|
22
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
23
|
+
};
|
24
|
+
|
25
|
+
static VALUE SkewbState_alloc(const VALUE klass) {
|
26
|
+
SkewbStateData* data;
|
27
|
+
const VALUE object = TypedData_Make_Struct(klass, SkewbStateData, &SkewbStateData_type, data);
|
28
|
+
for (size_t i = 0; i < total_skewb_stickers; ++i) {
|
29
|
+
data->stickers[i] = Qnil;
|
30
|
+
}
|
31
|
+
return object;
|
32
|
+
}
|
33
|
+
|
34
|
+
static int SkewbState_replace_face(const VALUE key, const VALUE value, const VALUE self) {
|
35
|
+
SkewbStateData* data;
|
36
|
+
GetSkewbStateData(self, data);
|
37
|
+
const face_index_t on_face_index = face_index(key);
|
38
|
+
for (size_t i = 0; i < skewb_stickers_per_face; ++i) {
|
39
|
+
data->stickers[on_face_index * skewb_stickers_per_face + i] = value;
|
40
|
+
}
|
41
|
+
return ST_CONTINUE;
|
42
|
+
}
|
43
|
+
|
44
|
+
static VALUE SkewbState_initialize(const VALUE self, const VALUE stickers) {
|
45
|
+
Check_Type(stickers, T_HASH);
|
46
|
+
SkewbStateData* data;
|
47
|
+
GetSkewbStateData(self, data);
|
48
|
+
if (RHASH_SIZE(stickers) != skewb_faces) {
|
49
|
+
rb_raise(rb_eTypeError, "Skewbs must have %d faces. Got %ld.", skewb_faces, RHASH_SIZE(stickers));
|
50
|
+
}
|
51
|
+
rb_hash_foreach(stickers, SkewbState_replace_face, self);
|
52
|
+
return self;
|
53
|
+
}
|
54
|
+
|
55
|
+
static VALUE SkewbState_hash(const VALUE self) {
|
56
|
+
const SkewbStateData* data;
|
57
|
+
GetSkewbStateData(self, data);
|
58
|
+
|
59
|
+
st_index_t hash = rb_hash_start((st_index_t)SkewbState_hash);
|
60
|
+
for (size_t i = 0; i < total_skewb_stickers; i++) {
|
61
|
+
const VALUE sub_hash = rb_hash(data->stickers[i]);
|
62
|
+
hash = rb_hash_uint(hash, NUM2LONG(sub_hash));
|
63
|
+
}
|
64
|
+
return ST2FIX(rb_hash_end(hash));
|
65
|
+
}
|
66
|
+
|
67
|
+
static VALUE SkewbState_eql(const VALUE self, const VALUE other) {
|
68
|
+
if (self == other) {
|
69
|
+
return Qtrue;
|
70
|
+
}
|
71
|
+
if (rb_obj_class(self) != rb_obj_class(other)) {
|
72
|
+
return Qfalse;
|
73
|
+
}
|
74
|
+
const SkewbStateData* self_data;
|
75
|
+
GetSkewbStateData(self, self_data);
|
76
|
+
const SkewbStateData* other_data;
|
77
|
+
GetSkewbStateData(other, other_data);
|
78
|
+
for (size_t i = 0; i < total_skewb_stickers; ++i) {
|
79
|
+
if (!color_eq(self_data->stickers[i], other_data->stickers[i])) {
|
80
|
+
return Qfalse;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return Qtrue;
|
84
|
+
}
|
85
|
+
|
86
|
+
static VALUE SkewbState_dup(const VALUE self) {
|
87
|
+
const SkewbStateData* data;
|
88
|
+
GetSkewbStateData(self, data);
|
89
|
+
SkewbStateData* dupped_data;
|
90
|
+
const VALUE dupped = TypedData_Make_Struct(rb_obj_class(self), SkewbStateData, &SkewbStateData_type, dupped_data);
|
91
|
+
*dupped_data = *data;
|
92
|
+
return dupped;
|
93
|
+
}
|
94
|
+
|
95
|
+
static VALUE SkewbState_entry(const VALUE self, const VALUE coordinate) {
|
96
|
+
const SkewbStateData* data;
|
97
|
+
GetSkewbStateData(self, data);
|
98
|
+
return data->stickers[SkewbCoordinate_sticker_index(coordinate)];
|
99
|
+
}
|
100
|
+
|
101
|
+
static VALUE SkewbState_store(const VALUE self, const VALUE coordinate, const VALUE value) {
|
102
|
+
SkewbStateData* data;
|
103
|
+
GetSkewbStateData(self, data);
|
104
|
+
return data->stickers[SkewbCoordinate_sticker_index(coordinate)] = value;
|
105
|
+
}
|
106
|
+
|
107
|
+
static void apply_twisted_corner_cycle(VALUE* const stickers, const Corner corner, const bool invert) {
|
108
|
+
size_t twisted_corner_cycle[3];
|
109
|
+
for (size_t i = 0; i < 3; ++i) {
|
110
|
+
const Corner corner_variant = rotated_corner(corner, i);
|
111
|
+
twisted_corner_cycle[i] = corner_sticker_index(corner_variant);
|
112
|
+
}
|
113
|
+
apply_sticker_cycle(stickers, twisted_corner_cycle, 3, invert);
|
114
|
+
}
|
115
|
+
|
116
|
+
static void apply_center_cycle(VALUE* const stickers, const Corner corner, const bool invert) {
|
117
|
+
size_t center_cycle[3];
|
118
|
+
for (size_t i = 0; i < 3; ++i) {
|
119
|
+
center_cycle[i] = center_sticker_index(corner.face_indices[i]);
|
120
|
+
}
|
121
|
+
apply_sticker_cycle(stickers, center_cycle, 3, invert);
|
122
|
+
}
|
123
|
+
|
124
|
+
static void apply_moved_corner_cycles(VALUE* const stickers, const Corner corner, const bool invert) {
|
125
|
+
Corner adjacent_corners[3];
|
126
|
+
for (size_t corner_index = 0; corner_index < 3; ++corner_index) {
|
127
|
+
adjacent_corners[corner_index].face_indices[0] = opposite_face_index(corner.face_indices[corner_index]);
|
128
|
+
adjacent_corners[corner_index].face_indices[1] = corner.face_indices[(corner_index + 2) % 3];
|
129
|
+
adjacent_corners[corner_index].face_indices[2] = corner.face_indices[(corner_index + 1) % 3];
|
130
|
+
}
|
131
|
+
// We have 3 different cycles, one for the outside stickers and one two for the remaining stickers of the
|
132
|
+
// corners that contain the outside stickers.
|
133
|
+
for (size_t cycle_index = 0; cycle_index < 3; ++cycle_index) {
|
134
|
+
size_t sticker_cycle[3];
|
135
|
+
for (size_t corner_index = 0; corner_index < 3; ++corner_index) {
|
136
|
+
// The current corner, but twisted such that the sticker we care about in the current cycle faces up.
|
137
|
+
const Corner current_corner_for_current_cycle = rotated_corner(adjacent_corners[corner_index], cycle_index);
|
138
|
+
sticker_cycle[corner_index] = corner_sticker_index(current_corner_for_current_cycle);
|
139
|
+
}
|
140
|
+
apply_sticker_cycle(stickers, sticker_cycle, 3, invert);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
void rotate_corner_for_skewb_state(const Corner corner, direction_t direction, SkewbStateData* const skewb_state) {
|
145
|
+
direction = CROP_MOD(direction, 3);
|
146
|
+
if (direction == 0) {
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
const bool invert = direction == 2;
|
150
|
+
|
151
|
+
apply_twisted_corner_cycle(skewb_state->stickers, corner, invert);
|
152
|
+
apply_center_cycle(skewb_state->stickers, corner, invert);
|
153
|
+
apply_moved_corner_cycles(skewb_state->stickers, corner, invert);
|
154
|
+
}
|
155
|
+
|
156
|
+
static void apply_center_rotation(VALUE* const stickers, const face_index_t axis_face_index, const direction_t direction) {
|
157
|
+
Sticker4Cycle center_cycle;
|
158
|
+
for (size_t i = 0; i < 4; ++i) {
|
159
|
+
center_cycle.indices[i] = center_sticker_index(neighbor_face_index(axis_face_index, i));
|
160
|
+
}
|
161
|
+
apply_sticker_4cycle(stickers, center_cycle, direction);
|
162
|
+
}
|
163
|
+
|
164
|
+
static void apply_corner_rotations(VALUE* const stickers, const face_index_t axis_face_index, const direction_t direction) {
|
165
|
+
Corner corner;
|
166
|
+
corner.face_indices[2] = axis_face_index;
|
167
|
+
for (size_t cycle_index = 0; cycle_index < 3; ++cycle_index) {
|
168
|
+
Sticker4Cycle corner_cycle;
|
169
|
+
for (size_t corner_index = 0; corner_index < 4; ++corner_index) {
|
170
|
+
for (size_t i = 0; i < 2; ++i) {
|
171
|
+
corner.face_indices[i] = neighbor_face_index(axis_face_index, corner_index + i);
|
172
|
+
}
|
173
|
+
corner_cycle.indices[corner_index] = corner_sticker_index(rotated_corner(corner, cycle_index));
|
174
|
+
}
|
175
|
+
apply_sticker_4cycle(stickers, corner_cycle, direction);
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
void rotate_skewb_state(const face_index_t axis_face_index, direction_t direction, SkewbStateData* const skewb_state) {
|
180
|
+
direction = CROP_MOD(direction, 4);
|
181
|
+
if (direction == 0) {
|
182
|
+
return;
|
183
|
+
}
|
184
|
+
apply_center_rotation(skewb_state->stickers, axis_face_index, direction);
|
185
|
+
apply_corner_rotations(skewb_state->stickers, axis_face_index, direction);
|
186
|
+
apply_corner_rotations(skewb_state->stickers, opposite_face_index(axis_face_index), invert_cube_direction(direction));
|
187
|
+
}
|
188
|
+
|
189
|
+
static VALUE SkewbState_face_solved(const VALUE self, const VALUE face_symbol) {
|
190
|
+
const SkewbStateData* data;
|
191
|
+
GetSkewbStateData(self, data);
|
192
|
+
const face_index_t solved_face_index = face_index(face_symbol);
|
193
|
+
const size_t center_index = center_sticker_index(solved_face_index);
|
194
|
+
const VALUE color = data->stickers[center_index];
|
195
|
+
for (int i = 1; i < 5; ++i) {
|
196
|
+
if (!color_eq(data->stickers[center_index + i], color)) {
|
197
|
+
return Qfalse;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
return Qtrue;
|
201
|
+
}
|
202
|
+
|
203
|
+
void init_skewb_state_class_under(const VALUE module) {
|
204
|
+
SkewbStateClass = rb_define_class_under(module, "SkewbState", rb_cObject);
|
205
|
+
rb_define_alloc_func(SkewbStateClass, SkewbState_alloc);
|
206
|
+
rb_define_method(SkewbStateClass, "initialize", SkewbState_initialize, 1);
|
207
|
+
rb_define_method(SkewbStateClass, "hash", SkewbState_hash, 0);
|
208
|
+
rb_define_method(SkewbStateClass, "eql?", SkewbState_eql, 1);
|
209
|
+
rb_define_alias(SkewbStateClass, "==", "eql?");
|
210
|
+
rb_define_method(SkewbStateClass, "dup", SkewbState_dup, 0);
|
211
|
+
rb_define_method(SkewbStateClass, "[]", SkewbState_entry, 1);
|
212
|
+
rb_define_method(SkewbStateClass, "[]=", SkewbState_store, 2);
|
213
|
+
rb_define_method(SkewbStateClass, "face_solved?", SkewbState_face_solved, 1);
|
214
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
#include "skewb_coordinate.h"
|
6
|
+
#include "utils.h"
|
7
|
+
|
8
|
+
typedef struct {
|
9
|
+
VALUE stickers[total_skewb_stickers];
|
10
|
+
} SkewbStateData;
|
11
|
+
|
12
|
+
extern const rb_data_type_t SkewbStateData_type;
|
13
|
+
|
14
|
+
#define GetSkewbStateData(obj, data) \
|
15
|
+
do { \
|
16
|
+
TypedData_Get_Struct((obj), SkewbStateData, &SkewbStateData_type, (data)); \
|
17
|
+
} while (0)
|
18
|
+
|
19
|
+
void rotate_corner_for_skewb_state(Corner corner, direction_t direction, SkewbStateData* skewb_state);
|
20
|
+
|
21
|
+
void rotate_skewb_state(face_index_t axis_face_index, direction_t direction, SkewbStateData* skewb_state);
|
22
|
+
|
23
|
+
void init_skewb_state_class_under(VALUE module);
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#include "utils.h"
|
2
|
+
|
3
|
+
static void swap(VALUE* const stickers, size_t i, size_t j) {
|
4
|
+
VALUE buffer;
|
5
|
+
buffer = stickers[i];
|
6
|
+
stickers[i] = stickers[j];
|
7
|
+
stickers[j] = buffer;
|
8
|
+
}
|
9
|
+
|
10
|
+
direction_t invert_cube_direction(direction_t direction) {
|
11
|
+
return (4 - direction) % 4;
|
12
|
+
}
|
13
|
+
|
14
|
+
direction_t invert_skewb_direction(direction_t direction) {
|
15
|
+
return (3 - direction) % 3;
|
16
|
+
}
|
17
|
+
|
18
|
+
void apply_sticker_cycle(VALUE* const stickers, const size_t* const indices, const size_t size, const bool invert) {
|
19
|
+
const size_t direction = invert ? size - 1 : 1;
|
20
|
+
const size_t last_index = (direction * (size - 1)) % size;
|
21
|
+
const VALUE buffer = stickers[indices[last_index]];
|
22
|
+
for (size_t i = size - 1; i >= 1; --i) {
|
23
|
+
const size_t target_index = (direction * i) % size;
|
24
|
+
const size_t source_index = (direction * (i - 1)) % size;
|
25
|
+
stickers[indices[target_index]] = stickers[indices[source_index]];
|
26
|
+
}
|
27
|
+
stickers[indices[0]] = buffer;
|
28
|
+
}
|
29
|
+
|
30
|
+
void apply_sticker_4cycle(VALUE* const stickers, const Sticker4Cycle cycle, const direction_t direction) {
|
31
|
+
const direction_t d = (direction % 4 + 4) % 4;
|
32
|
+
if (d == 0) {
|
33
|
+
return;
|
34
|
+
} else if (d == 2) {
|
35
|
+
swap(stickers, cycle.indices[0], cycle.indices[2]);
|
36
|
+
swap(stickers, cycle.indices[1], cycle.indices[3]);
|
37
|
+
} else {
|
38
|
+
apply_sticker_cycle(stickers, cycle.indices, 4, d == 3);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
int color_eq(const VALUE left, const VALUE right) {
|
43
|
+
return RTEST(rb_equal(left, right));
|
44
|
+
}
|
45
|
+
|
46
|
+
// Magic table of values that we use for computing the log2.
|
47
|
+
static const int tab64[64] = {
|
48
|
+
63, 0, 58, 1, 59, 47, 53, 2,
|
49
|
+
60, 39, 48, 27, 54, 33, 42, 3,
|
50
|
+
61, 51, 37, 40, 49, 18, 28, 20,
|
51
|
+
55, 30, 34, 11, 43, 14, 22, 4,
|
52
|
+
62, 57, 46, 52, 38, 26, 32, 41,
|
53
|
+
50, 36, 17, 19, 29, 10, 13, 21,
|
54
|
+
56, 45, 25, 31, 35, 16, 9, 12,
|
55
|
+
44, 24, 15, 8, 23, 7, 6, 5};
|
56
|
+
|
57
|
+
int log2_64_floor(uint64_t value) {
|
58
|
+
value |= value >> 1;
|
59
|
+
value |= value >> 2;
|
60
|
+
value |= value >> 4;
|
61
|
+
value |= value >> 8;
|
62
|
+
value |= value >> 16;
|
63
|
+
value |= value >> 32;
|
64
|
+
return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
|
65
|
+
}
|
66
|
+
|
67
|
+
uint64_t iexp(const uint64_t base, uint32_t exp) {
|
68
|
+
uint64_t result = 1;
|
69
|
+
for (uint32_t m = 1 << 31; m; m >>= 1) {
|
70
|
+
result = result * result;
|
71
|
+
if (m & exp) {
|
72
|
+
result *= base;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return result;
|
76
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <stdint.h>
|
5
|
+
|
6
|
+
#define MIN(a, b) ((a) > (b) ? (b) : (a))
|
7
|
+
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
8
|
+
#define CROP_MOD(a, b) (((a) % (b) + (b)) % (b))
|
9
|
+
|
10
|
+
typedef char direction_t;
|
11
|
+
|
12
|
+
typedef struct {
|
13
|
+
size_t indices[4];
|
14
|
+
} Sticker4Cycle;
|
15
|
+
|
16
|
+
direction_t invert_cube_direction(direction_t direction);
|
17
|
+
|
18
|
+
direction_t invert_skewb_direction(direction_t direction);
|
19
|
+
|
20
|
+
void apply_sticker_cycle(VALUE* stickers, const size_t* indices, size_t size, bool invert);
|
21
|
+
|
22
|
+
void apply_sticker_4cycle(VALUE* stickers, Sticker4Cycle cycle, direction_t direction);
|
23
|
+
|
24
|
+
int color_eq(VALUE left, VALUE right);
|
25
|
+
|
26
|
+
int log2_64_floor(uint64_t value);
|
27
|
+
|
28
|
+
uint64_t iexp(uint64_t base, uint32_t exp);
|
data/lib/twisty_puzzles.rb
CHANGED
@@ -22,6 +22,7 @@ require 'twisty_puzzles/cube_print_helper'
|
|
22
22
|
require 'twisty_puzzles/cube_state'
|
23
23
|
require 'twisty_puzzles/letter_scheme'
|
24
24
|
require 'twisty_puzzles/move_type_creator'
|
25
|
+
require 'twisty_puzzles/native'
|
25
26
|
require 'twisty_puzzles/parser'
|
26
27
|
require 'twisty_puzzles/part_cycle_factory'
|
27
28
|
require 'twisty_puzzles/puzzle'
|
@@ -150,8 +150,7 @@ module TwistyPuzzles
|
|
150
150
|
when :qtm then slice_factor * direction_factor
|
151
151
|
when :htm then slice_factor
|
152
152
|
when :stm then 1
|
153
|
-
when :qstm then direction_factor
|
154
|
-
when :sqtm then direction_factor
|
153
|
+
when :qstm, :sqtm then direction_factor
|
155
154
|
else raise ArgumentError, "Invalid move metric #{metric.inspect}."
|
156
155
|
end
|
157
156
|
end
|
@@ -20,7 +20,7 @@ module TwistyPuzzles
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def parse_named_captures(match)
|
23
|
-
present_named_captures = match.named_captures.
|
23
|
+
present_named_captures = match.named_captures.compact
|
24
24
|
present_named_captures.map do |name, string|
|
25
25
|
key = parse_part_key(name).to_sym
|
26
26
|
value = parse_move_part(name, string)
|
@@ -135,7 +135,7 @@ module TwistyPuzzles
|
|
135
135
|
def self.alg_plus_cancelled_move(algorithm, move, cube_size)
|
136
136
|
if move.is_a?(Rotation) && (tail_rotations = num_tail_rotations(algorithm)) >= 2
|
137
137
|
Algorithm.new(algorithm.moves[0...-tail_rotations]) +
|
138
|
-
cancelled_rotations(algorithm.moves[-tail_rotations
|
138
|
+
cancelled_rotations(algorithm.moves[-tail_rotations..] + [move])
|
139
139
|
else
|
140
140
|
Algorithm.new(algorithm.moves[0...-1]) +
|
141
141
|
algorithm.moves[-1].join_with_cancellation(move, cube_size)
|
@@ -56,7 +56,7 @@ module TwistyPuzzles
|
|
56
56
|
raise ArgumentError unless colors.include?(top_color)
|
57
57
|
raise ArgumentError unless colors.include?(front_color)
|
58
58
|
|
59
|
-
#
|
59
|
+
# NOTE: The reason that this is so complicated is that we want it to still work if the
|
60
60
|
# chirality corner gets exchanged.
|
61
61
|
|
62
62
|
# Do the obvious and handle opposites of the top and front color so we have no
|
@@ -17,6 +17,7 @@ module TwistyPuzzles
|
|
17
17
|
def initialize(algorithm)
|
18
18
|
raise ArgumentError unless algorithm.is_a?(Algorithm)
|
19
19
|
|
20
|
+
super()
|
20
21
|
@algorithm = algorithm
|
21
22
|
end
|
22
23
|
|
@@ -47,6 +48,7 @@ module TwistyPuzzles
|
|
47
48
|
raise ArgumentError unless first_part.is_a?(Algorithm)
|
48
49
|
raise ArgumentError unless second_part.is_a?(Algorithm)
|
49
50
|
|
51
|
+
super()
|
50
52
|
@first_part = first_part
|
51
53
|
@second_part = second_part
|
52
54
|
end
|
@@ -85,6 +87,7 @@ module TwistyPuzzles
|
|
85
87
|
raise ArgumentError, 'Inner commutator has to be a commutator.'
|
86
88
|
end
|
87
89
|
|
90
|
+
super()
|
88
91
|
@setup = setup
|
89
92
|
@inner_commutator = inner_commutator
|
90
93
|
end
|