twisty_puzzles 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/lib/twisty_puzzles.rb +37 -0
  4. data/lib/twisty_puzzles/abstract_direction.rb +38 -39
  5. data/lib/twisty_puzzles/abstract_move_parser.rb +32 -33
  6. data/lib/twisty_puzzles/algorithm.rb +112 -113
  7. data/lib/twisty_puzzles/algorithm_transformation.rb +19 -21
  8. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +55 -56
  9. data/lib/twisty_puzzles/cancellation_helper.rb +124 -125
  10. data/lib/twisty_puzzles/commutator.rb +79 -80
  11. data/lib/twisty_puzzles/compiled_algorithm.rb +31 -32
  12. data/lib/twisty_puzzles/compiled_cube_algorithm.rb +49 -50
  13. data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +18 -19
  14. data/lib/twisty_puzzles/coordinate.rb +245 -246
  15. data/lib/twisty_puzzles/cube.rb +494 -495
  16. data/lib/twisty_puzzles/cube_constants.rb +40 -41
  17. data/lib/twisty_puzzles/cube_direction.rb +15 -18
  18. data/lib/twisty_puzzles/cube_move.rb +289 -290
  19. data/lib/twisty_puzzles/cube_move_parser.rb +75 -76
  20. data/lib/twisty_puzzles/cube_print_helper.rb +132 -133
  21. data/lib/twisty_puzzles/cube_state.rb +80 -81
  22. data/lib/twisty_puzzles/move_type_creator.rb +17 -18
  23. data/lib/twisty_puzzles/parser.rb +176 -179
  24. data/lib/twisty_puzzles/part_cycle_factory.rb +39 -42
  25. data/lib/twisty_puzzles/puzzle.rb +16 -17
  26. data/lib/twisty_puzzles/reversible_applyable.rb +24 -25
  27. data/lib/twisty_puzzles/rotation.rb +74 -75
  28. data/lib/twisty_puzzles/skewb_direction.rb +14 -15
  29. data/lib/twisty_puzzles/skewb_move.rb +48 -49
  30. data/lib/twisty_puzzles/skewb_move_parser.rb +50 -51
  31. data/lib/twisty_puzzles/skewb_notation.rb +115 -118
  32. data/lib/twisty_puzzles/skewb_state.rb +120 -121
  33. data/lib/twisty_puzzles/state_helper.rb +20 -21
  34. data/lib/twisty_puzzles/sticker_cycle.rb +43 -44
  35. data/lib/twisty_puzzles/utils.rb +3 -0
  36. data/lib/twisty_puzzles/version.rb +3 -1
  37. metadata +3 -3
@@ -9,105 +9,104 @@ require 'twisty_puzzles/utils/array_helper'
9
9
  require 'twisty_puzzles/native'
10
10
 
11
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
12
+ # Represents the state (i.e. the sticker positions) of a cube.
13
+ class CubeState
14
+ include Utils::ArrayHelper
15
+ include CubePrintHelper
16
+ include StateHelper
17
+ include CubeConstants
18
+
19
+ def self.check_cube_size(cube_size)
20
+ raise TypeError unless cube_size.is_a?(Integer)
21
+ raise ArgumentError, 'Cubes of size smaller than 2 are not supported.' if cube_size < 2
22
+ end
35
23
 
36
- stickers_hash = create_stickers_hash(stickers)
37
- new(Native::CubeState.new(cube_size, stickers_hash))
24
+ def self.from_stickers(cube_size, stickers)
25
+ CubeState.check_cube_size(cube_size)
26
+ unless stickers.length == FACE_SYMBOLS.length
27
+ raise ArgumentError, "Cubes must have #{FACE_SYMBOLS.length} sides."
38
28
  end
39
29
 
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
30
+ unless stickers.all? { |p| p.length == cube_size && p.all? { |q| q.length == cube_size } }
31
+ raise ArgumentError,
32
+ "All sides of a #{cube_size}x#{cube_size} must be #{cube_size}x#{cube_size}."
53
33
  end
54
34
 
55
- def initialize(native)
56
- raise TypeError unless native.is_a?(Native::CubeState)
35
+ stickers_hash = create_stickers_hash(stickers)
36
+ new(Native::CubeState.new(cube_size, stickers_hash))
37
+ end
57
38
 
58
- @native = native
59
- end
39
+ def self.create_stickers_hash(stickers)
40
+ FACE_SYMBOLS.zip(stickers).map do |face_symbol, face_stickers|
41
+ face = Face.for_face_symbol(face_symbol)
42
+ face_hash = {
43
+ stickers: face_stickers,
44
+ # Note that in the ruby code, x and y are exchanged s.t. one can say bla[x][y],
45
+ # but the C code does the more logical thing,
46
+ # so we have to swap coordinates here.
47
+ x_base_face_symbol: face.coordinate_index_base_face(1).face_symbol,
48
+ y_base_face_symbol: face.coordinate_index_base_face(0).face_symbol
49
+ }
50
+ [face_symbol, face_hash]
51
+ end.to_h
52
+ end
60
53
 
61
- def dup
62
- CubeState.new(@native.dup)
63
- end
54
+ def initialize(native)
55
+ raise TypeError unless native.is_a?(Native::CubeState)
64
56
 
65
- attr_reader :native
57
+ @native = native
58
+ end
66
59
 
67
- def n
68
- @native.cube_size
69
- end
60
+ def dup
61
+ CubeState.new(@native.dup)
62
+ end
70
63
 
71
- def stickers; end
64
+ attr_reader :native
72
65
 
73
- def eql?(other)
74
- self.class.equal?(other.class) && @native == other.native
75
- end
66
+ def n
67
+ @native.cube_size
68
+ end
76
69
 
77
- alias == eql?
70
+ def stickers; end
78
71
 
79
- def hash
80
- @hash ||= [self.class, @native].hash
81
- end
72
+ def eql?(other)
73
+ self.class.equal?(other.class) && @native == other.native
74
+ end
82
75
 
83
- # TODO: Get rid of this backwards compatibility artifact
84
- def sticker_array(face)
85
- raise TypeError unless face.is_a?(Face)
76
+ alias == eql?
86
77
 
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
78
+ def hash
79
+ @hash ||= [self.class, @native].hash
80
+ end
96
81
 
97
- def to_s
98
- cube_string(self, :nocolor)
99
- end
82
+ # TODO: Get rid of this backwards compatibility artifact
83
+ def sticker_array(face)
84
+ raise TypeError unless face.is_a?(Face)
85
+
86
+ @native.sticker_array(
87
+ face.face_symbol,
88
+ # Note that in the ruby code, x and y are exchanged s.t. one can say bla[x][y],
89
+ # but the C code does the more logical thing,
90
+ # so we have to swap coordinates here.
91
+ face.coordinate_index_base_face(1).face_symbol,
92
+ face.coordinate_index_base_face(0).face_symbol
93
+ )
94
+ end
100
95
 
101
- def colored_to_s
102
- cube_string(self, :color)
103
- end
96
+ def to_s
97
+ cube_string(self, :nocolor)
98
+ end
104
99
 
105
- def [](coordinate)
106
- @native[coordinate.native]
107
- end
100
+ def colored_to_s
101
+ cube_string(self, :color)
102
+ end
108
103
 
109
- def []=(coordinate, color)
110
- @native[coordinate.native] = color
111
- end
104
+ def [](coordinate)
105
+ @native[coordinate.native]
106
+ end
107
+
108
+ def []=(coordinate, color)
109
+ @native[coordinate.native] = color
112
110
  end
111
+ end
113
112
  end
@@ -1,27 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TwistyPuzzles
4
-
5
- # Class for creating one move type from its parts.
6
- # Helper class for parsing logic.
7
- class MoveTypeCreator
8
- def initialize(capture_keys, move_class)
9
- raise TypeError unless move_class.is_a?(Class)
10
- raise TypeError unless capture_keys.all? { |k| k.is_a?(Symbol) }
4
+ # Class for creating one move type from its parts.
5
+ # Helper class for parsing logic.
6
+ class MoveTypeCreator
7
+ def initialize(capture_keys, move_class)
8
+ raise TypeError unless move_class.is_a?(Class)
9
+ raise TypeError unless capture_keys.all? { |k| k.is_a?(Symbol) }
11
10
 
12
- @capture_keys = capture_keys.freeze
13
- @move_class = move_class
14
- end
11
+ @capture_keys = capture_keys.freeze
12
+ @move_class = move_class
13
+ end
15
14
 
16
- def applies_to?(parsed_parts)
17
- parsed_parts.keys.sort == @capture_keys.sort
18
- end
15
+ def applies_to?(parsed_parts)
16
+ parsed_parts.keys.sort == @capture_keys.sort
17
+ end
19
18
 
20
- def create(parsed_parts)
21
- raise ArgumentError unless applies_to?(parsed_parts)
19
+ def create(parsed_parts)
20
+ raise ArgumentError unless applies_to?(parsed_parts)
22
21
 
23
- fields = @capture_keys.map { |name| parsed_parts[name] }
24
- @move_class.new(*fields)
25
- end
22
+ fields = @capture_keys.map { |name| parsed_parts[name] }
23
+ @move_class.new(*fields)
26
24
  end
25
+ end
27
26
  end
@@ -6,217 +6,214 @@ require 'twisty_puzzles/cube_move_parser'
6
6
  require 'twisty_puzzles/skewb_move_parser'
7
7
  require 'twisty_puzzles/twisty_puzzles_error'
8
8
 
9
- module TwistyPuzzles
10
- # rubocop:disable Style/Documentation
11
-
12
- # rubocop:enable Style/Documentation
13
- class CommutatorParseError < TwistyPuzzlesError
14
- end
15
-
16
- # Parser for commutators and algorithms.
17
- class Parser
18
- OPENING_BRACKET = '['
19
- OPENING_PAREN = '('
20
- CLOSING_BRACKET = ']'
21
- CLOSING_PAREN = ')'
22
- TIMES = '*'
23
-
24
- def initialize(alg_string, move_parser)
25
- @alg_string = alg_string
26
- @scanner = StringScanner.new(alg_string)
27
- @move_parser = move_parser
28
- end
29
-
30
- def parse_open_paren
31
- complain('beginning of trigger') unless @scanner.getch == OPENING_PAREN
32
- end
33
-
34
- def parse_close_paren
35
- complain('end of trigger') unless @scanner.getch == CLOSING_PAREN
36
- end
9
+ module TwistyPuzzles # rubocop:disable Style/Documentation
10
+ class CommutatorParseError < TwistyPuzzlesError
11
+ end
12
+
13
+ # Parser for commutators and algorithms.
14
+ class Parser
15
+ OPENING_BRACKET = '['
16
+ OPENING_PAREN = '('
17
+ CLOSING_BRACKET = ']'
18
+ CLOSING_PAREN = ')'
19
+ TIMES = '*'
20
+
21
+ def initialize(alg_string, move_parser)
22
+ @alg_string = alg_string
23
+ @scanner = StringScanner.new(alg_string)
24
+ @move_parser = move_parser
25
+ end
37
26
 
38
- def parse_times
39
- complain('times symbol of multiplier') unless @scanner.getch == TIMES
40
- end
27
+ def parse_open_paren
28
+ complain('beginning of trigger') unless @scanner.getch == OPENING_PAREN
29
+ end
41
30
 
42
- def parse_factor
43
- number = @scanner.scan(/\d+/)
44
- complain('factor of multiplier') unless number
45
- Integer(number, 10)
46
- end
31
+ def parse_close_paren
32
+ complain('end of trigger') unless @scanner.getch == CLOSING_PAREN
33
+ end
47
34
 
48
- def parse_multiplier
49
- skip_spaces
50
- parse_times
51
- skip_spaces
52
- parse_factor
53
- end
35
+ def parse_times
36
+ complain('times symbol of multiplier') unless @scanner.getch == TIMES
37
+ end
54
38
 
55
- def parse_trigger
56
- parse_open_paren
57
- skip_spaces
58
- moves = parse_moves_with_triggers
59
- skip_spaces
60
- parse_close_paren
61
- skip_spaces
62
- case @scanner.peek(1)
63
- when TIMES
64
- moves * parse_multiplier
65
- when ('0'..'9')
66
- moves * parse_factor
67
- else
68
- moves
69
- end
70
- end
39
+ def parse_factor
40
+ number = @scanner.scan(/\d+/)
41
+ complain('factor of multiplier') unless number
42
+ Integer(number, 10)
43
+ end
71
44
 
72
- # Parses at least one move and allows for triggers in parentheses.
73
- def parse_moves_with_triggers
74
- skip_spaces
75
- if @scanner.peek(1) == OPENING_PAREN
76
- parse_trigger + parse_moves_with_triggers
77
- else
78
- parse_moves
79
- end
80
- end
45
+ def parse_multiplier
46
+ skip_spaces
47
+ parse_times
48
+ skip_spaces
49
+ parse_factor
50
+ end
81
51
 
82
- # Parses at least one move.
83
- def parse_nonempty_moves
84
- moves = parse_moves
85
- complain('move') if moves.empty?
52
+ def parse_trigger
53
+ parse_open_paren
54
+ skip_spaces
55
+ moves = parse_moves_with_triggers
56
+ skip_spaces
57
+ parse_close_paren
58
+ skip_spaces
59
+ case @scanner.peek(1)
60
+ when TIMES
61
+ moves * parse_multiplier
62
+ when ('0'..'9')
63
+ moves * parse_factor
64
+ else
86
65
  moves
87
66
  end
67
+ end
88
68
 
89
- # Parses a series of moves.
90
- def parse_moves
91
- moves = []
92
- while (m = begin skip_spaces; parse_move_internal end)
93
- moves.push(m)
94
- end
95
- Algorithm.new(moves)
96
- end
97
-
98
- def complain(parsed_object)
99
- raise CommutatorParseError, <<~ERROR.chomp
100
- Couldn't parse #{parsed_object} here:
101
- #{@alg_string}
102
- #{' ' * @scanner.pos}^"
103
- ERROR
104
- end
105
-
106
- def check_eos(parsed_object)
107
- complain("end of #{parsed_object}") unless @scanner.eos?
108
- end
109
-
110
- def parse_open_bracket
111
- complain('beginning of commutator') unless @scanner.getch == OPENING_BRACKET
69
+ # Parses at least one move and allows for triggers in parentheses.
70
+ def parse_moves_with_triggers
71
+ skip_spaces
72
+ if @scanner.peek(1) == OPENING_PAREN
73
+ parse_trigger + parse_moves_with_triggers
74
+ else
75
+ parse_moves
112
76
  end
77
+ end
113
78
 
114
- def parse_close_bracket
115
- complain('end of commutator') unless @scanner.getch == CLOSING_BRACKET
116
- end
79
+ # Parses at least one move.
80
+ def parse_nonempty_moves
81
+ moves = parse_moves
82
+ complain('move') if moves.empty?
83
+ moves
84
+ end
117
85
 
118
- def parse_commutator
119
- skip_spaces
120
- if @scanner.peek(1) == OPENING_BRACKET
121
- parse_commutator_internal
122
- else
123
- FakeCommutator.new(parse_moves_with_triggers)
124
- end
86
+ # Parses a series of moves.
87
+ def parse_moves
88
+ moves = []
89
+ while (m = begin skip_spaces; parse_move_internal end)
90
+ moves.push(m)
125
91
  end
92
+ Algorithm.new(moves)
93
+ end
126
94
 
127
- def parse_algorithm
128
- skip_spaces
129
- parse_moves_with_triggers
130
- end
95
+ def complain(parsed_object)
96
+ raise CommutatorParseError, <<~ERROR.chomp
97
+ Couldn't parse #{parsed_object} here:
98
+ #{@alg_string}
99
+ #{' ' * @scanner.pos}^"
100
+ ERROR
101
+ end
131
102
 
132
- def parse_setup_commutator_inner
133
- skip_spaces
134
- if @scanner.peek(1) == OPENING_BRACKET
135
- parse_pure_commutator
136
- else
137
- FakeCommutator.new(parse_moves_with_triggers)
138
- end
139
- end
103
+ def check_eos(parsed_object)
104
+ complain("end of #{parsed_object}") unless @scanner.eos?
105
+ end
140
106
 
141
- def parse_pure_commutator
142
- skip_spaces
143
- parse_open_bracket
144
- first_part = parse_nonempty_moves
145
- skip_spaces
146
- complain('middle of pure commutator') unless @scanner.getch == ','
147
- second_part = parse_nonempty_moves
148
- skip_spaces
149
- parse_close_bracket
150
- PureCommutator.new(first_part, second_part)
151
- end
107
+ def parse_open_bracket
108
+ complain('beginning of commutator') unless @scanner.getch == OPENING_BRACKET
109
+ end
152
110
 
153
- def parse_commutator_internal_after_separator(setup_or_first_part, separator)
154
- if [':', ';'].include?(separator)
155
- inner_commutator = parse_setup_commutator_inner
156
- SetupCommutator.new(setup_or_first_part, inner_commutator)
157
- elsif separator == ','
158
- second_part = parse_nonempty_moves
159
- PureCommutator.new(setup_or_first_part, second_part)
160
- else
161
- complain('end of setup or middle of pure commutator') unless @scanner.eos?
162
- end
163
- end
111
+ def parse_close_bracket
112
+ complain('end of commutator') unless @scanner.getch == CLOSING_BRACKET
113
+ end
164
114
 
165
- def parse_commutator_internal
166
- skip_spaces
167
- parse_open_bracket
168
- setup_or_first_part = parse_nonempty_moves
169
- skip_spaces
170
- separator = @scanner.getch
171
- comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
172
- skip_spaces
173
- parse_close_bracket
174
- skip_spaces
175
- complain('end of commutator') unless @scanner.eos?
176
- comm
115
+ def parse_commutator
116
+ skip_spaces
117
+ if @scanner.peek(1) == OPENING_BRACKET
118
+ parse_commutator_internal
119
+ else
120
+ FakeCommutator.new(parse_moves_with_triggers)
177
121
  end
122
+ end
178
123
 
179
- def parse_move_internal
180
- move = @scanner.scan(@move_parser.regexp)
181
- return unless move
182
-
183
- @move_parser.parse_move(move)
184
- end
124
+ def parse_algorithm
125
+ skip_spaces
126
+ parse_moves_with_triggers
127
+ end
185
128
 
186
- def skip_spaces
187
- @scanner.skip(/\s+/)
129
+ def parse_setup_commutator_inner
130
+ skip_spaces
131
+ if @scanner.peek(1) == OPENING_BRACKET
132
+ parse_pure_commutator
133
+ else
134
+ FakeCommutator.new(parse_moves_with_triggers)
188
135
  end
189
136
  end
190
137
 
191
- def parse_commutator(alg_string, complete_parse = true)
192
- parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
193
- commutator = parser.parse_commutator
194
- parser.check_eos('commutator') if complete_parse
195
- commutator
138
+ def parse_pure_commutator
139
+ skip_spaces
140
+ parse_open_bracket
141
+ first_part = parse_nonempty_moves
142
+ skip_spaces
143
+ complain('middle of pure commutator') unless @scanner.getch == ','
144
+ second_part = parse_nonempty_moves
145
+ skip_spaces
146
+ parse_close_bracket
147
+ PureCommutator.new(first_part, second_part)
196
148
  end
197
149
 
198
- def parse_cube_algorithm(alg_string, complete_parse = true)
199
- parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
200
- algorithm = parser.parse_algorithm
201
- parser.check_eos('algorithm') if complete_parse
202
- algorithm
150
+ def parse_commutator_internal_after_separator(setup_or_first_part, separator)
151
+ if [':', ';'].include?(separator)
152
+ inner_commutator = parse_setup_commutator_inner
153
+ SetupCommutator.new(setup_or_first_part, inner_commutator)
154
+ elsif separator == ','
155
+ second_part = parse_nonempty_moves
156
+ PureCommutator.new(setup_or_first_part, second_part)
157
+ else
158
+ complain('end of setup or middle of pure commutator') unless @scanner.eos?
159
+ end
203
160
  end
204
161
 
205
- def parse_cube_move(move_string)
206
- CubeMoveParser::INSTANCE.parse_move(move_string)
162
+ def parse_commutator_internal
163
+ skip_spaces
164
+ parse_open_bracket
165
+ setup_or_first_part = parse_nonempty_moves
166
+ skip_spaces
167
+ separator = @scanner.getch
168
+ comm = parse_commutator_internal_after_separator(setup_or_first_part, separator)
169
+ skip_spaces
170
+ parse_close_bracket
171
+ skip_spaces
172
+ complain('end of commutator') unless @scanner.eos?
173
+ comm
207
174
  end
208
175
 
209
- alias parse_algorithm parse_cube_algorithm
210
- alias parse_move parse_cube_move
176
+ def parse_move_internal
177
+ move = @scanner.scan(@move_parser.regexp)
178
+ return unless move
211
179
 
212
- def parse_skewb_algorithm(alg_string, notation, complete_parse = true)
213
- parser = Parser.new(alg_string, SkewbMoveParser.new(notation))
214
- algorithm = parser.parse_algorithm
215
- parser.check_eos('algorithm') if complete_parse
216
- algorithm
180
+ @move_parser.parse_move(move)
217
181
  end
218
182
 
219
- def parse_skewb_move(move_string, notation)
220
- SkewbMoveParser.new(notation).parse_move(move_string)
183
+ def skip_spaces
184
+ @scanner.skip(/\s+/)
221
185
  end
186
+ end
187
+
188
+ def parse_commutator(alg_string, complete_parse = true)
189
+ parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
190
+ commutator = parser.parse_commutator
191
+ parser.check_eos('commutator') if complete_parse
192
+ commutator
193
+ end
194
+
195
+ def parse_cube_algorithm(alg_string, complete_parse = true)
196
+ parser = Parser.new(alg_string, CubeMoveParser::INSTANCE)
197
+ algorithm = parser.parse_algorithm
198
+ parser.check_eos('algorithm') if complete_parse
199
+ algorithm
200
+ end
201
+
202
+ def parse_cube_move(move_string)
203
+ CubeMoveParser::INSTANCE.parse_move(move_string)
204
+ end
205
+
206
+ alias parse_algorithm parse_cube_algorithm
207
+ alias parse_move parse_cube_move
208
+
209
+ def parse_skewb_algorithm(alg_string, notation, complete_parse = true)
210
+ parser = Parser.new(alg_string, SkewbMoveParser.new(notation))
211
+ algorithm = parser.parse_algorithm
212
+ parser.check_eos('algorithm') if complete_parse
213
+ algorithm
214
+ end
215
+
216
+ def parse_skewb_move(move_string, notation)
217
+ SkewbMoveParser.new(notation).parse_move(move_string)
218
+ end
222
219
  end