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.
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)