twisty_puzzles 0.0.9 → 0.0.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3dfb23949825572c531ac7bfcea491151629f8673160605069b1793a827d961a
4
- data.tar.gz: f343cc589e99ac129717f47a7b3dbe4dcb3d4a0cbcae2e0f8c42b12b22ad842c
3
+ metadata.gz: 20cf60d5066716b3eaa148e33f1e8c598387573170e1005d034f3128e6b971cb
4
+ data.tar.gz: 2770afd2a3a625f281089401c2a2ef47a6c61843d09026e9d999ee79139fe54e
5
5
  SHA512:
6
- metadata.gz: 2fc20aef94e333318c53ee292b5418424ad8880cc2f8ff18c2102d0799d96728ccc9a98869269bf9c6a046174dcf5f71182cf6498021838d8772c18a7008cfe2
7
- data.tar.gz: b79344d44bf6fd32cd9443c79405dcab052867a746e23fecc0b1496bd96b36fc6c4dd09d2a8c3d33389d7e5b5daaf0ca4a0552cce8948c59ac590da458c6150d
6
+ metadata.gz: 2bdf4691d6efea522b259510bcdb405820784870566948d47b653c91e2d8ca495d9946e17f0d9f703ee234bf0758b3a59969dc52e4f91eafa94659c624ac3930
7
+ data.tar.gz: b28d42481604cbec6b748b025a3293eed64e2c7a90b0e6186c6eadeed63352f81b562e835f54bf6b4fbc9905bf047c917b269896c5504628c7d9c7352c1b70ec
@@ -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)
@@ -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
@@ -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
@@ -55,7 +55,9 @@ module TwistyPuzzles
55
55
  self
56
56
  else
57
57
  rotation_neighbors = rotation.axis_face.neighbors
58
- face_index = rotation_neighbors.index(@axis_face) || raise
58
+ face_index = rotation_neighbors.index(@axis_face)
59
+ raise unless face_index
60
+
59
61
  new_axis_face =
60
62
  rotation_neighbors[(face_index + rotation.direction.value) % rotation_neighbors.length]
61
63
  fields = replace_once(identifying_fields, @axis_face, new_axis_face)
@@ -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
@@ -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 =
@@ -141,7 +143,7 @@ module TwistyPuzzles
141
143
  "Color #{c} cannot be part of the color scheme because it is a reserved color."
142
144
  end
143
145
  end
144
- raise ArgumentError unless face_symbols_to_colors.values.all? { |c| c.is_a?(Symbol) }
146
+ raise ArgumentError unless face_symbols_to_colors.values.all?(Symbol)
145
147
  end
146
148
 
147
149
  def add_missing_mappings(turned_face_symbols_to_colors, chirality_corner_source, unknown_index)
@@ -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.
@@ -28,7 +28,7 @@ module TwistyPuzzles
28
28
 
29
29
  # Middle coordinate for uneven numbers, the one before for even numbers
30
30
  def self.middle_or_before(cube_size)
31
- cube_size - cube_size / 2 - 1
31
+ cube_size - (cube_size / 2) - 1
32
32
  end
33
33
 
34
34
  # Middle coordinate for uneven numbers, the one after for even numbers
@@ -38,7 +38,7 @@ module TwistyPuzzles
38
38
 
39
39
  # The last coordinate that is strictly before the middle
40
40
  def self.last_before_middle(cube_size)
41
- cube_size / 2 - 1
41
+ (cube_size / 2) - 1
42
42
  end
43
43
 
44
44
  def self.canonicalize(index, cube_size)
@@ -310,7 +310,7 @@ module TwistyPuzzles
310
310
 
311
311
  def self.for_corner(corner)
312
312
  native = Native::SkewbCoordinate.for_corner(corner.face_symbols)
313
- new(Face.for_face_symbol(corner.face_symbols.first), 1 + corner.piece_index % 4, native)
313
+ new(Face.for_face_symbol(corner.face_symbols.first), 1 + (corner.piece_index % 4), native)
314
314
  end
315
315
 
316
316
  def hash
@@ -34,6 +34,14 @@ module TwistyPuzzles
34
34
 
35
35
  attr_reader :piece_index, :face_symbols
36
36
 
37
+ def self.min_parseable_face_symbols
38
+ self::FACES
39
+ end
40
+
41
+ def self.max_parseable_face_symbols
42
+ self::FACES
43
+ end
44
+
37
45
  def self.generate_parts
38
46
  valid_face_symbol_combinations =
39
47
  FACE_SYMBOLS.permutation(self::FACES).select do |p|
@@ -64,10 +72,18 @@ module TwistyPuzzles
64
72
  true
65
73
  end
66
74
 
75
+ def num_incarnations(_cube_size)
76
+ 1
77
+ end
78
+
67
79
  def base_index_on_face(cube_size, incarnation_index)
68
80
  base_index_on_other_face(solved_face, cube_size, incarnation_index)
69
81
  end
70
82
 
83
+ def base_index_on_other_face(face, cube_size, incarnation_index)
84
+ raise NotImplementedError
85
+ end
86
+
71
87
  def self.for_face_symbols_internal(face_symbols)
72
88
  raise unless face_symbols.length == self::FACES
73
89
 
@@ -82,6 +98,10 @@ module TwistyPuzzles
82
98
  self::ELEMENTS[index]
83
99
  end
84
100
 
101
+ def self.valid?(_face_symbols)
102
+ false
103
+ end
104
+
85
105
  def <=>(other)
86
106
  @piece_index <=> other.piece_index
87
107
  end
@@ -110,7 +130,7 @@ module TwistyPuzzles
110
130
  # Rotate a piece such that the given face symbol is the first face symbol.
111
131
  def rotate_face_symbol_up(face_symbol)
112
132
  index = @face_symbols.index(face_symbol)
113
- raise "Part #{self} doesn't have face symbol #{c}." unless index
133
+ raise "Part #{self} doesn't have face symbol #{face_symbol}." unless index
114
134
 
115
135
  rotate_by(index)
116
136
  end
@@ -133,13 +153,9 @@ module TwistyPuzzles
133
153
  (0...@face_symbols.length).map { |i| rotate_by(i) }
134
154
  end
135
155
 
136
- def self.create_for_face_symbols(face_symbols)
137
- new(face_symbols)
138
- end
139
-
140
156
  def self.parse(piece_description)
141
157
  face_symbols =
142
- piece_description.upcase.strip.split('').map do |e|
158
+ piece_description.upcase.strip.chars.map do |e|
143
159
  FACE_SYMBOLS[FACE_NAMES.index(e)]
144
160
  end
145
161
  for_face_symbols(face_symbols)
@@ -319,6 +335,14 @@ module TwistyPuzzles
319
335
  class MoveableCenter < Part
320
336
  FACES = 1
321
337
 
338
+ def self.min_parseable_face_symbols
339
+ self::CORRESPONDING_PART_CLASS::FACES
340
+ end
341
+
342
+ def self.max_parseable_face_symbols
343
+ self::CORRESPONDING_PART_CLASS::FACES
344
+ end
345
+
322
346
  def self.min_cube_size
323
347
  4
324
348
  end
@@ -338,10 +362,6 @@ module TwistyPuzzles
338
362
  find_only(self::ELEMENTS) { |e| e.corresponding_part == corresponding_part }
339
363
  end
340
364
 
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
365
  def face_symbol
346
366
  @face_symbols[0]
347
367
  end
@@ -463,11 +483,15 @@ module TwistyPuzzles
463
483
  false
464
484
  end
465
485
 
486
+ def self.max_parseable_face_symbols
487
+ FACES + 1
488
+ end
489
+
466
490
  def self.for_face_symbols(face_symbols)
467
491
  # One additional face symbol is usually mentioned for wings.
468
492
  raise unless face_symbols.length == FACES || face_symbols.length == FACES + 1
469
493
 
470
- if face_symbols.length == 3
494
+ if face_symbols.length == FACES + 1
471
495
  for_corner_face_symbols(face_symbols)
472
496
  else
473
497
  for_face_symbols_internal(face_symbols)
@@ -514,7 +538,7 @@ module TwistyPuzzles
514
538
  end
515
539
 
516
540
  def num_incarnations(cube_size)
517
- [cube_size / 2 - 1, 0].max
541
+ cube_size > 3 ? (cube_size / 2) - 1 : 0
518
542
  end
519
543
 
520
544
  # One index of such a piece on a on a NxN face.
@@ -530,14 +554,6 @@ module TwistyPuzzles
530
554
  class Corner < Part
531
555
  FACES = 3
532
556
 
533
- def self.create_for_face_symbols(face_symbols)
534
- piece_candidates =
535
- face_symbols[1..].permutation.map do |cs|
536
- new([face_symbols[0]] + cs)
537
- end
538
- find_only(piece_candidates, &:valid?)
539
- end
540
-
541
557
  def self.for_face_symbols(face_symbols)
542
558
  unless face_symbols.length == FACES
543
559
  raise "Invalid number of face_symbols to create a corner: #{face_symbols.inspect}"
@@ -621,7 +637,7 @@ module TwistyPuzzles
621
637
  ELEMENTS = generate_parts
622
638
 
623
639
  def num_incarnations(cube_size)
624
- [cube_size / 2 - 1, 0].max
640
+ cube_size > 3 ? (cube_size / 2) - 1 : 0
625
641
  end
626
642
 
627
643
  # One index of such a piece on a on a NxN face.
@@ -644,10 +660,10 @@ module TwistyPuzzles
644
660
  end
645
661
 
646
662
  def num_incarnations(cube_size)
647
- if cube_size.even?
663
+ if cube_size.even? || cube_size <= 3
648
664
  0
649
665
  else
650
- [cube_size / 2 - 1, 0].max
666
+ (cube_size / 2) - 1
651
667
  end
652
668
  end
653
669
 
@@ -68,8 +68,8 @@ module TwistyPuzzles
68
68
 
69
69
  def equivalent_slice_move?(other, cube_size)
70
70
  cube_size == 3 && other.slice_index == 1 &&
71
- (@axis_face == other.axis_face && @direction == other.direction ||
72
- @axis_face == other.axis_face.opposite && @direction == other.direction.inverse)
71
+ ((@axis_face == other.axis_face && @direction == other.direction) ||
72
+ (@axis_face == other.axis_face.opposite && @direction == other.direction.inverse))
73
73
  end
74
74
  end
75
75
 
@@ -72,9 +72,9 @@ module TwistyPuzzles
72
72
  def skewb_ascii_art_line(first_color, middle_color, last_color, num_first_color)
73
73
  raise if num_first_color > SKEWB_FACE_SIZE / 2
74
74
 
75
- first_color * num_first_color +
76
- middle_color * (SKEWB_FACE_SIZE - 2 * num_first_color) +
77
- last_color * num_first_color
75
+ (first_color * num_first_color) +
76
+ (middle_color * (SKEWB_FACE_SIZE - (2 * num_first_color))) +
77
+ (last_color * num_first_color)
78
78
  end
79
79
 
80
80
  def skewb_ascii_art(center_color, corner_colors)
@@ -116,7 +116,7 @@ module TwistyPuzzles
116
116
  front_face = face_lines(cube_state, :F, 1, 3) { |c| color_character(c, color_mode) }
117
117
  right_face = face_lines(cube_state, :R, 1, 3) { |c| color_character(c, color_mode) }
118
118
  pll_line = front_face.first + right_face.first
119
- (top_face + [pll_line] * 3).join("\n")
119
+ (top_face + ([pll_line] * 3)).join("\n")
120
120
  end
121
121
 
122
122
  def cube_string(cube_state, color_mode)
@@ -150,7 +150,7 @@ module TwistyPuzzles
150
150
  end
151
151
 
152
152
  def pad_lines(lines, padding)
153
- lines.map { |line| empty_name * padding + line }
153
+ lines.map { |line| (empty_name * padding) + line }
154
154
  end
155
155
 
156
156
  def zip_concat_lines(*args)
@@ -75,6 +75,16 @@ module TwistyPuzzles
75
75
 
76
76
  alias == eql?
77
77
 
78
+ def rotation_algorithms
79
+ Rotation::ALL_ROTATIONS.combination(2).reject do |r0, r1|
80
+ r0.inverse == r1
81
+ end.map { |rs| Algorithm.new(rs) } # rubocop:disable Style/MultilineBlockChain
82
+ end
83
+
84
+ def equal_modulo_rotations?(other)
85
+ rotation_algorithms.any? { |r| r.apply_temporarily_to(self) { |state| state == other } }
86
+ end
87
+
78
88
  def hash
79
89
  @hash ||= [self.class, @native].hash
80
90
  end
@@ -6,7 +6,7 @@ module TwistyPuzzles
6
6
  class MoveTypeCreator
7
7
  def initialize(capture_keys, move_class)
8
8
  raise TypeError unless move_class.is_a?(Class)
9
- raise TypeError unless capture_keys.all? { |k| k.is_a?(Symbol) }
9
+ raise TypeError unless capture_keys.all?(Symbol)
10
10
 
11
11
  @capture_keys = capture_keys.freeze
12
12
  @move_class = move_class
@@ -77,8 +77,8 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
77
77
  end
78
78
 
79
79
  # Parses at least one move.
80
- def parse_nonempty_moves
81
- moves = parse_moves
80
+ def parse_nonempty_moves_with_triggers
81
+ moves = parse_moves_with_triggers
82
82
  complain('move') if moves.empty?
83
83
  moves
84
84
  end
@@ -117,10 +117,26 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
117
117
  if @scanner.peek(1) == OPENING_BRACKET
118
118
  parse_commutator_internal
119
119
  else
120
- FakeCommutator.new(parse_moves_with_triggers)
120
+ parse_commutator_no_brackets
121
121
  end
122
122
  end
123
123
 
124
+ def parse_commutator_no_brackets
125
+ setup_or_first_part_or_algorithm = parse_moves_with_triggers
126
+ skip_spaces
127
+ if @scanner.eos? || !SEPARATORS.include?(@scanner.peek(1))
128
+ return FakeCommutator.new(setup_or_first_part_or_algorithm)
129
+ end
130
+
131
+ setup_or_first_part = setup_or_first_part_or_algorithm
132
+ complain('move') if setup_or_first_part.empty?
133
+ separator = parse_separator
134
+ comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
135
+ skip_spaces
136
+ complain('end of commutator') unless @scanner.eos?
137
+ comm
138
+ end
139
+
124
140
  def parse_algorithm
125
141
  skip_spaces
126
142
  parse_moves_with_triggers
@@ -131,40 +147,67 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
131
147
  if @scanner.peek(1) == OPENING_BRACKET
132
148
  parse_pure_commutator
133
149
  else
134
- FakeCommutator.new(parse_moves_with_triggers)
150
+ parse_pure_commutator_no_brackets
135
151
  end
136
152
  end
137
153
 
138
154
  def parse_pure_commutator
139
155
  skip_spaces
140
156
  parse_open_bracket
141
- first_part = parse_nonempty_moves
157
+ first_part = parse_nonempty_moves_with_triggers
142
158
  skip_spaces
143
- complain('middle of pure commutator') unless @scanner.getch == ','
144
- second_part = parse_nonempty_moves
159
+ parse_comma
160
+ second_part = parse_nonempty_moves_with_triggers
145
161
  skip_spaces
146
162
  parse_close_bracket
147
163
  PureCommutator.new(first_part, second_part)
148
164
  end
149
165
 
166
+ def parse_pure_commutator_no_brackets
167
+ first_part_or_algorithm = parse_moves_with_triggers
168
+ skip_spaces
169
+ if @scanner.eos? || !@scanner.peek(1) == ','
170
+ return FakeCommutator.new(first_part_or_algorithm)
171
+ end
172
+
173
+ first_part = first_part_or_algorithm
174
+ complain('move') if first_part.empty?
175
+ parse_comma
176
+ second_part = parse_nonempty_moves_with_triggers
177
+ skip_spaces
178
+ PureCommutator.new(first_part, second_part)
179
+ end
180
+
181
+ def parse_comma
182
+ complain('middle of pure commutator') unless @scanner.getch == ','
183
+ end
184
+
150
185
  def parse_commutator_internal_after_separator(setup_or_first_part, separator)
151
186
  if [':', ';'].include?(separator)
152
187
  inner_commutator = parse_setup_commutator_inner
153
188
  SetupCommutator.new(setup_or_first_part, inner_commutator)
154
189
  elsif separator == ','
155
- second_part = parse_nonempty_moves
190
+ second_part = parse_nonempty_moves_with_triggers
156
191
  PureCommutator.new(setup_or_first_part, second_part)
157
192
  else
158
193
  complain('end of setup or middle of pure commutator') unless @scanner.eos?
159
194
  end
160
195
  end
161
196
 
197
+ SEPARATORS = %w[; : ,].freeze
198
+
199
+ def parse_separator
200
+ separator = @scanner.getch
201
+ complain('separator between commutator parts') unless SEPARATORS.include?(separator)
202
+ separator
203
+ end
204
+
162
205
  def parse_commutator_internal
163
206
  skip_spaces
164
207
  parse_open_bracket
165
- setup_or_first_part = parse_nonempty_moves
208
+ setup_or_first_part = parse_nonempty_moves_with_triggers
166
209
  skip_spaces
167
- separator = @scanner.getch
210
+ separator = parse_separator
168
211
  comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
169
212
  skip_spaces
170
213
  parse_close_bracket
@@ -35,10 +35,12 @@ module TwistyPuzzles
35
35
 
36
36
  direction = translated_direction(skewb_direction)
37
37
 
38
- Algorithm.new([
39
- Rotation.new(corner.faces[skewb_direction.value], direction),
40
- Rotation.new(corner.faces[0], direction)
41
- ])
38
+ Algorithm.new(
39
+ [
40
+ Rotation.new(corner.faces[skewb_direction.value], direction),
41
+ Rotation.new(corner.faces[0], direction)
42
+ ]
43
+ )
42
44
  end
43
45
 
44
46
  def to_s
@@ -26,10 +26,12 @@ module TwistyPuzzles
26
26
  next unless c2.face_symbols.include?(c1_rot.face_symbols.first)
27
27
 
28
28
  c2_rot = c2.rotate_face_symbol_up(c1_rot.face_symbols.first)
29
- check_parts.push([
30
- SkewbCoordinate.for_corner(c1_rot),
31
- SkewbCoordinate.for_corner(c2_rot)
32
- ])
29
+ check_parts.push(
30
+ [
31
+ SkewbCoordinate.for_corner(c1_rot),
32
+ SkewbCoordinate.for_corner(c2_rot)
33
+ ]
34
+ )
33
35
  end
34
36
  matching_corners.push(check_parts)
35
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TwistyPuzzles
4
- VERSION = '0.0.9'
4
+ VERSION = '0.0.13'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twisty_puzzles
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernhard F. Brodowsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-21 00:00:00.000000000 Z
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize