twisty_puzzles 0.0.1
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 +7 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/LICENSE +21 -0
- data/README.md +32 -0
- data/ext/twisty_puzzles/native/extconf.rb +5 -0
- data/lib/twisty_puzzles/abstract_direction.rb +54 -0
- data/lib/twisty_puzzles/abstract_move.rb +170 -0
- data/lib/twisty_puzzles/abstract_move_parser.rb +45 -0
- data/lib/twisty_puzzles/algorithm.rb +155 -0
- data/lib/twisty_puzzles/algorithm_transformation.rb +33 -0
- data/lib/twisty_puzzles/axis_face_and_direction_move.rb +78 -0
- data/lib/twisty_puzzles/cancellation_helper.rb +165 -0
- data/lib/twisty_puzzles/color_scheme.rb +174 -0
- data/lib/twisty_puzzles/commutator.rb +118 -0
- data/lib/twisty_puzzles/compiled_algorithm.rb +48 -0
- data/lib/twisty_puzzles/compiled_cube_algorithm.rb +67 -0
- data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +28 -0
- data/lib/twisty_puzzles/coordinate.rb +318 -0
- data/lib/twisty_puzzles/cube.rb +660 -0
- data/lib/twisty_puzzles/cube_constants.rb +53 -0
- data/lib/twisty_puzzles/cube_direction.rb +27 -0
- data/lib/twisty_puzzles/cube_move.rb +384 -0
- data/lib/twisty_puzzles/cube_move_parser.rb +100 -0
- data/lib/twisty_puzzles/cube_print_helper.rb +160 -0
- data/lib/twisty_puzzles/cube_state.rb +113 -0
- data/lib/twisty_puzzles/letter_scheme.rb +72 -0
- data/lib/twisty_puzzles/move_type_creator.rb +27 -0
- data/lib/twisty_puzzles/parser.rb +222 -0
- data/lib/twisty_puzzles/part_cycle_factory.rb +59 -0
- data/lib/twisty_puzzles/puzzle.rb +26 -0
- data/lib/twisty_puzzles/reversible_applyable.rb +37 -0
- data/lib/twisty_puzzles/rotation.rb +105 -0
- data/lib/twisty_puzzles/skewb_direction.rb +24 -0
- data/lib/twisty_puzzles/skewb_move.rb +59 -0
- data/lib/twisty_puzzles/skewb_move_parser.rb +73 -0
- data/lib/twisty_puzzles/skewb_notation.rb +147 -0
- data/lib/twisty_puzzles/skewb_state.rb +163 -0
- data/lib/twisty_puzzles/state_helper.rb +32 -0
- data/lib/twisty_puzzles/sticker_cycle.rb +70 -0
- data/lib/twisty_puzzles/twisty_puzzles_error.rb +6 -0
- data/lib/twisty_puzzles/utils/array_helper.rb +109 -0
- data/lib/twisty_puzzles/utils/string_helper.rb +26 -0
- data/lib/twisty_puzzles/utils.rb +7 -0
- data/lib/twisty_puzzles/version.rb +3 -0
- data/lib/twisty_puzzles.rb +5 -0
- metadata +249 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/utils/array_helper'
|
4
|
+
|
5
|
+
module TwistyPuzzles
|
6
|
+
|
7
|
+
# Various constants about the cube.
|
8
|
+
module CubeConstants
|
9
|
+
include Utils::ArrayHelper
|
10
|
+
|
11
|
+
# The order determines the priority of the faces.
|
12
|
+
FACE_SYMBOLS = %i[U F R L B D].freeze
|
13
|
+
OPPOSITE_FACE_SYMBOLS = [%i[U D], %i[F B], %i[R L]].freeze
|
14
|
+
raise unless FACE_SYMBOLS.sort == OPPOSITE_FACE_SYMBOLS.flatten.sort
|
15
|
+
|
16
|
+
FACE_NAMES = FACE_SYMBOLS.map(&:to_s).freeze
|
17
|
+
ALPHABET_SIZE = 24
|
18
|
+
# Stickers on each Skewb face.
|
19
|
+
SKEWB_STICKERS = 5
|
20
|
+
CHIRALITY_FACE_SYMBOLS = %i[U R F].freeze
|
21
|
+
|
22
|
+
def opposite_face_symbol(face_symbol)
|
23
|
+
candidates = OPPOSITE_FACE_SYMBOLS.select { |ss| ss.include?(face_symbol) }
|
24
|
+
raise if candidates.length > 1
|
25
|
+
raise ArgumentError, "Invalid face symbol #{face_symbol}." if candidates.empty?
|
26
|
+
|
27
|
+
only(only(candidates).reject { |s| s == face_symbol })
|
28
|
+
end
|
29
|
+
|
30
|
+
def chirality_canonical_face_symbol(face_symbol)
|
31
|
+
if CHIRALITY_FACE_SYMBOLS.include?(face_symbol)
|
32
|
+
face_symbol
|
33
|
+
else
|
34
|
+
opposite_face_symbol(face_symbol)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid_chirality?(face_symbols)
|
39
|
+
# To make it comparable to our CHIRALITY_FACE_SYMBOLS, we switch each face used in c
|
40
|
+
# different from the ones used in the CHIRALITY_FACE_SYMBOLS for the opposite face.
|
41
|
+
canonical_face_symbols = face_symbols.map { |f| chirality_canonical_face_symbol(f) }
|
42
|
+
|
43
|
+
# Each time we swap a face for the opposite, the chirality direction should be inverted.
|
44
|
+
no_swapped_face_symbols = canonical_face_symbols.zip(face_symbols).count { |a, b| a != b }
|
45
|
+
inverted = no_swapped_face_symbols.odd?
|
46
|
+
inverted_face_symbols = inverted ? canonical_face_symbols.reverse : canonical_face_symbols
|
47
|
+
|
48
|
+
# If the corner is not equal modulo rotation to CHIRALITY_FACE_SYMBOLS after this
|
49
|
+
# transformation, the original corner had a bad chirality.
|
50
|
+
turned_equals?(inverted_face_symbols, CHIRALITY_FACE_SYMBOLS)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/abstract_direction'
|
4
|
+
|
5
|
+
module TwistyPuzzles
|
6
|
+
|
7
|
+
# Represents the direction of a cube move or rotation.
|
8
|
+
class CubeDirection < AbstractDirection
|
9
|
+
NUM_DIRECTIONS = 4
|
10
|
+
NON_ZERO_DIRECTIONS = (1...NUM_DIRECTIONS).map { |d| new(d) }.freeze
|
11
|
+
ALL_DIRECTIONS = Array.new(NUM_DIRECTIONS) { |d| new(d) }.freeze
|
12
|
+
ZERO = new(0)
|
13
|
+
FORWARD = new(1)
|
14
|
+
DOUBLE = new(2)
|
15
|
+
BACKWARD = new(3)
|
16
|
+
|
17
|
+
def name
|
18
|
+
SIMPLE_DIRECTION_NAMES[@value]
|
19
|
+
end
|
20
|
+
|
21
|
+
def double_move?
|
22
|
+
@value == 2
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,384 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/abstract_move'
|
4
|
+
require 'twisty_puzzles/axis_face_and_direction_move'
|
5
|
+
require 'twisty_puzzles/algorithm'
|
6
|
+
require 'twisty_puzzles/puzzle'
|
7
|
+
|
8
|
+
module TwistyPuzzles
|
9
|
+
|
10
|
+
# Helper class to print various types of M slice moves.
|
11
|
+
module MSlicePrintHelper
|
12
|
+
def to_s
|
13
|
+
use_face = AbstractMove::SLICE_NAMES.key?(@axis_face)
|
14
|
+
axis_face = use_face ? @axis_face : @axis_face.opposite
|
15
|
+
direction = use_face ? @direction : @direction.inverse
|
16
|
+
slice_name = AbstractMove::SLICE_NAMES[axis_face]
|
17
|
+
"#{slice_name}#{direction.name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Base class for cube moves.
|
22
|
+
class CubeMove < AxisFaceAndDirectionMove
|
23
|
+
def puzzles
|
24
|
+
[Puzzle::NXN_CUBE]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# A fat M slice move that moves everything but the outer layers.
|
29
|
+
class FatMSliceMove < CubeMove
|
30
|
+
include MSlicePrintHelper
|
31
|
+
|
32
|
+
def prepend_rotation(_other, _cube_size)
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def prepend_fat_m_slice_move(other, _cube_size)
|
37
|
+
return unless same_axis?(other)
|
38
|
+
|
39
|
+
other_direction = other.translated_direction(@axis_face)
|
40
|
+
Algorithm.move(FatMSliceMove.new(@axis_face, @direction + other_direction))
|
41
|
+
end
|
42
|
+
|
43
|
+
def prepend_fat_move(other, cube_size)
|
44
|
+
# Note that changing the order is safe because that method returns nil if no cancellation
|
45
|
+
# can be performed.
|
46
|
+
other.prepend_fat_m_slice_move(self, cube_size)
|
47
|
+
end
|
48
|
+
|
49
|
+
def prepend_slice_move(_other, _cube_size)
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def slice_move?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def equivalent_internal?(other, cube_size)
|
58
|
+
case other
|
59
|
+
when SliceMove
|
60
|
+
return equivalent_slice_move?(other, cube_size)
|
61
|
+
when FatMSliceMove
|
62
|
+
return @axis_face == other.axis_face.opposite && @direction == other.direction.inverse
|
63
|
+
end
|
64
|
+
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def equivalent_slice_move?(other, cube_size)
|
71
|
+
cube_size == 3 && other.slice_index == 1 &&
|
72
|
+
(@axis_face == other.axis_face && @direction == other.direction ||
|
73
|
+
@axis_face == other.axis_face.opposite && @direction == other.direction.inverse)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# An M slice move for which we don't know yet whether it's an inner or fat M slice move.
|
78
|
+
class MaybeFatMSliceMaybeInnerMSliceMove < CubeMove
|
79
|
+
include MSlicePrintHelper
|
80
|
+
|
81
|
+
# For even layered cubes, m slice moves are meant as very fat moves where only the outer
|
82
|
+
# layers stay.
|
83
|
+
# For odd layered cubes, we only move the very middle.
|
84
|
+
def decide_meaning(cube_size)
|
85
|
+
if cube_size.even?
|
86
|
+
FatMSliceMove.new(@axis_face, @direction)
|
87
|
+
else
|
88
|
+
InnerMSliceMove.new(@axis_face, @direction, cube_size / 2)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# A fat move with a width. For width 1, this becomes a normal outer move.
|
94
|
+
class FatMove < CubeMove
|
95
|
+
def initialize(axis_face, direction, width = 1)
|
96
|
+
super(axis_face, direction)
|
97
|
+
raise TypeError unless width.is_a?(Integer)
|
98
|
+
raise ArgumentError, "Invalid width #{width} for fat move." unless width >= 1
|
99
|
+
|
100
|
+
@width = width
|
101
|
+
end
|
102
|
+
|
103
|
+
OUTER_MOVES = Face::ELEMENTS.product(CubeDirection::NON_ZERO_DIRECTIONS).map do |f, d|
|
104
|
+
FatMove.new(f, d)
|
105
|
+
end.freeze
|
106
|
+
|
107
|
+
attr_reader :width
|
108
|
+
|
109
|
+
def identifying_fields
|
110
|
+
super + [@width]
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"#{@width > 2 ? @width : ''}#{@axis_face.name}#{@width > 1 ? 'w' : ''}#{@direction.name}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def slice_move?
|
118
|
+
false
|
119
|
+
end
|
120
|
+
|
121
|
+
def with_width(width)
|
122
|
+
FatMove.new(@axis_face, @direction, width)
|
123
|
+
end
|
124
|
+
|
125
|
+
def inverted_width(cube_size)
|
126
|
+
cube_size - @width
|
127
|
+
end
|
128
|
+
|
129
|
+
def prepend_rotation(other, cube_size)
|
130
|
+
# Note that changing the order is safe because that method returns nil if no cancellation
|
131
|
+
# can be performed.
|
132
|
+
other.prepend_fat_move(self, cube_size)
|
133
|
+
end
|
134
|
+
|
135
|
+
def prepend_fat_m_slice_move(other, cube_size)
|
136
|
+
if adjacent_mslice_move?(other)
|
137
|
+
Algorithm.move(FatMove.new(@axis_face, @direction, cube_size - 1))
|
138
|
+
elsif contained_mslice_move?(other, cube_size)
|
139
|
+
Algorithm.move(FatMove.new(@axis_face, @direction, 1))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
144
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
145
|
+
def prepend_fat_move(other, cube_size)
|
146
|
+
if same_fat_block?(other)
|
147
|
+
merge_with_same_fat_block(other)
|
148
|
+
elsif opposite_fat_block?(other, cube_size)
|
149
|
+
merge_with_opposite_fat_block(other)
|
150
|
+
elsif leaves_inner_slice_move?(other)
|
151
|
+
Algorithm.move(inner_slice_move)
|
152
|
+
elsif other.leaves_inner_slice_move?(self)
|
153
|
+
Algorithm.move(other.inner_slice_move)
|
154
|
+
elsif leaves_inner_fat_mslice_move?(other, cube_size)
|
155
|
+
Algorithm.move(inner_fat_mslice_move(cube_size))
|
156
|
+
elsif other.leaves_inner_fat_mslice_move?(self, cube_size)
|
157
|
+
Algorithm.move(other.inner_fat_mslice_move(cube_size))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
161
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
162
|
+
|
163
|
+
def prepend_slice_move(other, cube_size)
|
164
|
+
return unless same_axis?(other)
|
165
|
+
|
166
|
+
translated_direction = other.translated_direction(@axis_face)
|
167
|
+
translated_slice_index = other.translated_slice_index(@axis_face, cube_size)
|
168
|
+
move =
|
169
|
+
case translated_slice_index
|
170
|
+
when @width
|
171
|
+
return unless translated_direction == @direction
|
172
|
+
|
173
|
+
with_width(@width + 1)
|
174
|
+
when @width - 1
|
175
|
+
return unless translated_direction == @direction.inverse
|
176
|
+
|
177
|
+
with_width(@width - 1)
|
178
|
+
else
|
179
|
+
return
|
180
|
+
end
|
181
|
+
Algorithm.move(move)
|
182
|
+
end
|
183
|
+
|
184
|
+
protected
|
185
|
+
|
186
|
+
def merge_with_same_fat_block(other)
|
187
|
+
Algorithm.move(FatMove.new(@axis_face, @direction + other.direction, @width))
|
188
|
+
end
|
189
|
+
|
190
|
+
def merge_with_opposite_fat_block(other)
|
191
|
+
rotation = Rotation.new(@axis_face, @direction)
|
192
|
+
move = FatMove.new(other.axis_face, other.direction + @direction, other.width)
|
193
|
+
Algorithm.new([move, rotation])
|
194
|
+
end
|
195
|
+
|
196
|
+
# The outermost slice move inside this fat move.
|
197
|
+
def inner_slice_move
|
198
|
+
raise ArgumentError unless @width >= 2
|
199
|
+
|
200
|
+
SliceMove.new(@axis_face, @direction, @width - 1)
|
201
|
+
end
|
202
|
+
|
203
|
+
# The fat M-slice move inside this fat move.
|
204
|
+
def inner_fat_mslice_move(cube_size)
|
205
|
+
raise ArgumentError unless cube_size.even? && @width == cube_size - 1
|
206
|
+
|
207
|
+
FatMSliceMove.new(@axis_face, @direction)
|
208
|
+
end
|
209
|
+
|
210
|
+
def contained_mslice_move?(other, cube_size)
|
211
|
+
same_axis?(other) && @width == cube_size - 1 &&
|
212
|
+
@direction == other.translated_direction(@axis_face).inverse
|
213
|
+
end
|
214
|
+
|
215
|
+
def adjacent_mslice_move?(other)
|
216
|
+
same_axis?(other) && @width == 1 && @direction == other.translated_direction(@axis_face)
|
217
|
+
end
|
218
|
+
|
219
|
+
def same_fat_block?(other)
|
220
|
+
@axis_face == other.axis_face && @width == other.width
|
221
|
+
end
|
222
|
+
|
223
|
+
def leaves_inner_slice_move?(other)
|
224
|
+
@axis_face == other.axis_face && @width == other.width + 1 &&
|
225
|
+
@direction == other.direction.inverse
|
226
|
+
end
|
227
|
+
|
228
|
+
def leaves_inner_fat_mslice_move?(other, cube_size)
|
229
|
+
cube_size.even? && @axis_face == other.axis_face && @width == cube_size - 1 &&
|
230
|
+
other.width == 1 && @direction == other.direction.inverse
|
231
|
+
end
|
232
|
+
|
233
|
+
def opposite_fat_block?(other, cube_size)
|
234
|
+
@axis_face == other.axis_face.opposite && @width + other.width == cube_size
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# A slice move of any slice, not necessary the middle one.
|
239
|
+
class SliceMove < CubeMove
|
240
|
+
def initialize(axis_face, direction, slice_index)
|
241
|
+
super(axis_face, direction)
|
242
|
+
raise TypeError unless slice_index.is_a?(Integer)
|
243
|
+
unless slice_index >= 1
|
244
|
+
raise ArgumentError, "Invalid slice index #{slice_index} for slice move."
|
245
|
+
end
|
246
|
+
|
247
|
+
@slice_index = slice_index
|
248
|
+
end
|
249
|
+
|
250
|
+
attr_reader :slice_index
|
251
|
+
|
252
|
+
def identifying_fields
|
253
|
+
super + [@slice_index]
|
254
|
+
end
|
255
|
+
|
256
|
+
def to_s
|
257
|
+
"#{@slice_index > 1 ? @slice_index : ''}#{@axis_face.name.downcase}#{@direction.name}"
|
258
|
+
end
|
259
|
+
|
260
|
+
def slice_move?
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
264
|
+
def mirror(normal_face)
|
265
|
+
if normal_face.same_axis?(@axis_face)
|
266
|
+
SliceMove.new(@axis_face.opposite, @direction.inverse, @slice_index)
|
267
|
+
else
|
268
|
+
inverse
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def equivalent_internal?(other, cube_size)
|
273
|
+
return other.equivalent_internal?(self, cube_size) if other.is_a?(FatMSliceMove)
|
274
|
+
return simplified(cube_size) == other.simplified(cube_size) if other.is_a?(SliceMove)
|
275
|
+
|
276
|
+
false
|
277
|
+
end
|
278
|
+
|
279
|
+
def translated_slice_index(other_axis_face, cube_size)
|
280
|
+
if @slice_index >= cube_size - 1
|
281
|
+
raise ArgumentError,
|
282
|
+
"Slice index #{@slice_index} of #{self} is invalid for cube size #{cube_size}."
|
283
|
+
end
|
284
|
+
|
285
|
+
case @axis_face
|
286
|
+
when other_axis_face then @slice_index
|
287
|
+
when other_axis_face.opposite then invert_slice_index(cube_size)
|
288
|
+
else raise ArgumentError
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def prepend_rotation(_other, _cube_size)
|
293
|
+
nil
|
294
|
+
end
|
295
|
+
|
296
|
+
def prepend_fat_m_slice_move(_other, _cube_size)
|
297
|
+
nil
|
298
|
+
end
|
299
|
+
|
300
|
+
def prepend_fat_move(other, cube_size)
|
301
|
+
# Note that changing the order is safe because that method returns nil if no cancellation
|
302
|
+
# can be performed.
|
303
|
+
other.prepend_slice_move(self, cube_size)
|
304
|
+
end
|
305
|
+
|
306
|
+
def prepend_slice_move(other, cube_size)
|
307
|
+
return unless same_axis?(other)
|
308
|
+
|
309
|
+
# Only for 4x4, we can join two adjacent slice moves into a fat m slice move.
|
310
|
+
this = simplified(cube_size)
|
311
|
+
if this.can_join_to_fat_mslice?(other, cube_size)
|
312
|
+
return Algorithm.move(FatMSliceMove.new(other.axis_face, other.direction))
|
313
|
+
end
|
314
|
+
|
315
|
+
other = other.simplified(cube_size)
|
316
|
+
return unless this.same_slice?(other)
|
317
|
+
|
318
|
+
Algorithm.move(
|
319
|
+
SliceMove.new(
|
320
|
+
other.axis_face,
|
321
|
+
other.direction + this.translated_direction(other.axis_face),
|
322
|
+
other.slice_index
|
323
|
+
)
|
324
|
+
)
|
325
|
+
end
|
326
|
+
|
327
|
+
protected
|
328
|
+
|
329
|
+
def simplified(cube_size)
|
330
|
+
if @slice_index >= cube_size - 1
|
331
|
+
raise ArgumentError,
|
332
|
+
"Slice index #{@slice_index} of #{self} is invalid for cube size #{cube_size}."
|
333
|
+
end
|
334
|
+
|
335
|
+
if @slice_index >= (cube_size + 1) / 2
|
336
|
+
SliceMove.new(@axis_face.opposite, @direction.inverse, invert_slice_index(cube_size))
|
337
|
+
else
|
338
|
+
self
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def invert_slice_index(cube_size)
|
343
|
+
cube_size - 1 - @slice_index
|
344
|
+
end
|
345
|
+
|
346
|
+
# Note that this is only a partial implementation of what we need internally.
|
347
|
+
# It does NOT get all cases correctly because there might be equivalent versions of the
|
348
|
+
# same slice move.
|
349
|
+
def can_join_to_fat_mslice?(other, cube_size)
|
350
|
+
cube_size == 4 && @slice_index == 1 &&
|
351
|
+
mirror(@axis_face).equivalent_internal?(other, cube_size)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Note that this is only a partial implementation of what we need internally.
|
355
|
+
# It does NOT get all cases correctly because there might be equivalent versions of the
|
356
|
+
# same slice move.
|
357
|
+
def same_slice?(other)
|
358
|
+
@axis_face == other.axis_face && @slice_index == other.slice_index
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Inner M slice moves that move only one middle layer.
|
363
|
+
class InnerMSliceMove < SliceMove
|
364
|
+
include MSlicePrintHelper
|
365
|
+
end
|
366
|
+
|
367
|
+
# Not that this represents a move that is written as 'u' which is a slice move on bigger cubes
|
368
|
+
# but a fat move on 3x3...
|
369
|
+
class MaybeFatMaybeSliceMove < CubeMove
|
370
|
+
# We handle the annoying inconsistency that u is a slice move for bigger cubes, but a fat move
|
371
|
+
# for 3x3.
|
372
|
+
def decide_meaning(cube_size)
|
373
|
+
case cube_size
|
374
|
+
when 2 then raise ArgumentError
|
375
|
+
when 3 then FatMove.new(@axis_face, @direction, 2)
|
376
|
+
else SliceMove.new(@axis_face, @direction, 1)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def to_s
|
381
|
+
"#{@axis_face.name.downcase}#{@direction.name}"
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|