Source code for cube_solver.cube.enums

"""Enums module."""
from __future__ import annotations

from enum import Enum, auto
from typing import Union, List, Tuple, Iterator

from .defs import NONE


[docs] class IntEnum(int, Enum): """Integer enumeration.""" __repr__ = Enum.__str__
[docs] class Axis(IntEnum): """Axis enumeration.""" NONE = NONE #: No axis. X = auto() #: `X` axis along :attr:`Cubie.R` and :attr:`Cubie.L` centers. Y = auto() #: `Y` axis along :attr:`Cubie.U` and :attr:`Cubie.D` centers. Z = auto() #: `Z` axis along :attr:`Cubie.F` and :attr:`Cubie.B` centers. DIAG_111 = auto() #: Diagonal axis along :attr:`Cubie.UFR` and :attr:`Cubie.DBL` corners. DIAG_M11 = auto() #: Diagonal axis along :attr:`Cubie.UFL` and :attr:`Cubie.DBR` corners. DIAG_1M1 = auto() #: Diagonal axis along :attr:`Cubie.UBL` and :attr:`Cubie.DFR` corners. DIAG_11M = auto() #: Diagonal axis along :attr:`Cubie.UBR` and :attr:`Cubie.DFL` corners. EDGE_011 = auto() #: Edge axis along :attr:`Cubie.UF` and :attr:`Cubie.DB` edges. EDGE_0M1 = auto() #: Edge axis along :attr:`Cubie.UB` and :attr:`Cubie.DF` edges. EDGE_101 = auto() #: Edge axis along :attr:`Cubie.FR` and :attr:`Cubie.BL` edges. EDGE_M01 = auto() #: Edge axis along :attr:`Cubie.FL` and :attr:`Cubie.BR` edges. EDGE_110 = auto() #: Edge axis along :attr:`Cubie.UR` and :attr:`Cubie.DL` edges. EDGE_M10 = auto() #: Edge axis along :attr:`Cubie.UL` and :attr:`Cubie.DR` edges. @property def is_cartesian(self) -> bool: """Whether this is a cartesian axis.""" return 0 <= self < 3 @property def is_diagonal(self) -> bool: """Whether this is a diagonal axis.""" return 3 <= self < 7 @property def is_edge(self) -> bool: """Whether this is an edge axis.""" return 7 <= self < 13
[docs] @classmethod def axes(cls) -> Iterator[Axis]: """Iterate over valid axes.""" for i in range(13): yield cls(i)
[docs] @classmethod def cartesian_axes(cls) -> Iterator[Axis]: """Iterate over cartesian axes.""" for i in range(3): yield cls(i)
[docs] @classmethod def diagonal_axes(cls) -> Iterator[Axis]: """Iterate over diagonal axes.""" for i in range(3, 7): yield cls(i)
[docs] @classmethod def edge_axes(cls) -> Iterator[Axis]: """Iterate over edge axes.""" for i in range(7, 13): yield cls(i)
[docs] class Orbit(IntEnum): """Orbit enumeration.""" NONE = NONE #: No orbit. SLICE_MIDDLE = auto() #: `Middle` slice orbit. SLICE_EQUATOR = auto() #: `Equator` slice orbit. SLICE_STANDING = auto() #: `Standing` slice orbit. TETRAD_111 = auto() """`Tetrad` orbit containing the :attr:`Cubie.UBL`, :attr:`Cubie.UFR`, :attr:`Cubie.DBR`, and :attr:`Cubie.DFL` corners.""" TETRAD_M11 = auto() """`Tetrad` orbit containing the :attr:`Cubie.UBR`, :attr:`Cubie.UFL`, :attr:`Cubie.DBL`, and :attr:`Cubie.DFR` corners.""" @property def is_slice(self) -> bool: """Whether this is a slice orbit.""" return 0 <= self < 3 @property def is_tetrad(self) -> bool: """Whether this is a tetrad orbit.""" return 3 <= self < 5
[docs] @classmethod def orbits(cls) -> Iterator[Orbit]: """Iterate over valid orbits.""" for i in range(5): yield cls(i)
[docs] @classmethod def slices(cls) -> Iterator[Orbit]: """Iterate over slice orbits.""" for i in range(3): yield cls(i)
[docs] @classmethod def tetrads(cls) -> Iterator[Orbit]: """Iterate over tetrad orbits.""" for i in range(3, 5): yield cls(i)
[docs] class Layer(IntEnum): """Layer enumeration.""" NONE = NONE #: No layer. UP = auto() #: `Up` layer. FRONT = auto() #: `Front` layer. RIGHT = auto() #: `Right` layer. DOWN = auto() #: `Down` layer. BACK = auto() #: `Back` layer. LEFT = auto() #: `Left` layer. MIDDLE = auto() #: `Middle` layer. EQUATOR = auto() #: `Equator` layer. STANDING = auto() #: `Standing` layer. @property def char(self) -> str: """Character representation of the layer.""" return self.name[0] @property def axis(self) -> Axis: """Layer axis.""" return layer_axis[self] @property def perm(self) -> List[List[Cubie]]: """Layer permutation.""" return layer_perm[self] @property def is_outer(self) -> bool: """Whether this is an outer layer.""" return 0 <= self < 6 @property def is_inner(self) -> bool: """Whether this is an inner layer.""" return 6 <= self < 9
[docs] @classmethod def from_char(cls, char: str) -> Layer: """ Return the corresponding :class:`Layer` enum. Parameters ---------- char : {'N', 'U', 'F', 'R', 'D', 'B', 'L', 'M', 'E', 'S'} Character representing the layer. * `'N'` means :attr:`Layer.NONE`. * `'U'` means :attr:`Layer.UP`. * `'F'` means :attr:`Layer.FRONT`. * `'R'` means :attr:`Layer.RIGHT`. * `'D'` means :attr:`Layer.DOWN`. * `'B'` means :attr:`Layer.BACK`. * `'L'` means :attr:`Layer.LEFT`. * `'M'` means :attr:`Layer.MIDDLE`. * `'E'` means :attr:`Layer.EQUATOR`. * `'S'` means :attr:`Layer.STANDING`. Returns ------- layer : Layer :class:`Layer` enum. """ if not isinstance(char, str): raise TypeError(f"char must be str, not {type(char).__name__}") try: return char_layer[char] except KeyError: raise ValueError(f"invalid face character (got '{char}')")
[docs] @classmethod def layers(cls) -> Iterator[Layer]: """Iterate over valid layers.""" for i in range(9): yield cls(i)
[docs] @classmethod def outers(cls) -> Iterator[Layer]: """Iterate over outer layers.""" for i in range(6): yield cls(i)
[docs] @classmethod def inners(cls) -> Iterator[Layer]: """Iterate over inner layers.""" for i in range(6, 9): yield cls(i)
[docs] class Color(IntEnum): """Color enumeration.""" NONE = NONE #: No color. WHITE = auto() #: `White` color. GREEN = auto() #: `Green` color. RED = auto() #: `Red` color. YELLOW = auto() #: `Yellow` color. BLUE = auto() #: `Blue` color. ORANGE = auto() #: `Orange` color. @property def char(self) -> str: """Character representation of the color.""" return self.name[0]
[docs] @classmethod def from_char(cls, char: str) -> Color: """ Return the corresponding :class:`Color` enum. Parameters ---------- char : {'N', 'W', 'G', 'R', 'Y', 'B', 'O'} Character representing the color. * `'N'` means :attr:`Color.NONE`. * `'W'` means :attr:`Color.WHITE`. * `'G'` means :attr:`Color.GREEN`. * `'R'` means :attr:`Color.RED`. * `'Y'` means :attr:`Color.YELLOW`. * `'B'` means :attr:`Color.BLUE`. * `'O'` means :attr:`Color.ORANGE`. Returns ------- color : Color :class:`Color` enum. """ if not isinstance(char, str): raise TypeError(f"char must be str, not {type(char).__name__}") try: return char_color[char] except KeyError: raise ValueError(f"invalid color character (got '{char}')")
[docs] @classmethod def colors(cls) -> Iterator[Color]: """Iterate over valid colors.""" for i in range(6): yield cls(i)
[docs] class Face(IntEnum): """Face enumeration.""" NONE = NONE #: No face. UP = auto() #: `Up` face. FRONT = auto() #: `Front` face. RIGHT = auto() #: `Right` face. DOWN = auto() #: `Down` face. BACK = auto() #: `Back` face. LEFT = auto() #: `Left` face. @property def char(self) -> str: """Character representation of the face.""" return self.name[0] @property def axis(self) -> Axis: """Face axis.""" if self == Face.NONE: return Axis.NONE return Layer[self.name].axis @property def opposite(self) -> Face: """Opposite face.""" return face_opposite[self] @property def _index(self) -> Tuple[Union[int, slice], ...]: """Face index of the color representation array.""" return face_cubie_index[self]
[docs] @classmethod def from_char(cls, char: str) -> Face: """ Return the corresponding :class:`Face` enum. Parameters ---------- char : {'N', 'U', 'F', 'R', 'D', 'B', 'L'} Character representing the face. * `'N'` means :attr:`Face.NONE`. * `'U'` means :attr:`Face.UP`. * `'F'` means :attr:`Face.FRONT`. * `'R'` means :attr:`Face.RIGHT`. * `'D'` means :attr:`Face.DOWN`. * `'B'` means :attr:`Face.BACK`. * `'L'` means :attr:`Face.LEFT`. Returns ------- face : Face :class:`Face` enum. """ if not isinstance(char, str): raise TypeError(f"char must be str, not {type(char).__name__}") try: return char_face[char] except KeyError: raise ValueError(f"invalid face character (got '{char}')")
[docs] @classmethod def faces(cls) -> Iterator[Face]: """Iterate over valid faces.""" for i in range(6): yield cls(i)
[docs] class Cubie(IntEnum): """Cubie enumeration.""" NONE = NONE #: No cubie. # corners UBL = auto() #: `Up-Back-Left` corner. UFR = auto() #: `Up-Front-Right` corner. DBR = auto() #: `Down-Back-Right` corner. DFL = auto() #: `Down-Front-Left` corner. UBR = auto() #: `Up-Back-Right` corner. UFL = auto() #: `Up-Front-Left` corner. DBL = auto() #: `Down-Back-Left` corner. DFR = auto() #: `Down-Front-Right` corner. # edges UB = auto() #: `Up-Back` edge. UF = auto() #: `Up-Front` edge. DB = auto() #: `Down-Back` edge. DF = auto() #: `Down-Front` edge. UL = auto() #: `Up-Left` edge. UR = auto() #: `Up-Right` edge. DL = auto() #: `Down-Left` edge. DR = auto() #: `Down-Right` edge. BL = auto() #: `Back-Left` edge. BR = auto() #: `Back-Right` edge. FL = auto() #: `Front-Left` edge. FR = auto() #: `Front-Right` edge. # centers U = auto() #: `Up` center. F = auto() #: `Front` center. R = auto() #: `Right` center. D = auto() #: `Down` center. B = auto() #: `Back` center. L = auto() #: `Left` center. # core CORE = auto() #: `Core`. @property def axis(self) -> Axis: """Cubie axis.""" if self.is_center: return Layer.from_char(self.name).axis return cubie_axis[self] @property def orbit(self) -> Orbit: """ Cubie orbit. * :attr:`Orbit.NONE`: :attr:`Cubie.NONE`, :attr:`Cubie.CORE`, :attr:`Cubie.U`, :attr:`Cubie.F`, :attr:`Cubie.R`, :attr:`Cubie.D`, :attr:`Cubie.B`, :attr:`Cubie.L` * :attr:`Orbit.SLICE_MIDDLE`: :attr:`Cubie.UB`, :attr:`Cubie.UF`, :attr:`Cubie.DB`, :attr:`Cubie.DF` * :attr:`Orbit.SLICE_EQUATOR`: :attr:`Cubie.BL`, :attr:`Cubie.BR`, :attr:`Cubie.FL`, :attr:`Cubie.FR` * :attr:`Orbit.SLICE_STANDING`: :attr:`Cubie.UL`, :attr:`Cubie.UR`, :attr:`Cubie.DL`, :attr:`Cubie.DR` * :attr:`Orbit.TETRAD_111`: :attr:`Cubie.UBL`, :attr:`Cubie.UFR`, :attr:`Cubie.DBR`, :attr:`Cubie.DFL` * :attr:`Orbit.TETRAD_M11`: :attr:`Cubie.UBR`, :attr:`Cubie.UFL`, :attr:`Cubie.DBL`, :attr:`Cubie.DFR` """ if self.is_center: return Orbit.NONE return cubie_orbit[self] @property def faces(self) -> List[Face]: """Cubie visible faces.""" if self == Cubie.NONE: return [Face.NONE] if self == Cubie.CORE: return [] faces = [Face.from_char(char) for char in self.name] if self.orbit == Orbit.TETRAD_111: faces[1:] = faces[2], faces[1] return faces @property def _index(self) -> Tuple[int, ...]: """Cubie index of the color representation array.""" return cubie_index[self] @property def is_corner(self) -> bool: """Whether the cubie is a corner.""" return 0 <= self < 8 @property def is_edge(self) -> bool: """Whether the cubie is an edge.""" return 8 <= self < 20 @property def is_center(self) -> bool: """Whether the cubie is a center.""" return 20 <= self < 26
[docs] @classmethod def from_faces(cls, faces: List[Face]) -> Cubie: """ Return the corresponding :class:`Cubie` enum. Parameters ---------- faces : list of Face Visible faces of the cubie. If `faces` represent a corner, they must be provided in clockwise order to verify that it's a valid corner. Returns ------- cubie : Cubie :class:`Cubie` enum. """ if not isinstance(faces, list): raise TypeError(f"faces must be list, not {type(faces).__name__}") if len(faces) > 3: raise ValueError(f"faces length must be at most 3 (got {len(faces)})") min_faces = [] for i, face in enumerate(faces): if not isinstance(face, Face): raise TypeError(f"faces elements must be Face, not {type(faces[0]).__name__}") min_faces.append(faces[i:] + faces[:i]) if min_faces: min_faces = min(min_faces) try: return faces_cubie[tuple(min_faces)] except KeyError: raise ValueError(f"invalid cubie faces (got {faces})")
[docs] @classmethod def cubies(cls) -> Iterator[Cubie]: """Iterate over valud cubies.""" for i in range(27): yield cls(i)
[docs] @classmethod def corners(cls) -> Iterator[Cubie]: """Iterate over corner cubies.""" for i in range(8): yield cls(i)
[docs] @classmethod def edges(cls) -> Iterator[Cubie]: """Iterate over edge cubies.""" for i in range(8, 20): yield cls(i)
[docs] @classmethod def centers(cls) -> Iterator[Cubie]: """Iterate over center cubies.""" for i in range(20, 26): yield cls(i)
[docs] class Move(IntEnum): """Move enumeration.""" NONE = NONE #: No move. # face moves U1 = auto() #: `U` face move. U2 = auto() #: `U2` face move. U3 = auto() #: `U'` face move. F1 = auto() #: `F` face move. F2 = auto() #: `F2` face move. F3 = auto() #: `F'` face move. R1 = auto() #: `R` face move. R2 = auto() #: `R2` face move. R3 = auto() #: `R'` face move. D1 = auto() #: `D` face move. D2 = auto() #: `D2` face move. D3 = auto() #: `D'` face move. B1 = auto() #: `B` face move. B2 = auto() #: `B2` face move. B3 = auto() #: `B'` face move. L1 = auto() #: `L` face move. L2 = auto() #: `L2` face move. L3 = auto() #: `L'` face move. # slice moves M1 = auto() #: `M` slice move. M2 = auto() #: `M2` slice move. M3 = auto() #: `M'` slice move. E1 = auto() #: `E` slice move. E2 = auto() #: `E2` slice move. E3 = auto() #: `E'` slice move. S1 = auto() #: `S` slice move. S2 = auto() #: `S2` slice move. S3 = auto() #: `S'` slice move. # wide moves UW1 = auto() #: `Uw` or `u` wide move. UW2 = auto() #: `Uw2` or `u2` wide move. UW3 = auto() #: `Uw'` or `u'` wide move. FW1 = auto() #: `Fw` or `f` wide move. FW2 = auto() #: `Fw2` or `f2` wide move. FW3 = auto() #: `Fw'` or `f'` wide move. RW1 = auto() #: `Rw` or `r` wide move. RW2 = auto() #: `Rw2` or `r2` wide move. RW3 = auto() #: `Rw'` or `r'` wide move. DW1 = auto() #: `Dw` or `d` wide move. DW2 = auto() #: `Dw2` or `d2` wide move. DW3 = auto() #: `Dw'` or `d'` wide move. BW1 = auto() #: `Bw` or `b` wide move. BW2 = auto() #: `Bw2` or `b2` wide move. BW3 = auto() #: `Bw'` or `b'` wide move. LW1 = auto() #: `Lw` or `l` wide move. LW2 = auto() #: `Lw2` or `l2` wide move. LW3 = auto() #: `Lw'` or `l'` wide move. # cube rotations X1 = auto() #: `x` rotation. X2 = auto() #: `x2` rotation. X3 = auto() #: `x'` rotation. Y1 = auto() #: `y` rotation. Y2 = auto() #: `y2` rotation. Y3 = auto() #: `y'` rotation. Z1 = auto() #: `z` rotation. Z2 = auto() #: `z2` rotation. Z3 = auto() #: `z'` rotation. @property def string(self) -> str: """String representation of the move.""" if self == Move.NONE: return "" str = self.name if str[0] in "XYZ": str = str[0].lower() + str[1:] elif str[1] == "W": str = self.name[0] + "w" + self.name[2:] if str[-1] == "1": str = str[:-1] elif str[-1] == "3": str = str[:-1] + "'" return str @property def axis(self) -> Axis: """Move axis.""" if self == Move.NONE: return Axis.NONE if self.is_rotation: return Axis[self.name[0]] return Layer.from_char(self.name[0]).axis @property def inverse(self) -> Move: """Inverse move.""" if self == Move.NONE: return Move.NONE return Move[self.name[:-1] + str(-int(self.name[-1]) % 4)] @property def layers(self) -> List[Layer]: """Move layers.""" if self == Move.NONE: return [Layer.NONE] if self.is_rotation: return [layer for layer in layer_order if layer.axis == self.axis] layers = [Layer.from_char(self.name[0])] if self.is_wide: layers += [layer for layer in Layer.inners() if layer.axis == self.axis] return layers @property def shifts(self) -> List[int]: """Permutation shift for each layer.""" if self == Move.NONE: return [0] shift = int(self.name[-1]) if self.name[-1] != "3" else -1 if self.is_rotation: return [shift, -shift, shift if self.axis == Axis.Z else -shift] shifts = [shift] if self.is_wide: if self.axis == Axis.Z: shift = -shift shifts += [-shift if Move[self.axis.name + "1"].layers[0].char == self.name[0] else shift] return shifts @property def is_face(self) -> bool: """Whether this is a face move.""" return 0 <= self < 18 @property def is_slice(self) -> bool: """Whether this is a slice move.""" return 18 <= self < 27 @property def is_wide(self) -> bool: """Whether this is a wide move.""" return 27 <= self < 45 @property def is_rotation(self) -> bool: """Whether the move is a rotation.""" return 45 <= self < 54
[docs] @classmethod def from_string(cls, string: str) -> Move: """ Return the corresponding :class:`Move` enum. Parameters ---------- string : str String representation of the move. Returns ------- move : Move :class:`Move` enum. """ if not isinstance(string, str): raise TypeError(f"string must be str, not {type(string).__name__}") try: return str_move[string] except KeyError: raise ValueError(f"invalid move string (got '{string}')")
[docs] @classmethod def moves(cls) -> Iterator[Move]: """Iterate over valid moves.""" for i in range(54): yield cls(i)
[docs] @classmethod def face_moves(cls) -> Iterator[Move]: """Iterate over face moves.""" for i in range(18): yield cls(i)
[docs] @classmethod def slice_moves(cls) -> Iterator[Move]: """Iterate over slice moves.""" for i in range(18, 27): yield cls(i)
[docs] @classmethod def wide_moves(cls) -> Iterator[Move]: """Iterate over wide moves.""" for i in range(27, 45): yield cls(i)
[docs] @classmethod def rotations(cls) -> Iterator[Move]: """Iterate over rotations.""" for i in range(45, 54): yield cls(i)
layer_axis = { Layer.NONE: Axis.NONE, Layer.UP: Axis.Y, Layer.FRONT: Axis.Z, Layer.RIGHT: Axis.X, Layer.DOWN: Axis.Y, Layer.BACK: Axis.Z, Layer.LEFT: Axis.X, Layer.MIDDLE: Axis.X, Layer.EQUATOR: Axis.Y, Layer.STANDING: Axis.Z } layer_perm = { Layer.NONE: [[Cubie.NONE]], Layer.UP: [[Cubie.UBL, Cubie.UBR, Cubie.UFR, Cubie.UFL], [Cubie.UB, Cubie.UR, Cubie.UF, Cubie.UL]], Layer.FRONT: [[Cubie.UFR, Cubie.DFR, Cubie.DFL, Cubie.UFL], [Cubie.UF, Cubie.FR, Cubie.DF, Cubie.FL]], Layer.RIGHT: [[Cubie.UFR, Cubie.UBR, Cubie.DBR, Cubie.DFR], [Cubie.UR, Cubie.BR, Cubie.DR, Cubie.FR]], Layer.DOWN: [[Cubie.DBR, Cubie.DBL, Cubie.DFL, Cubie.DFR], [Cubie.DB, Cubie.DL, Cubie.DF, Cubie.DR]], Layer.BACK: [[Cubie.UBL, Cubie.DBL, Cubie.DBR, Cubie.UBR], [Cubie.UB, Cubie.BL, Cubie.DB, Cubie.BR]], Layer.LEFT: [[Cubie.UBL, Cubie.UFL, Cubie.DFL, Cubie.DBL], [Cubie.UL, Cubie.FL, Cubie.DL, Cubie.BL]], Layer.MIDDLE: [[Cubie.U, Cubie.F, Cubie.D, Cubie.B], [Cubie.UB, Cubie.UF, Cubie.DF, Cubie.DB]], Layer.EQUATOR: [[Cubie.F, Cubie.R, Cubie.B, Cubie.L], [Cubie.BL, Cubie.FL, Cubie.FR, Cubie.BR]], Layer.STANDING: [[Cubie.U, Cubie.R, Cubie.D, Cubie.L], [Cubie.UL, Cubie.UR, Cubie.DR, Cubie.DL]] } face_opposite = { Face.NONE: Face.NONE, Face.UP: Face.DOWN, Face.FRONT: Face.BACK, Face.RIGHT: Face.LEFT, Face.DOWN: Face.UP, Face.BACK: Face.FRONT, Face.LEFT: Face.RIGHT } face_cubie_index = { Face.NONE: (slice(None), slice(None), slice(None)), Face.UP: (0, slice(None), slice(None)), Face.FRONT: (slice(None), 2, slice(None)), Face.RIGHT: (slice(None), slice(None, None, -1), 2), Face.DOWN: (2, slice(None, None, -1), slice(None)), Face.BACK: (slice(None), 0, slice(None, None, -1)), Face.LEFT: (slice(None), slice(None), 0) } cubie_axis = { Cubie.NONE: Axis.NONE, # corners Cubie.UBL: Axis.DIAG_1M1, Cubie.UFR: Axis.DIAG_111, Cubie.DBR: Axis.DIAG_M11, Cubie.DFL: Axis.DIAG_11M, Cubie.UBR: Axis.DIAG_11M, Cubie.UFL: Axis.DIAG_M11, Cubie.DBL: Axis.DIAG_111, Cubie.DFR: Axis.DIAG_1M1, # edges Cubie.UB: Axis.EDGE_0M1, Cubie.UF: Axis.EDGE_011, Cubie.DB: Axis.EDGE_011, Cubie.DF: Axis.EDGE_0M1, Cubie.UL: Axis.EDGE_M10, Cubie.UR: Axis.EDGE_110, Cubie.DL: Axis.EDGE_110, Cubie.DR: Axis.EDGE_M10, Cubie.BL: Axis.EDGE_101, Cubie.BR: Axis.EDGE_M01, Cubie.FL: Axis.EDGE_M01, Cubie.FR: Axis.EDGE_101, # core Cubie.CORE: Axis.NONE } cubie_orbit = { Cubie.NONE: Orbit.NONE, # corners Cubie.UBL: Orbit.TETRAD_111, Cubie.UFR: Orbit.TETRAD_111, Cubie.DBR: Orbit.TETRAD_111, Cubie.DFL: Orbit.TETRAD_111, Cubie.UBR: Orbit.TETRAD_M11, Cubie.UFL: Orbit.TETRAD_M11, Cubie.DBL: Orbit.TETRAD_M11, Cubie.DFR: Orbit.TETRAD_M11, # edges Cubie.UB: Orbit.SLICE_MIDDLE, Cubie.UF: Orbit.SLICE_MIDDLE, Cubie.DB: Orbit.SLICE_MIDDLE, Cubie.DF: Orbit.SLICE_MIDDLE, Cubie.UL: Orbit.SLICE_STANDING, Cubie.UR: Orbit.SLICE_STANDING, Cubie.DL: Orbit.SLICE_STANDING, Cubie.DR: Orbit.SLICE_STANDING, Cubie.BL: Orbit.SLICE_EQUATOR, Cubie.BR: Orbit.SLICE_EQUATOR, Cubie.FL: Orbit.SLICE_EQUATOR, Cubie.FR: Orbit.SLICE_EQUATOR, # core Cubie.CORE: Orbit.NONE } cubie_index = { Cubie.NONE: (1, 1, 1), # corners Cubie.UBL: (0, 0, 0), Cubie.UFR: (0, 2, 2), Cubie.DBR: (2, 0, 2), Cubie.DFL: (2, 2, 0), Cubie.UBR: (0, 0, 2), Cubie.UFL: (0, 2, 0), Cubie.DBL: (2, 0, 0), Cubie.DFR: (2, 2, 2), # edges Cubie.UB: (0, 0, 1), Cubie.UF: (0, 2, 1), Cubie.DB: (2, 0, 1), Cubie.DF: (2, 2, 1), Cubie.UL: (0, 1, 0), Cubie.UR: (0, 1, 2), Cubie.DL: (2, 1, 0), Cubie.DR: (2, 1, 2), Cubie.BL: (1, 0, 0), Cubie.BR: (1, 0, 2), Cubie.FL: (1, 2, 0), Cubie.FR: (1, 2, 2), # centers Cubie.U: (0, 1, 1), Cubie.F: (1, 2, 1), Cubie.R: (1, 1, 2), Cubie.D: (2, 1, 1), Cubie.B: (1, 0, 1), Cubie.L: (1, 1, 0), # core Cubie.CORE: (1, 1, 1) } char_layer = {layer.char: layer for layer in Layer} char_color = {color.char: color for color in Color} char_face = {face.char: face for face in Face} faces_cubie = {tuple(min([cb.faces[i:] + cb.faces[:i] for i in range(len(cb.faces))])if cb.faces else []): cb for cb in Cubie} layer_order = [Layer.UP, Layer.FRONT, Layer.RIGHT, Layer.DOWN, Layer.BACK, Layer.LEFT] + [*Layer.inners()] str_move = {move.string: move for move in Move} str_move.update({move.string[0].lower() + move.string[2:]: move for move in Move.wide_moves()})