twisty_puzzles 0.0.1 → 0.0.6

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +6 -1
  4. data/ext/twisty_puzzles/native/cube_algorithm.c +267 -0
  5. data/ext/twisty_puzzles/native/cube_algorithm.h +5 -0
  6. data/ext/twisty_puzzles/native/cube_average.c +184 -0
  7. data/ext/twisty_puzzles/native/cube_average.h +5 -0
  8. data/ext/twisty_puzzles/native/cube_coordinate.c +207 -0
  9. data/ext/twisty_puzzles/native/cube_coordinate.h +34 -0
  10. data/ext/twisty_puzzles/native/cube_state.c +264 -0
  11. data/ext/twisty_puzzles/native/cube_state.h +31 -0
  12. data/ext/twisty_puzzles/native/extconf.rb +1 -1
  13. data/ext/twisty_puzzles/native/face_symbols.c +67 -0
  14. data/ext/twisty_puzzles/native/face_symbols.h +34 -0
  15. data/ext/twisty_puzzles/native/native.c +28 -0
  16. data/ext/twisty_puzzles/native/skewb_algorithm.c +331 -0
  17. data/ext/twisty_puzzles/native/skewb_algorithm.h +5 -0
  18. data/ext/twisty_puzzles/native/skewb_coordinate.c +237 -0
  19. data/ext/twisty_puzzles/native/skewb_coordinate.h +36 -0
  20. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.c +271 -0
  21. data/ext/twisty_puzzles/native/skewb_layer_fingerprint.h +5 -0
  22. data/ext/twisty_puzzles/native/skewb_state.c +214 -0
  23. data/ext/twisty_puzzles/native/skewb_state.h +23 -0
  24. data/ext/twisty_puzzles/native/utils.c +76 -0
  25. data/ext/twisty_puzzles/native/utils.h +31 -0
  26. data/lib/twisty_puzzles.rb +38 -0
  27. data/lib/twisty_puzzles/abstract_direction.rb +38 -39
  28. data/lib/twisty_puzzles/abstract_move.rb +1 -2
  29. data/lib/twisty_puzzles/abstract_move_parser.rb +32 -33
  30. data/lib/twisty_puzzles/algorithm.rb +112 -113
  31. data/lib/twisty_puzzles/algorithm_transformation.rb +19 -21
  32. data/lib/twisty_puzzles/axis_face_and_direction_move.rb +56 -56
  33. data/lib/twisty_puzzles/cancellation_helper.rb +124 -125
  34. data/lib/twisty_puzzles/color_scheme.rb +1 -1
  35. data/lib/twisty_puzzles/commutator.rb +82 -80
  36. data/lib/twisty_puzzles/compiled_algorithm.rb +31 -32
  37. data/lib/twisty_puzzles/compiled_cube_algorithm.rb +49 -50
  38. data/lib/twisty_puzzles/compiled_skewb_algorithm.rb +18 -19
  39. data/lib/twisty_puzzles/coordinate.rb +243 -246
  40. data/lib/twisty_puzzles/cube.rb +494 -495
  41. data/lib/twisty_puzzles/cube_constants.rb +40 -41
  42. data/lib/twisty_puzzles/cube_direction.rb +15 -18
  43. data/lib/twisty_puzzles/cube_move.rb +285 -290
  44. data/lib/twisty_puzzles/cube_move_parser.rb +75 -76
  45. data/lib/twisty_puzzles/cube_print_helper.rb +133 -133
  46. data/lib/twisty_puzzles/cube_state.rb +80 -81
  47. data/lib/twisty_puzzles/move_type_creator.rb +17 -18
  48. data/lib/twisty_puzzles/parser.rb +176 -179
  49. data/lib/twisty_puzzles/part_cycle_factory.rb +39 -42
  50. data/lib/twisty_puzzles/puzzle.rb +16 -17
  51. data/lib/twisty_puzzles/reversible_applyable.rb +24 -25
  52. data/lib/twisty_puzzles/rotation.rb +76 -75
  53. data/lib/twisty_puzzles/skewb_direction.rb +14 -15
  54. data/lib/twisty_puzzles/skewb_move.rb +49 -49
  55. data/lib/twisty_puzzles/skewb_move_parser.rb +51 -51
  56. data/lib/twisty_puzzles/skewb_notation.rb +121 -118
  57. data/lib/twisty_puzzles/skewb_state.rb +120 -121
  58. data/lib/twisty_puzzles/state_helper.rb +20 -21
  59. data/lib/twisty_puzzles/sticker_cycle.rb +43 -44
  60. data/lib/twisty_puzzles/utils.rb +3 -0
  61. data/lib/twisty_puzzles/version.rb +3 -1
  62. metadata +30 -10
@@ -8,66 +8,66 @@ require 'twisty_puzzles/skewb_move'
8
8
  require 'twisty_puzzles/skewb_notation'
9
9
 
10
10
  module TwistyPuzzles
11
-
12
- # Parser for Skewb moves.
13
- class SkewbMoveParser < AbstractMoveParser
14
- MOVE_TYPE_CREATORS = [
15
- MoveTypeCreator.new(%i[axis_face cube_direction], Rotation),
16
- MoveTypeCreator.new(%i[axis_corner skewb_direction], SkewbMove)
17
- ].freeze
11
+ # Parser for Skewb moves.
12
+ class SkewbMoveParser < AbstractMoveParser
13
+ MOVE_TYPE_CREATORS = [
14
+ MoveTypeCreator.new(%i[axis_face cube_direction], Rotation),
15
+ MoveTypeCreator.new(%i[axis_corner skewb_direction], SkewbMove)
16
+ ].freeze
18
17
 
19
- def initialize(notation)
20
- raise TypeError unless notation.is_a?(SkewbNotation)
18
+ def initialize(notation)
19
+ raise TypeError unless notation.is_a?(SkewbNotation)
21
20
 
22
- @notation = notation
23
- end
21
+ super()
22
+ @notation = notation
23
+ end
24
24
 
25
- FIXED_CORNER_INSTANCE = SkewbMoveParser.new(SkewbNotation.fixed_corner)
26
- SARAH_INSTANCE = SkewbMoveParser.new(SkewbNotation.sarah)
27
- RUBIKS_INSTANCE = SkewbMoveParser.new(SkewbNotation.rubiks)
25
+ FIXED_CORNER_INSTANCE = SkewbMoveParser.new(SkewbNotation.fixed_corner)
26
+ SARAH_INSTANCE = SkewbMoveParser.new(SkewbNotation.sarah)
27
+ RUBIKS_INSTANCE = SkewbMoveParser.new(SkewbNotation.rubiks)
28
28
 
29
- def regexp
30
- @regexp ||=
31
- begin
32
- skewb_direction_names =
33
- AbstractDirection::POSSIBLE_SKEWB_DIRECTION_NAMES.flatten
34
- move_part = "(?:(?<skewb_move>[#{@notation.move_strings.join}])" \
35
- "(?<skewb_direction>[#{skewb_direction_names.join}]?))"
36
- rotation_direction_names =
37
- AbstractDirection::POSSIBLE_DIRECTION_NAMES.flatten
38
- rotation_direction_names.sort_by! { |e| -e.length }
39
- rotation_part = "(?:(?<axis_name>[#{AbstractMove::AXES.join}])" \
40
- "(?<cube_direction>#{rotation_direction_names.join('|')}))"
41
- Regexp.new("#{move_part}|#{rotation_part}")
42
- end
43
- end
29
+ def regexp
30
+ @regexp ||=
31
+ begin
32
+ skewb_direction_names =
33
+ AbstractDirection::POSSIBLE_SKEWB_DIRECTION_NAMES.flatten
34
+ move_part = "(?:(?<skewb_move>[#{@notation.move_strings.join}])" \
35
+ "(?<skewb_direction>[#{skewb_direction_names.join}]?))"
36
+ rotation_direction_names =
37
+ AbstractDirection::POSSIBLE_DIRECTION_NAMES.flatten
38
+ rotation_direction_names.sort_by! { |e| -e.length }
39
+ rotation_part = "(?:(?<axis_name>[#{AbstractMove::AXES.join}])" \
40
+ "(?<cube_direction>#{rotation_direction_names.join('|')}))"
41
+ Regexp.new("#{move_part}|#{rotation_part}")
42
+ end
43
+ end
44
44
 
45
- def move_type_creators
46
- MOVE_TYPE_CREATORS
47
- end
45
+ def move_type_creators
46
+ MOVE_TYPE_CREATORS
47
+ end
48
48
 
49
- def parse_skewb_direction(direction_string)
50
- if AbstractDirection::POSSIBLE_DIRECTION_NAMES[0].include?(direction_string)
51
- SkewbDirection::FORWARD
52
- elsif AbstractDirection::POSSIBLE_DIRECTION_NAMES[-1].include?(direction_string)
53
- SkewbDirection::BACKWARD
54
- else
55
- raise ArgumentError
56
- end
49
+ def parse_skewb_direction(direction_string)
50
+ if AbstractDirection::POSSIBLE_DIRECTION_NAMES[0].include?(direction_string)
51
+ SkewbDirection::FORWARD
52
+ elsif AbstractDirection::POSSIBLE_DIRECTION_NAMES[-1].include?(direction_string)
53
+ SkewbDirection::BACKWARD
54
+ else
55
+ raise ArgumentError
57
56
  end
57
+ end
58
58
 
59
- def parse_part_key(name)
60
- name.sub('name', 'face').sub('skewb_move', 'axis_corner')
61
- end
59
+ def parse_part_key(name)
60
+ name.sub('name', 'face').sub('skewb_move', 'axis_corner')
61
+ end
62
62
 
63
- def parse_move_part(name, value)
64
- case name
65
- when 'axis_name' then CubeMoveParser::INSTANCE.parse_axis_face(value)
66
- when 'cube_direction' then CubeMoveParser::INSTANCE.parse_direction(value)
67
- when 'skewb_move' then @notation.corner(value)
68
- when 'skewb_direction' then parse_skewb_direction(value)
69
- else raise
70
- end
63
+ def parse_move_part(name, value)
64
+ case name
65
+ when 'axis_name' then CubeMoveParser::INSTANCE.parse_axis_face(value)
66
+ when 'cube_direction' then CubeMoveParser::INSTANCE.parse_direction(value)
67
+ when 'skewb_move' then @notation.corner(value)
68
+ when 'skewb_direction' then parse_skewb_direction(value)
69
+ else raise
71
70
  end
72
71
  end
72
+ end
73
73
  end
@@ -7,141 +7,144 @@ require 'twisty_puzzles/skewb_direction'
7
7
  require 'twisty_puzzles/skewb_move'
8
8
 
9
9
  module TwistyPuzzles
10
-
11
- # Class that represents one notation for Skewb moves, e.g. Sarahs notation or fixed
12
- # corner notation.
13
- class SkewbNotation
14
- def initialize(name, move_corner_pairs)
15
- raise TypeError unless name.is_a?(String)
16
-
17
- check_move_corner_pairs(move_corner_pairs)
18
- @name = name
19
- @move_to_corner = move_corner_pairs.to_h.freeze
20
- @corner_to_move = move_corner_pairs.collect_concat do |m, c|
21
- c.rotations.map { |e| [e, m] }
22
- end.to_h.freeze
23
- @move_strings = move_corner_pairs.map(&:first).freeze
24
- @non_zero_moves =
25
- move_corner_pairs.map(&:last).product(SkewbDirection::NON_ZERO_DIRECTIONS).map do |c, d|
26
- SkewbMove.new(c, d)
27
- end.freeze
28
- freeze
10
+ # Class that represents one notation for Skewb moves, e.g. Sarahs notation or fixed
11
+ # corner notation.
12
+ class SkewbNotation
13
+ # rubocop:disable Metrics/AbcSize
14
+ def initialize(name, move_corner_pairs)
15
+ raise TypeError unless name.is_a?(String)
16
+
17
+ super()
18
+ check_move_corner_pairs(move_corner_pairs)
19
+ @name = name
20
+ @move_to_corner = move_corner_pairs.to_h.freeze
21
+ @corner_to_move = move_corner_pairs.collect_concat do |m, c|
22
+ c.rotations.map { |e| [e, m] }
23
+ end.to_h.freeze
24
+ @move_strings = move_corner_pairs.map(&:first).freeze
25
+ @non_zero_moves =
26
+ move_corner_pairs.map(&:last).product(SkewbDirection::NON_ZERO_DIRECTIONS).map do |c, d|
27
+ SkewbMove.new(c, d)
28
+ end.freeze
29
+ freeze
30
+ end
31
+ # rubocop:enable Metrics/AbcSize
32
+
33
+ # rubocop:disable Metrics/CyclomaticComplexity
34
+ def check_move_corner_pairs(move_corner_pairs)
35
+ move_corner_pairs.each do |m|
36
+ raise ArgumentError unless m.length == 2
37
+ raise TypeError unless m[0].is_a?(String)
38
+ raise TypeError unless m[1].is_a?(Corner)
29
39
  end
40
+ raise ArgumentError if move_corner_pairs.map(&:first).uniq.length != move_corner_pairs.length
30
41
 
31
- def check_move_corner_pairs(move_corner_pairs)
32
- move_corner_pairs.each do |m|
33
- raise ArgumentError unless m.length == 2
34
- raise TypeError unless m[0].is_a?(String)
35
- raise TypeError unless m[1].is_a?(Corner)
36
- end
37
- if move_corner_pairs.map(&:first).uniq.length != move_corner_pairs.length
38
- raise ArgumentError
42
+ check_corner_coverage(move_corner_pairs.map(&:last))
43
+ end
44
+ # rubocop:enable Metrics/CyclomaticComplexity
45
+
46
+ def check_corner_coverage(corners)
47
+ corner_closure = corners + corners.map(&:diagonal_opposite)
48
+ Corner::ELEMENTS.each do |corner|
49
+ unless corner_closure.any? { |c| c.turned_equals?(corner) }
50
+ raise ArgumentError,
51
+ "Turns around corner #{corner} cannot be represented in notation #{name}."
39
52
  end
40
-
41
- check_corner_coverage(move_corner_pairs.map(&:last))
42
53
  end
54
+ end
43
55
 
44
- def check_corner_coverage(corners)
45
- corner_closure = corners + corners.map(&:diagonal_opposite)
46
- Corner::ELEMENTS.each do |corner|
47
- unless corner_closure.any? { |c| c.turned_equals?(corner) }
48
- raise ArgumentError,
49
- "Turns around corner #{corner} cannot be represented in notation #{name}."
50
- end
51
- end
52
- end
56
+ attr_reader :name, :move_strings, :non_zero_moves
53
57
 
54
- attr_reader :name, :move_strings, :non_zero_moves
55
- private_class_method :new
56
-
57
- def self.fixed_corner
58
- @fixed_corner ||= new(
59
- 'fixed corner', [
60
- ['U', Corner.for_face_symbols(%i[U L B])],
61
- ['R', Corner.for_face_symbols(%i[D R B])],
62
- ['L', Corner.for_face_symbols(%i[D F L])],
63
- ['B', Corner.for_face_symbols(%i[D B L])]
64
- ]
65
- )
66
- end
58
+ private_class_method :new
67
59
 
68
- def self.sarah
69
- @sarah ||= new(
70
- 'sarah', [
71
- ['F', Corner.for_face_symbols(%i[U R F])],
72
- ['R', Corner.for_face_symbols(%i[U B R])],
73
- ['B', Corner.for_face_symbols(%i[U L B])],
74
- ['L', Corner.for_face_symbols(%i[U F L])]
75
- ]
76
- )
77
- end
60
+ def self.fixed_corner
61
+ @fixed_corner ||= new(
62
+ 'fixed corner', [
63
+ ['U', Corner.for_face_symbols(%i[U L B])],
64
+ ['R', Corner.for_face_symbols(%i[D R B])],
65
+ ['L', Corner.for_face_symbols(%i[D F L])],
66
+ ['B', Corner.for_face_symbols(%i[D B L])]
67
+ ]
68
+ )
69
+ end
78
70
 
79
- def self.rubiks
80
- @rubiks ||= new(
81
- 'rubiks', [
82
- ['F', Corner.for_face_symbols(%i[U R F])],
83
- ['R', Corner.for_face_symbols(%i[U B R])],
84
- ['B', Corner.for_face_symbols(%i[U L B])],
85
- ['L', Corner.for_face_symbols(%i[U F L])],
86
- ['f', Corner.for_face_symbols(%i[D F R])],
87
- ['r', Corner.for_face_symbols(%i[D R B])],
88
- ['b', Corner.for_face_symbols(%i[D B L])],
89
- ['l', Corner.for_face_symbols(%i[D L F])]
90
- ]
91
- )
92
- end
71
+ def self.sarah
72
+ @sarah ||= new(
73
+ 'sarah', [
74
+ ['F', Corner.for_face_symbols(%i[U R F])],
75
+ ['R', Corner.for_face_symbols(%i[U B R])],
76
+ ['B', Corner.for_face_symbols(%i[U L B])],
77
+ ['L', Corner.for_face_symbols(%i[U F L])]
78
+ ]
79
+ )
80
+ end
93
81
 
94
- def to_s
95
- @name
96
- end
82
+ def self.rubiks
83
+ @rubiks ||= new(
84
+ 'rubiks', [
85
+ ['F', Corner.for_face_symbols(%i[U R F])],
86
+ ['R', Corner.for_face_symbols(%i[U B R])],
87
+ ['B', Corner.for_face_symbols(%i[U L B])],
88
+ ['L', Corner.for_face_symbols(%i[U F L])],
89
+ ['f', Corner.for_face_symbols(%i[D F R])],
90
+ ['r', Corner.for_face_symbols(%i[D R B])],
91
+ ['b', Corner.for_face_symbols(%i[D B L])],
92
+ ['l', Corner.for_face_symbols(%i[D L F])]
93
+ ]
94
+ )
95
+ end
97
96
 
98
- def corner(move)
99
- @move_to_corner[move] || (raise ArgumentError)
100
- end
97
+ def to_s
98
+ @name
99
+ end
101
100
 
102
- def algorithm_to_string(algorithm)
103
- reversed_rotations = []
104
- num_tail_rotations = CancellationHelper.num_tail_rotations(algorithm)
105
- alg_string = algorithm.moves[0...algorithm.length - num_tail_rotations].map do |m|
106
- move_to_string(m, reversed_rotations)
107
- end.join(' ')
108
- new_tail_rotations = reversed_rotations.reverse! +
109
- algorithm.moves[algorithm.length - num_tail_rotations..-1]
110
- cancelled_rotations = Algorithm.new(new_tail_rotations).cancelled(3)
111
- cancelled_rotations.empty? ? alg_string : "#{alg_string} #{cancelled_rotations}"
112
- end
101
+ def corner(move)
102
+ @move_to_corner[move] || (raise ArgumentError)
103
+ end
104
+
105
+ def algorithm_to_string(algorithm)
106
+ reversed_rotations = []
107
+ num_tail_rotations = CancellationHelper.num_tail_rotations(algorithm)
108
+ alg_string = algorithm.moves[0...algorithm.length - num_tail_rotations].map do |m|
109
+ move_to_string(m, reversed_rotations)
110
+ end.join(' ')
111
+ new_tail_rotations = reversed_rotations.reverse! +
112
+ algorithm.moves[algorithm.length - num_tail_rotations..]
113
+ cancelled_rotations = Algorithm.new(new_tail_rotations).cancelled(3)
114
+ cancelled_rotations.empty? ? alg_string : "#{alg_string} #{cancelled_rotations}"
115
+ end
113
116
 
114
- private
117
+ private
115
118
 
116
- def move_to_string(move, reversed_rotations)
117
- reversed_rotations.each { |r| move = move.rotate_by(r.inverse) }
118
- case move
119
- when SkewbMove then skewb_move_to_string(move, reversed_rotations)
120
- when Rotation then move.to_s
121
- else raise ArgumentError, "Couldn't transform #{move} to #{@name} Skewb notation."
122
- end
119
+ def move_to_string(move, reversed_rotations)
120
+ reversed_rotations.each { |r| move = move.rotate_by(r.inverse) }
121
+ case move
122
+ when SkewbMove then skewb_move_to_string(move, reversed_rotations)
123
+ when Rotation then move.to_s
124
+ else raise ArgumentError, "Couldn't transform #{move} to #{@name} Skewb notation."
123
125
  end
126
+ end
124
127
 
125
- def skewb_move_to_string(move, reversed_rotations)
126
- move_string, rotate = move_to_string_internal(move)
127
- if rotate
128
- reversed_additional_rotations =
129
- Rotation.around_corner(move.axis_corner, move.direction).moves.reverse
130
- reversed_rotations.concat(reversed_additional_rotations)
131
- end
132
- "#{move_string}#{move.direction.name}"
128
+ def skewb_move_to_string(move, reversed_rotations)
129
+ move_string, rotate = move_to_string_internal(move)
130
+ if rotate
131
+ reversed_additional_rotations =
132
+ Rotation.around_corner(move.axis_corner, move.direction).moves.reverse
133
+ reversed_rotations.concat(reversed_additional_rotations)
133
134
  end
135
+ "#{move_string}#{move.direction.name}"
136
+ end
134
137
 
135
- # Returns the move string of the given move and true if a rotation has to be done to correct
136
- # for the fact that we actually used the opposite corner.
137
- def move_to_string_internal(move)
138
- if (move_string = @corner_to_move[move.axis_corner])
139
- [move_string, false]
140
- elsif (move_string = @corner_to_move[move.axis_corner.diagonal_opposite])
141
- [move_string, !move.direction.zero?]
142
- else
143
- raise
144
- end
138
+ # Returns the move string of the given move and true if a rotation has to be done to correct
139
+ # for the fact that we actually used the opposite corner.
140
+ def move_to_string_internal(move)
141
+ if (move_string = @corner_to_move[move.axis_corner])
142
+ [move_string, false]
143
+ elsif (move_string = @corner_to_move[move.axis_corner.diagonal_opposite])
144
+ [move_string, !move.direction.zero?]
145
+ else
146
+ raise
145
147
  end
146
148
  end
149
+ end
147
150
  end
@@ -7,157 +7,156 @@ require 'twisty_puzzles/state_helper'
7
7
  require 'twisty_puzzles/cube_constants'
8
8
 
9
9
  module TwistyPuzzles
10
-
11
- # Represents the state (i.e. the sticker positions) of a Skewb.
12
- class SkewbState
13
- include CubePrintHelper
14
- include StateHelper
15
- include CubeConstants
16
- # Pairs of coordinate pairs that should match in case of solved layers.
17
- MATCHING_CORNERS =
18
- begin
19
- matching_corners = []
20
- Corner::ELEMENTS.each do |c1|
21
- Corner::ELEMENTS.each do |c2|
22
- # Take corner pairs that have a common edge.
23
- next unless c1.common_edge_with?(c2)
24
-
25
- check_parts = []
26
- c1.rotations.each do |c1_rot|
27
- next unless c2.face_symbols.include?(c1_rot.face_symbols.first)
28
-
29
- c2_rot = c2.rotate_face_symbol_up(c1_rot.face_symbols.first)
30
- check_parts.push([
31
- SkewbCoordinate.for_corner(c1_rot),
32
- SkewbCoordinate.for_corner(c2_rot)
33
- ])
34
- end
35
- matching_corners.push(check_parts)
10
+ # Represents the state (i.e. the sticker positions) of a Skewb.
11
+ class SkewbState
12
+ include CubePrintHelper
13
+ include StateHelper
14
+ include CubeConstants
15
+ # Pairs of coordinate pairs that should match in case of solved layers.
16
+ MATCHING_CORNERS =
17
+ begin
18
+ matching_corners = []
19
+ Corner::ELEMENTS.each do |c1|
20
+ Corner::ELEMENTS.each do |c2|
21
+ # Take corner pairs that have a common edge.
22
+ next unless c1.common_edge_with?(c2)
23
+
24
+ check_parts = []
25
+ c1.rotations.each do |c1_rot|
26
+ next unless c2.face_symbols.include?(c1_rot.face_symbols.first)
27
+
28
+ c2_rot = c2.rotate_face_symbol_up(c1_rot.face_symbols.first)
29
+ check_parts.push([
30
+ SkewbCoordinate.for_corner(c1_rot),
31
+ SkewbCoordinate.for_corner(c2_rot)
32
+ ])
36
33
  end
34
+ matching_corners.push(check_parts)
37
35
  end
38
- matching_corners.uniq
39
36
  end
40
- # Pairs of stickers that can be used to check whether the "outside" of a layer on the given
41
- # face is a proper layer.
42
- LAYER_CHECK_NEIGHBORS =
43
- begin
44
- layer_check_neighbors = {}
45
- MATCHING_CORNERS.each do |a, b|
46
- [[a.first.face, b], [b.first.face, a]].each do |face, coordinates|
47
- # We take the first one we encounter, but it doesn't matter, we could take any.
48
- layer_check_neighbors[face] ||= coordinates
49
- end
37
+ matching_corners.uniq
38
+ end
39
+ # Pairs of stickers that can be used to check whether the "outside" of a layer on the given
40
+ # face is a proper layer.
41
+ LAYER_CHECK_NEIGHBORS =
42
+ begin
43
+ layer_check_neighbors = {}
44
+ MATCHING_CORNERS.each do |a, b|
45
+ [[a.first.face, b], [b.first.face, a]].each do |face, coordinates|
46
+ # We take the first one we encounter, but it doesn't matter, we could take any.
47
+ layer_check_neighbors[face] ||= coordinates
50
48
  end
51
- layer_check_neighbors
52
49
  end
50
+ layer_check_neighbors
51
+ end
53
52
 
54
- def initialize(native)
55
- raise TypeError unless native.is_a?(Native::SkewbState)
53
+ def initialize(native)
54
+ raise TypeError unless native.is_a?(Native::SkewbState)
56
55
 
57
- @native = native
58
- end
56
+ @native = native
57
+ end
59
58
 
60
- attr_reader :native
59
+ attr_reader :native
61
60
 
62
- def self.for_solved_colors(solved_colors)
63
- native = Native::SkewbState.new(solved_colors)
64
- new(native)
65
- end
61
+ def self.for_solved_colors(solved_colors)
62
+ native = Native::SkewbState.new(solved_colors)
63
+ new(native)
64
+ end
66
65
 
67
- def eql?(other)
68
- self.class.equal?(other.class) && @native == other.native
69
- end
66
+ def eql?(other)
67
+ self.class.equal?(other.class) && @native == other.native
68
+ end
70
69
 
71
- alias == eql?
70
+ alias == eql?
72
71
 
73
- def hash
74
- @hash ||= [self.class, @native].hash
75
- end
72
+ def hash
73
+ @hash ||= [self.class, @native].hash
74
+ end
76
75
 
77
- # TODO: Get rid of this backwards compatibility artifact
78
- def sticker_array(face)
79
- raise TypeError unless face.is_a?(Face)
76
+ # TODO: Get rid of this backwards compatibility artifact
77
+ def sticker_array(face)
78
+ raise TypeError unless face.is_a?(Face)
80
79
 
81
- center_sticker = self[SkewbCoordinate.for_center(face)]
82
- corner_stickers =
83
- face.clockwise_corners.sort.map do |c|
84
- self[SkewbCoordinate.for_corner(c)]
85
- end
86
- [center_sticker] + corner_stickers
87
- end
80
+ center_sticker = self[SkewbCoordinate.for_center(face)]
81
+ corner_stickers =
82
+ face.clockwise_corners.sort.map do |c|
83
+ self[SkewbCoordinate.for_corner(c)]
84
+ end
85
+ [center_sticker] + corner_stickers
86
+ end
88
87
 
89
- def dup
90
- SkewbState.new(@native.dup)
91
- end
88
+ def dup
89
+ SkewbState.new(@native.dup)
90
+ end
92
91
 
93
- def to_s
94
- skewb_string(self, :nocolor)
95
- end
92
+ def to_s
93
+ skewb_string(self, :nocolor)
94
+ end
96
95
 
97
- def colored_to_s
98
- skewb_string(self, :color)
99
- end
96
+ def colored_to_s
97
+ skewb_string(self, :color)
98
+ end
100
99
 
101
- def apply_move(move)
102
- move.apply_to(self)
103
- end
100
+ def apply_move(move)
101
+ move.apply_to(self)
102
+ end
104
103
 
105
- def apply_algorithm(alg)
106
- alg.apply_to(self)
107
- end
104
+ def apply_algorithm(alg)
105
+ alg.apply_to(self)
106
+ end
108
107
 
109
- def apply_rotation(rot)
110
- rot.apply_to_skewb(self)
111
- end
108
+ def apply_rotation(rot)
109
+ rot.apply_to_skewb(self)
110
+ end
112
111
 
113
- def [](coordinate)
114
- @native[coordinate.native]
115
- end
112
+ def [](coordinate)
113
+ @native[coordinate.native]
114
+ end
116
115
 
117
- def []=(coordinate, color)
118
- @native[coordinate.native] = color
119
- sticker_array(coordinate.face)[coordinate.coordinate] = color
120
- end
116
+ def []=(coordinate, color)
117
+ @native[coordinate.native] = color
118
+ sticker_array(coordinate.face)[coordinate.coordinate] = color
119
+ end
121
120
 
122
- def any_layer_solved?
123
- !solved_layers.empty?
124
- end
121
+ def any_layer_solved?
122
+ !solved_layers.empty?
123
+ end
125
124
 
126
- # Returns the color of all solved layers. Empty if there is none.
127
- def solved_layers
128
- solved_faces = Face::ELEMENTS.select { |f| layer_at_face_solved?(f) }
129
- solved_faces.map { |f| self[SkewbCoordinate.for_center(f)] }
130
- end
125
+ # Returns the color of all solved layers. Empty if there is none.
126
+ def solved_layers
127
+ solved_faces = Face::ELEMENTS.select { |f| layer_at_face_solved?(f) }
128
+ solved_faces.map { |f| self[SkewbCoordinate.for_center(f)] }
129
+ end
131
130
 
132
- def layer_solved?(color)
133
- Face::ELEMENTS.any? do |f|
134
- self[SkewbCoordinate.for_center(f)] == color && layer_at_face_solved?(f)
135
- end
131
+ def layer_solved?(color)
132
+ Face::ELEMENTS.any? do |f|
133
+ self[SkewbCoordinate.for_center(f)] == color && layer_at_face_solved?(f)
136
134
  end
135
+ end
137
136
 
138
- def center_face(color)
139
- Face::ELEMENTS.find { |f| self[SkewbCoordinate.for_center(f)] == color }
140
- end
137
+ def center_face(color)
138
+ Face::ELEMENTS.find { |f| self[SkewbCoordinate.for_center(f)] == color }
139
+ end
141
140
 
142
- def layer_check_neighbors(face)
143
- LAYER_CHECK_NEIGHBORS[face]
144
- end
141
+ def layer_check_neighbors(face)
142
+ LAYER_CHECK_NEIGHBORS[face]
143
+ end
145
144
 
146
- # Note that this does NOT say that the layer corresponding to the given face is solved.
147
- # The face argument is used as the position where a solved face is present.
148
- def layer_at_face_solved?(face)
149
- return false unless native.face_solved?(face.face_symbol)
145
+ # Note that this does NOT say that the layer corresponding to the given face is solved.
146
+ # The face argument is used as the position where a solved face is present.
147
+ def layer_at_face_solved?(face)
148
+ return false unless native.face_solved?(face.face_symbol)
150
149
 
151
- layer_check_neighbors(face).map { |c| self[c] }.uniq.length == 1
152
- end
150
+ layer_check_neighbors(face).map { |c| self[c] }.uniq.length == 1
151
+ end
153
152
 
154
- def rotate_face(face, direction)
155
- neighbors = face.neighbors
156
- inverse_order_face = face.coordinate_index_close_to(neighbors[0]) <
157
- face.coordinate_index_close_to(neighbors[1])
158
- direction = direction.inverse if inverse_order_face
159
- cycle = SkewbCoordinate.corners_on_face(face)
160
- apply_4sticker_cycle(cycle, direction)
161
- end
153
+ def rotate_face(face, direction)
154
+ neighbors = face.neighbors
155
+ inverse_order_face = face.coordinate_index_close_to(neighbors[0]) <
156
+ face.coordinate_index_close_to(neighbors[1])
157
+ direction = direction.inverse if inverse_order_face
158
+ cycle = SkewbCoordinate.corners_on_face(face)
159
+ apply_4sticker_cycle(cycle, direction)
162
160
  end
161
+ end
163
162
  end