from dataclasses import dataclass from typing import Dict, List, Tuple import hashlib M_SLICE_EDGES = ["UB", "UF", "DF", "DB"] U_EDGES = ["UB", "UR", "UF", "UL"] CENTERS = ["U", "F", "D", "B"] U_CORNERS = ["UBR", "UFR", "UFL", "UBL"] def cyclic_shift(d: Dict, keys: List, offset: int): if not all(key in d for key in keys): raise ValueError("some keys are missing from the dictionary") values = [d[key] for key in keys] effective_offset = offset % len(keys) shifted_values = values[effective_offset:] + values[:effective_offset] for key, value in zip(keys, shifted_values): d[key] = value @dataclass(unsafe_hash=True) class Edge: name: str oriented: bool class LSECube: def __init__(self): self.reset() def reset(self): # self.edges = { # "UB": Edge("UB", True), # "UR": Edge("UR", True), # "UF": Edge("UF", True), # "UL": Edge("UL", True), # "DB": Edge("DB", True), # "DF": Edge("DF", True), # } self.edges = { "UB": Edge("UB", True), "UR": Edge("UR", True), "UF": Edge("UF", True), "UL": Edge("UL", True), "DB": Edge("DB", True), "DF": Edge("DF", True), } self.centers = { "U": "U", "B": "B", "F": "F", "D": "D", } self.corners = { "UBR": "UBR", "UFR": "UFR", "UFL": "UFL", "UBL": "UBL", } def eolrb_hash(self): # TODO: I fucking hate this language. why can't hashing be simpelr hash_dict = lambda d: hashlib.md5(str(sorted(d.items())).encode()).hexdigest() eolrb_state = {} # mask edges to only care about LR edges and orientation of everything else for location, edge in self.edges.items(): if edge.name in ["UR", "UL"]: eolrb_state[location] = edge else: eolrb_state[location] = Edge("", edge.oriented) eolrb_state["center_oriented"] = self.centers["U"] in ["U", "D"] eolrb_state["UFR"] = self.corners["UFR"] return int( hashlib.md5(hash_dict(eolrb_state).encode()).hexdigest(), 16, ) def __repr__(self): return f"{self.edges=}, {self.centers=}, {self.corners=}" def M(self): cyclic_shift(self.edges, M_SLICE_EDGES, 3) for edge in M_SLICE_EDGES: self.edges[edge].oriented = not self.edges[edge].oriented cyclic_shift(self.centers, CENTERS, 3) def M2(self): cyclic_shift(self.edges, M_SLICE_EDGES, 2) cyclic_shift(self.centers, CENTERS, 2) def Mp(self): cyclic_shift(self.edges, M_SLICE_EDGES, 1) for edge in M_SLICE_EDGES: self.edges[edge].oriented = not self.edges[edge].oriented cyclic_shift(self.centers, CENTERS, 1) def U(self): cyclic_shift(self.edges, U_EDGES, 3) cyclic_shift(self.corners, U_CORNERS, 3) def U2(self): cyclic_shift(self.edges, U_EDGES, 2) cyclic_shift(self.corners, U_CORNERS, 2) def Up(self): cyclic_shift(self.edges, U_EDGES, 1) cyclic_shift(self.corners, U_CORNERS, 1) def cycle_corners(self, n: int): cyclic_shift(self.corners, U_CORNERS, 4 - n) def fix_auf(self) -> str: match self.corners["UFR"]: case "UFR": return "" case "UBR": self.Up() return "U'" case "UBL": self.U2() return "U2" case "UFL": self.U() return "U" return "" def alg(self, alg: str): move_functions = { "U": self.U, "U2": self.U2, "U2'": self.U2, "U'": self.Up, "M": self.M, "M2": self.M2, "M2'": self.M2, "M'": self.Mp, } for move in alg.split(): move_functions[move]() def reverse_algorithm(moves: List[str]) -> List[str]: def invert_move(move): if move.endswith("'"): return move[:-1] elif move.endswith("2"): return move else: return move + "'" reversed_moves = [invert_move(move) for move in reversed(moves)] return reversed_moves def condense_algorithm(moves: List[str]) -> List[str]: def apply_reduction(moves: List[str]) -> List[str]: result = [] for move in moves: base_move = move[0] multiplier = 1 if move.endswith("2"): multiplier = 2 if move.endswith("'"): multiplier = 3 result.extend([base_move] * multiplier) return result def reduce_moves(moves: List[str]) -> List[Tuple[str, int]]: if not moves: return [] condensed = [] current_element = moves[0] count = 1 for element in moves[1:]: if element == current_element: count += 1 else: condensed.append((current_element, count)) current_element = element count = 1 # Append the last element group condensed.append((current_element, count)) return condensed def build_reduced_alg(moves: List[Tuple[str, int]]) -> List[str]: result = [] for move in moves: move_type, count = move count = count % 4 match count: case 1: result.append(move_type) case 2: result.append(move_type + "2") case 3: result.append(move_type + "'") return result reduced_moves = apply_reduction(moves) # print(reduced_moves) counted_moves = reduce_moves(reduced_moves) # print(counted_moves) reduced_alg = build_reduced_alg(counted_moves) # print(reduced_alg) return reduced_alg