twisty_puzzles 0.0.5 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -1
  3. data/ext/twisty_puzzles/native/cube_algorithm.c +10 -18
  4. data/ext/twisty_puzzles/native/cube_average.c +2 -2
  5. data/ext/twisty_puzzles/native/cube_coordinate.c +16 -15
  6. data/ext/twisty_puzzles/native/cube_coordinate.h +7 -7
  7. data/ext/twisty_puzzles/native/cube_state.c +23 -33
  8. data/ext/twisty_puzzles/native/cube_state.h +2 -2
  9. data/ext/twisty_puzzles/native/face_symbols.h +2 -2
  10. data/ext/twisty_puzzles/native/skewb_algorithm.c +3 -12
  11. data/ext/twisty_puzzles/native/skewb_coordinate.c +0 -11
  12. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +3 -2
  13. data/ext/twisty_puzzles/native/skewb_state.c +0 -2
  14. data/ext/twisty_puzzles/native/utils.c +7 -1
  15. data/ext/twisty_puzzles/native/utils.h +2 -3
  16. data/lib/twisty_puzzles/abstract_move.rb +5 -2
  17. data/lib/twisty_puzzles/abstract_move_parser.rb +3 -3
  18. data/lib/twisty_puzzles/algorithm.rb +1 -1
  19. data/lib/twisty_puzzles/algorithm_transformation.rb +27 -19
  20. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +4 -1
  21. data/lib/twisty_puzzles/cancellation_helper.rb +1 -1
  22. data/lib/twisty_puzzles/color_scheme.rb +8 -6
  23. data/lib/twisty_puzzles/commutator.rb +7 -0
  24. data/lib/twisty_puzzles/compiled_algorithm.rb +4 -4
  25. data/lib/twisty_puzzles/coordinate.rb +4 -6
  26. data/lib/twisty_puzzles/cube.rb +19 -23
  27. data/lib/twisty_puzzles/cube_move.rb +0 -4
  28. data/lib/twisty_puzzles/cube_move_parser.rb +23 -23
  29. data/lib/twisty_puzzles/cube_print_helper.rb +4 -3
  30. data/lib/twisty_puzzles/cube_state.rb +10 -0
  31. data/lib/twisty_puzzles/parser.rb +3 -3
  32. data/lib/twisty_puzzles/rotation.rb +8 -4
  33. data/lib/twisty_puzzles/skewb_move.rb +1 -0
  34. data/lib/twisty_puzzles/skewb_move_parser.rb +1 -0
  35. data/lib/twisty_puzzles/skewb_notation.rb +7 -1
  36. data/lib/twisty_puzzles/skewb_state.rb +6 -4
  37. data/lib/twisty_puzzles/version.rb +1 -1
  38. metadata +11 -12
@@ -56,17 +56,6 @@ size_t center_sticker_index(const face_index_t on_face_index) {
56
56
  return on_face_index * skewb_stickers_per_face;
57
57
  }
58
58
 
59
- static VALUE part_type_from_symbol(const VALUE part_type_symbol) {
60
- Check_Type(face_symbol, T_SYMBOL);
61
- if (SYM2ID(part_type_symbol) == center_part_type_id) {
62
- return CENTER;
63
- } else if (SYM2ID(part_type_symbol) == corner_part_type_id) {
64
- return CORNER;
65
- } else {
66
- rb_raise(rb_eArgError, "Invalid part type symbol %+"PRIsVALUE"", part_type_symbol);
67
- }
68
- }
69
-
70
59
  static VALUE part_type_to_symbol(const SkewbPartType part_type) {
71
60
  // Caching these keys isn't easy because the garbage collector will get them.
72
61
  switch (part_type) {
@@ -32,7 +32,7 @@ static ID times = 848;
32
32
 
33
33
  typedef struct {
34
34
  Corner corner_pairs[max_corner_pair_group_size][2];
35
- int num_corner_pairs;
35
+ size_t num_corner_pairs;
36
36
  // Number of different group fingerprints. This can be used to merge several group fingerprints.
37
37
  int num_group_fingerprints;
38
38
  } CornerPairGroup;
@@ -55,7 +55,7 @@ static bool has_color_at(const ActualCornerStickers actual, const size_t index,
55
55
  }
56
56
 
57
57
  typedef struct {
58
- int layer_index;
58
+ size_t layer_index;
59
59
  bool is_oriented;
60
60
  bool is_present;
61
61
  } ActualCornerStickersInfo;
@@ -268,4 +268,5 @@ void init_skewb_layer_fingerprint_method_under(const VALUE module) {
268
268
  plus = rb_intern("+");
269
269
  times = rb_intern("*");
270
270
  rb_define_singleton_method(module, "skewb_layer_fingerprint", skewb_layer_fingerprint, 2);
271
+ init_corner_pair_groups();
271
272
  }
@@ -43,8 +43,6 @@ static int SkewbState_replace_face(const VALUE key, const VALUE value, const VAL
43
43
 
44
44
  static VALUE SkewbState_initialize(const VALUE self, const VALUE stickers) {
45
45
  Check_Type(stickers, T_HASH);
46
- SkewbStateData* data;
47
- GetSkewbStateData(self, data);
48
46
  if (RHASH_SIZE(stickers) != skewb_faces) {
49
47
  rb_raise(rb_eTypeError, "Skewbs must have %d faces. Got %ld.", skewb_faces, RHASH_SIZE(stickers));
50
48
  }
@@ -64,7 +64,7 @@ int log2_64_floor(uint64_t value) {
64
64
  return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
65
65
  }
66
66
 
67
- uint64_t iexp(const uint64_t base, uint32_t exp) {
67
+ uint64_t iexp(const uint64_t base, const uint32_t exp) {
68
68
  uint64_t result = 1;
69
69
  for (uint32_t m = 1 << 31; m; m >>= 1) {
70
70
  result = result * result;
@@ -74,3 +74,9 @@ uint64_t iexp(const uint64_t base, uint32_t exp) {
74
74
  }
75
75
  return result;
76
76
  }
77
+
78
+ void check_cube_size(const long cube_size) {
79
+ if (cube_size <= 0) {
80
+ rb_raise(rb_eArgError, "Cube size must be positive. Got %ld.", cube_size);
81
+ }
82
+ }
@@ -5,11 +5,8 @@
5
5
 
6
6
  #define MIN(a, b) ((a) > (b) ? (b) : (a))
7
7
  #define MAX(a, b) ((a) > (b) ? (a) : (b))
8
- #define FALSE 0
9
- #define TRUE 1
10
8
  #define CROP_MOD(a, b) (((a) % (b) + (b)) % (b))
11
9
 
12
- typedef char bool;
13
10
  typedef char direction_t;
14
11
 
15
12
  typedef struct {
@@ -29,3 +26,5 @@ int color_eq(VALUE left, VALUE right);
29
26
  int log2_64_floor(uint64_t value);
30
27
 
31
28
  uint64_t iexp(uint64_t base, uint32_t exp);
29
+
30
+ void check_cube_size(long cube_size);
@@ -143,6 +143,10 @@ module TwistyPuzzles
143
143
  prepend_slice_move(other, cube_size)
144
144
  end
145
145
 
146
+ def prepend_slice_move(_other, _cube_size)
147
+ raise NotImplementedError, "#{self.class}#prepend_slice_move is not implemented"
148
+ end
149
+
146
150
  private
147
151
 
148
152
  def move_count_internal(metric, slice_factor, direction_factor)
@@ -150,8 +154,7 @@ module TwistyPuzzles
150
154
  when :qtm then slice_factor * direction_factor
151
155
  when :htm then slice_factor
152
156
  when :stm then 1
153
- when :qstm then direction_factor
154
- when :sqtm then direction_factor
157
+ when :qstm, :sqtm then direction_factor
155
158
  else raise ArgumentError, "Invalid move metric #{metric.inspect}."
156
159
  end
157
160
  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)
@@ -31,14 +31,14 @@ module TwistyPuzzles
31
31
  def parse_move(move_string)
32
32
  match = move_string.match(regexp)
33
33
  if !match || !match.pre_match.empty? || !match.post_match.empty?
34
- raise ArgumentError("Invalid move #{move_string}.")
34
+ raise ArgumentError, "Invalid move #{move_string}."
35
35
  end
36
36
 
37
37
  parsed_parts = parse_named_captures(match)
38
38
  move_type_creators.each do |parser|
39
39
  return parser.create(parsed_parts) if parser.applies_to?(parsed_parts)
40
40
  end
41
- raise "No move type creator applies to #{parsed_parts}"
41
+ raise ArgumentError, "No move type creator applies to #{parsed_parts}"
42
42
  end
43
43
  end
44
44
  end
@@ -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)
@@ -4,28 +4,36 @@ require 'twisty_puzzles/cube_direction'
4
4
  require 'twisty_puzzles/rotation'
5
5
 
6
6
  module TwistyPuzzles
7
- AlgorithmTransformation =
8
- Struct.new(:rotation, :mirror, :mirror_normal_face) do
9
- def transformed(algorithm)
10
- algorithm = algorithm.mirror(mirror_normal_face) if mirror
11
- algorithm.rotate_by(rotation)
12
- end
7
+ # A transformation consisting of mirroring and rotating that can be applied to an algorithm.
8
+ class AlgorithmTransformation
9
+ def initialize(rotation, mirror, mirror_normal_face)
10
+ @rotation = rotation
11
+ @mirror = mirror
12
+ @mirror_normal_face = mirror_normal_face
13
+ end
13
14
 
14
- def identity?
15
- rotation.identity? && !mirror
16
- end
15
+ attr_reader :rotation, :mirror, :mirror_normal_face
17
16
 
18
- # Returns algorithm transformations that mirror an algorithm and rotate it around a face.
19
- def self.around_face(face)
20
- around_face_rotations = CubeDirection::ALL_DIRECTIONS.map { |d| Rotation.new(face, d) }
21
- mirror_normal_face = face.neighbors.first
22
- around_face_rotations.product([true, false]).map do |r, m|
23
- AlgorithmTransformation.new(r, m, mirror_normal_face)
24
- end
25
- end
17
+ def transformed(algorithm)
18
+ algorithm = algorithm.mirror(mirror_normal_face) if mirror
19
+ algorithm.rotate_by(rotation)
20
+ end
26
21
 
27
- def self.around_face_without_identity(face)
28
- around_face(face).reject(&:identity?)
22
+ def identity?
23
+ rotation.identity? && !mirror
24
+ end
25
+
26
+ # Returns algorithm transformations that mirror an algorithm and rotate it around a face.
27
+ def self.around_face(face)
28
+ around_face_rotations = CubeDirection::ALL_DIRECTIONS.map { |d| Rotation.new(face, d) }
29
+ mirror_normal_face = face.neighbors.first
30
+ around_face_rotations.product([true, false]).map do |r, m|
31
+ AlgorithmTransformation.new(r, m, mirror_normal_face)
29
32
  end
30
33
  end
34
+
35
+ def self.around_face_without_identity(face)
36
+ around_face(face).reject(&:identity?)
37
+ end
38
+ end
31
39
  end
@@ -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
@@ -54,7 +55,9 @@ module TwistyPuzzles
54
55
  self
55
56
  else
56
57
  rotation_neighbors = rotation.axis_face.neighbors
57
- face_index = rotation_neighbors.index(@axis_face) || raise
58
+ face_index = rotation_neighbors.index(@axis_face)
59
+ raise unless face_index
60
+
58
61
  new_axis_face =
59
62
  rotation_neighbors[(face_index + rotation.direction.value) % rotation_neighbors.length]
60
63
  fields = replace_once(identifying_fields, @axis_face, new_axis_face)
@@ -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)
@@ -37,7 +37,7 @@ module TwistyPuzzles
37
37
  end
38
38
 
39
39
  def part_for_colors(part_type, colors)
40
- raise ArgumentError unless part_type.is_a?(Class)
40
+ raise ArgumentError unless part_type.is_a?(Class) && (part_type < Part)
41
41
 
42
42
  part_type.for_face_symbols(colors.map { |c| face_symbol(c) })
43
43
  end
@@ -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
@@ -95,10 +95,12 @@ module TwistyPuzzles
95
95
 
96
96
  def chirality_corner_source_and_unknown_index(obvious_turned_face_symbols_to_colors)
97
97
  corner_matcher =
98
- CornerMatcher.new(CHIRALITY_FACE_SYMBOLS.map do |s|
99
- # This will return nil for exactly one face that we don't know yet.
100
- @colors_to_face_symbols[obvious_turned_face_symbols_to_colors[s]]
101
- end)
98
+ CornerMatcher.new(
99
+ CHIRALITY_FACE_SYMBOLS.map do |s|
100
+ # This will return nil for exactly one face that we don't know yet.
101
+ @colors_to_face_symbols[obvious_turned_face_symbols_to_colors[s]]
102
+ end
103
+ )
102
104
 
103
105
  # There should be exactly one corner that gets mapped to the chirality corner.
104
106
  chirality_corner_source =
@@ -10,6 +10,10 @@ module TwistyPuzzles
10
10
  def cancellations(other, cube_size, metric = :htm)
11
11
  algorithm.cancellations(other.algorithm, cube_size, metric)
12
12
  end
13
+
14
+ def algorithm
15
+ raise NotImplementedError
16
+ end
13
17
  end
14
18
 
15
19
  # Algorithm that is used like a commutator but actually isn't one.
@@ -17,6 +21,7 @@ module TwistyPuzzles
17
21
  def initialize(algorithm)
18
22
  raise ArgumentError unless algorithm.is_a?(Algorithm)
19
23
 
24
+ super()
20
25
  @algorithm = algorithm
21
26
  end
22
27
 
@@ -47,6 +52,7 @@ module TwistyPuzzles
47
52
  raise ArgumentError unless first_part.is_a?(Algorithm)
48
53
  raise ArgumentError unless second_part.is_a?(Algorithm)
49
54
 
55
+ super()
50
56
  @first_part = first_part
51
57
  @second_part = second_part
52
58
  end
@@ -85,6 +91,7 @@ module TwistyPuzzles
85
91
  raise ArgumentError, 'Inner commutator has to be a commutator.'
86
92
  end
87
93
 
94
+ super()
88
95
  @setup = setup
89
96
  @inner_commutator = inner_commutator
90
97
  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)
@@ -90,7 +90,7 @@ module TwistyPuzzles
90
90
  base_coordinate = Coordinate.from_indices(
91
91
  part.solved_face, cube_size, *part.base_index_on_face(cube_size, incarnation_index)
92
92
  )
93
- other_face_symbols = part.corresponding_part.face_symbols[1..-1]
93
+ other_face_symbols = part.corresponding_part.face_symbols[1..]
94
94
  match_coordinate_internal(base_coordinate, other_face_symbols)
95
95
  end
96
96
 
@@ -99,7 +99,7 @@ module TwistyPuzzles
99
99
  def self.solved_positions(part, cube_size, incarnation_index)
100
100
  solved_coordinate = solved_position(part, cube_size, incarnation_index)
101
101
  other_coordinates =
102
- part.face_symbols[1..-1].map.with_index do |f, i|
102
+ part.face_symbols[1..].map.with_index do |f, i|
103
103
  face = Face.for_face_symbol(f)
104
104
  # The reverse is important for edge like parts. We are not in the same position as usual
105
105
  # solved pieces would be.
@@ -109,7 +109,7 @@ module TwistyPuzzles
109
109
  base_coordinate = Coordinate.from_indices(face, cube_size, *base_indices)
110
110
  other_face_symbols = [part.face_symbols[0]] +
111
111
  part.corresponding_part.face_symbols[1...i + 1] +
112
- part.corresponding_part.face_symbols[i + 2..-1]
112
+ part.corresponding_part.face_symbols[i + 2..]
113
113
  match_coordinate_internal(base_coordinate, other_face_symbols)
114
114
  end
115
115
  [solved_coordinate] + other_coordinates
@@ -276,7 +276,7 @@ module TwistyPuzzles
276
276
  @native = native
277
277
  end
278
278
 
279
- attr_reader :native
279
+ attr_reader :native, :coordinate
280
280
 
281
281
  private_class_method :new
282
282
 
@@ -311,7 +311,5 @@ module TwistyPuzzles
311
311
  def face
312
312
  @face ||= Face.for_face_symbol(@native.face)
313
313
  end
314
-
315
- attr_reader :coordinate
316
314
  end
317
315
  end
@@ -64,10 +64,18 @@ module TwistyPuzzles
64
64
  true
65
65
  end
66
66
 
67
+ def num_incarnations(_cube_size)
68
+ 1
69
+ end
70
+
67
71
  def base_index_on_face(cube_size, incarnation_index)
68
72
  base_index_on_other_face(solved_face, cube_size, incarnation_index)
69
73
  end
70
74
 
75
+ def base_index_on_other_face(face, cube_size, incarnation_index)
76
+ raise NotImplementedError
77
+ end
78
+
71
79
  def self.for_face_symbols_internal(face_symbols)
72
80
  raise unless face_symbols.length == self::FACES
73
81
 
@@ -82,6 +90,10 @@ module TwistyPuzzles
82
90
  self::ELEMENTS[index]
83
91
  end
84
92
 
93
+ def self.valid?(_face_symbols)
94
+ false
95
+ end
96
+
85
97
  def <=>(other)
86
98
  @piece_index <=> other.piece_index
87
99
  end
@@ -97,7 +109,7 @@ module TwistyPuzzles
97
109
  end
98
110
 
99
111
  def inspect
100
- self.class.to_s + '(' + @face_symbols.map(&:to_s).join(', ') + ')'
112
+ "#{self.class}(#{@face_symbols.map(&:to_s).join(', ')})"
101
113
  end
102
114
 
103
115
  def to_s
@@ -110,7 +122,7 @@ module TwistyPuzzles
110
122
  # Rotate a piece such that the given face symbol is the first face symbol.
111
123
  def rotate_face_symbol_up(face_symbol)
112
124
  index = @face_symbols.index(face_symbol)
113
- raise "Part #{self} doesn't have face symbol #{c}." unless index
125
+ raise "Part #{self} doesn't have face symbol #{face_symbol}." unless index
114
126
 
115
127
  rotate_by(index)
116
128
  end
@@ -133,10 +145,6 @@ module TwistyPuzzles
133
145
  (0...@face_symbols.length).map { |i| rotate_by(i) }
134
146
  end
135
147
 
136
- def self.create_for_face_symbols(face_symbols)
137
- new(face_symbols)
138
- end
139
-
140
148
  def self.parse(piece_description)
141
149
  face_symbols =
142
150
  piece_description.upcase.strip.split('').map do |e|
@@ -338,10 +346,6 @@ module TwistyPuzzles
338
346
  find_only(self::ELEMENTS) { |e| e.corresponding_part == corresponding_part }
339
347
  end
340
348
 
341
- def self.create_for_face_symbols(face_symbols)
342
- new(self::CORRESPONDING_PART_CLASS.create_for_face_symbols(face_symbols))
343
- end
344
-
345
349
  def face_symbol
346
350
  @face_symbols[0]
347
351
  end
@@ -365,7 +369,7 @@ module TwistyPuzzles
365
369
  attr_reader :corresponding_part
366
370
 
367
371
  def inspect
368
- self.class.to_s + '(' + face_symbol.to_s + ', ' + @corresponding_part.inspect + ')'
372
+ "#{self.class}(#{face_symbol}, #{@corresponding_part.inspect})"
369
373
  end
370
374
 
371
375
  def rotate_by(_number)
@@ -514,7 +518,7 @@ module TwistyPuzzles
514
518
  end
515
519
 
516
520
  def num_incarnations(cube_size)
517
- [cube_size / 2 - 1, 0].max
521
+ cube_size > 3 ? cube_size / 2 - 1 : 0
518
522
  end
519
523
 
520
524
  # One index of such a piece on a on a NxN face.
@@ -530,14 +534,6 @@ module TwistyPuzzles
530
534
  class Corner < Part
531
535
  FACES = 3
532
536
 
533
- def self.create_for_face_symbols(face_symbols)
534
- piece_candidates =
535
- face_symbols[1..-1].permutation.map do |cs|
536
- new([face_symbols[0]] + cs)
537
- end
538
- find_only(piece_candidates, &:valid?)
539
- end
540
-
541
537
  def self.for_face_symbols(face_symbols)
542
538
  unless face_symbols.length == FACES
543
539
  raise "Invalid number of face_symbols to create a corner: #{face_symbols.inspect}"
@@ -621,7 +617,7 @@ module TwistyPuzzles
621
617
  ELEMENTS = generate_parts
622
618
 
623
619
  def num_incarnations(cube_size)
624
- [cube_size / 2 - 1, 0].max
620
+ cube_size > 3 ? cube_size / 2 - 1 : 0
625
621
  end
626
622
 
627
623
  # One index of such a piece on a on a NxN face.
@@ -644,10 +640,10 @@ module TwistyPuzzles
644
640
  end
645
641
 
646
642
  def num_incarnations(cube_size)
647
- if cube_size.even?
643
+ if cube_size.even? || cube_size <= 3
648
644
  0
649
645
  else
650
- [cube_size / 2 - 1, 0].max
646
+ cube_size / 2 - 1
651
647
  end
652
648
  end
653
649