Class: TwistyPuzzles::Part

Inherits:
Object
  • Object
show all
Extended by:
CubeConstants, Utils::ArrayHelper
Includes:
Comparable, CubeConstants, Utils::ArrayHelper
Defined in:
lib/twisty_puzzles/cube.rb

Overview

Base class of cube parts. Represents one part or the position of one part on the cube.

Direct Known Subclasses

Corner, Edge, Face, Midge, MoveableCenter, Wing

Constant Summary

Constants included from CubeConstants

CubeConstants::ALPHABET_SIZE, CubeConstants::CHIRALITY_FACE_SYMBOLS, CubeConstants::FACE_NAMES, CubeConstants::FACE_SYMBOLS, CubeConstants::OPPOSITE_FACE_SYMBOLS, CubeConstants::SKEWB_STICKERS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils::ArrayHelper

apply_permutation, check_types, find_only, only, replace_once, rotate_out_nils

Methods included from CubeConstants

chirality_canonical_face_symbol, opposite_face_symbol, valid_chirality?

Constructor Details

#initialize(face_symbols, piece_index) ⇒ Part

Returns a new instance of Part.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/twisty_puzzles/cube.rb', line 16

def initialize(face_symbols, piece_index)
  clazz = self.class
  if face_symbols.any? { |c| c.class != Symbol || !FACE_SYMBOLS.include?(c) }
    raise ArgumentError, "Faces symbols contain invalid item: #{face_symbols.inspect}"
  end

  if face_symbols.length != clazz::FACES
    raise ArgumentError, "Invalid number of face symbols #{face_symbols.length} for " \
                         "#{clazz}. Must be #{clazz::FACES}. Got face symbols: " \
                         "#{face_symbols.inspect}"
  end
  if face_symbols.uniq != face_symbols
    raise ArgumentError, "Non-unique face symbols #{face_symbols} for #{clazz}."
  end

  @face_symbols = face_symbols
  @piece_index = piece_index
end

Instance Attribute Details

#face_symbolsObject (readonly)

Returns the value of attribute face_symbols.



35
36
37
# File 'lib/twisty_puzzles/cube.rb', line 35

def face_symbols
  @face_symbols
end

#piece_indexObject (readonly)

Returns the value of attribute piece_index.



35
36
37
# File 'lib/twisty_puzzles/cube.rb', line 35

def piece_index
  @piece_index
end

Class Method Details

.exists_on_cube_size?(cube_size) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
78
# File 'lib/twisty_puzzles/cube.rb', line 75

def self.exists_on_cube_size?(cube_size)
  cube_size >= min_cube_size && cube_size <= max_cube_size &&
    (cube_size.even? ? exists_on_even_cube_sizes? : exists_on_odd_cube_sizes?)
end

.exists_on_even_cube_sizes?Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/twisty_puzzles/cube.rb', line 67

def self.exists_on_even_cube_sizes?
  true
end

.exists_on_odd_cube_sizes?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/twisty_puzzles/cube.rb', line 71

def self.exists_on_odd_cube_sizes?
  true
end

.for_face_symbols(face_symbols) ⇒ Object



98
99
100
# File 'lib/twisty_puzzles/cube.rb', line 98

def self.for_face_symbols(face_symbols)
  for_face_symbols_internal(face_symbols)
end

.for_face_symbols_internal(face_symbols) ⇒ Object



92
93
94
95
96
# File 'lib/twisty_puzzles/cube.rb', line 92

def self.for_face_symbols_internal(face_symbols)
  raise unless face_symbols.length == self::FACES

  find_only(self::ELEMENTS) { |e| e.face_symbols == face_symbols }
end

.for_index(index) ⇒ Object



102
103
104
# File 'lib/twisty_puzzles/cube.rb', line 102

def self.for_index(index)
  self::ELEMENTS[index]
end

.generate_partsObject



45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/twisty_puzzles/cube.rb', line 45

def self.generate_parts
  valid_face_symbol_combinations =
    FACE_SYMBOLS.permutation(self::FACES).select do |p|
      valid?(p)
    end
  parts = valid_face_symbol_combinations.map.with_index { |p, i| new(p, i) }
  unless parts.length <= ALPHABET_SIZE
    raise "Generated #{parts.length} parts for #{self}, but the alphabet size is only " \
          "#{ALPHABET_SIZE}."
  end

  parts.freeze
end

.max_cube_sizeObject



63
64
65
# File 'lib/twisty_puzzles/cube.rb', line 63

def self.max_cube_size
  Float::INFINITY
end

.max_parseable_face_symbolsObject



41
42
43
# File 'lib/twisty_puzzles/cube.rb', line 41

def self.max_parseable_face_symbols
  self::FACES
end

.min_cube_sizeObject



59
60
61
# File 'lib/twisty_puzzles/cube.rb', line 59

def self.min_cube_size
  2
end

.min_parseable_face_symbolsObject



37
38
39
# File 'lib/twisty_puzzles/cube.rb', line 37

def self.min_parseable_face_symbols
  self::FACES
end

.parse(piece_description) ⇒ Object



172
173
174
175
176
177
178
# File 'lib/twisty_puzzles/cube.rb', line 172

def self.parse(piece_description)
  face_symbols =
    piece_description.upcase.strip.chars.map do |e|
      FACE_SYMBOLS[FACE_NAMES.index(e)]
    end
  for_face_symbols(face_symbols)
end

.valid?(_face_symbols) ⇒ Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/twisty_puzzles/cube.rb', line 106

def self.valid?(_face_symbols)
  false
end

Instance Method Details

#<=>(other) ⇒ Object



110
111
112
# File 'lib/twisty_puzzles/cube.rb', line 110

def <=>(other)
  @piece_index <=> other.piece_index
end

#base_index_on_face(cube_size, incarnation_index) ⇒ Object



84
85
86
# File 'lib/twisty_puzzles/cube.rb', line 84

def base_index_on_face(cube_size, incarnation_index)
  base_index_on_other_face(solved_face, cube_size, incarnation_index)
end

#base_index_on_other_face(face, cube_size, incarnation_index) ⇒ Object

Raises:

  • (NotImplementedError)


88
89
90
# File 'lib/twisty_puzzles/cube.rb', line 88

def base_index_on_other_face(face, cube_size, incarnation_index)
  raise NotImplementedError
end

#corresponding_partObject

Only overridden by moveable centers, but returns self for convenience.



181
182
183
# File 'lib/twisty_puzzles/cube.rb', line 181

def corresponding_part
  self
end

#eql?(other) ⇒ Boolean Also known as: ==

Returns:

  • (Boolean)


114
115
116
# File 'lib/twisty_puzzles/cube.rb', line 114

def eql?(other)
  self.class.equal?(other.class) && @piece_index == other.piece_index
end

#facesObject



194
195
196
# File 'lib/twisty_puzzles/cube.rb', line 194

def faces
  @faces ||= @face_symbols.map { |f| Face.for_face_symbol(f) }
end

#hashObject



120
121
122
# File 'lib/twisty_puzzles/cube.rb', line 120

def hash
  @hash ||= [self.class, @piece_index].hash
end

#inspectObject



124
125
126
# File 'lib/twisty_puzzles/cube.rb', line 124

def inspect
  @inspect ||= "#{self.class.name.split('::').last}(#{@face_symbols.map(&:to_s).join(', ')})"
end

#mirror(normal_face) ⇒ Object



156
157
158
159
160
# File 'lib/twisty_puzzles/cube.rb', line 156

def mirror(normal_face)
  mirrored_face_symbols = corresponding_part.faces.map { |f| f.mirror(normal_face) }
  transformed_face_symbols = [mirrored_face_symbols[0]] + mirrored_face_symbols[1..].reverse
  self.class.for_face_symbols(transformed_face_symbols.map(&:face_symbol))
end

#num_incarnations(_cube_size) ⇒ Object



80
81
82
# File 'lib/twisty_puzzles/cube.rb', line 80

def num_incarnations(_cube_size)
  1
end

#rotate_by(number) ⇒ Object



147
148
149
# File 'lib/twisty_puzzles/cube.rb', line 147

def rotate_by(number)
  self.class.for_face_symbols(@face_symbols.rotate(number))
end

#rotate_by_rotation(rotation) ⇒ Object



151
152
153
154
# File 'lib/twisty_puzzles/cube.rb', line 151

def rotate_by_rotation(rotation)
  rotated_face_symbols = corresponding_part.faces.map { |f| f.rotate_by_rotation(rotation) }
  self.class.for_face_symbols(rotated_face_symbols.map(&:face_symbol))
end

#rotate_face_symbol_up(face_symbol) ⇒ Object

Rotate a piece such that the given face symbol is the first face symbol.



136
137
138
139
140
141
# File 'lib/twisty_puzzles/cube.rb', line 136

def rotate_face_symbol_up(face_symbol)
  index = @face_symbols.index(face_symbol)
  raise "Part #{self} doesn't have face symbol #{face_symbol}." unless index

  rotate_by(index)
end

#rotate_face_up(face) ⇒ Object



143
144
145
# File 'lib/twisty_puzzles/cube.rb', line 143

def rotate_face_up(face)
  rotate_face_symbol_up(face.face_symbol)
end

#rotationsObject



168
169
170
# File 'lib/twisty_puzzles/cube.rb', line 168

def rotations
  (0...@face_symbols.length).map { |i| rotate_by(i) }
end

#solved_coordinate(cube_size, incarnation_index = 0) ⇒ Object



190
191
192
# File 'lib/twisty_puzzles/cube.rb', line 190

def solved_coordinate(cube_size, incarnation_index = 0)
  Coordinate.solved_position(self, cube_size, incarnation_index)
end

#solved_faceObject

The primary face that this piece is in in the solved state.



186
187
188
# File 'lib/twisty_puzzles/cube.rb', line 186

def solved_face
  @solved_face ||= Face.for_face_symbol(@face_symbols.first)
end

#to_sObject



128
129
130
131
132
133
# File 'lib/twisty_puzzles/cube.rb', line 128

def to_s
  corresponding_part.face_symbols.collect.with_index do |c, i|
    face_name = FACE_NAMES[FACE_SYMBOLS.index(c)]
    i < self.class::FACES ? face_name : face_name.downcase
  end.join
end

#turned_equals?(other) ⇒ Boolean

Returns true if the pieces are equal modulo rotation.

Returns:

  • (Boolean)


163
164
165
166
# File 'lib/twisty_puzzles/cube.rb', line 163

def turned_equals?(other)
  @face_symbols.include?(other.face_symbols.first) &&
    rotate_face_symbol_up(other.face_symbols.first) == other
end