Class: TwistyPuzzles::AbstractMove

Inherits:
Object
  • Object
show all
Includes:
Comparable, Utils::ArrayHelper, Utils::StringHelper
Defined in:
lib/twisty_puzzles/abstract_move.rb

Overview

Base class for moves.

Direct Known Subclasses

AxisFaceAndDirectionMove, SkewbMove

Constant Summary collapse

AXES =
%w[y z x].freeze
SLICE_FACES =

rubocop:disable Style/StringHashKeys

{ 'E' => Face::D, 'S' => Face::F, 'M' => Face::L }.freeze
SLICE_NAMES =

rubocop:enable Style/StringHashKeys

SLICE_FACES.invert.freeze
MOVE_METRICS =
%i[qtm htm stm sqtm qstm].freeze

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, #turned_equals?

Methods included from Utils::StringHelper

#camel_case_to_snake_case, #format_time, #simple_class_name, #snake_case_class_name

Class Method Details

.check_move_metric(metric) ⇒ Object

Raises:

  • (ArgumentError)


48
49
50
# File 'lib/twisty_puzzles/abstract_move.rb', line 48

def self.check_move_metric(metric)
  raise ArgumentError, "Invalid move metric #{metric}." unless MOVE_METRICS.include?(metric)
end

Instance Method Details

#<=>(other) ⇒ Object



21
22
23
# File 'lib/twisty_puzzles/abstract_move.rb', line 21

def <=>(other)
  [self.class.name] + identifying_fields <=> [other.class.name] + other.identifying_fields
end

#can_swap?(other) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/twisty_puzzles/abstract_move.rb', line 60

def can_swap?(other)
  is_a?(Rotation) || other.is_a?(Rotation)
end

#decide_meaning(_cube_size) ⇒ Object

We handle the annoying inconsistency that u is a slice move for bigger cubes, but a fat move for 3x3. Furthermore, M slice moves are fat m slice moves for even cubes and normal m slice moves for odd cubes.



137
138
139
# File 'lib/twisty_puzzles/abstract_move.rb', line 137

def decide_meaning(_cube_size)
  self
end

#directionObject

Raises:

  • (NotImplementedError)


99
100
101
# File 'lib/twisty_puzzles/abstract_move.rb', line 99

def direction
  raise NotImplementedError
end

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

Returns:

  • (Boolean)


29
30
31
# File 'lib/twisty_puzzles/abstract_move.rb', line 29

def eql?(other)
  self.class == other.class && identifying_fields == other.identifying_fields
end

#equivalent?(other, cube_size) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/twisty_puzzles/abstract_move.rb', line 52

def equivalent?(other, cube_size)
  decide_meaning(cube_size).equivalent_internal?(other.decide_meaning(cube_size), cube_size)
end

#equivalent_internal?(other, _cube_size) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
# File 'lib/twisty_puzzles/abstract_move.rb', line 56

def equivalent_internal?(other, _cube_size)
  self == other
end

#hashObject



25
26
27
# File 'lib/twisty_puzzles/abstract_move.rb', line 25

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

#identifying_fieldsObject

Raises:

  • (NotImplementedError)


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

def identifying_fields
  raise NotImplementedError
end

#identity?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/twisty_puzzles/abstract_move.rb', line 44

def identity?
  direction.zero?
end

#inverseObject



39
40
41
42
# File 'lib/twisty_puzzles/abstract_move.rb', line 39

def inverse
  fields = replace_once(identifying_fields, direction, direction.inverse)
  self.class.new(*fields)
end

#join_with_cancellation(other, cube_size) ⇒ Object

Return an algorithm from cancelling this move with ‘other` and cancelling as much as possible. Note that it doesn’t cancel rotations with moves even if we theoretically could do this by using uncanonical wide moves. Expects prepend_xyz methods to be present. That one can return a cancelled implementation or nil if nothing can be cancelled.

Raises:

  • (ArgumentError)


123
124
125
126
127
128
129
130
131
132
# File 'lib/twisty_puzzles/abstract_move.rb', line 123

def join_with_cancellation(other, cube_size)
  raise ArgumentError if (puzzles & other.puzzles).empty?

  maybe_alg = prepend_to(other, cube_size)
  if maybe_alg
    Algorithm.new(maybe_alg.moves.select { |m| m.direction.non_zero? })
  else
    Algorithm.new([self, other].select { |m| m.direction.non_zero? })
  end
end

#mirror(_normal_face) ⇒ Object

Raises:

  • (NotImplementedError)


107
108
109
# File 'lib/twisty_puzzles/abstract_move.rb', line 107

def mirror(_normal_face)
  raise NotImplementedError
end

#move_count(cube_size, metric = :htm) ⇒ Object

Cube size is needed to decide whether ‘u’ is a slice move (like on bigger cubes) or a fat move (like on 3x3).

Raises:

  • (TypeError)


84
85
86
87
88
89
90
91
92
93
# File 'lib/twisty_puzzles/abstract_move.rb', line 84

def move_count(cube_size, metric = :htm)
  raise TypeError unless cube_size.is_a?(Integer)

  AbstractMove.check_move_metric(metric)
  return 0 if direction.zero?

  slice_factor = decide_meaning(cube_size).slice_move? ? 2 : 1
  direction_factor = direction.double_move? ? 2 : 1
  move_count_internal(metric, slice_factor, direction_factor)
end

#prepend_inner_m_slice_move(other, cube_size) ⇒ Object

In terms of prepending, inner M slice moves are exactly like other slice moves.



142
143
144
# File 'lib/twisty_puzzles/abstract_move.rb', line 142

def prepend_inner_m_slice_move(other, cube_size)
  prepend_slice_move(other, cube_size)
end

#prepend_slice_move(_other, _cube_size) ⇒ Object

Raises:

  • (NotImplementedError)


146
147
148
# File 'lib/twisty_puzzles/abstract_move.rb', line 146

def prepend_slice_move(_other, _cube_size)
  raise NotImplementedError, "#{self.class}#prepend_slice_move is not implemented"
end

#puzzlesObject

The superclass for all moves that work on the same type puzzle as the given one (modulo cube size, i.e. 3x3 is the same as 4x4, but Skewb is different).

Raises:

  • (NotImplementedError)


113
114
115
# File 'lib/twisty_puzzles/abstract_move.rb', line 113

def puzzles
  raise NotImplementedError
end

#rotate_by(_rotation) ⇒ Object

Raises:

  • (NotImplementedError)


103
104
105
# File 'lib/twisty_puzzles/abstract_move.rb', line 103

def rotate_by(_rotation)
  raise NotImplementedError
end

#slice_move?Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


95
96
97
# File 'lib/twisty_puzzles/abstract_move.rb', line 95

def slice_move?
  raise NotImplementedError, "Not implemented for #{self}:#{self.class}."
end

#swap(other) ⇒ Object

For moves A, B, returns [C, D] if they can be swapped.

Raises:

  • (ArgumentError)


65
66
67
68
69
70
71
72
73
74
75
# File 'lib/twisty_puzzles/abstract_move.rb', line 65

def swap(other)
  raise ArgumentError unless can_swap?(other)

  if is_a?(Rotation)
    [other.rotate_by(inverse), self]
  elsif other.is_a?(Rotation)
    [other, rotate_by(other)]
  else
    swap_internal(other)
  end
end

#swap_internal(other) ⇒ Object

Raises:

  • (NotImplementedError)


77
78
79
80
# File 'lib/twisty_puzzles/abstract_move.rb', line 77

def swap_internal(other)
  raise NotImplementedError,
        "Not implemented for #{self}:#{self.class} and #{other}:#{other.class}."
end