twisty_puzzles 0.0.11 → 0.0.15
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 +4 -4
- data/lib/twisty_puzzles/algorithm.rb +5 -2
- data/lib/twisty_puzzles/commutator.rb +15 -0
- data/lib/twisty_puzzles/coordinate.rb +3 -3
- data/lib/twisty_puzzles/cube.rb +25 -5
- data/lib/twisty_puzzles/cube_move.rb +2 -2
- data/lib/twisty_puzzles/cube_print_helper.rb +5 -5
- data/lib/twisty_puzzles/parser.rb +73 -14
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5631e9c30819f3520d04ed81aa9b1ceebbee7a3da5c3b51bf2a81947f8fb3a1
|
4
|
+
data.tar.gz: fc4769b6c1ebab5d14cb64ddb3e39356a408750d1d0c7954d4929156296b9f9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c941aaef4dbf2bc8d580d712d669042061c20670aadcf2ee353f353eae4eb31713de9d4791f6d8126e4427ef9e7125562e26b53c85f0a92e09bfe120e8f24f04
|
7
|
+
data.tar.gz: 5e943e567b069fd2b2e21c77fdc7a48ad3e81fb365832439cd1f402da838b5771bc7af4b2293bf635fa1b6de158ddfbea78f9981e690ce3e72a6149c6b042bd2
|
@@ -133,9 +133,12 @@ module TwistyPuzzles
|
|
133
133
|
|
134
134
|
def *(other)
|
135
135
|
raise TypeError unless other.is_a?(Integer)
|
136
|
-
raise ArgumentError if other.negative?
|
137
136
|
|
138
|
-
|
137
|
+
if other.negative?
|
138
|
+
inverse * -other
|
139
|
+
else
|
140
|
+
self.class.new(@moves * other)
|
141
|
+
end
|
139
142
|
end
|
140
143
|
|
141
144
|
def compiled_for_skewb
|
@@ -83,6 +83,21 @@ module TwistyPuzzles
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
+
# Slash commutator of the form A B A2 B' A.
|
87
|
+
class SlashCommutator < PureCommutator
|
88
|
+
def inverse
|
89
|
+
SlashCommutator.new(first_part.inverse, second_part)
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
"[#{@first_part}/#{@second_part}]"
|
94
|
+
end
|
95
|
+
|
96
|
+
def algorithm
|
97
|
+
first_part + second_part + (first_part * 2) + second_part.inverse + first_part
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
86
101
|
# Setup commutator of the form A B A'.
|
87
102
|
class SetupCommutator < Commutator
|
88
103
|
def initialize(setup, inner_commutator)
|
@@ -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
|
data/lib/twisty_puzzles/cube.rb
CHANGED
@@ -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|
|
@@ -147,7 +155,7 @@ module TwistyPuzzles
|
|
147
155
|
|
148
156
|
def self.parse(piece_description)
|
149
157
|
face_symbols =
|
150
|
-
piece_description.upcase.strip.
|
158
|
+
piece_description.upcase.strip.chars.map do |e|
|
151
159
|
FACE_SYMBOLS[FACE_NAMES.index(e)]
|
152
160
|
end
|
153
161
|
for_face_symbols(face_symbols)
|
@@ -327,6 +335,14 @@ module TwistyPuzzles
|
|
327
335
|
class MoveableCenter < Part
|
328
336
|
FACES = 1
|
329
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
|
+
|
330
346
|
def self.min_cube_size
|
331
347
|
4
|
332
348
|
end
|
@@ -467,11 +483,15 @@ module TwistyPuzzles
|
|
467
483
|
false
|
468
484
|
end
|
469
485
|
|
486
|
+
def self.max_parseable_face_symbols
|
487
|
+
FACES + 1
|
488
|
+
end
|
489
|
+
|
470
490
|
def self.for_face_symbols(face_symbols)
|
471
491
|
# One additional face symbol is usually mentioned for wings.
|
472
492
|
raise unless face_symbols.length == FACES || face_symbols.length == FACES + 1
|
473
493
|
|
474
|
-
if face_symbols.length ==
|
494
|
+
if face_symbols.length == FACES + 1
|
475
495
|
for_corner_face_symbols(face_symbols)
|
476
496
|
else
|
477
497
|
for_face_symbols_internal(face_symbols)
|
@@ -518,7 +538,7 @@ module TwistyPuzzles
|
|
518
538
|
end
|
519
539
|
|
520
540
|
def num_incarnations(cube_size)
|
521
|
-
cube_size > 3 ? cube_size / 2 - 1 : 0
|
541
|
+
cube_size > 3 ? (cube_size / 2) - 1 : 0
|
522
542
|
end
|
523
543
|
|
524
544
|
# One index of such a piece on a on a NxN face.
|
@@ -617,7 +637,7 @@ module TwistyPuzzles
|
|
617
637
|
ELEMENTS = generate_parts
|
618
638
|
|
619
639
|
def num_incarnations(cube_size)
|
620
|
-
cube_size > 3 ? cube_size / 2 - 1 : 0
|
640
|
+
cube_size > 3 ? (cube_size / 2) - 1 : 0
|
621
641
|
end
|
622
642
|
|
623
643
|
# One index of such a piece on a on a NxN face.
|
@@ -643,7 +663,7 @@ module TwistyPuzzles
|
|
643
663
|
if cube_size.even? || cube_size <= 3
|
644
664
|
0
|
645
665
|
else
|
646
|
-
cube_size / 2 - 1
|
666
|
+
(cube_size / 2) - 1
|
647
667
|
end
|
648
668
|
end
|
649
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)
|
@@ -16,6 +16,12 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
16
16
|
OPENING_PAREN = '('
|
17
17
|
CLOSING_BRACKET = ']'
|
18
18
|
CLOSING_PAREN = ')'
|
19
|
+
SLASH = '/'
|
20
|
+
COMMA = ','
|
21
|
+
SETUP_SEPARATORS = %w[; :].freeze
|
22
|
+
PURE_SEPARATORS = [SLASH, COMMA].freeze
|
23
|
+
SEPARATORS = (SETUP_SEPARATORS + PURE_SEPARATORS).freeze
|
24
|
+
|
19
25
|
TIMES = '*'
|
20
26
|
|
21
27
|
def initialize(alg_string, move_parser)
|
@@ -77,8 +83,8 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
77
83
|
end
|
78
84
|
|
79
85
|
# Parses at least one move.
|
80
|
-
def
|
81
|
-
moves =
|
86
|
+
def parse_nonempty_moves_with_triggers
|
87
|
+
moves = parse_moves_with_triggers
|
82
88
|
complain('move') if moves.empty?
|
83
89
|
moves
|
84
90
|
end
|
@@ -117,10 +123,26 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
117
123
|
if @scanner.peek(1) == OPENING_BRACKET
|
118
124
|
parse_commutator_internal
|
119
125
|
else
|
120
|
-
|
126
|
+
parse_commutator_no_brackets
|
121
127
|
end
|
122
128
|
end
|
123
129
|
|
130
|
+
def parse_commutator_no_brackets
|
131
|
+
setup_or_first_part_or_algorithm = parse_moves_with_triggers
|
132
|
+
skip_spaces
|
133
|
+
if @scanner.eos? || !SEPARATORS.include?(@scanner.peek(1))
|
134
|
+
return FakeCommutator.new(setup_or_first_part_or_algorithm)
|
135
|
+
end
|
136
|
+
|
137
|
+
setup_or_first_part = setup_or_first_part_or_algorithm
|
138
|
+
complain('move') if setup_or_first_part.empty?
|
139
|
+
separator = parse_separator
|
140
|
+
comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
|
141
|
+
skip_spaces
|
142
|
+
complain('end of commutator') unless @scanner.eos?
|
143
|
+
comm
|
144
|
+
end
|
145
|
+
|
124
146
|
def parse_algorithm
|
125
147
|
skip_spaces
|
126
148
|
parse_moves_with_triggers
|
@@ -131,40 +153,77 @@ module TwistyPuzzles # rubocop:disable Style/Documentation
|
|
131
153
|
if @scanner.peek(1) == OPENING_BRACKET
|
132
154
|
parse_pure_commutator
|
133
155
|
else
|
134
|
-
|
156
|
+
parse_pure_commutator_no_brackets
|
135
157
|
end
|
136
158
|
end
|
137
159
|
|
138
160
|
def parse_pure_commutator
|
139
161
|
skip_spaces
|
140
162
|
parse_open_bracket
|
141
|
-
first_part =
|
163
|
+
first_part = parse_nonempty_moves_with_triggers
|
142
164
|
skip_spaces
|
143
|
-
|
144
|
-
second_part =
|
165
|
+
separator = parse_pure_separator
|
166
|
+
second_part = parse_nonempty_moves_with_triggers
|
145
167
|
skip_spaces
|
146
168
|
parse_close_bracket
|
147
|
-
|
169
|
+
pseudo_pure_commutator(separator, first_part, second_part)
|
170
|
+
end
|
171
|
+
|
172
|
+
def parse_pure_commutator_no_brackets
|
173
|
+
first_part_or_algorithm = parse_moves_with_triggers
|
174
|
+
skip_spaces
|
175
|
+
if @scanner.eos? || !PURE_SEPARATORS.include?(@scanner.peek(1))
|
176
|
+
return FakeCommutator.new(first_part_or_algorithm)
|
177
|
+
end
|
178
|
+
|
179
|
+
first_part = first_part_or_algorithm
|
180
|
+
complain('move') if first_part.empty?
|
181
|
+
separator = parse_pure_separator
|
182
|
+
second_part = parse_nonempty_moves_with_triggers
|
183
|
+
skip_spaces
|
184
|
+
pseudo_pure_commutator(separator, first_part, second_part)
|
185
|
+
end
|
186
|
+
|
187
|
+
def parse_pure_separator
|
188
|
+
separator = @scanner.getch
|
189
|
+
complain('middle of pure commutator') unless PURE_SEPARATORS.include?(separator)
|
190
|
+
separator
|
148
191
|
end
|
149
192
|
|
150
193
|
def parse_commutator_internal_after_separator(setup_or_first_part, separator)
|
151
|
-
if
|
194
|
+
if SETUP_SEPARATORS.include?(separator)
|
152
195
|
inner_commutator = parse_setup_commutator_inner
|
153
196
|
SetupCommutator.new(setup_or_first_part, inner_commutator)
|
154
|
-
elsif separator
|
155
|
-
second_part =
|
156
|
-
|
197
|
+
elsif PURE_SEPARATORS.include?(separator)
|
198
|
+
second_part = parse_nonempty_moves_with_triggers
|
199
|
+
pseudo_pure_commutator(separator, setup_or_first_part, second_part)
|
157
200
|
else
|
158
201
|
complain('end of setup or middle of pure commutator') unless @scanner.eos?
|
159
202
|
end
|
160
203
|
end
|
161
204
|
|
205
|
+
def pseudo_pure_commutator(separator, first_part, second_part)
|
206
|
+
if separator == COMMA
|
207
|
+
PureCommutator.new(first_part, second_part)
|
208
|
+
elsif separator == SLASH
|
209
|
+
SlashCommutator.new(first_part, second_part)
|
210
|
+
else
|
211
|
+
complain('middle of pure commutator') unless PURE_SEPARATORS.include?(separator)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def parse_separator
|
216
|
+
separator = @scanner.getch
|
217
|
+
complain('separator between commutator parts') unless SEPARATORS.include?(separator)
|
218
|
+
separator
|
219
|
+
end
|
220
|
+
|
162
221
|
def parse_commutator_internal
|
163
222
|
skip_spaces
|
164
223
|
parse_open_bracket
|
165
|
-
setup_or_first_part =
|
224
|
+
setup_or_first_part = parse_nonempty_moves_with_triggers
|
166
225
|
skip_spaces
|
167
|
-
separator =
|
226
|
+
separator = parse_separator
|
168
227
|
comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
|
169
228
|
skip_spaces
|
170
229
|
parse_close_bracket
|
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.
|
4
|
+
version: 0.0.15
|
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: 2021-
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|