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,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
|