Nxnxn Rubik 39scube Algorithm Github Python Patched

Patch code excerpt (from their commit history):

# Before (memory-heavy)
self.state = [[[0 for _ in range(N)] for _ in range(N)] for _ in range(6)]
# Original had: step_size = 360 // (N-1)  # N=3 => division by 2? Actually fine, but N=1 broke.
# Patched:
if N <= 1:
    raise ValueError("N must be >= 2")
step_size = 360 // max(1, (N-2))
from rubikscubennnsolver.RubiksCubeNNNEven import RubiksCubeNNNEven
from rubikscubennnsolver.RubiksCubeNNNOdd import RubiksCubeNNNOdd

cube = RubiksCubeNNNEven(4, 'URFDLB') # color orientation cube.randomize() cube.solve() print(cube.solution) nxnxn rubik 39scube algorithm github python patched

If you’ve ever tried to move beyond the standard 3x3 to a 4x4 "Revenge" or a daunting 5x5 "Professor’s Cube," you know the complexity doesn't just scale linearly—it explodes. While the standard 3x3 has roughly 43 quintillion states, larger cubes quickly surpass the computational capacity of brute-force solvers. Patch code excerpt (from their commit history): #

I recently dove into a GitHub repository that implements a generalized NxNxn solver in Python, utilizing a patched version of the Two-Phase Algorithm (often based on the Kociemba method). Here is a breakdown of how the algorithm works and how the implementation handles the "patched" logic for variable cube sizes. from rubikscubennnsolver

import numpy as np
from copy import deepcopy

class RubikNNN: """ NxNxN Rubik's Cube simulator with patched slice move handling. Fixes: correct middle slice indexing for even N, proper wide move generation, piece orientation tracking. """

def __init__(self, N):
    self.N = N
    self.state = self._init_state()
    self.move_history = []
def _init_state(self):
    """Create solved cube state: 6 faces, each NxN array of colors."""
    colors = ['U', 'R', 'F', 'D', 'L', 'B']
    faces = face: np.full((self.N, self.N), color) for face, color in zip('URFDLB', colors)
    return faces
def _rotate_face_clockwise(self, face):
    """Rotate a single face 90° clockwise."""
    self.state[face] = np.rot90(self.state[face], k=-1)
def _rotate_face_counterclockwise(self, face):
    self.state[face] = np.rot90(self.state[face], k=1)
def _slice_move(self, layer, face, direction, wide=False):
    """
    Patched slice move: layer 0 = outermost, layer N-1 = innermost.
    wide=True means move all layers from 0 to `layer`.
    """
    layers = range(layer + 1) if wide else [layer]
    for l in layers:
        self._single_layer_move(l, face, direction)
def _single_layer_move(self, layer, face, direction):
    """
    Perform a move on a single layer (affects adjacent faces).
    face: 'U', 'R', 'F', 'D', 'L', 'B'
    direction: +1 for clockwise (as seen facing the face), -1 for CCW.
    """
    N = self.N
    # Map face to adjacent face rings
    if face == 'U':
        # Up face: affects F, R, B, L at row = layer (from top)
        row = layer
        temp = self.state['F'][row, :].copy()
        if direction == 1:
            self.state['F'][row, :] = self.state['R'][row, :]
            self.state['R'][row, :] = self.state['B'][row, :]
            self.state['B'][row, :] = self.state['L'][row, :]
            self.state['L'][row, :] = temp
        else:
            self.state['F'][row, :] = self.state['L'][row, :]
            self.state['L'][row, :] = self.state['B'][row, :]
            self.state['B'][row, :] = self.state['R'][row, :]
            self.state['R'][row, :] = temp
        if layer == 0:
            self._rotate_face_clockwise('U') if direction == 1 else self._rotate_face_counterclockwise('U')
elif face == 'D':
        row = N - 1 - layer
        temp = self.state['F'][row, :].copy()
        if direction == 1:
            self.state['F'][row, :] = self.state['L'][row, :]
            self.state['L'][row, :] = self.state['B'][row, :]
            self.state['B'][row, :] = self.state['R'][row, :]
            self.state['R'][row, :] = temp
        else:
            self.state['F'][row, :] = self.state['R'][row, :]
            self.state['R'][row, :] = self.state['B'][row, :]
            self.state['B'][row, :] = self.state['L'][row, :]
            self.state['L'][row, :] = temp
        if layer == 0:
            self._rotate_face_clockwise('D') if direction == 1 else self._rotate_face_counterclockwise('D')
# Similar for F, R, B, L... (omitted here for brevity, but full version available)
    # [Full code would handle all 6 faces with proper column/row indexing]
def move(self, move_str):
    """
    Parse and execute a move string like "U", "U'", "U2", "2U", "Uw", "3Rw'".
    """
    # Simplified parser: assumes format [layer][face][w][']
    layer = 0
    wide = False
    i = 0
    # Extract layer number
    while i < len(move_str) and move_str[i].isdigit():
        i += 1
    if i > 0:
        layer = int(move_str[:i]) - 1
    # Extract face
    face = move_str[i]
    i += 1
    # Check for 'w' (wide move)
    if i < len(move_str) and move_str[i] == 'w':
        wide = True
        i += 1
    # Check for modifier
    modifier = move_str[i:] if i < len(move_str) else ''
    turns = 1
    if modifier == "'":
        turns = -1
    elif modifier == '2':
        turns = 2
for _ in range(abs(turns)):
        self._slice_move(layer, face, 1 if turns > 0 else -1, wide)
def get_piece(self, piece_type, position):
    """
    Returns a dictionary representing a cube piece.
    piece_type: 'corner', 'edge', 'center'
    position: tuple of (face1, face2, face3) for corners, etc.
    """
    # Simplified: returns colors at given position
    colors = []
    for face in position:
        colors.append(self.state[face][0, 0])  # placeholder logic
    return "type": piece_type, "colors": colors, "position": position
def __str__(self):
    """Print cube state (simplified)."""
    out = ""
    for face in "URFDLB":
        out += f"face:\nself.state[face]\n"
    return out