def yellow_corners(state_str): side_faces = ["F", "R", "B", "L"] aim_corners = [{"D", side_faces[i], side_faces[(i + 1) % 4]} for i in range(4)] corners = [] for f in side_faces: n = get_normal(f) m = np.cross(Y, n) pos = -Y + n + m corners.append({ get_color_from_state_str(state_str, pos, -Y), get_color_from_state_str(state_str, pos, n), get_color_from_state_str(state_str, pos, m) }) is_rightly_positioned = [c == cc for c, cc in zip(aim_corners, corners)] if sum(is_rightly_positioned) == 1: i = is_rightly_positioned.index(True) # Belgium # print("One belgium found", i) to_right = aim_corners[(i + 1) % 4] == corners[(i + 3) % 4] # print(f"{to_right=}") moves = BELGIUM if not to_right else flip_left_right(BELGIUM) return rotate_moves_about_y(moves, get_normal(side_faces[(i + to_right) % 4])) # print("Not belgium found") return BELGIUM
def orient_yellow_corners(state_str): normals = (Z, X, -Z, -X) # Look for two consecutive misoriented corners for i, n in enumerate(normals): m = np.cross(Y, n) # Right pos = -Y + n + m c_down = get_color_from_state_str(state_str, pos, -Y) if c_down != "D": # print(f"Corner wrongly oriented {i} - {c_down}") if get_color_from_state_str(state_str, pos - 2 * n, -Y) != "D": # print("Next corner also misoriented") if c_down == get_face_for_normal(m): # print("Moving corners down") return rotate_moves_about_y(FLIPPED_DOUBLE_CHAIR, n) else: # print("Moving corners up") return rotate_moves_about_y(DOUBLE_CHAIR, n) # Else only two diagonal misoriented corners for i, n in enumerate(normals): m = np.cross(Y, n) # Right pos = -Y + n + m c_down = get_color_from_state_str(state_str, pos, -Y) if c_down != "D": # print("Diagonal") moves = FLIPPED_DOUBLE_CHAIR if c_down == get_face_for_normal( m) else DOUBLE_CHAIR return [neg_move(get_face_for_normal(m))] + rotate_moves_about_y( moves, m) + [get_face_for_normal(m)] raise ValueError("No misoriented corner: is cube done?")
def is_yellow_cross_oriented(state_str): for n in (X, -X, Z, -Z): pos = np.array([0, -1, 0]) + n c_down = get_color_from_state_str(state_str, pos, -Y) c_side = get_color_from_state_str(state_str, pos, n) if c_down != "D" or get_face_for_normal(n) != c_side: return False return True
def white_cross(state_str): # Start with 2nd crown edges for x in (-1, 1): for z in (-1, 1): pos = np.array([x, 0, z]) cols = [ get_color_from_state_str(state_str, pos, x * X), get_color_from_state_str(state_str, pos, z * Z) ] if "U" in cols: # print("2nd crown") i = [c != "U" for c in cols].index(True) col = cols[i] n = [x * X, z * Z][i] v = get_normal(col) # print(n, v) u = [x * X, z * Z][1 - i] m1, m3 = _get_up_turn(n, v) m2 = get_face_for_normal(n) if np.cross(n, u)[1] > 0: m2 += "'" # print(m1, m2, m3) return m1 + [m2] + m3 # Then down edges for x, z, n in [(-1, 0, -X), (1, 0, X), (0, -1, -Z), (0, 1, Z)]: pos = np.array([x, -1, z]) c_side = get_color_from_state_str(state_str, pos, n) c_down = get_color_from_state_str(state_str, pos, -Y) if c_down == "U": # print("Down - down") m1, m3 = _get_up_turn(get_normal(c_side), n) m2 = get_face_for_normal(n) + "2" # print(m1, m2, m3) return m1 + [m2] + m3 elif c_side == "U": # print("Down - side") m1, m3 = _get_up_turn(get_normal(c_side), n) m = get_face_for_normal(n) mm = get_face_for_normal(np.cross(n, Y)) m2 = [m, "U", mm, "U'"] return m1 + m2 + m3 # Then up edges (if misplaced) for x, z, n in [(-1, 0, -X), (1, 0, X), (0, -1, -Z), (0, 1, Z)]: pos = np.array([x, 1, z]) # print(pos, get_color_from_state_str(state_str, pos, n)) if get_color_from_state_str(state_str, pos, n) != get_face_for_normal(n): # print("Misplaced", pos, n) return [get_face_for_normal(n)] # Simply get it out raise ValueError("No move found, it cross done?")
def is_second_crown_done(state_str): for x in (-1, 1): for z in (-1, 1): pos = np.array([x, 0, z]) cx = get_color_from_state_str(state_str, pos, x * X) cz = get_color_from_state_str(state_str, pos, z * Z) if cx != get_face_for_normal(x * X) or cz != get_face_for_normal( z * Z): return False return True
def is_white_corners_done(state_str): for x in (-1, 1): for z in (-1, 1): pos = np.array([x, 1, z]) cu = get_color_from_state_str(state_str, pos, Y) cx = get_color_from_state_str(state_str, pos, x * X) cz = get_color_from_state_str(state_str, pos, z * Z) if cu != "U" or cx != get_face_for_normal( x * X) or cz != get_face_for_normal(z * Z): return False return True
def is_white_cross_done(state_str): for x in (-1, 1): pos = np.array([x, 1, 0]) # print(pos, get_color_from_state_str(state_str, pos, Y), get_color_from_state_str(state_str, pos, x * X), get_face_for_normal(x * X)) if get_color_from_state_str(state_str, pos, Y) != "U" or \ get_color_from_state_str(state_str, pos, x * X) != get_face_for_normal(x * X): return False for z in (-1, 1): pos = np.array([0, 1, z]) if get_color_from_state_str(state_str, pos, Y) != "U" or \ get_color_from_state_str(state_str, pos, z * Z) != get_face_for_normal(z * Z): return False return True
def is_yellow_corners_positioned(state_str): side_faces = ["F", "R", "B", "L"] for i, f in enumerate(side_faces): n = get_normal(f) m = np.cross(Y, n) pos = -Y + n + m corner = { get_color_from_state_str(state_str, pos, -Y), get_color_from_state_str(state_str, pos, n), get_color_from_state_str(state_str, pos, m) } aim_corner = {"D", side_faces[i], side_faces[(i + 1) % 4]} if aim_corner != corner: return False return True
def is_yellow_cross_done(state_str): for n in (X, -X, Z, -Z): pos = np.array([0, -1, 0]) + n c_down = get_color_from_state_str(state_str, pos, -Y) if c_down != "D": return False return True
def orient_yellow_cross(state_str): ALL_DIRECTIONS = [X, -Z, -X, Z] # Check if just one color already aligned colors = [ get_color_from_state_str(state_str, -Y + n, n) for n in ALL_DIRECTIONS ] aim_colors = [get_face_for_normal(n) for n in ALL_DIRECTIONS] is_rightly_positioned = [c == cc for c, cc in zip(colors, aim_colors)] if sum(is_rightly_positioned) == 1: # print("One is right") i = is_rightly_positioned.index(True) to_right = colors[(i + 1) % 4] == aim_colors[(i + 3) % 4] moves = CHAIR if not to_right else flip_left_right(CHAIR) # print(to_right) return rotate_moves_about_y(moves, ALL_DIRECTIONS[i]) # Else try to rotate Up for shift in range(4): is_rightly_positioned = [ c == cc for c, cc in zip(rotate_list(colors, shift), aim_colors) ] if sum(is_rightly_positioned) == 1: # print(f"Turn down {shift} times") # Just turn they go back to start of function return ["D"] * shift # Else run CHAIR return CHAIR
def white_corners(state_str): # Down corners for x in (-1, 1): for z in (-1, 1): pos = np.array([x, -1, z]) csx = get_color_from_state_str(state_str, pos, x * X) csz = get_color_from_state_str(state_str, pos, z * Z) c_down = get_color_from_state_str(state_str, pos, -Y) if csx == "U" or csz == "U": # White on side # print("Down - side", csx, csz, c_down) n = x * X if csx == "U" else z * Z v = z * Z if csx == "U" else x * X col = csz if csx == "U" else csx m1, m3 = _get_up_turn(get_normal(col), v) to_right = np.cross(v, n)[1] > 0 m2 = [get_face_for_normal(n), "D"] if to_right: m2[0] += "'" m2[1] += "'" m2.append(neg_move(m2[0])) return m1 + m2 + m3 elif c_down == "U": # White down -> just move it on side csx = get_color_from_state_str(state_str, pos, x * X) csz = get_color_from_state_str(state_str, pos, z * Z) # print("Down - down", csx, csz) m1, m3 = _get_up_turn(get_normal(csx), z * Z) # Rotate around x f = get_face_for_normal(x * X) m2 = [f, "D'", neg_move(f)] if x * z > 0: m2 = [neg_move(m) for m in m2] # print(m1 + m2 + m3) return m1 + m2 + m3 # Up corners -> move it down for x in (-1, 1): for z in (-1, 1): pos = np.array([x, 1, z]) cu = get_color_from_state_str(state_str, pos, Y) csx = get_color_from_state_str(state_str, pos, x * X) csz = get_color_from_state_str(state_str, pos, z * Z) if cu != "U" or csx != get_face_for_normal( x * X) or csz != get_face_for_normal(z * Z): # print("Corner wrongly oriented") n = x * X if csx == "U" else z * Z v = z * Z if csx == "U" else x * X m2 = [ get_face_for_normal(n), "D", neg_move(get_face_for_normal(n)) ] to_right = np.cross(v, n)[1] > 0 if to_right: m2 = [neg_move(m) for m in m2] return m2 raise ValueError("No move found: is white face finished?")
def second_crown(state_str): for x, z, n in [(-1, 0, -X), (1, 0, X), (0, -1, -Z), (0, 1, Z)]: pos = np.array([x, -1, z]) c_side = get_color_from_state_str(state_str, pos, n) c_down = get_color_from_state_str(state_str, pos, -Y) if "D" not in [c_side, c_down]: # Down edges # print("Down edge", c_down, c_side) # Move in front of c_side color theta = angle(n, get_normal(c_side), ignore_axis=1) theta = (np.round(theta / np.pi * 2) * 90) % 360 m1 = [] if theta == 90: m1 = ["D'"] elif theta == 180: m1 = ["D2"] if theta == 270: m1 = ["D"] # is_right if c_down is to its right to_left = np.cross(get_normal(c_side), get_normal(c_down))[1] > 0 moves = BASE_SECOND_CROWN_MOVE if not to_left: # print("to_right") moves = flip_left_right(moves) moves = rotate_moves_about_y(moves, get_normal(c_side)) return m1 + moves for x in (-1, 1): for z in (-1, 1): pos = np.array([x, 0, z]) cs1 = get_color_from_state_str(state_str, pos, x * X) cs2 = get_color_from_state_str(state_str, pos, z * Z) if cs1 != get_face_for_normal(x * X) or cs2 != get_face_for_normal( z * Z): # Wrong place / orientation # print("Edge misplaced", cs1, cs2, x, z) if x * z == -1: rotate_about = x * X else: rotate_about = z * Z moves = rotate_moves_about_y(BASE_SECOND_CROWN_MOVE, rotate_about) return moves raise ValueError("No edge for second crown, is second crown done?")
def test_get_color_from_state_str(): state = "UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB" for x in (-1, 0, 1): for y in (-1, 0, 1): for z in (-1, 0, 1): if x == y == z == 0: continue pos = np.array([x, y, z]) for n in (x * X, y * Y, z * Z): if np.linalg.norm(n) != 0: assert get_color_from_state_str( state, pos, n) == get_face_for_normal(n)
def yellow_cross(state_str): posx = -Y + X posmx = -Y - X posz = -Y + Z posmz = -Y - Z col_x = get_color_from_state_str(state_str, posx, -Y) col_mx = get_color_from_state_str(state_str, posmx, -Y) col_z = get_color_from_state_str(state_str, posz, -Y) col_mz = get_color_from_state_str(state_str, posmz, -Y) if "D" not in [col_x, col_mx, col_z, col_mz]: # print("No yellow") return BASE_YELLOW_CROSS elif col_x == col_mx == "D": # print("Line along X") return rotate_moves_about_y(BASE_YELLOW_CROSS, X) elif col_z == col_mz == "D": # print("Line along Z") return BASE_YELLOW_CROSS else: # print("Yellow corner") corner = (col_x == "D") * X + (col_mx == "D") * (-X) + ( col_z == "D") * Z + (col_mz == "D") * (-Z) n = Rotation.from_rotvec(Y * np.pi * 3 / 8).apply(corner) return rotate_moves_about_y(BASE_YELLOW_CROSS2, -n)
def generate_cubes_from_state_str(state_str, check=False): if check: try: kociemba.solve(state_str) except ValueError as e: raise ValueError(f"Invalid state string {state_str}") base = np.eye(3) cubes = [None] * 26 i = 0 for x in (-1, 0, 1): for y in (-1, 0, 1): for z in (-1, 0, 1): if x == y == z == 0: # Skip center cube continue pos = np.array([x, y, z]) colors = [False] * 6 basis = [None] * 3 for axis in np.where(pos != 0)[0]: color = get_color_from_state_str(state_str, pos, pos[axis] * base[axis]) norm = get_normal(color) basis[axis] = norm * pos[axis] colors[FACE_ORDER.index(color)] = True rot = get_rot_from_basis(basis) original_pos = rot.apply(pos) ox, oy, oz = original_pos id = 9 * (ox + 1) + 3 * (oy + 1) + (oz + 1) index = 9 * (x + 1) + 3 * (y + 1) + (z + 1) if index > 13: index = index - 1 if id > 13: id = id - 1 cube = Cube(initial_rotation=rot.inv(), colors=colors, id=id) cubes[index] = cube i += 1 return np.array(cubes)