twisty_puzzles 0.0.1 → 0.0.2
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/CHANGELOG.md +9 -1
- data/lib/twisty_puzzles.rb +37 -0
- data/lib/twisty_puzzles/abstract_direction.rb +38 -39
- data/lib/twisty_puzzles/abstract_move_parser.rb +32 -33
- data/lib/twisty_puzzles/algorithm.rb +112 -113
- data/lib/twisty_puzzles/algorithm_transformation.rb +19 -21
- data/lib/twisty_puzzles/axis_face_and_direction_move.rb +55 -56
- data/lib/twisty_puzzles/cancellation_helper.rb +124 -125
- data/lib/twisty_puzzles/commutator.rb +79 -80
- data/lib/twisty_puzzles/compiled_algorithm.rb +31 -32
- data/lib/twisty_puzzles/compiled_cube_algorithm.rb +49 -50
- data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +18 -19
- data/lib/twisty_puzzles/coordinate.rb +245 -246
- data/lib/twisty_puzzles/cube.rb +494 -495
- data/lib/twisty_puzzles/cube_constants.rb +40 -41
- data/lib/twisty_puzzles/cube_direction.rb +15 -18
- data/lib/twisty_puzzles/cube_move.rb +289 -290
- data/lib/twisty_puzzles/cube_move_parser.rb +75 -76
- data/lib/twisty_puzzles/cube_print_helper.rb +132 -133
- data/lib/twisty_puzzles/cube_state.rb +80 -81
- data/lib/twisty_puzzles/move_type_creator.rb +17 -18
- data/lib/twisty_puzzles/parser.rb +176 -179
- data/lib/twisty_puzzles/part_cycle_factory.rb +39 -42
- data/lib/twisty_puzzles/puzzle.rb +16 -17
- data/lib/twisty_puzzles/reversible_applyable.rb +24 -25
- data/lib/twisty_puzzles/rotation.rb +74 -75
- data/lib/twisty_puzzles/skewb_direction.rb +14 -15
- data/lib/twisty_puzzles/skewb_move.rb +48 -49
- data/lib/twisty_puzzles/skewb_move_parser.rb +50 -51
- data/lib/twisty_puzzles/skewb_notation.rb +115 -118
- data/lib/twisty_puzzles/skewb_state.rb +120 -121
- data/lib/twisty_puzzles/state_helper.rb +20 -21
- data/lib/twisty_puzzles/sticker_cycle.rb +43 -44
- data/lib/twisty_puzzles/utils.rb +3 -0
- data/lib/twisty_puzzles/version.rb +3 -1
- metadata +3 -3
@@ -10,91 +10,90 @@ require 'twisty_puzzles/rotation'
|
|
10
10
|
require 'twisty_puzzles/skewb_move'
|
11
11
|
|
12
12
|
module TwistyPuzzles
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
13
|
+
# Parser for cube moves.
|
14
|
+
class CubeMoveParser < AbstractMoveParser
|
15
|
+
REGEXP =
|
16
|
+
begin
|
17
|
+
axes_part = "(?<axis_name>[#{AbstractMove::AXES.join}])"
|
18
|
+
face_names = CubeConstants::FACE_NAMES.join
|
19
|
+
fat_move_part =
|
20
|
+
"(?<width>\\d*)(?<fat_face_name>[#{face_names}])w"
|
21
|
+
normal_move_part = "(?<face_name>[#{face_names}])"
|
22
|
+
downcased_face_names = face_names.downcase
|
23
|
+
maybe_fat_maybe_slice_move_part =
|
24
|
+
"(?<maybe_fat_face_maybe_slice_name>[#{downcased_face_names}])"
|
25
|
+
slice_move_part =
|
26
|
+
"(?<slice_index>\\d+)(?<slice_name>[#{downcased_face_names}])"
|
27
|
+
mslice_move_part =
|
28
|
+
"(?<mslice_name>[#{AbstractMove::SLICE_FACES.keys.join}])"
|
29
|
+
move_part = "(?:#{axes_part}|" \
|
30
|
+
"#{fat_move_part}|" \
|
31
|
+
"#{normal_move_part}|" \
|
32
|
+
"#{maybe_fat_maybe_slice_move_part}|" \
|
33
|
+
"#{slice_move_part}|#{mslice_move_part})"
|
34
|
+
direction_names =
|
35
|
+
AbstractDirection::POSSIBLE_DIRECTION_NAMES.flatten
|
36
|
+
direction_names.sort_by! { |e| -e.length }
|
37
|
+
direction_part = "(?<direction>#{direction_names.join('|')})"
|
38
|
+
Regexp.new("#{move_part}#{direction_part}")
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
MOVE_TYPE_CREATORS = [
|
42
|
+
MoveTypeCreator.new(%i[axis_face direction], Rotation),
|
43
|
+
MoveTypeCreator.new(%i[fat_face direction width], FatMove),
|
44
|
+
MoveTypeCreator.new(%i[face direction], FatMove),
|
45
|
+
MoveTypeCreator.new(%i[maybe_fat_face_maybe_slice_face direction], MaybeFatMaybeSliceMove),
|
46
|
+
MoveTypeCreator.new(%i[slice_face direction slice_index], SliceMove),
|
47
|
+
MoveTypeCreator.new(%i[mslice_face direction], MaybeFatMSliceMaybeInnerMSliceMove)
|
48
|
+
].freeze
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
INSTANCE = CubeMoveParser.new
|
51
|
+
def regexp
|
52
|
+
REGEXP
|
53
|
+
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
def move_type_creators
|
56
|
+
MOVE_TYPE_CREATORS
|
57
|
+
end
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
def parse_part_key(name)
|
60
|
+
name.sub('_name', '_face').sub('face_face', 'face')
|
61
|
+
end
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
def parse_direction(direction_string)
|
64
|
+
value = AbstractDirection::POSSIBLE_DIRECTION_NAMES.index do |ds|
|
65
|
+
ds.include?(direction_string)
|
66
|
+
end + 1
|
67
|
+
CubeDirection.new(value)
|
68
|
+
end
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
def parse_axis_face(axis_face_string)
|
71
|
+
Face::ELEMENTS[AbstractMove::AXES.index(axis_face_string)]
|
72
|
+
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
def parse_mslice_face(mslice_name)
|
75
|
+
AbstractMove::SLICE_FACES[mslice_name]
|
76
|
+
end
|
78
77
|
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
def parse_width(width_string)
|
79
|
+
width_string.empty? ? 2 : Integer(width_string, 10)
|
80
|
+
end
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
82
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
83
|
+
def parse_move_part(name, value)
|
84
|
+
case name
|
85
|
+
when 'axis_name' then parse_axis_face(value)
|
86
|
+
when 'width' then parse_width(value)
|
87
|
+
when 'slice_index' then Integer(value, 10)
|
88
|
+
when 'fat_face_name', 'face_name' then Face.by_name(value)
|
89
|
+
when 'maybe_fat_face_maybe_slice_name', 'slice_name'
|
90
|
+
Face.by_name(value.upcase)
|
91
|
+
when 'mslice_name'
|
92
|
+
parse_mslice_face(value)
|
93
|
+
when 'direction' then parse_direction(value)
|
94
|
+
else raise
|
97
95
|
end
|
98
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
99
96
|
end
|
97
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
98
|
+
end
|
100
99
|
end
|
@@ -4,157 +4,156 @@ require 'colorize'
|
|
4
4
|
require 'twisty_puzzles/utils/array_helper'
|
5
5
|
|
6
6
|
module TwistyPuzzles
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else color
|
17
|
-
end
|
7
|
+
# Module to print and display cube and Skewb states.
|
8
|
+
module CubePrintHelper
|
9
|
+
include Utils::ArrayHelper
|
10
|
+
|
11
|
+
def color_symbol(color)
|
12
|
+
case color
|
13
|
+
when :orange then :light_red
|
14
|
+
when :unknown then :light_black
|
15
|
+
else color
|
18
16
|
end
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
char = color.to_s[0].upcase
|
37
|
-
if color_mode == :color
|
38
|
-
char.colorize(background: color_symbol(color))
|
39
|
-
else
|
40
|
-
char
|
41
|
-
end
|
19
|
+
COLOR_MODES = %i[color nocolor].freeze
|
20
|
+
ColorInfo = Struct.new(:reverse_lines_mode, :reverse_columns_mode, :skewb_corner_permutation)
|
21
|
+
FACE_SYMBOL_INFOS = {
|
22
|
+
U: ColorInfo.new(:reverse, :reverse, [2, 3, 0, 1]),
|
23
|
+
L: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1]),
|
24
|
+
F: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1]),
|
25
|
+
R: ColorInfo.new(:keep, :keep, [1, 0, 3, 2]),
|
26
|
+
B: ColorInfo.new(:keep, :keep, [1, 0, 3, 2]),
|
27
|
+
D: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1])
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
def color_character(color, color_mode)
|
31
|
+
unless COLOR_MODES.include?(color_mode)
|
32
|
+
raise ArgumentError, "Invalid color mode #{color_mode}"
|
42
33
|
end
|
43
34
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
else
|
50
|
-
raise
|
51
|
-
end
|
35
|
+
char = color.to_s[0].upcase
|
36
|
+
if color_mode == :color
|
37
|
+
char.colorize(background: color_symbol(color))
|
38
|
+
else
|
39
|
+
char
|
52
40
|
end
|
41
|
+
end
|
53
42
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
[maybe_reverse(face_symbol_info.reverse_columns_mode, line).join] * row_multiplicity
|
62
|
-
end
|
63
|
-
maybe_reverse(face_symbol_info.reverse_lines_mode, lines)
|
43
|
+
def maybe_reverse(reverse_mode, stuff)
|
44
|
+
if reverse_mode == :reverse
|
45
|
+
stuff.reverse
|
46
|
+
elsif reverse_mode == :keep
|
47
|
+
stuff
|
48
|
+
else
|
49
|
+
raise
|
64
50
|
end
|
51
|
+
end
|
65
52
|
|
66
|
-
|
67
|
-
|
68
|
-
|
53
|
+
def face_lines(cube_state, face_symbol, row_multiplicity = 1, column_multiplicity = 1)
|
54
|
+
face = Face.for_face_symbol(face_symbol)
|
55
|
+
face_symbol_info = FACE_SYMBOL_INFOS[face_symbol]
|
56
|
+
stickers = cube_state.sticker_array(face)
|
57
|
+
lines =
|
58
|
+
stickers.collect_concat do |sticker_line|
|
59
|
+
line = sticker_line.map { |c| yield(c) * column_multiplicity }
|
60
|
+
[maybe_reverse(face_symbol_info.reverse_columns_mode, line).join] * row_multiplicity
|
61
|
+
end
|
62
|
+
maybe_reverse(face_symbol_info.reverse_lines_mode, lines)
|
63
|
+
end
|
69
64
|
|
70
|
-
|
65
|
+
def simple_face_lines(cube_state, face_symbol, color_mode)
|
66
|
+
face_lines(cube_state, face_symbol) { |c| color_character(c, color_mode) }
|
67
|
+
end
|
71
68
|
|
72
|
-
|
73
|
-
raise if num_first_color > SKEWB_FACE_SIZE / 2
|
69
|
+
SKEWB_FACE_SIZE = 5
|
74
70
|
|
75
|
-
|
76
|
-
|
77
|
-
last_color * num_first_color
|
78
|
-
end
|
71
|
+
def skewb_ascii_art_line(first_color, middle_color, last_color, num_first_color)
|
72
|
+
raise if num_first_color > SKEWB_FACE_SIZE / 2
|
79
73
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
(1..SKEWB_FACE_SIZE / 2).to_a.reverse.map do |i|
|
85
|
-
skewb_ascii_art_line(corner_colors[0], center_color, corner_colors[1], i)
|
86
|
-
end
|
87
|
-
middle_part = SKEWB_FACE_SIZE.odd? ? [center_color * SKEWB_FACE_SIZE] : []
|
88
|
-
last_part =
|
89
|
-
(1..SKEWB_FACE_SIZE / 2).map do |i|
|
90
|
-
skewb_ascii_art_line(corner_colors[2], center_color, corner_colors[3], i)
|
91
|
-
end
|
92
|
-
first_part + middle_part + last_part
|
93
|
-
end
|
74
|
+
first_color * num_first_color +
|
75
|
+
middle_color * (SKEWB_FACE_SIZE - 2 * num_first_color) +
|
76
|
+
last_color * num_first_color
|
77
|
+
end
|
94
78
|
|
95
|
-
|
96
|
-
|
97
|
-
# rgggw
|
98
|
-
# ggggg
|
99
|
-
# ogggb
|
100
|
-
# oogbb
|
101
|
-
def skewb_face_lines(cube_state, face_symbol, color_mode)
|
102
|
-
face = Face.for_face_symbol(face_symbol)
|
103
|
-
face_symbol_info = FACE_SYMBOL_INFOS[face_symbol]
|
104
|
-
stickers = cube_state.sticker_array(face)
|
105
|
-
center_color = color_character(stickers[0], color_mode)
|
106
|
-
corner_colors = stickers[1..-1].map { |c| color_character(c, color_mode) }
|
107
|
-
permuted_corner_colors =
|
108
|
-
apply_permutation(corner_colors, face_symbol_info.skewb_corner_permutation)
|
109
|
-
raise unless corner_colors.length == 4
|
110
|
-
|
111
|
-
skewb_ascii_art(center_color, permuted_corner_colors)
|
112
|
-
end
|
79
|
+
def skewb_ascii_art(center_color, corner_colors)
|
80
|
+
raise unless corner_colors.length == 4
|
113
81
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
82
|
+
first_part =
|
83
|
+
(1..SKEWB_FACE_SIZE / 2).to_a.reverse.map do |i|
|
84
|
+
skewb_ascii_art_line(corner_colors[0], center_color, corner_colors[1], i)
|
85
|
+
end
|
86
|
+
middle_part = SKEWB_FACE_SIZE.odd? ? [center_color * SKEWB_FACE_SIZE] : []
|
87
|
+
last_part =
|
88
|
+
(1..SKEWB_FACE_SIZE / 2).map do |i|
|
89
|
+
skewb_ascii_art_line(corner_colors[2], center_color, corner_colors[3], i)
|
90
|
+
end
|
91
|
+
first_part + middle_part + last_part
|
92
|
+
end
|
121
93
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
94
|
+
# Prints a Skewb face like this:
|
95
|
+
# rrgww
|
96
|
+
# rgggw
|
97
|
+
# ggggg
|
98
|
+
# ogggb
|
99
|
+
# oogbb
|
100
|
+
def skewb_face_lines(cube_state, face_symbol, color_mode)
|
101
|
+
face = Face.for_face_symbol(face_symbol)
|
102
|
+
face_symbol_info = FACE_SYMBOL_INFOS[face_symbol]
|
103
|
+
stickers = cube_state.sticker_array(face)
|
104
|
+
center_color = color_character(stickers[0], color_mode)
|
105
|
+
corner_colors = stickers[1..-1].map { |c| color_character(c, color_mode) }
|
106
|
+
permuted_corner_colors =
|
107
|
+
apply_permutation(corner_colors, face_symbol_info.skewb_corner_permutation)
|
108
|
+
raise unless corner_colors.length == 4
|
109
|
+
|
110
|
+
skewb_ascii_art(center_color, permuted_corner_colors)
|
111
|
+
end
|
134
112
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
middle_belt = zip_concat_lines(left_face, front_face, right_face, back_face)
|
143
|
-
lines = pad_lines(top_face, SKEWB_FACE_SIZE) + middle_belt +
|
144
|
-
pad_lines(bottom_face, SKEWB_FACE_SIZE)
|
145
|
-
lines.join("\n")
|
146
|
-
end
|
113
|
+
def ll_string(cube_state, color_mode)
|
114
|
+
top_face = face_lines(cube_state, :U, 2, 3) { |c| color_character(c, color_mode) }
|
115
|
+
front_face = face_lines(cube_state, :F, 1, 3) { |c| color_character(c, color_mode) }
|
116
|
+
right_face = face_lines(cube_state, :R, 1, 3) { |c| color_character(c, color_mode) }
|
117
|
+
pll_line = front_face.first + right_face.first
|
118
|
+
(top_face + [pll_line] * 3).join("\n")
|
119
|
+
end
|
147
120
|
|
148
|
-
|
149
|
-
|
150
|
-
|
121
|
+
def cube_string(cube_state, color_mode)
|
122
|
+
top_face = simple_face_lines(cube_state, :U, color_mode)
|
123
|
+
left_face = simple_face_lines(cube_state, :L, color_mode)
|
124
|
+
front_face = simple_face_lines(cube_state, :F, color_mode)
|
125
|
+
right_face = simple_face_lines(cube_state, :R, color_mode)
|
126
|
+
back_face = simple_face_lines(cube_state, :B, color_mode)
|
127
|
+
bottom_face = simple_face_lines(cube_state, :D, color_mode)
|
128
|
+
middle_belt = zip_concat_lines(left_face, front_face, right_face, back_face)
|
129
|
+
lines = pad_lines(top_face, cube_state.n) + middle_belt +
|
130
|
+
pad_lines(bottom_face, cube_state.n)
|
131
|
+
lines.join("\n")
|
132
|
+
end
|
151
133
|
|
152
|
-
|
153
|
-
|
154
|
-
|
134
|
+
def skewb_string(skewb_state, color_mode)
|
135
|
+
top_face = skewb_face_lines(skewb_state, :U, color_mode)
|
136
|
+
left_face = skewb_face_lines(skewb_state, :L, color_mode)
|
137
|
+
front_face = skewb_face_lines(skewb_state, :F, color_mode)
|
138
|
+
right_face = skewb_face_lines(skewb_state, :R, color_mode)
|
139
|
+
back_face = skewb_face_lines(skewb_state, :B, color_mode)
|
140
|
+
bottom_face = skewb_face_lines(skewb_state, :D, color_mode)
|
141
|
+
middle_belt = zip_concat_lines(left_face, front_face, right_face, back_face)
|
142
|
+
lines = pad_lines(top_face, SKEWB_FACE_SIZE) + middle_belt +
|
143
|
+
pad_lines(bottom_face, SKEWB_FACE_SIZE)
|
144
|
+
lines.join("\n")
|
145
|
+
end
|
155
146
|
|
156
|
-
|
157
|
-
|
158
|
-
|
147
|
+
def empty_name
|
148
|
+
' '
|
149
|
+
end
|
150
|
+
|
151
|
+
def pad_lines(lines, padding)
|
152
|
+
lines.map { |line| empty_name * padding + line }
|
153
|
+
end
|
154
|
+
|
155
|
+
def zip_concat_lines(*args)
|
156
|
+
args.transpose.map(&:join)
|
159
157
|
end
|
158
|
+
end
|
160
159
|
end
|