initial commit
This commit is contained in:
205
cube.py
Normal file
205
cube.py
Normal file
@ -0,0 +1,205 @@
|
||||
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("", True),
|
||||
"UR": Edge("UR", True),
|
||||
"UF": Edge("", True),
|
||||
"UL": Edge("UL", True),
|
||||
"DB": Edge("", True),
|
||||
"DF": Edge("", True),
|
||||
}
|
||||
self.centers = {
|
||||
"U": "U",
|
||||
"B": "B",
|
||||
"F": "F",
|
||||
"D": "D",
|
||||
}
|
||||
self.corners = {
|
||||
"UBR": "UBR",
|
||||
"UFR": "UFR",
|
||||
"UFL": "UFL",
|
||||
"UBL": "UBL",
|
||||
}
|
||||
|
||||
def __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()
|
||||
edges_hash = hash_dict(self.edges)
|
||||
centers_hash = hash_dict(self.centers)
|
||||
corners_hash = hash_dict(self.corners)
|
||||
return int(
|
||||
hashlib.md5(
|
||||
(edges_hash + centers_hash + corners_hash).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
|
||||
Reference in New Issue
Block a user