212 lines
6.0 KiB
Python
212 lines
6.0 KiB
Python
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
|