twisty_puzzles 0.0.9 → 0.0.10
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/lib/twisty_puzzles/abstract_move.rb +4 -0
- data/lib/twisty_puzzles/abstract_move_parser.rb +2 -2
- data/lib/twisty_puzzles/algorithm_transformation.rb +27 -19
- data/lib/twisty_puzzles/axis_face_and_direction_move.rb +3 -1
- data/lib/twisty_puzzles/color_scheme.rb +7 -5
- data/lib/twisty_puzzles/commutator.rb +4 -0
- data/lib/twisty_puzzles/coordinate.rb +0 -19
- data/lib/twisty_puzzles/cube.rb +17 -21
- data/lib/twisty_puzzles/cube_state.rb +10 -0
- data/lib/twisty_puzzles/rotation.rb +6 -4
- data/lib/twisty_puzzles/skewb_state.rb +6 -4
- data/lib/twisty_puzzles/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7616a5e5bd64a5beb6ea98bed6a6f6bc2dc5ae145491db533e4693cd10d0ff6d
|
4
|
+
data.tar.gz: 8bc2cd597844fb86f9168c8e368c6bbb01cc34a82598ebef7f5dcb77c1e3713c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42c3be051514680426bed25b66ca09b8c420f1423a778db10512c37663c42128136acf6f4e72847bed68d4111e8563ef5eb1cd427883bf2802e072853f1900b0
|
7
|
+
data.tar.gz: d26d1c98f90f7bbd15474ac45f200a483983daadfae40016e5f9d2f0ca44b20a652e480f8bbfc13d939f904140358a1acc741fcbbb09306f35b51a298f3a9cf1
|
@@ -143,6 +143,10 @@ module TwistyPuzzles
|
|
143
143
|
prepend_slice_move(other, cube_size)
|
144
144
|
end
|
145
145
|
|
146
|
+
def prepend_slice_move(_other, _cube_size)
|
147
|
+
raise NotImplementedError, "#{self.class}#prepend_slice_move is not implemented"
|
148
|
+
end
|
149
|
+
|
146
150
|
private
|
147
151
|
|
148
152
|
def move_count_internal(metric, slice_factor, direction_factor)
|
@@ -31,14 +31,14 @@ module TwistyPuzzles
|
|
31
31
|
def parse_move(move_string)
|
32
32
|
match = move_string.match(regexp)
|
33
33
|
if !match || !match.pre_match.empty? || !match.post_match.empty?
|
34
|
-
raise ArgumentError
|
34
|
+
raise ArgumentError, "Invalid move #{move_string}."
|
35
35
|
end
|
36
36
|
|
37
37
|
parsed_parts = parse_named_captures(match)
|
38
38
|
move_type_creators.each do |parser|
|
39
39
|
return parser.create(parsed_parts) if parser.applies_to?(parsed_parts)
|
40
40
|
end
|
41
|
-
raise "No move type creator applies to #{parsed_parts}"
|
41
|
+
raise ArgumentError, "No move type creator applies to #{parsed_parts}"
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -4,28 +4,36 @@ require 'twisty_puzzles/cube_direction'
|
|
4
4
|
require 'twisty_puzzles/rotation'
|
5
5
|
|
6
6
|
module TwistyPuzzles
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
# A transformation consisting of mirroring and rotating that can be applied to an algorithm.
|
8
|
+
class AlgorithmTransformation
|
9
|
+
def initialize(rotation, mirror, mirror_normal_face)
|
10
|
+
@rotation = rotation
|
11
|
+
@mirror = mirror
|
12
|
+
@mirror_normal_face = mirror_normal_face
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
rotation.identity? && !mirror
|
16
|
-
end
|
15
|
+
attr_reader :rotation, :mirror, :mirror_normal_face
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
around_face_rotations.product([true, false]).map do |r, m|
|
23
|
-
AlgorithmTransformation.new(r, m, mirror_normal_face)
|
24
|
-
end
|
25
|
-
end
|
17
|
+
def transformed(algorithm)
|
18
|
+
algorithm = algorithm.mirror(mirror_normal_face) if mirror
|
19
|
+
algorithm.rotate_by(rotation)
|
20
|
+
end
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
def identity?
|
23
|
+
rotation.identity? && !mirror
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns algorithm transformations that mirror an algorithm and rotate it around a face.
|
27
|
+
def self.around_face(face)
|
28
|
+
around_face_rotations = CubeDirection::ALL_DIRECTIONS.map { |d| Rotation.new(face, d) }
|
29
|
+
mirror_normal_face = face.neighbors.first
|
30
|
+
around_face_rotations.product([true, false]).map do |r, m|
|
31
|
+
AlgorithmTransformation.new(r, m, mirror_normal_face)
|
29
32
|
end
|
30
33
|
end
|
34
|
+
|
35
|
+
def self.around_face_without_identity(face)
|
36
|
+
around_face(face).reject(&:identity?)
|
37
|
+
end
|
38
|
+
end
|
31
39
|
end
|
@@ -55,7 +55,9 @@ module TwistyPuzzles
|
|
55
55
|
self
|
56
56
|
else
|
57
57
|
rotation_neighbors = rotation.axis_face.neighbors
|
58
|
-
face_index = rotation_neighbors.index(@axis_face)
|
58
|
+
face_index = rotation_neighbors.index(@axis_face)
|
59
|
+
raise unless face_index
|
60
|
+
|
59
61
|
new_axis_face =
|
60
62
|
rotation_neighbors[(face_index + rotation.direction.value) % rotation_neighbors.length]
|
61
63
|
fields = replace_once(identifying_fields, @axis_face, new_axis_face)
|
@@ -37,7 +37,7 @@ module TwistyPuzzles
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def part_for_colors(part_type, colors)
|
40
|
-
raise ArgumentError unless part_type.is_a?(Class)
|
40
|
+
raise ArgumentError unless part_type.is_a?(Class) && (part_type < Part)
|
41
41
|
|
42
42
|
part_type.for_face_symbols(colors.map { |c| face_symbol(c) })
|
43
43
|
end
|
@@ -95,10 +95,12 @@ module TwistyPuzzles
|
|
95
95
|
|
96
96
|
def chirality_corner_source_and_unknown_index(obvious_turned_face_symbols_to_colors)
|
97
97
|
corner_matcher =
|
98
|
-
CornerMatcher.new(
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
CornerMatcher.new(
|
99
|
+
CHIRALITY_FACE_SYMBOLS.map do |s|
|
100
|
+
# This will return nil for exactly one face that we don't know yet.
|
101
|
+
@colors_to_face_symbols[obvious_turned_face_symbols_to_colors[s]]
|
102
|
+
end
|
103
|
+
)
|
102
104
|
|
103
105
|
# There should be exactly one corner that gets mapped to the chirality corner.
|
104
106
|
chirality_corner_source =
|
@@ -10,6 +10,10 @@ module TwistyPuzzles
|
|
10
10
|
def cancellations(other, cube_size, metric = :htm)
|
11
11
|
algorithm.cancellations(other.algorithm, cube_size, metric)
|
12
12
|
end
|
13
|
+
|
14
|
+
def algorithm
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
13
17
|
end
|
14
18
|
|
15
19
|
# Algorithm that is used like a commutator but actually isn't one.
|
@@ -6,7 +6,6 @@ require 'twisty_puzzles/native'
|
|
6
6
|
|
7
7
|
module TwistyPuzzles
|
8
8
|
# Coordinate of a sticker on the cube.
|
9
|
-
# rubocop:disable Metrics/ClassLength
|
10
9
|
class Coordinate
|
11
10
|
def self.highest_coordinate(cube_size)
|
12
11
|
cube_size - 1
|
@@ -122,23 +121,6 @@ module TwistyPuzzles
|
|
122
121
|
from_indices(face, cube_size, m, m)
|
123
122
|
end
|
124
123
|
|
125
|
-
def self.face(face, cube_size)
|
126
|
-
neighbor_a, neighbor_b = face.neighbors[0..1]
|
127
|
-
coordinate_range(cube_size).collect_concat do |x|
|
128
|
-
coordinate_range(cube_size).map do |y|
|
129
|
-
from_face_distances(face, cube_size, neighbor_a => x, neighbor_b => y)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def self.layer(face, cube_size)
|
135
|
-
face.neighbors.zip(face.neighbors.rotate(1)).collect_concat do |neighbor, next_neighbor|
|
136
|
-
coordinate_range(cube_size).map do |i|
|
137
|
-
from_face_distances(neighbor, cube_size, face => 0, next_neighbor => i)
|
138
|
-
end
|
139
|
-
end + self.face(face, cube_size)
|
140
|
-
end
|
141
|
-
|
142
124
|
def self.edges_outside(face, cube_size)
|
143
125
|
face.neighbors.zip(face.neighbors.rotate(1)).collect_concat do |neighbor, next_neighbor|
|
144
126
|
1.upto(cube_size - 2).map do |i|
|
@@ -279,7 +261,6 @@ module TwistyPuzzles
|
|
279
261
|
rots
|
280
262
|
end
|
281
263
|
end
|
282
|
-
# rubocop:enable Metrics/ClassLength
|
283
264
|
|
284
265
|
# Coordinate of a sticker on the Skewb.
|
285
266
|
class SkewbCoordinate
|
data/lib/twisty_puzzles/cube.rb
CHANGED
@@ -64,10 +64,18 @@ module TwistyPuzzles
|
|
64
64
|
true
|
65
65
|
end
|
66
66
|
|
67
|
+
def num_incarnations(_cube_size)
|
68
|
+
1
|
69
|
+
end
|
70
|
+
|
67
71
|
def base_index_on_face(cube_size, incarnation_index)
|
68
72
|
base_index_on_other_face(solved_face, cube_size, incarnation_index)
|
69
73
|
end
|
70
74
|
|
75
|
+
def base_index_on_other_face(face, cube_size, incarnation_index)
|
76
|
+
raise NotImplementedError
|
77
|
+
end
|
78
|
+
|
71
79
|
def self.for_face_symbols_internal(face_symbols)
|
72
80
|
raise unless face_symbols.length == self::FACES
|
73
81
|
|
@@ -82,6 +90,10 @@ module TwistyPuzzles
|
|
82
90
|
self::ELEMENTS[index]
|
83
91
|
end
|
84
92
|
|
93
|
+
def self.valid?(_face_symbols)
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
85
97
|
def <=>(other)
|
86
98
|
@piece_index <=> other.piece_index
|
87
99
|
end
|
@@ -110,7 +122,7 @@ module TwistyPuzzles
|
|
110
122
|
# Rotate a piece such that the given face symbol is the first face symbol.
|
111
123
|
def rotate_face_symbol_up(face_symbol)
|
112
124
|
index = @face_symbols.index(face_symbol)
|
113
|
-
raise "Part #{self} doesn't have face symbol #{
|
125
|
+
raise "Part #{self} doesn't have face symbol #{face_symbol}." unless index
|
114
126
|
|
115
127
|
rotate_by(index)
|
116
128
|
end
|
@@ -133,10 +145,6 @@ module TwistyPuzzles
|
|
133
145
|
(0...@face_symbols.length).map { |i| rotate_by(i) }
|
134
146
|
end
|
135
147
|
|
136
|
-
def self.create_for_face_symbols(face_symbols)
|
137
|
-
new(face_symbols)
|
138
|
-
end
|
139
|
-
|
140
148
|
def self.parse(piece_description)
|
141
149
|
face_symbols =
|
142
150
|
piece_description.upcase.strip.split('').map do |e|
|
@@ -338,10 +346,6 @@ module TwistyPuzzles
|
|
338
346
|
find_only(self::ELEMENTS) { |e| e.corresponding_part == corresponding_part }
|
339
347
|
end
|
340
348
|
|
341
|
-
def self.create_for_face_symbols(face_symbols)
|
342
|
-
new(self::CORRESPONDING_PART_CLASS.create_for_face_symbols(face_symbols))
|
343
|
-
end
|
344
|
-
|
345
349
|
def face_symbol
|
346
350
|
@face_symbols[0]
|
347
351
|
end
|
@@ -514,7 +518,7 @@ module TwistyPuzzles
|
|
514
518
|
end
|
515
519
|
|
516
520
|
def num_incarnations(cube_size)
|
517
|
-
|
521
|
+
cube_size > 3 ? cube_size / 2 - 1 : 0
|
518
522
|
end
|
519
523
|
|
520
524
|
# One index of such a piece on a on a NxN face.
|
@@ -530,14 +534,6 @@ module TwistyPuzzles
|
|
530
534
|
class Corner < Part
|
531
535
|
FACES = 3
|
532
536
|
|
533
|
-
def self.create_for_face_symbols(face_symbols)
|
534
|
-
piece_candidates =
|
535
|
-
face_symbols[1..].permutation.map do |cs|
|
536
|
-
new([face_symbols[0]] + cs)
|
537
|
-
end
|
538
|
-
find_only(piece_candidates, &:valid?)
|
539
|
-
end
|
540
|
-
|
541
537
|
def self.for_face_symbols(face_symbols)
|
542
538
|
unless face_symbols.length == FACES
|
543
539
|
raise "Invalid number of face_symbols to create a corner: #{face_symbols.inspect}"
|
@@ -621,7 +617,7 @@ module TwistyPuzzles
|
|
621
617
|
ELEMENTS = generate_parts
|
622
618
|
|
623
619
|
def num_incarnations(cube_size)
|
624
|
-
|
620
|
+
cube_size > 3 ? cube_size / 2 - 1 : 0
|
625
621
|
end
|
626
622
|
|
627
623
|
# One index of such a piece on a on a NxN face.
|
@@ -644,10 +640,10 @@ module TwistyPuzzles
|
|
644
640
|
end
|
645
641
|
|
646
642
|
def num_incarnations(cube_size)
|
647
|
-
if cube_size.even?
|
643
|
+
if cube_size.even? || cube_size <= 3
|
648
644
|
0
|
649
645
|
else
|
650
|
-
|
646
|
+
cube_size / 2 - 1
|
651
647
|
end
|
652
648
|
end
|
653
649
|
|
@@ -75,6 +75,16 @@ module TwistyPuzzles
|
|
75
75
|
|
76
76
|
alias == eql?
|
77
77
|
|
78
|
+
def rotation_algorithms
|
79
|
+
Rotation::ALL_ROTATIONS.combination(2).reject do |r0, r1|
|
80
|
+
r0.inverse == r1
|
81
|
+
end.map { |rs| Algorithm.new(rs) } # rubocop:disable Style/MultilineBlockChain
|
82
|
+
end
|
83
|
+
|
84
|
+
def equal_modulo_rotations?(other)
|
85
|
+
rotation_algorithms.any? { |r| r.apply_temporarily_to(self) { |state| state == other } }
|
86
|
+
end
|
87
|
+
|
78
88
|
def hash
|
79
89
|
@hash ||= [self.class, @native].hash
|
80
90
|
end
|
@@ -35,10 +35,12 @@ module TwistyPuzzles
|
|
35
35
|
|
36
36
|
direction = translated_direction(skewb_direction)
|
37
37
|
|
38
|
-
Algorithm.new(
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
Algorithm.new(
|
39
|
+
[
|
40
|
+
Rotation.new(corner.faces[skewb_direction.value], direction),
|
41
|
+
Rotation.new(corner.faces[0], direction)
|
42
|
+
]
|
43
|
+
)
|
42
44
|
end
|
43
45
|
|
44
46
|
def to_s
|
@@ -26,10 +26,12 @@ module TwistyPuzzles
|
|
26
26
|
next unless c2.face_symbols.include?(c1_rot.face_symbols.first)
|
27
27
|
|
28
28
|
c2_rot = c2.rotate_face_symbol_up(c1_rot.face_symbols.first)
|
29
|
-
check_parts.push(
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
check_parts.push(
|
30
|
+
[
|
31
|
+
SkewbCoordinate.for_corner(c1_rot),
|
32
|
+
SkewbCoordinate.for_corner(c2_rot)
|
33
|
+
]
|
34
|
+
)
|
33
35
|
end
|
34
36
|
matching_corners.push(check_parts)
|
35
37
|
end
|