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,5 @@
1
+ #pragma once
2
+
3
+ #include <ruby.h>
4
+
5
+ void init_skewb_layer_fingerprint_method_under(VALUE module);
@@ -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);
@@ -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.reject { |_n, v| v.nil? }
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)
@@ -128,7 +128,7 @@ module TwistyPuzzles
128
128
  AbstractMove.check_move_metric(metric)
129
129
  return 0 if empty?
130
130
 
131
- @moves.map { |m| m.move_count(cube_size, metric) }.reduce(:+)
131
+ @moves.sum { |m| m.move_count(cube_size, metric) }
132
132
  end
133
133
 
134
134
  def *(other)
@@ -11,6 +11,7 @@ module TwistyPuzzles
11
11
  raise TypeError, "Unsuitable axis face #{axis_face}." unless axis_face.is_a?(Face)
12
12
  raise TypeError unless direction.is_a?(CubeDirection)
13
13
 
14
+ super()
14
15
  @axis_face = axis_face
15
16
  @direction = direction
16
17
  end
@@ -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..-1] + [move])
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
- # Note: The reason that this is so complicated is that we want it to still work if the
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
@@ -26,10 +26,10 @@ module TwistyPuzzles
26
26
  def inverse
27
27
  @inverse ||=
28
28
  begin
29
- alg = self.class.new(@native.inverse)
30
- alg.inverse = self
31
- alg
32
- end
29
+ alg = self.class.new(@native.inverse)
30
+ alg.inverse = self
31
+ alg
32
+ end
33
33
  end
34
34
 
35
35
  def +(other)