Skip to content

Instantly share code, notes, and snippets.

@dsetzer
Last active February 19, 2026 05:46
Show Gist options
  • Select an option

  • Save dsetzer/50b226332d0efd93f97883e6c58670f5 to your computer and use it in GitHub Desktop.

Select an option

Save dsetzer/50b226332d0efd93f97883e6c58670f5 to your computer and use it in GitHub Desktop.
Prototype for solving numbered cube by converting it's state into a standard rubik's revenge state (kociemba notation)
from enum import Enum
from typing import List, Dict
from cube_state import CubeState
from collections import Counter
import subprocess
class TileType(Enum):
CORNER = 'corner'
EDGE_A = 'edge_a'
EDGE_B = 'edge_b'
CENTER = 'center'
class CubeTile:
TILE_TYPES = {
TileType.CORNER: [1, 4, 13, 16],
TileType.EDGE_A: [2, 8, 9, 15],
TileType.EDGE_B: [3, 5, 12, 14],
TileType.CENTER: [6, 7, 10, 11]
}
def __init__(self, number: int, current_position: int):
self.number = number
self.color = None
self.tile_type = self._detect_tile_type(current_position)
self.current_face = None
self.current_position = current_position
self.expected_face = None
self.expected_position = number
def _detect_tile_type(self, current_position: int) -> TileType:
for tile_type, positions in self.TILE_TYPES.items():
if current_position in positions:
return tile_type
valid_positions = [pos for positions in self.TILE_TYPES.values() for pos in positions]
raise ValueError(f"Invalid face position: {current_position}. Valid positions are: {valid_positions}")
def __repr__(self):
return f"Tile(Label {self.number}, Type {self.tile_type}, Color {self.color}, Face {self.current_face}, Pos {self.current_position}, Solved Face {self.expected_face}, Solved Pos {self.expected_position})"
class CubeFace:
COLOR_MAP = {'U': 'W', 'R': 'R', 'F': 'G', 'D': 'Y', 'L': 'O', 'B': 'B'}
def __init__(self, side: str, numbered_state: List[int]):
self.side = side
self.color = self.COLOR_MAP[side]
self.current_tiles: List[CubeTile] = [None] * 16
self.solved_tiles = []
self._initialize_tiles(numbered_state)
def _initialize_tiles(self, numbered_state: List[int]):
for current_position, number in enumerate(numbered_state, 1):
tile = CubeTile(number, current_position)
self.current_tiles[current_position - 1] = tile
tile.current_face = self.side
if number == current_position:
tile.color = self.color
tile.expected_face = self.side
self.solved_tiles.append(tile)
def reset_colors(self):
self.solved_tiles = [
tile for tile in self.current_tiles
if tile.number == tile.current_position
]
for tile in self.current_tiles:
tile.color = self.color if tile in self.solved_tiles else None
def next_unsolved_number(self):
solved_numbers = {tile.number for tile in self.solved_tiles}
for number in range(1, 17):
if number not in solved_numbers:
return number
def __repr__(self):
return f"Face({self.side}, {self.color})"
class Cube:
KOCIEMBA_ORDER = ['U', 'R', 'F', 'D', 'L', 'B']
FACE_MAP = {'U': 1, 'R': 1, 'F': 2, 'D': 3, 'L': 4, 'B': 5}
COLOR_MAP = {'U': 'W', 'R': 'R', 'F': 'G', 'D': 'Y', 'L': 'O', 'B': 'B'}
def __init__(self, numbered_state: List[List[int]]):
self.faces: Dict[str, CubeFace] = {
side: CubeFace(side, numbered_state[face_index])
for face_index, side in enumerate(self.FACE_MAP.keys())
}
def generate_standard_state(self) -> str:
# Reset tile colors
for face in self.faces.values():
face.reset_colors()
solved_tile_count = sum(len(face.solved_tiles) for face in self.faces.values())
while solved_tile_count < 96:
# Assign colors to the remaining tiles
for face in self.faces.values():
next_unsolved_number = face.next_unsolved_number()
if next_unsolved_number is None:
continue
elif next_unsolved_number <= len(face.current_tiles):
other_tile = self.locate_tile_for_face(face, next_unsolved_number)
if other_tile:
# Check neighboring faces for color consistency
neighboring_faces = self.get_neighboring_faces(face.side)
for neighbor in neighboring_faces:
neighbor_tile = self.locate_tile_for_face(neighbor, next_unsolved_number)
if neighbor_tile and neighbor_tile.color:
other_tile.color = neighbor_tile.color
break
else:
other_tile.color = face.color
other_tile.expected_face = face.side
face.solved_tiles.append(other_tile)
solved_tile_count += 1
print(f"Solved {solved_tile_count} tiles")
# Generate the Kociemba state string
kociemba_state = []
for face in self.KOCIEMBA_ORDER:
solved_tiles = self.faces[face].solved_tiles
solved_tiles.sort(key=lambda x: x.number)
for tile in solved_tiles:
kociemba_state.append(tile.current_face)
return "".join(kociemba_state)
def get_neighboring_faces(self, side: str) -> List[CubeFace]:
neighbors = {
'U': ['F', 'R', 'B', 'L'],
'D': ['F', 'R', 'B', 'L'],
'F': ['U', 'R', 'D', 'L'],
'B': ['U', 'R', 'D', 'L'],
'L': ['U', 'F', 'D', 'B'],
'R': ['U', 'F', 'D', 'B']
}
return [self.faces[neighbor] for neighbor in neighbors[side]]
def locate_tile_for_face(self, face: CubeFace, current_position: int) -> CubeTile:
for other_face in self.faces.values():
for other_tile in other_face.current_tiles:
if other_tile.expected_face is not None:
continue
if other_tile.number == current_position and other_tile not in face.solved_tiles:
return other_tile
def locate_tiles_by_color(self, color: str):
tile_faces = []
for face in self.faces.values():
for tile in face.current_tiles:
if tile.color == color:
tile_faces.append((tile.number, face.side))
return sorted(tile_faces, key=lambda x: x[0])
def print_cube_state(self):
for side in self.KOCIEMBA_ORDER:
face = self.faces[side]
print(f"Face {face.side}:")
for i, tile in enumerate(face.current_tiles, 1):
print(tile.color, end=" ")
if i % 4 == 0:
print()
print()
cubeState = CubeState()
print("\nScrambling the cube...")
cubeState.scramble()
cubeState.debug_info()
# Example usage and testing
example_cube_state = cubeState.get_state()
cube = Cube(example_cube_state)
result = cube.generate_standard_state()
print("\nKociemba state:", result)
# Print color distribution
color_counts = Counter(result)
print("\nColor counts:\n" + "\n".join([f"{color}: {count}" for color, count in color_counts.items()]))
print("\nCube state visualization:")
cube.print_cube_state()
try:
command = f'./rubiks-cube-solver.py --state {result}'
process = subprocess.run(command, shell=True, capture_output=True, text=True)
# Print the output
print("\nCommand Output:")
print(process.stdout)
print("Command Error (if any):")
print(process.stderr)
except Exception as e:
print("Error running command:", e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment