#!/usr/bin/env python3 """ Rubik's Cube logic extracted from the environment for independent testing """ # Define the face colors for visualization UP_COLOR = 'W' # White DOWN_COLOR = 'Y' # Yellow RIGHT_COLOR = 'R' # Red LEFT_COLOR = 'O' # Orange FRONT_COLOR = 'G' # Green BACK_COLOR = 'B' # Blue class Cube: """ A Rubik's cube implementation with accurate move handling. """ def __init__(self): # Initialize a solved cube self.reset() def reset(self): """Reset the cube to solved state""" # Initialize the cube as a 3D array [face][row][col] # Faces: 0=UP, 1=DOWN, 2=LEFT, 3=RIGHT, 4=FRONT, 5=BACK self.cube = [ [[UP_COLOR for _ in range(3)] for _ in range(3)], # UP [[DOWN_COLOR for _ in range(3)] for _ in range(3)], # DOWN [[LEFT_COLOR for _ in range(3)] for _ in range(3)], # LEFT [[RIGHT_COLOR for _ in range(3)] for _ in range(3)], # RIGHT [[FRONT_COLOR for _ in range(3)] for _ in range(3)], # FRONT [[BACK_COLOR for _ in range(3)] for _ in range(3)] # BACK ] def is_solved(self) -> bool: """Check if the cube is solved""" for face in self.cube: center_color = face[1][1] # Center color never changes for row in face: for color in row: if color != center_color: return False return True def count_solved_cubies(self) -> float: """ Count the number of stickers in their correct position Returns a normalized score between 0 and 1 """ # Create a solved reference cube reference = Cube() # Count matching stickers total_stickers = 6 * 9 # 6 faces, 9 stickers per face match_count = 0 for face_idx in range(6): for i in range(3): for j in range(3): if self.cube[face_idx][i][j] == reference.cube[face_idx][i][j]: match_count += 1 return match_count / total_stickers def rotate(self, move: str): """ Perform a move on the cube using standard notation U, D, L, R, F, B are clockwise rotations of respective faces U', D', L', R', F', B' are counterclockwise rotations U2, D2, L2, R2, F2, B2 are double (180°) rotations """ # Map move notation to face index and rotation count face_map = { 'U': 0, 'D': 1, 'L': 2, 'R': 3, 'F': 4, 'B': 5 } # Parse the move if len(move) == 0: raise ValueError("Empty move") face = move[0] if face not in face_map: raise ValueError(f"Invalid face: {face}") face_idx = face_map[face] # Handle rotation direction if len(move) == 1: # Clockwise rotation count = 1 elif len(move) == 2: if move[1] == "'": # Counterclockwise rotation count = 3 elif move[1] == "2": # Double rotation count = 2 else: raise ValueError(f"Invalid move modifier: {move[1]}") else: raise ValueError(f"Invalid move format: {move}") # Apply the rotation 'count' times for _ in range(count): self._rotate_face_clockwise(face_idx) self._rotate_adjacent_faces(face_idx) def _rotate_face_clockwise(self, face_idx: int): """Rotate a face clockwise""" face = self.cube[face_idx] new_face = [[None for _ in range(3)] for _ in range(3)] # Copy with 90-degree clockwise rotation for i in range(3): for j in range(3): new_face[j][2-i] = face[i][j] self.cube[face_idx] = new_face def _rotate_adjacent_faces(self, face_idx: int): """Rotate the appropriate edges on adjacent faces""" if face_idx == 0: # UP face # Rotate the top edges of FRONT, RIGHT, BACK, LEFT temp = self.cube[4][0][:] # Save FRONT top edge self.cube[4][0] = self.cube[2][0][:] # FRONT <- LEFT self.cube[2][0] = self.cube[5][0][:] # LEFT <- BACK self.cube[5][0] = self.cube[3][0][:] # BACK <- RIGHT self.cube[3][0] = temp # RIGHT <- FRONT elif face_idx == 1: # DOWN face # Rotate the bottom edges of FRONT, LEFT, BACK, RIGHT temp = self.cube[4][2][:] # Save FRONT bottom edge self.cube[4][2] = self.cube[3][2][:] # FRONT <- RIGHT self.cube[3][2] = self.cube[5][2][:] # RIGHT <- BACK self.cube[5][2] = self.cube[2][2][:] # BACK <- LEFT self.cube[2][2] = temp # LEFT <- FRONT elif face_idx == 2: # LEFT face # Rotate the left edges of UP, FRONT, DOWN, BACK # Need to extract and set columns, not rows temp = [self.cube[0][i][0] for i in range(3)] # Save UP left column # UP left <- BACK right (reversed) for i in range(3): self.cube[0][i][0] = self.cube[5][2-i][2] # BACK right <- DOWN left (reversed) for i in range(3): self.cube[5][i][2] = self.cube[1][2-i][0] # DOWN left <- FRONT left for i in range(3): self.cube[1][i][0] = self.cube[4][i][0] # FRONT left <- UP left for i in range(3): self.cube[4][i][0] = temp[i] elif face_idx == 3: # RIGHT face # Rotate the right edges of UP, BACK, DOWN, FRONT temp = [self.cube[0][i][2] for i in range(3)] # Save UP right column # UP right <- FRONT right for i in range(3): self.cube[0][i][2] = self.cube[4][i][2] # FRONT right <- DOWN right for i in range(3): self.cube[4][i][2] = self.cube[1][i][2] # DOWN right <- BACK left (reversed) for i in range(3): self.cube[1][i][2] = self.cube[5][2-i][0] # BACK left <- UP right (reversed) for i in range(3): self.cube[5][i][0] = temp[2-i] elif face_idx == 4: # FRONT face # Rotate the edges of UP bottom, RIGHT left, DOWN top, LEFT right # UP bottom row temp = self.cube[0][2][:] # UP bottom <- LEFT right (rotated) for i in range(3): self.cube[0][2][i] = self.cube[2][2-i][2] # LEFT right <- DOWN top (rotated) for i in range(3): self.cube[2][i][2] = self.cube[1][0][i] # DOWN top <- RIGHT left (rotated) for i in range(3): self.cube[1][0][i] = self.cube[3][2-i][0] # RIGHT left <- UP bottom (rotated) for i in range(3): self.cube[3][i][0] = temp[i] elif face_idx == 5: # BACK face # Rotate the edges of UP top, LEFT left, DOWN bottom, RIGHT right # UP top row temp = self.cube[0][0][:] # UP top <- RIGHT right (rotated) for i in range(3): self.cube[0][0][i] = self.cube[3][2-i][2] # RIGHT right <- DOWN bottom (rotated) for i in range(3): self.cube[3][i][2] = self.cube[1][2][i] # DOWN bottom <- LEFT left (rotated) for i in range(3): self.cube[1][2][i] = self.cube[2][2-i][0] # LEFT left <- UP top (rotated) for i in range(3): self.cube[2][i][0] = temp[i] def __str__(self) -> str: """Convert cube to string representation""" face_names = ['U', 'D', 'L', 'R', 'F', 'B'] result = [] for i, face in enumerate(self.cube): result.append(f"{face_names[i]}: {' '.join(face[0])}") result.append(f" {' '.join(face[1])}") result.append(f" {' '.join(face[2])}") return "\n".join(result) def test_basic_moves(): """Test basic moves and their inverses""" print("=== TESTING BASIC MOVES ===") # Test each basic move and its inverse for move, inverse in [ ("R", "R'"), ("L", "L'"), ("U", "U'"), ("D", "D'"), ("F", "F'"), ("B", "B'") ]: cube = Cube() cube.rotate(move) cube.rotate(inverse) solved = cube.is_solved() print(f"Move {move} followed by {inverse}: {'PASS' if solved else 'FAIL'}") if not solved: print("Final cube state:") print(str(cube)) def test_double_moves(): """Test double (180°) moves""" print("\n=== TESTING DOUBLE MOVES ===") # Test each double move applied twice for move in ["U2", "D2", "L2", "R2", "F2", "B2"]: cube = Cube() cube.rotate(move) cube.rotate(move) solved = cube.is_solved() print(f"Double move {move} applied twice: {'PASS' if solved else 'FAIL'}") if not solved: print("Final cube state:") print(str(cube)) def test_complex_algorithms(): """Test more complex algorithms""" print("\n=== TESTING COMPLEX ALGORITHMS ===") # Test algorithms algorithms = [ { "name": "Sexy Move (R U R' U') × 6", "moves": ["R", "U", "R'", "U'"] * 6, "should_solve": True }, { "name": "Scramble + Inverse", "moves": ["R", "U", "F'", "L", "D2"] + ["D2", "L'", "F", "U'", "R'"], "should_solve": True }, { "name": "Sune Algorithm (R U R' U R U2 R')", "moves": ["R", "U", "R'", "U", "R", "U2", "R'"], "should_solve": False } ] for algo in algorithms: cube = Cube() print(f"\nTesting: {algo['name']}") # Apply moves for move in algo["moves"]: cube.rotate(move) # Check result is_solved = cube.is_solved() expected = algo["should_solve"] if is_solved == expected: print(f"Result: PASS (Expected {'solved' if expected else 'not solved'}, Got {'solved' if is_solved else 'not solved'})") else: print(f"Result: FAIL (Expected {'solved' if expected else 'not solved'}, Got {'solved' if is_solved else 'not solved'})") print("Final cube state:") print(str(cube)) # Show progress percentage if not solved if not is_solved: progress = cube.count_solved_cubies() print(f"Progress toward solution: {progress:.2f}") def test_scramble_and_count(): """Test scrambling and counting progress""" print("\n=== TESTING SCRAMBLING AND PROGRESS TRACKING ===") # Create a cube and apply random-like scramble cube = Cube() print("Solved cube:") print(str(cube)) print(f"Is solved: {cube.is_solved()}") print(f"Progress: {cube.count_solved_cubies():.2f}") # Apply a sequence of moves to scramble scramble = ["R", "U", "F", "D", "L", "B'", "R'", "U2"] print(f"\nApplying scramble: {' '.join(scramble)}") for move in scramble: cube.rotate(move) print("Scrambled cube:") print(str(cube)) print(f"Is solved: {cube.is_solved()}") print(f"Progress: {cube.count_solved_cubies():.2f}") if __name__ == "__main__": test_basic_moves() test_double_moves() test_complex_algorithms() test_scramble_and_count()