twisty_puzzles 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/abstract_direction'
|
4
|
+
require 'twisty_puzzles/abstract_move_parser'
|
5
|
+
require 'twisty_puzzles/cube'
|
6
|
+
require 'twisty_puzzles/cube_constants'
|
7
|
+
require 'twisty_puzzles/cube_move'
|
8
|
+
require 'twisty_puzzles/move_type_creator'
|
9
|
+
require 'twisty_puzzles/rotation'
|
10
|
+
require 'twisty_puzzles/skewb_move'
|
11
|
+
|
12
|
+
module TwistyPuzzles
|
13
|
+
|
14
|
+
# Parser for cube moves.
|
15
|
+
class CubeMoveParser < AbstractMoveParser
|
16
|
+
REGEXP =
|
17
|
+
begin
|
18
|
+
axes_part = "(?<axis_name>[#{AbstractMove::AXES.join}])"
|
19
|
+
face_names = CubeConstants::FACE_NAMES.join
|
20
|
+
fat_move_part =
|
21
|
+
"(?<width>\\d*)(?<fat_face_name>[#{face_names}])w"
|
22
|
+
normal_move_part = "(?<face_name>[#{face_names}])"
|
23
|
+
downcased_face_names = face_names.downcase
|
24
|
+
maybe_fat_maybe_slice_move_part =
|
25
|
+
"(?<maybe_fat_face_maybe_slice_name>[#{downcased_face_names}])"
|
26
|
+
slice_move_part =
|
27
|
+
"(?<slice_index>\\d+)(?<slice_name>[#{downcased_face_names}])"
|
28
|
+
mslice_move_part =
|
29
|
+
"(?<mslice_name>[#{AbstractMove::SLICE_FACES.keys.join}])"
|
30
|
+
move_part = "(?:#{axes_part}|" \
|
31
|
+
"#{fat_move_part}|" \
|
32
|
+
"#{normal_move_part}|" \
|
33
|
+
"#{maybe_fat_maybe_slice_move_part}|" \
|
34
|
+
"#{slice_move_part}|#{mslice_move_part})"
|
35
|
+
direction_names =
|
36
|
+
AbstractDirection::POSSIBLE_DIRECTION_NAMES.flatten
|
37
|
+
direction_names.sort_by! { |e| -e.length }
|
38
|
+
direction_part = "(?<direction>#{direction_names.join('|')})"
|
39
|
+
Regexp.new("#{move_part}#{direction_part}")
|
40
|
+
end
|
41
|
+
|
42
|
+
MOVE_TYPE_CREATORS = [
|
43
|
+
MoveTypeCreator.new(%i[axis_face direction], Rotation),
|
44
|
+
MoveTypeCreator.new(%i[fat_face direction width], FatMove),
|
45
|
+
MoveTypeCreator.new(%i[face direction], FatMove),
|
46
|
+
MoveTypeCreator.new(%i[maybe_fat_face_maybe_slice_face direction], MaybeFatMaybeSliceMove),
|
47
|
+
MoveTypeCreator.new(%i[slice_face direction slice_index], SliceMove),
|
48
|
+
MoveTypeCreator.new(%i[mslice_face direction], MaybeFatMSliceMaybeInnerMSliceMove)
|
49
|
+
].freeze
|
50
|
+
|
51
|
+
INSTANCE = CubeMoveParser.new
|
52
|
+
def regexp
|
53
|
+
REGEXP
|
54
|
+
end
|
55
|
+
|
56
|
+
def move_type_creators
|
57
|
+
MOVE_TYPE_CREATORS
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_part_key(name)
|
61
|
+
name.sub('_name', '_face').sub('face_face', 'face')
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_direction(direction_string)
|
65
|
+
value = AbstractDirection::POSSIBLE_DIRECTION_NAMES.index do |ds|
|
66
|
+
ds.include?(direction_string)
|
67
|
+
end + 1
|
68
|
+
CubeDirection.new(value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_axis_face(axis_face_string)
|
72
|
+
Face::ELEMENTS[AbstractMove::AXES.index(axis_face_string)]
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_mslice_face(mslice_name)
|
76
|
+
AbstractMove::SLICE_FACES[mslice_name]
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_width(width_string)
|
80
|
+
width_string.empty? ? 2 : Integer(width_string, 10)
|
81
|
+
end
|
82
|
+
|
83
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
84
|
+
def parse_move_part(name, value)
|
85
|
+
case name
|
86
|
+
when 'axis_name' then parse_axis_face(value)
|
87
|
+
when 'width' then parse_width(value)
|
88
|
+
when 'slice_index' then Integer(value, 10)
|
89
|
+
when 'fat_face_name', 'face_name' then Face.by_name(value)
|
90
|
+
when 'maybe_fat_face_maybe_slice_name', 'slice_name'
|
91
|
+
Face.by_name(value.upcase)
|
92
|
+
when 'mslice_name'
|
93
|
+
parse_mslice_face(value)
|
94
|
+
when 'direction' then parse_direction(value)
|
95
|
+
else raise
|
96
|
+
end
|
97
|
+
end
|
98
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
require 'twisty_puzzles/utils/array_helper'
|
5
|
+
|
6
|
+
module TwistyPuzzles
|
7
|
+
|
8
|
+
# Module to print and display cube and Skewb states.
|
9
|
+
module CubePrintHelper
|
10
|
+
include Utils::ArrayHelper
|
11
|
+
|
12
|
+
def color_symbol(color)
|
13
|
+
case color
|
14
|
+
when :orange then :light_red
|
15
|
+
when :unknown then :light_black
|
16
|
+
else color
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
COLOR_MODES = %i[color nocolor].freeze
|
21
|
+
ColorInfo = Struct.new(:reverse_lines_mode, :reverse_columns_mode, :skewb_corner_permutation)
|
22
|
+
FACE_SYMBOL_INFOS = {
|
23
|
+
U: ColorInfo.new(:reverse, :reverse, [2, 3, 0, 1]),
|
24
|
+
L: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1]),
|
25
|
+
F: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1]),
|
26
|
+
R: ColorInfo.new(:keep, :keep, [1, 0, 3, 2]),
|
27
|
+
B: ColorInfo.new(:keep, :keep, [1, 0, 3, 2]),
|
28
|
+
D: ColorInfo.new(:keep, :reverse, [2, 0, 3, 1])
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
def color_character(color, color_mode)
|
32
|
+
unless COLOR_MODES.include?(color_mode)
|
33
|
+
raise ArgumentError, "Invalid color mode #{color_mode}"
|
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
|
42
|
+
end
|
43
|
+
|
44
|
+
def maybe_reverse(reverse_mode, stuff)
|
45
|
+
if reverse_mode == :reverse
|
46
|
+
stuff.reverse
|
47
|
+
elsif reverse_mode == :keep
|
48
|
+
stuff
|
49
|
+
else
|
50
|
+
raise
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def face_lines(cube_state, face_symbol, row_multiplicity = 1, column_multiplicity = 1)
|
55
|
+
face = Face.for_face_symbol(face_symbol)
|
56
|
+
face_symbol_info = FACE_SYMBOL_INFOS[face_symbol]
|
57
|
+
stickers = cube_state.sticker_array(face)
|
58
|
+
lines =
|
59
|
+
stickers.collect_concat do |sticker_line|
|
60
|
+
line = sticker_line.map { |c| yield(c) * column_multiplicity }
|
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)
|
64
|
+
end
|
65
|
+
|
66
|
+
def simple_face_lines(cube_state, face_symbol, color_mode)
|
67
|
+
face_lines(cube_state, face_symbol) { |c| color_character(c, color_mode) }
|
68
|
+
end
|
69
|
+
|
70
|
+
SKEWB_FACE_SIZE = 5
|
71
|
+
|
72
|
+
def skewb_ascii_art_line(first_color, middle_color, last_color, num_first_color)
|
73
|
+
raise if num_first_color > SKEWB_FACE_SIZE / 2
|
74
|
+
|
75
|
+
first_color * num_first_color +
|
76
|
+
middle_color * (SKEWB_FACE_SIZE - 2 * num_first_color) +
|
77
|
+
last_color * num_first_color
|
78
|
+
end
|
79
|
+
|
80
|
+
def skewb_ascii_art(center_color, corner_colors)
|
81
|
+
raise unless corner_colors.length == 4
|
82
|
+
|
83
|
+
first_part =
|
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
|
94
|
+
|
95
|
+
# Prints a Skewb face like this:
|
96
|
+
# rrgww
|
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
|
113
|
+
|
114
|
+
def ll_string(cube_state, color_mode)
|
115
|
+
top_face = face_lines(cube_state, :U, 2, 3) { |c| color_character(c, color_mode) }
|
116
|
+
front_face = face_lines(cube_state, :F, 1, 3) { |c| color_character(c, color_mode) }
|
117
|
+
right_face = face_lines(cube_state, :R, 1, 3) { |c| color_character(c, color_mode) }
|
118
|
+
pll_line = front_face.first + right_face.first
|
119
|
+
(top_face + [pll_line] * 3).join("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
def cube_string(cube_state, color_mode)
|
123
|
+
top_face = simple_face_lines(cube_state, :U, color_mode)
|
124
|
+
left_face = simple_face_lines(cube_state, :L, color_mode)
|
125
|
+
front_face = simple_face_lines(cube_state, :F, color_mode)
|
126
|
+
right_face = simple_face_lines(cube_state, :R, color_mode)
|
127
|
+
back_face = simple_face_lines(cube_state, :B, color_mode)
|
128
|
+
bottom_face = simple_face_lines(cube_state, :D, color_mode)
|
129
|
+
middle_belt = zip_concat_lines(left_face, front_face, right_face, back_face)
|
130
|
+
lines = pad_lines(top_face, cube_state.n) + middle_belt +
|
131
|
+
pad_lines(bottom_face, cube_state.n)
|
132
|
+
lines.join("\n")
|
133
|
+
end
|
134
|
+
|
135
|
+
def skewb_string(skewb_state, color_mode)
|
136
|
+
top_face = skewb_face_lines(skewb_state, :U, color_mode)
|
137
|
+
left_face = skewb_face_lines(skewb_state, :L, color_mode)
|
138
|
+
front_face = skewb_face_lines(skewb_state, :F, color_mode)
|
139
|
+
right_face = skewb_face_lines(skewb_state, :R, color_mode)
|
140
|
+
back_face = skewb_face_lines(skewb_state, :B, color_mode)
|
141
|
+
bottom_face = skewb_face_lines(skewb_state, :D, color_mode)
|
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
|
147
|
+
|
148
|
+
def empty_name
|
149
|
+
' '
|
150
|
+
end
|
151
|
+
|
152
|
+
def pad_lines(lines, padding)
|
153
|
+
lines.map { |line| empty_name * padding + line }
|
154
|
+
end
|
155
|
+
|
156
|
+
def zip_concat_lines(*args)
|
157
|
+
args.transpose.map(&:join)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/cube'
|
4
|
+
require 'twisty_puzzles/cube_constants'
|
5
|
+
require 'twisty_puzzles/coordinate'
|
6
|
+
require 'twisty_puzzles/cube_print_helper'
|
7
|
+
require 'twisty_puzzles/state_helper'
|
8
|
+
require 'twisty_puzzles/utils/array_helper'
|
9
|
+
require 'twisty_puzzles/native'
|
10
|
+
|
11
|
+
module TwistyPuzzles
|
12
|
+
|
13
|
+
# Represents the state (i.e. the sticker positions) of a cube.
|
14
|
+
class CubeState
|
15
|
+
include Utils::ArrayHelper
|
16
|
+
include CubePrintHelper
|
17
|
+
include StateHelper
|
18
|
+
include CubeConstants
|
19
|
+
|
20
|
+
def self.check_cube_size(cube_size)
|
21
|
+
raise TypeError unless cube_size.is_a?(Integer)
|
22
|
+
raise ArgumentError, 'Cubes of size smaller than 2 are not supported.' if cube_size < 2
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_stickers(cube_size, stickers)
|
26
|
+
CubeState.check_cube_size(cube_size)
|
27
|
+
unless stickers.length == FACE_SYMBOLS.length
|
28
|
+
raise ArgumentError, "Cubes must have #{FACE_SYMBOLS.length} sides."
|
29
|
+
end
|
30
|
+
|
31
|
+
unless stickers.all? { |p| p.length == cube_size && p.all? { |q| q.length == cube_size } }
|
32
|
+
raise ArgumentError,
|
33
|
+
"All sides of a #{cube_size}x#{cube_size} must be #{cube_size}x#{cube_size}."
|
34
|
+
end
|
35
|
+
|
36
|
+
stickers_hash = create_stickers_hash(stickers)
|
37
|
+
new(Native::CubeState.new(cube_size, stickers_hash))
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.create_stickers_hash(stickers)
|
41
|
+
FACE_SYMBOLS.zip(stickers).map do |face_symbol, face_stickers|
|
42
|
+
face = Face.for_face_symbol(face_symbol)
|
43
|
+
face_hash = {
|
44
|
+
stickers: face_stickers,
|
45
|
+
# Note that in the ruby code, x and y are exchanged s.t. one can say bla[x][y],
|
46
|
+
# but the C code does the more logical thing,
|
47
|
+
# so we have to swap coordinates here.
|
48
|
+
x_base_face_symbol: face.coordinate_index_base_face(1).face_symbol,
|
49
|
+
y_base_face_symbol: face.coordinate_index_base_face(0).face_symbol
|
50
|
+
}
|
51
|
+
[face_symbol, face_hash]
|
52
|
+
end.to_h
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(native)
|
56
|
+
raise TypeError unless native.is_a?(Native::CubeState)
|
57
|
+
|
58
|
+
@native = native
|
59
|
+
end
|
60
|
+
|
61
|
+
def dup
|
62
|
+
CubeState.new(@native.dup)
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :native
|
66
|
+
|
67
|
+
def n
|
68
|
+
@native.cube_size
|
69
|
+
end
|
70
|
+
|
71
|
+
def stickers; end
|
72
|
+
|
73
|
+
def eql?(other)
|
74
|
+
self.class.equal?(other.class) && @native == other.native
|
75
|
+
end
|
76
|
+
|
77
|
+
alias == eql?
|
78
|
+
|
79
|
+
def hash
|
80
|
+
@hash ||= [self.class, @native].hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# TODO: Get rid of this backwards compatibility artifact
|
84
|
+
def sticker_array(face)
|
85
|
+
raise TypeError unless face.is_a?(Face)
|
86
|
+
|
87
|
+
@native.sticker_array(
|
88
|
+
face.face_symbol,
|
89
|
+
# Note that in the ruby code, x and y are exchanged s.t. one can say bla[x][y],
|
90
|
+
# but the C code does the more logical thing,
|
91
|
+
# so we have to swap coordinates here.
|
92
|
+
face.coordinate_index_base_face(1).face_symbol,
|
93
|
+
face.coordinate_index_base_face(0).face_symbol
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
cube_string(self, :nocolor)
|
99
|
+
end
|
100
|
+
|
101
|
+
def colored_to_s
|
102
|
+
cube_string(self, :color)
|
103
|
+
end
|
104
|
+
|
105
|
+
def [](coordinate)
|
106
|
+
@native[coordinate.native]
|
107
|
+
end
|
108
|
+
|
109
|
+
def []=(coordinate, color)
|
110
|
+
@native[coordinate.native] = color
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'twisty_puzzles/cube'
|
4
|
+
|
5
|
+
module TwistyPuzzles
|
6
|
+
# Letter scheme that maps stickers to letters.
|
7
|
+
class LetterScheme
|
8
|
+
def initialize
|
9
|
+
alphabet.each do |letter|
|
10
|
+
raise "Uncanonical letter #{letter} in alphabet." if letter != canonicalize_letter(letter)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def letter(piece)
|
15
|
+
alphabet[piece.piece_index]
|
16
|
+
end
|
17
|
+
|
18
|
+
def for_letter(part_type, desired_letter)
|
19
|
+
canonicalized_letter = canonicalize_letter(desired_letter)
|
20
|
+
part_type::ELEMENTS.find { |e| letter(e) == canonicalized_letter }
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid_letter?(letter)
|
24
|
+
alphabet.include?(canonicalize_letter(letter))
|
25
|
+
end
|
26
|
+
|
27
|
+
def alphabet
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def canonicalize_letter(_letter)
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_part(part_type, part_string)
|
36
|
+
if valid_letter?(part_string)
|
37
|
+
for_letter(part_type, part_string)
|
38
|
+
else
|
39
|
+
part_type.parse(part_string)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias parse_buffer parse_part
|
44
|
+
end
|
45
|
+
|
46
|
+
# Letter scheme used by Bernhard Brodowsky.
|
47
|
+
class BernhardLetterScheme < LetterScheme
|
48
|
+
PART_TYPE_BUFFERS = {
|
49
|
+
Corner => Corner.for_face_symbols(%i[U L B]),
|
50
|
+
Edge => Edge.for_face_symbols(%i[U F]),
|
51
|
+
Wing => Wing.for_face_symbols(%i[F U]),
|
52
|
+
XCenter => XCenter.for_face_symbols(%i[U R F]),
|
53
|
+
TCenter => TCenter.for_face_symbols(%i[U F])
|
54
|
+
}.freeze
|
55
|
+
def alphabet
|
56
|
+
@alphabet ||= 'a'.upto('x').to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
def canonicalize_letter(letter)
|
60
|
+
letter.downcase
|
61
|
+
end
|
62
|
+
|
63
|
+
# Letters that we shoot to by default.
|
64
|
+
def shoot_letters(_part_type)
|
65
|
+
%w[a b d l h t p]
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_buffer(part_type)
|
69
|
+
PART_TYPE_BUFFERS[part_type]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|