""" Este es el Solucionador de Cubos Esta versión contiene una interfaz gráfica Última edición el: 05/12/2014 Escrito originalmente por: - Lucas Liberacki (https://github.com/CubeLuke) - & Tom Brannan (https://github.com/TomBrannan) Modificado por manuelver """ from random import randint from tkinter import * import copy import webbrowser import os import tkinter as tk # Variables globales moves_list = [] last_scramble = [] f2l_list = [] step_moves_list = [] solution_length = 0 # creates a 3d list representing a solved cube def make_cube(): global step_moves_list, f2l_list, moves_list step_moves_list = [0, 0, 0, 0] f2l_list = [] moves_list = [] return [[ ['W', 'W', 'W'], ['W', 'W', 'W'], ['W', 'W', 'W']], # Arriba/blanco [['G', 'G', 'G'], ['G', 'G', 'G'], ['G', 'G', 'G']], # Frontal/verde [['R', 'R', 'R'], ['R', 'R', 'R'], ['R', 'R', 'R']], # Derecha/rojo [['O', 'O', 'O'], ['O', 'O', 'O'], ['O', 'O', 'O']], # Izquierda/naranja [['Y', 'Y', 'Y'], ['Y', 'Y', 'Y'], ['Y', 'Y', 'Y']], # Abajo/amarillo [['B', 'B', 'B'], ['B', 'B', 'B'], ['B', 'B', 'B'] # Trasero/azul ]] a = make_cube() # Imprime una representación en cadena del cubo en el intérprete def print_cube(): print('\t\t'+str(a[5][0])+'\n\t\t'+str(a[5][1])+'\n\t\t'+str(a[5][2])) print(str(a[3][0])+' '+str(a[0][0])+' '+str(a[2][0])) print(str(a[3][1])+' '+str(a[0][1])+' '+str(a[2][1])) print(str(a[3][2])+' '+str(a[0][2])+' '+str(a[2][2])) print('\t\t'+str(a[1][0])+'\n\t\t'+str(a[1][1])+'\n\t\t'+str(a[1][2])) print('\t\t'+str(a[4][0])+'\n\t\t'+str(a[4][1])+'\n\t\t'+str(a[4][2])) # Simplifica la lista de movimientos y devuelve una representación en cadena de # los movimientos def get_moves(): simplify_moves() s = "" for i in moves_list: s += str(i) + " " s = str.replace(s, "i", "'")[:-1] return s # Devuelve una representación en cadena del último scramble def get_scramble(): s = "" for i in last_scramble: s += str(i) + " " s = str.replace(s, "i", "'")[:-1] return s # Función auxiliar: # devuelve True si todos los elementos en un conjunto son iguales def all_same(items): return all(x == items[0] for x in items) # Transforma un movimiento dado en el movimiento correspondiente después de una # rotación Y def yTransform(move): if move[0] in ["U", "D"]: return move if move[0] == "F": return "R" + move[1:] if move[0] == "R": return "B" + move[1:] if move[0] == "B": return "L" + move[1:] if move[0] == "L": return "F" + move[1:] raise Exception("Invalid move to yTransform: " + move) # Modifica la lista global de movimientos eliminando redundancias def simplify_moves(): global moves_list, solution_length new_list = [] prev_move = "" yCount = 0 for move in moves_list: if move == "Y": yCount += 1 yCount %= 4 continue if move == "Yi": yCount += 3 yCount %= 4 continue if move == "Y2": yCount += 2 yCount %= 4 continue if yCount > 0: for i in range(yCount): move = yTransform(move) if prev_move == "" or prev_move == '': prev_move = move new_list.append(move) continue if move[0] == prev_move[0]: if len(move) == 1: if len(prev_move) <= 1: del new_list[-1] mv = move[0] + "2" new_list.append(mv) prev_move = mv continue if prev_move[1] == "i": del new_list[-1] prev_move = new_list[-1] if len(new_list) > 0 else "" continue if prev_move[1] == "2": del new_list[-1] mv = move[0] + "i" new_list.append(mv) prev_move = mv continue if move[1] == "i": if len(prev_move) == 1: del new_list[-1] prev_move = new_list[-1] if len(new_list) > 0 else "" continue if prev_move[1] == "i": del new_list[-1] mv = move[0] + "2" new_list.append(mv) prev_move = mv continue if prev_move[1] == "2": del new_list[-1] mv = move[0] new_list.append(mv) prev_move = mv continue if move[1] == "2": if len(prev_move) == 1: del new_list[-1] mv = move[0] + "i" new_list.append(mv) prev_move = mv continue if prev_move[1] == "i": del new_list[-1] mv = move[0] new_list.append(mv) prev_move = mv continue if prev_move[1] == "2": del new_list[-1] prev_move = new_list[-1] if len(new_list) > 0 else "" continue new_list.append(move) prev_move = move solution_length = len(new_list) moves_list = new_list # Configura el cubo para realizar un movimiento rotando esa cara hacia arriba def setup(face): face = str.lower(face) if face == "f": move("X") elif face == "r": move("Zi") elif face == "l": move("Z") elif face == "d": move("X2") elif face == "b": move("Xi") else: raise Exception("Invalid setup; face: " + face) # Realiza la inversa de setup para restaurar la orientación previa del cubo def undo(face): face = str.lower(face) if face == "f": move("Xi") elif face == "r": move("Z") elif face == "l": move("Zi") elif face == "d": move("X2") elif face == "b": move("X") else: raise Exception("Invalid undo; face: " + face) # Tokeniza una cadena de movimientos def m(s): s = str.replace(s, "'", "i") k = s.split(' ') global moves_list, solution_length solution_length += len(k) for word in k: moves_list.append(word) move(word) # Realiza un movimiento configurando, ejecutando movimientos U y deshaciendo la # configuración def move(mv): mv = str.lower(mv) if mv == "u": U() elif mv == "u2": move("U") move("U") elif mv == "ui": move("U") move("U") move("U") elif mv == "f": setup("F") U() undo("F") elif mv == "f2": move("F") move("F") elif mv == "fi": move("F") move("F") move("F") elif mv == "r": setup("R") U() undo("R") elif mv == "r2": move("R") move("R") elif mv == "ri": move("R") move("R") move("R") elif mv == "l": setup("L") U() undo("L") elif mv == "l2": move("L") move("L") elif mv == "li": move("L") move("L") move("L") elif mv == "b": setup("B") U() undo("B") elif mv == "b2": move("B") move("B") elif mv == "bi": move("B") move("B") move("B") elif mv == "d": setup("D") U() undo("D") elif mv == "d2": move("D") move("D") elif mv == "di": move("D") move("D") move("D") elif mv == "x": rotate("X") elif mv == "x2": move("X") move("X") elif mv == "xi": move("X") move("X") move("X") elif mv == "y": rotate("Y") elif mv == "y2": move("Y") move("Y") elif mv == "yi": move("Y") move("Y") move("Y") elif mv == "z": rotate("Z") elif mv == "z2": move("Z") move("Z") elif mv == "zi": move("Z") move("Z") move("Z") elif mv == "uw": move("D") move("Y") elif mv == "uw2": move("UW") move("UW") elif mv == "uwi": move("UW") move("UW") move("UW") elif mv == "m": move("Li") move("R") move("Xi") elif mv == "mi": move("M") move("M") move("M") elif mv == "m2": move("M") move("M") elif mv == "rw": move("L") move("X") elif mv == "rwi": move("RW") move("RW") move("RW") elif mv == "rw2": move("RW") move("RW") elif mv == "fw": move("Bi") move("Z") elif mv == "fwi": move("FW") move("FW") move("FW") elif mv == "fw2": move("FW") move("FW") elif mv == "lw": move("R") move("Xi") elif mv == "lwi": move("LW") move("LW") move("LW") elif mv == "lw2": move("LW") move("LW") elif mv == "bw": move("F") move("Zi") elif mv == "bwi": move("BW") move("BW") move("BW") elif mv == "bw2": move("BW") move("BW") elif mv == "dw": move("U") move("Yi") elif mv == "dwi": move("DW") move("DW") move("DW") elif mv == "dw2": move("DW") move("DW") else: raise Exception("Invalid Move: " + str(mv)) # rota todo el cubo a lo largo de un eje particular def rotate(axis): axis = str.lower(axis) if axis == 'x': # R temp = a[0] a[0] = a[1] a[1] = a[4] a[4] = a[5] a[5] = temp rotate_face_counterclockwise("L") rotate_face_clockwise("R") elif axis == 'y': # U temp = a[1] a[1] = a[2] a[2] = a[5] a[5] = a[3] a[3] = temp # después de los intercambios, rotate_face_clockwise("L") rotate_face_clockwise("F") rotate_face_clockwise("R") rotate_face_clockwise("B") rotate_face_clockwise("U") rotate_face_counterclockwise("D") elif axis == 'z': # F temp = a[0] a[0] = a[3] a[3] = a[4] a[4] = a[2] a[2] = temp rotate_face_clockwise("L") rotate_face_clockwise("L") rotate_face_clockwise("D") rotate_face_clockwise("D") rotate_face_clockwise("F") rotate_face_counterclockwise("B") else: raise Exception("Rotation inválida: " + axis) # realiza un movimiento U def U(): # rota la cara U temp = a[0][0][0] a[0][0][0] = a[0][2][0] a[0][2][0] = a[0][2][2] a[0][2][2] = a[0][0][2] a[0][0][2] = temp temp = a[0][0][1] a[0][0][1] = a[0][1][0] a[0][1][0] = a[0][2][1] a[0][2][1] = a[0][1][2] a[0][1][2] = temp # rota otros temp = a[5][2][0] a[5][2][0] = a[3][2][2] a[3][2][2] = a[1][0][2] a[1][0][2] = a[2][0][0] a[2][0][0] = temp temp = a[5][2][1] a[5][2][1] = a[3][1][2] a[3][1][2] = a[1][0][1] a[1][0][1] = a[2][1][0] a[2][1][0] = temp temp = a[5][2][2] a[5][2][2] = a[3][0][2] a[3][0][2] = a[1][0][0] a[1][0][0] = a[2][2][0] a[2][2][0] = temp # Rota una cara particular en sentido antihorario def rotate_face_counterclockwise(face): rotate_face_clockwise(face) rotate_face_clockwise(face) rotate_face_clockwise(face) # Rota una cara particular en sentido horario def rotate_face_clockwise(face): f_id = -1 face = str.lower(face) if face == "u": f_id = 0 elif face == "f": f_id = 1 elif face == "r": f_id = 2 elif face == "l": f_id = 3 elif face == "d": f_id = 4 elif face == "b": f_id = 5 else: raise Exception("Cara inválida: " + face) temp = a[f_id][0][0] a[f_id][0][0] = a[f_id][2][0] a[f_id][2][0] = a[f_id][2][2] a[f_id][2][2] = a[f_id][0][2] a[f_id][0][2] = temp temp = a[f_id][0][1] a[f_id][0][1] = a[f_id][1][0] a[f_id][1][0] = a[f_id][2][1] a[f_id][2][1] = a[f_id][1][2] a[f_id][1][2] = temp # Mezcla aleatoriamente el cubo dado un número de movimientos, o dada una lista # de movimientos def scramble(moves=25): global last_scramble, moves_list, solution_length, a a = make_cube() if hasattr(moves, '__iter__'): # mezcla dada una lista de movimientos m(moves) moves_list = [] solution_length = 0 temp = moves.split(' ') last_scramble = temp else: # mezcla aleatoriamente un cierto número de veces moves_list = [] # reiniciar moves_list last_scramble = [] # reiniciar última mezcla prevMove = "" for i in range(moves): while True: thisMove = "" r = randint(0, 5) if r == 0: thisMove += "U" elif r == 1: thisMove += "F" elif r == 2: thisMove += "R" elif r == 3: thisMove += "L" elif r == 4: thisMove += "D" elif r == 5: thisMove += "B" if thisMove == "U" and prevMove != "U" and prevMove != "D": break if thisMove == "F" and prevMove != "F" and prevMove != "B": break if thisMove == "R" and prevMove != "R" and prevMove != "L": break if thisMove == "L" and prevMove != "L" and prevMove != "R": break if thisMove == "D" and prevMove != "D" and prevMove != "U": break if thisMove == "B" and prevMove != "B" and prevMove != "F": break r = randint(0, 3) if r == 1: move(thisMove + "i") last_scramble.append(thisMove + "i") elif r == 2: move(thisMove + "2") last_scramble.append(thisMove + "2") else: move(thisMove) last_scramble.append(thisMove) prevMove = thisMove # Resuelve el cruce superior como parte del paso OLL def topCross(): # si todos los bordes son iguales entre sí (todos siendo blancos) if a[0][0][1] == a[0][1][0] == a[0][1][2] == a[0][2][1]: # print("Cruce ya hecho, paso omitido") return # Si esto es cierto, tenemos nuestro cruce y podemos pasar al siguiente # paso else: while a[0][0][1] != "W" or a[0][1][0] != "W" or a[0][1][2] != "W" or a[0][2][1] != "W": if a[0][1][0] == a[0][1][2]: # si tenemos una línea horizontal, solo haz el algoritmo m("F R U Ri Ui Fi") break # rompiendo sin tener que revisar las condiciones # mientras de nuevo, esto nos dará un cruce elif a[0][0][1] == a[0][2][1]: # si tenemos una línea vertical, haz un U y luego el algoritmo m("U F R U Ri Ui Fi") break elif a[0][0][1] != "W" and a[0][1][0] != "W" and a[0][1][2] != "W" and a[0][2][1] != "W": # Esto significaría que tenemos un caso de punto, así que # realiza el algoritmo m("F U R Ui Ri Fi U F R U Ri Ui Fi") break elif a[0][1][2] == a[0][2][1] or a[0][0][1] == a[0][1][0]: # Si tenemos un caso de L en la parte superior izquierda o la # inferior derecha, nos dará una línea m("F R U Ri Ui Fi") else: # Esto es si no tenemos una línea, punto, cruce, o L en la parte # superior izquierda o inferior derecha m("U") # devuelve True si la parte superior está resuelta def isTopSolved(): # determina si la parte superior del cubo está resuelta. if a[0][0][0] == a[0][0][1] == a[0][0][2] == a[0][1][0] == a[0][1][1] == a[0][1][2] == a[0][2][0] == a[0][2][1] == a[0][2][2]: return True else: return False # Coloca una sola pieza de borde en la ubicación correcta para el cruce # Asume que el cruce está formado en la parte inferior y es la cara amarilla # Verifica todos los bordes en la cara frontal/superior, luego en la parte trasera-derecha/izquierda si es necesario def putCrossEdge(): global moves_list for i in range(3): if i == 1: m("Ri U R F2") # bring out back-right edge elif i == 2: m("L Ui Li F2") # bring out back-left edge for j in range(4): for k in range(4): if "Y" in [a[4][0][1], a[1][2][1]]: return m("F") m("U") # Realiza el primer paso de la solución: el cruce def cross(): for i in range(4): putCrossEdge() assert "Y" in [a[4][0][1], a[1][2][1]] if a[1][2][1] == "Y": m("Fi R U Ri F2") # orient if necessary m("Di") # permutar para corregir la cara: mueve la cara hacia abajo hasta que 2 # estén alineadas, luego intercambia los otros 2 si necesitan ser # intercambiados condition = False while not condition: fSame = a[1][1][1] == a[1][2][1] rSame = a[2][1][1] == a[2][1][2] bSame = a[5][1][1] == a[5][0][1] lSame = a[3][1][1] == a[3][1][0] condition = (fSame, rSame, bSame, lSame).count(True) >= 2 if not condition: m("D") if (fSame, rSame, bSame, lSame).count(True) == 4: return assert (fSame, rSame, bSame, lSame).count(True) == 2 if not fSame and not bSame: m("F2 U2 B2 U2 F2") # intercambiar frente-atras elif not rSame and not lSame: m("R2 U2 L2 U2 R2") # intercambiar derecha-izquierda elif not fSame and not rSame: m("F2 Ui R2 U F2") # intercambiar frente-derecha elif not rSame and not bSame: m("R2 Ui B2 U R2") # intercambiar derecha-atras elif not bSame and not lSame: m("B2 Ui L2 U B2") # intercambiar atras-izquierda elif not lSame and not fSame: m("L2 Ui F2 U L2") # intercambiar izquierda-frente fSame = a[1][1][1] == a[1][2][1] rSame = a[2][1][1] == a[2][1][2] bSame = a[5][1][1] == a[5][0][1] lSame = a[3][1][1] == a[3][1][0] assert all([fSame, rSame, bSame, lSame]) # Esto usa todos los algoritmos de f2l para resolver todos los casos posibles def solveFrontSlot(): # Esto será F2L, con los 42 casos rmid = a[2][1][1] fmid = a[1][1][1] dmid = a[4][1][1] # Orientaciones de las esquinas si están en la capa U, la primera letra # indica la dirección en la que el color está mirando fCorU = a[1][0][2] == dmid and a[0][2][2] == fmid and a[2][2][0] == rmid rCorU = a[2][2][0] == dmid and a[1][0][2] == fmid and a[0][2][2] == rmid uCorU = a[0][2][2] == dmid and a[2][2][0] == fmid and a[1][0][2] == rmid # Orientaciones de las esquinas para la ubicación correcta en la capa D fCorD = a[1][2][2] == dmid and a[2][2][2] == fmid and a[4][0][2] == rmid rCorD = a[2][2][2] == dmid and a[4][0][2] == fmid and a[1][2][2] == rmid # Este es el lugar resuelto dCorD = a[4][0][2] == dmid and a[1][2][2] == fmid and a[2][2][2] == rmid # Orientaciones de los bordes en la capa U, versión normal o invertida # basada en la cara F norEdgeFU = a[1][0][1] == fmid and a[0][2][1] == rmid norEdgeLU = a[3][1][2] == fmid and a[0][1][0] == rmid norEdgeBU = a[5][2][1] == fmid and a[0][0][1] == rmid norEdgeRU = a[2][1][0] == fmid and a[0][1][2] == rmid norEdgeAny = norEdgeFU or norEdgeLU or norEdgeBU or norEdgeRU flipEdgeFU = a[0][2][1] == fmid and a[1][0][1] == rmid flipEdgeLU = a[0][1][0] == fmid and a[3][1][2] == rmid flipEdgeBU = a[0][0][1] == fmid and a[5][2][1] == rmid flipEdgeRU = a[0][1][2] == fmid and a[2][1][0] == rmid flipEdgeAny = flipEdgeFU or flipEdgeLU or flipEdgeBU or flipEdgeRU # orientaciones de los bordes para la inserción normal o invertida en el # lugar. # Este es el lugar resuelto norEdgeInsert = a[1][1][2] == fmid and a[2][2][1] == rmid flipEdgeInsert = a[2][2][1] == fmid and a[1][1][2] == rmid # estos son para si los espacios de la parte trasera derecha o delantera # izquierda están abiertos o no backRight = a[4][2][2] == dmid and a[5][1][2] == a[5][0][2] == a[5][1][1] and a[2][0][1] == a[2][0][2] == rmid frontLeft = a[4][0][0] == dmid and a[1][1][0] == a[1][2][0] == fmid and a[3][2][0] == a[3][2][1] == a[3][1][1] if dCorD and norEdgeInsert: return # Casos fáciles elif fCorU and flipEdgeRU: # Caso 1 m("U R Ui Ri") elif rCorU and norEdgeFU: # Caso 2 m("F Ri Fi R") elif fCorU and norEdgeLU: # Caso 3 m("Fi Ui F") elif rCorU and flipEdgeBU: # Caso 4 m("R U Ri") # Reposicionar el borde elif fCorU and flipEdgeBU: # Caso 5 m("F2 Li Ui L U F2") elif rCorU and norEdgeLU: # Caso 6 m("R2 B U Bi Ui R2") elif fCorU and flipEdgeLU: # Caso 7 m("Ui R U2 Ri U2 R Ui Ri") elif rCorU and norEdgeBU: # Caso 8 m("U Fi U2 F Ui F Ri Fi R") # Reposicionar el borde y voltear la esquina elif fCorU and norEdgeBU: # Caso 9 m("Ui R Ui Ri U Fi Ui F") elif rCorU and flipEdgeLU: # Caso 10 if not backRight: m("Ri U R2 U Ri") else: m("Ui R U Ri U R U Ri") elif fCorU and norEdgeRU: # Caso 11 m("Ui R U2 Ri U Fi Ui F") elif rCorU and flipEdgeFU: # Caso 12 if not backRight: m("Ri U2 R2 U Ri") else: m("Ri U2 R2 U R2 U R") elif fCorU and norEdgeFU: # Caso 13 if not backRight: m("Ri U R Fi Ui F") else: m("U Fi U F Ui Fi Ui F") elif rCorU and flipEdgeRU: # Caso 14 m("Ui R Ui Ri U R U Ri") # Separar el par yendo sobre elif fCorU and flipEdgeFU: # Caso 15 if not backRight: m("Ui Ri U R Ui R U Ri") elif not frontLeft: m("U R Ui Ri D R Ui Ri Di") else: m("U Ri F R Fi U R U Ri") elif rCorU and norEdgeRU: # Caso 16 m("R Ui Ri U2 Fi Ui F") elif uCorU and flipEdgeRU: # Caso 17 m("R U2 Ri Ui R U Ri") elif uCorU and norEdgeFU: # Caso 18 m("Fi U2 F U Fi Ui F") # Par hecho en el lado elif uCorU and flipEdgeBU: # Caso 19 m("U R U2 R2 F R Fi") elif uCorU and norEdgeLU: # Caso 20 m("Ui Fi U2 F2 Ri Fi R") elif uCorU and flipEdgeLU: # Caso 21 m("R B U2 Bi Ri") elif uCorU and norEdgeBU: # Caso 22 m("Fi Li U2 L F") # Casos raros elif uCorU and flipEdgeFU: # Caso 23 m("U2 R2 U2 Ri Ui R Ui R2") elif uCorU and norEdgeRU: # Caso 24 m("U Fi Li U L F R U Ri") # Esquina en su lugar, borde en la cara U (Todos estos casos también tienen movimientos de preparación en caso de que el borde esté en la orientación incorrecta) elif dCorD and flipEdgeAny: # Caso 25 if flipEdgeBU: m("U") # movimiento de preparación elif flipEdgeLU: m("U2") # movimiento de preparación elif flipEdgeFU: m("Ui") # movimiento de preparación if not backRight: m("R2 Ui Ri U R2") else: m("Ri Fi R U R Ui Ri F") elif dCorD and norEdgeAny: # Caso 26 if norEdgeRU: m("U") # movimiento de preparación elif norEdgeBU: m("U2") # movimiento de preparación elif norEdgeLU: m("Ui") # movimiento de preparación m("U R Ui Ri F Ri Fi R") elif fCorD and flipEdgeAny: # Caso 27 if flipEdgeBU: m("U") # movimiento de preparación elif flipEdgeLU: m("U2") # movimiento de preparación elif flipEdgeFU: m("Ui") # movimiento de preparación m("R Ui Ri U R Ui Ri") elif rCorD and norEdgeAny: # Caso 28 if norEdgeRU: m("U") # movimiento de preparación elif norEdgeBU: m("U2") # movimiento de preparación elif norEdgeLU: m("Ui") # movimiento de preparación m("R U Ri Ui F Ri Fi R") elif fCorD and norEdgeAny: # Caso 29 if norEdgeRU: m("U") # movimiento de preparación elif norEdgeBU: m("U2") # movimiento de preparación elif norEdgeLU: m("Ui") # movimiento de preparación m("U2 R Ui Ri Fi Ui F") elif rCorD and flipEdgeAny: # Caso 30 if flipEdgeBU: m("U") # movimiento de preparación elif flipEdgeLU: m("U2") # movimiento de preparación elif flipEdgeFU: m("Ui") # movimiento de preparación m("R U Ri Ui R U Ri") # Borde en su lugar, esquina en la cara U elif uCorU and flipEdgeInsert: # Caso 31 m("R U2 Ri Ui F Ri Fi R") elif uCorU and norEdgeInsert: # Caso 32 m("R2 U R2 U R2 U2 R2") elif fCorU and norEdgeInsert: # Caso 33 m("Ui R Ui Ri U2 R Ui Ri") elif rCorU and norEdgeInsert: # Caso 34 m("Ui R U2 Ri U R U Ri") elif fCorU and flipEdgeInsert: # Caso 35 m("U2 R Ui Ri Ui Fi Ui F") elif rCorU and flipEdgeInsert: # Caso 36 m("U Fi Ui F Ui R U Ri") # Borde y esquina en su lugar # Caso 37 es el caso Lol, ya completado elif dCorD and flipEdgeInsert: # Caso 38 (Caso típico de par f2l invertido) m("R2 U2 F R2 Fi U2 Ri U Ri") elif fCorD and norEdgeInsert: # Caso 39 m("R2 U2 Ri Ui R Ui Ri U2 Ri") elif rCorD and norEdgeInsert: # Caso 40 m("R U2 R U Ri U R U2 R2") elif fCorD and flipEdgeInsert: # Caso 41 m("F2 Li Ui L U F Ui F") elif rCorD and flipEdgeInsert: # Caso 42 m("R Ui Ri Fi Li U2 L F") # Devuelve verdadero si la esquina f2l en el lugar FR está insertada y orientada correctamente def f2lCorner(): # Este es el lugar resuelto return a[4][0][2] == a[4][1][1] and a[1][2][2] == a[1][1][1] and a[2][2][2] == a[2][1][1] # Devuelve verdadero si el borde f2l en el lugar FR está insertado y orientado correctamente def f2lEdge(): # Este es el lugar resuelto return a[1][1][2] == a[1][1][1] and a[2][2][1] == a[2][1][1] # Devuelve verdadero si el borde f2l y la esquina están correctamente insertados y orientados en la posición FR def f2lCorrect(): return f2lCorner() and f2lEdge() # Devuelve verdadero si el borde f2l está en la capa superior en absoluto def f2lEdgeOnTop(): rmid = a[2][1][1] fmid = a[1][1][1] dmid = a[4][1][1] # orientaciones del borde en la capa U, versión normal o invertida basada en la cara F norEdgeFU = a[1][0][1] == fmid and a[0][2][1] == rmid norEdgeLU = a[3][1][2] == fmid and a[0][1][0] == rmid norEdgeBU = a[5][2][1] == fmid and a[0][0][1] == rmid norEdgeRU = a[2][1][0] == fmid and a[0][1][2] == rmid norEdgeAny = norEdgeFU or norEdgeLU or norEdgeBU or norEdgeRU flipEdgeFU = a[0][2][1] == fmid and a[1][0][1] == rmid flipEdgeLU = a[0][1][0] == fmid and a[3][1][2] == rmid flipEdgeBU = a[0][0][1] == fmid and a[5][2][1] == rmid flipEdgeRU = a[0][1][2] == fmid and a[2][1][0] == rmid flipEdgeAny = flipEdgeFU or flipEdgeLU or flipEdgeBU or flipEdgeRU return norEdgeAny or flipEdgeAny # Devuelve verdadero si el borde f2l está insertado. Puede estar correctamente orientado o invertido. def f2lEdgeInserted(): rmid = a[2][1][1] fmid = a[1][1][1] # orientaciones del borde para la inserción normal o invertida en el lugar # Este es el lugar resuelto norEdgeInsert = a[1][1][2] == fmid and a[2][2][1] == rmid flipEdgeInsert = a[2][2][1] == fmid and a[1][1][2] == rmid return norEdgeInsert or flipEdgeInsert # Esto se usa para determinar si el borde f2l frontal está insertado o no, el parámetro es para el borde solicitado. toma BR, BL y FL como válidos def f2lEdgeInserted2(p): rmid = a[2][1][1] fmid = a[1][1][1] # orientaciones del borde para la inserción normal o invertida en el lugar # Este es el lugar resuelto norEdgeInsert = a[1][1][2] == fmid and a[2][2][1] == rmid flipEdgeInsert = a[2][2][1] == fmid and a[1][1][2] == rmid # Orientaciones del borde en comparación con los colores del Frente y el Derecho BR = (a[5][1][2] == fmid and a[2][0][1] == rmid) or ( a[5][1][2] == rmid and a[2][0][1] == fmid) BL = (a[3][0][1] == fmid and a[5][1][0] == rmid) or ( a[3][0][1] == rmid and a[5][1][0] == fmid) FL = (a[3][2][1] == fmid and a[1][1][0] == rmid) or ( a[3][2][1] == rmid and a[1][1][0] == fmid) if p == "BR": if BR: return True else: return False elif p == "BL": if BL: return True return False elif p == "FL": if FL: return True return False elif p == "FR": if norEdgeInsert or flipEdgeInsert: return True return False # Devuelve verdadero si la esquina f2l está insertada, no tiene que estar orientada correctamente def f2lCornerInserted(): rmid = a[2][1][1] fmid = a[1][1][1] dmid = a[4][1][1] # Orientaciones de la esquina para la ubicación correcta en la capa D fCorD = a[1][2][2] == dmid and a[2][2][2] == fmid and a[4][0][2] == rmid rCorD = a[2][2][2] == dmid and a[4][0][2] == fmid and a[1][2][2] == rmid # Este es el lugar resuelto dCorD = a[4][0][2] == dmid and a[1][2][2] == fmid and a[2][2][2] == rmid return fCorD or rCorD or dCorD # Devuelve verdadero si hay una esquina f2l ubicada en la orientación FR def f2lFRCor(): rmid = a[2][1][1] fmid = a[1][1][1] dmid = a[4][1][1] # orientaciones de las esquinas si están en la capa U, la primera letra indica la dirección en la que el color está mirando fCorU = a[1][0][2] == dmid and a[0][2][2] == fmid and a[2][2][0] == rmid rCorU = a[2][2][0] == dmid and a[1][0][2] == fmid and a[0][2][2] == rmid uCorU = a[0][2][2] == dmid and a[2][2][0] == fmid and a[1][0][2] == rmid return fCorU or rCorU or uCorU # Devuelve verdadero si hay un borde f2l ubicado en la posición FU def f2lFUEdge(): rmid = a[2][1][1] fmid = a[1][1][1] norEdgeFU = a[1][0][1] == fmid and a[0][2][1] == rmid flipEdgeFU = a[0][2][1] == fmid and a[1][0][1] == rmid return norEdgeFU or flipEdgeFU # Devuelve verdadero si la esquina f2l está ubicada en la capa U def f2lCornerOnTop(): wasFound = False for i in range(4): # Realiza 4 movimientos U para encontrar la esquina if f2lFRCor(): wasFound = True m("U") return wasFound # Devolverá la ubicación de la esquina que pertenece al lugar FR. Devuelve BR, BL, FL o FR. def f2lCornerCheck(): r = "FR" count = 0 while count < 4: if count == 0: if f2lCornerInserted(): r = "FR" elif count == 1: if f2lCornerInserted(): r = "FL" elif count == 2: if f2lCornerInserted(): r = "BL" elif count == 3: if f2lCornerInserted(): r = "BR" m("D") count += 1 return r # Devolverá la ubicación del borde que pertenece al lugar FR. # Devuelve BR, BL, FL o FR. def f2lEdgeCheck(): if f2lEdgeInserted2("FL"): return "FL" elif f2lEdgeInserted2("BL"): return "BL" elif f2lEdgeInserted2("BR"): return "BR" elif f2lEdgeInserted2("FR"): return "FR" else: raise Exception("Excepción en f2lEdgeCheck()") # Esto es para el caso en que el Borde está insertado, pero la esquina no def f2lEdgeNoCorner(): topEdgeTop = a[0][2][1] topEdgeFront = a[1][0][1] rmid = a[2][1][1] bmid = a[5][1][1] lmid = a[3][1][1] fmid = a[1][1][1] # Esto es para comparar el borde frontal con otros bordes diversos para algs avanzados/lookahead BREdge = (topEdgeTop == rmid or topEdgeTop == bmid) and ( topEdgeFront == rmid or topEdgeFront == bmid) BLEdge = (topEdgeTop == lmid or topEdgeTop == bmid) and ( topEdgeFront == lmid or topEdgeFront == bmid) FLEdge = (topEdgeTop == fmid or topEdgeTop == lmid) and ( topEdgeFront == fmid or topEdgeFront == lmid) if f2lCornerOnTop(): while True: solveFrontSlot() if f2lCorrect(): break m("U") else: if f2lCornerCheck() == "BR": if BREdge: m("Ri Ui R U2") else: m("Ri U R U") elif f2lCornerCheck() == "BL": if BLEdge: m("L U Li U") else: m("L Ui Li U2") elif f2lCornerCheck() == "FL": if FLEdge: m("Li U L Ui") else: m("Li Ui L") solveFrontSlot() if not f2lCorrect(): raise Exception("Excepción encontrada en f2lEdgeNoCorner()") # Este es el caso para si la esquina está insertada, pero el borde no def f2lCornerNoEdge(): topEdgeTop = a[0][2][1] topEdgeFront = a[1][0][1] rmid = a[2][1][1] bmid = a[5][1][1] lmid = a[3][1][1] fmid = a[1][1][1] # Esto es para comparar el borde frontal con otros bordes diversos para algoritmos avanzados/lookahead BREdge = (topEdgeTop == rmid or topEdgeTop == bmid) and ( topEdgeFront == rmid or topEdgeFront == bmid) BLEdge = (topEdgeTop == lmid or topEdgeTop == bmid) and ( topEdgeFront == lmid or topEdgeFront == bmid) FLEdge = (topEdgeTop == fmid or topEdgeTop == lmid) and ( topEdgeFront == fmid or topEdgeFront == lmid) if f2lEdgeOnTop(): while True: solveFrontSlot() if f2lCorrect(): break m("U") else: if f2lEdgeCheck() == "BR": if BREdge: m("Ri Ui R U2") else: m("Ri U R U") elif f2lEdgeCheck() == "BL": if BLEdge: m("L U Li U") else: m("L Ui Li U2") elif f2lEdgeCheck() == "FL": if FLEdge: m("Li U L Ui") else: m("Li Ui L") solveFrontSlot() if not f2lCorrect(): raise Exception("Excepción encontrada en f2lCornerNoEdge()") # Este es el caso para si la esquina está en la parte superior, y el borde no. Ninguno está insertado correctamente. El borde debe estar en otro lugar. def f2lCornerTopNoEdge(): topEdgeTop = a[0][2][1] topEdgeFront = a[1][0][1] rmid = a[2][1][1] bmid = a[5][1][1] lmid = a[3][1][1] fmid = a[1][1][1] # Esto es para comparar el borde frontal con otros bordes diversos para algoritmos avanzados/lookahead BREdge = (topEdgeTop == rmid or topEdgeTop == bmid) and ( topEdgeFront == rmid or topEdgeFront == bmid) BLEdge = (topEdgeTop == lmid or topEdgeTop == bmid) and ( topEdgeFront == lmid or topEdgeFront == bmid) FLEdge = (topEdgeTop == fmid or topEdgeTop == lmid) and ( topEdgeFront == fmid or topEdgeFront == lmid) # Gira la parte superior hasta que la esquina en la cara U esté en la posición correcta while True: if f2lFRCor(): break m("U") # Verificaremos bordes adicionales para elegir un algoritmo más adecuado para buscar if f2lEdgeCheck() == "BR": if BREdge: m("Ri Ui R") else: m("Ri U R") elif f2lEdgeCheck() == "BL": if BLEdge: m("U2 L Ui Li") else: m("L Ui Li U") elif f2lEdgeCheck() == "FL": if FLEdge: m("U2 Li Ui L U2") else: m("Li Ui L U") solveFrontSlot() if not f2lCorrect(): raise Exception("Excepción encontrada en f2lCornerTopNoEdge()") # Este es el caso para si el borde está en la parte superior, y la esquina no. Ninguno está insertado correctamente. La esquina debe estar en otro lugar. # La búsqueda para este paso es comparar el borde trasero con los lugares, en lugar del frontal como en otros casos def f2lEdgeTopNoCorner(): BackEdgeTop = a[0][0][1] BackEdgeBack = a[5][2][1] rmid = a[2][1][1] bmid = a[5][1][1] lmid = a[3][1][1] fmid = a[1][1][1] rs1 = BackEdgeTop == rmid or BackEdgeTop == bmid rs2 = BackEdgeBack == rmid or BackEdgeBack == bmid # Esto es para comparar el borde trasero con otros bordes diversos para algoritmos avanzados/lookahead BREdge = rs1 and rs2 BLEdge = (BackEdgeTop == lmid or BackEdgeTop == bmid) and ( BackEdgeBack == lmid or BackEdgeBack == bmid) FLEdge = (BackEdgeTop == fmid or BackEdgeTop == lmid) and ( BackEdgeBack == fmid or BackEdgeBack == lmid) # Gira la parte superior hasta que el borde en la cara U esté en la posición correcta while True: if f2lFUEdge(): break m("U") # Verificaremos bordes adicionales para elegir un algoritmo más adecuado para buscar if f2lCornerCheck() == "BR": if BREdge: m("Ri U R U") else: m("Ui Ri U R U") elif f2lCornerCheck() == "BL": if BLEdge: m("L Ui Li U2") else: m("U2 L U2 Li") elif f2lCornerCheck() == "FL": if FLEdge: m("Li Ui L") else: m("U Li Ui L") solveFrontSlot() if not f2lCorrect(): raise Exception("Excepción encontrada en f2lEdgeTopNoCorner()") # Este es el caso para si el borde o la esquina no están en la parte superior, y no están insertados correctamente. Ambos deben estar en otros lugares. def f2lNoEdgeOrCorner(): # La estrategia aquí es primero encontrar la esquina y sacarla. La colocaré en la posición FR donde corresponde # Luego verificaré si tengo un caso, y si todo está resuelto. # Si no lo tengo resuelto en este punto, tendré que seguir lo que ocurre en f2lCornerTopNoEdge() BackEdgeTop = a[0][0][1] BackEdgeBack = a[5][2][1] rmid = a[2][1][1] bmid = a[5][1][1] lmid = a[3][1][1] fmid = a[1][1][1] # Esto es para comparar el borde trasero con otros bordes diversos para algoritmos avanzados/lookahead BREdge = (BackEdgeTop == rmid or BackEdgeTop == bmid) and ( BackEdgeBack == rmid or BackEdgeBack == bmid) BLEdge = (BackEdgeTop == lmid or BackEdgeTop == bmid) and ( BackEdgeBack == lmid or BackEdgeBack == bmid) FLEdge = (BackEdgeTop == fmid or BackEdgeTop == lmid) and ( BackEdgeBack == fmid or BackEdgeBack == lmid) # Verificaremos bordes adicionales para elegir un algoritmo más adecuado para buscar if f2lCornerCheck() == "BR": if BREdge: m("Ri U R U") else: m("Ui Ri U R U") elif f2lCornerCheck() == "BL": if BLEdge: m("L Ui Li U2") else: m("U2 L U2 Li") elif f2lCornerCheck() == "FL": if FLEdge: m("Li Ui L") else: m("U Li Ui L") solveFrontSlot() if f2lCorrect(): return else: f2lCornerTopNoEdge() if not f2lCorrect(): raise Exception("Excepción encontrada en f2lNoEdgeOrCorner()") # Devolverá verdadero si el f2l está completado def isf2lDone(): rside = a[2][0][1] == a[2][0][2] == a[2][1][1] == a[2][1][2] == a[2][2][1] == a[2][2][2] bside = a[5][0][0] == a[5][0][1] == a[5][0][2] == a[5][1][0] == a[5][1][1] == a[5][1][2] lside = a[3][0][0] == a[3][0][1] == a[3][1][0] == a[3][1][1] == a[3][2][0] == a[3][2][1] fside = a[1][1][0] == a[1][1][1] == a[1][1][2] == a[1][2][0] == a[1][2][1] == a[1][2][2] return rside and bside and lside and fside # f2l resolverá las primeras 2 capas, verifica cada caso, luego hace un movimiento Y para verificar el siguiente def f2l(): pairsSolved = 0 uMoves = 0 while pairsSolved < 4: if not f2lCorrect(): # mientras no esté correcto el f2l: while uMoves < 4: # 4 movimientos antes de verificar casos raros solveFrontSlot() if f2lCorrect(): pairsSolved += 1 f2l_list.append("Caso Normal") break else: f2l_list.append("Escaneando") uMoves += 1 m("U") if not f2lCorrect(): if not f2lCornerInserted() and f2lEdgeInserted(): f2l_list.append("Caso raro 1") f2lEdgeNoCorner() pairsSolved += 1 elif not f2lEdgeInserted() and f2lCornerInserted(): f2l_list.append("Caso raro 2") f2lCornerNoEdge() pairsSolved += 1 # En este punto, no pueden estar insertados, deben estar en la U u otro lugar elif not f2lEdgeOnTop() and f2lCornerOnTop(): f2l_list.append("Caso raro 3") f2lCornerTopNoEdge() pairsSolved += 1 elif f2lEdgeOnTop() and not f2lCornerOnTop(): f2l_list.append("Caso raro 4") f2lEdgeTopNoCorner() solveFrontSlot() pairsSolved += 1 elif not f2lEdgeOnTop() and not f2lCornerOnTop(): f2l_list.append("Caso raro 5") f2lNoEdgeOrCorner() pairsSolved += 1 else: raise Exception("Excepción de Caso Imposible en f2l") else: pairsSolved += 1 f2l_list.append("Tenemos ") f2l_list.append(str(pairsSolved)) uMoves = 0 m("Y") assert (isf2lDone()) def fish(): return [a[0][0][0], a[0][0][2], a[0][2][0], a[0][2][2]].count(a[0][1][1]) == 1 def sune(): m("R U Ri U R U2 Ri") def antisune(): m("R U2 Ri Ui R Ui Ri") def getfish(): for i in range(4): if fish(): return sune() if fish(): return antisune() m("U") assert fish() def bOLL(): getfish() if fish(): while a[0][0][2] != a[0][1][1]: m("U") if a[1][0][0] == a[0][1][1]: antisune() elif a[5][2][0] == a[0][1][1]: m("U2") sune() else: raise Exception("Algo salió mal") # Something went wrong else: raise Exception("Fish no configurado") # Fish not set up assert isTopSolved() def getCornerState(): corner0 = a[1][0][0] == a[1][1][1] and a[3][2][2] == a[3][1][1] corner1 = a[1][0][2] == a[1][1][1] and a[2][2][0] == a[2][1][1] corner2 = a[5][2][2] == a[5][1][1] and a[2][0][0] == a[2][1][1] corner3 = a[5][2][0] == a[5][1][1] and a[3][0][2] == a[3][1][1] return [corner0, corner1, corner2, corner3] # Permuta las esquinas de la capa superior, las orienta correctamente def permuteCorners(): for i in range(2): for j in range(4): num = getCornerState().count(True) if num == 4: return if num == 1: index = getCornerState().index(True) for k in range(index): m("Y") if a[1][0][2] == a[2][1][1]: m("R2 B2 R F Ri B2 R Fi R") else: m("Ri F Ri B2 R Fi Ri B2 R2") for f in range(index): m("Yi") return m("U") m("R2 B2 R F Ri B2 R Fi R") # Permuta los bordes de la capa superior, debe ser H, Z o U perms después de la orientación def permuteEdges(): if all(getEdgeState()): return if a[1][0][1] == a[5][1][1] and a[5][2][1] == a[1][1][1]: # Permutación H m("R2 U2 R U2 R2 U2 R2 U2 R U2 R2") elif a[1][0][1] == a[2][1][1] and a[2][1][0] == a[1][1][1]: # Permutación Z normal m("U Ri Ui R Ui R U R Ui Ri U R U R2 Ui Ri U") elif a[1][0][1] == a[3][1][1] and a[3][1][2] == a[1][1][1]: # Permutación Z no orientada m("Ri Ui R Ui R U R Ui Ri U R U R2 Ui Ri U2") else: uNum = 0 while True: if a[5][2][0] == a[5][2][1] == a[5][2][2]: # la barra sólida está en la parte trasera if a[3][1][2] == a[1][0][0]: # significa que tenemos que hacer un ciclo en sentido contrario a las agujas del reloj m("R Ui R U R U R Ui Ri Ui R2") break else: m("R2 U R U Ri Ui Ri Ui Ri U Ri") break else: m("U") uNum += 1 for x in range(uNum): m("Ui") def getEdgeState(): fEdge = a[1][0][1] == a[1][1][1] rEdge = a[2][1][0] == a[2][1][1] bEdge = a[5][2][1] == a[5][1][1] lEdge = a[3][1][2] == a[3][1][1] return [fEdge, rEdge, bEdge, lEdge] def topCorners(): permuteCorners() assert all(getCornerState()) def topEdges(): permuteEdges() assert all(getEdgeState()) def bPLL(): topCorners() topEdges() def isSolved(): uside = a[0][0][0] == a[0][0][1] == a[0][0][2] == a[0][1][0] == a[0][1][1] == a[0][1][2] == a[0][2][0] == a[0][2][1] == a[0][2][2] fside = a[1][0][0] == a[1][0][1] == a[1][0][2] == a[1][1][0] == a[1][1][1] == a[1][1][2] == a[1][2][0] == a[1][2][1] == a[1][2][2] rside = a[2][0][0] == a[2][0][1] == a[2][0][2] == a[2][1][0] == a[2][1][1] == a[2][1][2] == a[2][2][0] == a[2][2][1] == a[2][2][2] lside = a[3][0][0] == a[3][0][1] == a[3][0][2] == a[3][1][0] == a[3][1][1] == a[3][1][2] == a[3][2][0] == a[3][2][1] == a[3][2][2] dside = a[4][0][0] == a[4][0][1] == a[4][0][2] == a[4][1][0] == a[4][1][1] == a[4][1][2] == a[4][2][0] == a[4][2][1] == a[4][2][2] bside = a[5][0][0] == a[5][0][1] == a[5][0][2] == a[5][1][0] == a[5][1][1] == a[5][1][2] == a[5][2][0] == a[5][2][1] == a[5][2][2] return uside and fside and rside and lside and dside and bside def solve(): cross() simplify_moves() step_moves_list[0] = solution_length f2l() simplify_moves() step_moves_list[1] = solution_length - step_moves_list[0] topCross() getfish() bOLL() simplify_moves() step_moves_list[2] = solution_length - \ step_moves_list[1] - step_moves_list[0] bPLL() simplify_moves() step_moves_list[3] = solution_length - step_moves_list[2] - \ step_moves_list[1] - step_moves_list[0] assert (isSolved()) # Realiza simulaciones de resolución, devolverá una lista con el número de # movimientos, cuál fue el mejor y el scramble utilizado para obtener el mejor. # El peor, cuál fue el peor, y el scramble utilizado para obtener el peor. # scimNum es el número de simulaciones a realizar, es un IntVar() def simulation(simNum): global a bestScram = [] worstScram = [] best = 200 worst = 0 Bestnumber = 0 WorstNumber = 0 if simNum.get() >= 50000: print("No hagas más de 50,000 resoluciones a la vez") return for i in range(simNum.get()): a = make_cube() step_moves_list = [0, 0, 0, 0] f2l_list = [] moves_list = [] last_scramble = [] scramble(25) solve() simplify_moves() if solution_length < best: best = solution_length bestScram = get_scramble() BestNumber = i if solution_length > worst: worst = solution_length worstScram = get_scramble() WorstNumber = i return [best, BestNumber, bestScram, worst, WorstNumber, worstScram] ################################################################################ # Abajo está todo el trabajo para la parte GUI del Resolutor de Cubos ################################################################################ # Estos son todos los globales utilizados para la GUI root = None frame = None canvas = None ScrambleLabel = None SolutionLabel = None SolutionNumberLabel = None isTransparent = None F2LNumberLabel = None CrossNumberLabel = None OLLNumberLabel = None PLLNumberLabel = None SimulateBestLabel = None SimulateWorstLabel = None # cubePoints son todas las coordenadas x e y para los polígonos utilizados para # los azulejos def cubePoints(): # h y w pueden cambiarse para permitir que el cubo se mueva por la pantalla h = 125 w = 115 # cara derecha # capa 1 r00p = [0+w, 0+h, 0+w, 50+h, 33+w, 30+h, 33+w, -20+h] r01p = [33+w, -20+h, 33+w, 30+h, 66+w, 10+h, 66+w, -40+h] r02p = [66+w, -40+h, 66+w, 10+h, 99+w, -10+h, 99+w, -60+h] # capa 2 r10p = [0+w, 50+h, 0+w, 100+h, 33+w, 80+h, 33+w, 30+h] r11p = [33+w, 30+h, 33+w, 80+h, 66+w, 60+h, 66+w, 10+h] r12p = [66+w, 10+h, 66+w, 60+h, 99+w, 40+h, 99+w, -10+h] # capa 3 r20p = [0+w, 100+h, 0+w, 150+h, 33+w, 130+h, 33+w, 80+h] r21p = [33+w, 80+h, 33+w, 130+h, 66+w, 110+h, 66+w, 60+h] r22p = [66+w, 60+h, 66+w, 110+h, 99+w, 90+h, 99+w, 40+h] # cara izquierda (la cara izquierda será la cara delantera, sin embargo se # llama l para distinguir entre izquierda y derecha) # capa 1 l00p = [-66+w, -40+h, -66+w, 10+h, -99+w, -10+h, -99+w, -60+h] l01p = [-33+w, -20+h, -33+w, 30+h, -66+w, 10+h, -66+w, -40+h] l02p = [0+w, 0+h, 0+w, 50+h, -33+w, 30+h, -33+w, -20+h] # capa 2 l10p = [-66+w, 10+h, -66+w, 60+h, -99+w, 40+h, -99+w, -10+h] l11p = [-33+w, 30+h, -33+w, 80+h, -66+w, 60+h, -66+w, 10+h] l12p = [0+w, 50+h, 0+w, 100+h, -33+w, 80+h, -33+w, 30+h] # capa 3 l20p = [-66+w, 60+h, -66+w, 110+h, -99+w, 90+h, -99+w, 40+h] l21p = [-33+w, 80+h, -33+w, 130+h, -66+w, 110+h, -66+w, 60+h] l22p = [0+w, 100+h, 0+w, 150+h, -33+w, 130+h, -33+w, 80+h] # cara superior # capa 1 u00p = [0+w, -75+h, -33+w, -94+h, 0+w, -111+h, 33+w, -94+h] u01p = [36+w, -57+h, 0+w, -75+h, 33+w, -94+h, 69+w, -76+h] u02p = [66+w, -40+h, 36+w, -57+h, 69+w, -76+h, 99+w, -60+h] # capa 2 u10p = [-33+w, -57+h, -66+w, -77+h, -33+w, -94+h, 0+w, -75+h] u11p = [0+w, -38+h, -33+w, -57+h, 0+w, -75+h, 36+w, -57+h] u12p = [33+w, -20+h, 0+w, -38+h, 36+w, -57+h, 66+w, -40+h] # capa 3 u20p = [-66+w, -40+h, -99+w, -60+h, -66+w, -77+h, -33+w, -57+h] u21p = [-33+w, -20+h, -66+w, -40+h, -33+w, -57+h, 0+w, -38+h] u22p = [0+w, 0+h, -33+w, -20+h, 0+w, -38+h, 33+w, -20+h] dh = h + 200 dw = w # cara inferior # capa 1 d00p = [0+dw, -75+dh, -33+dw, -94+dh, 0+dw, -111+dh, 33+dw, -94+dh] d01p = [36+dw, -57+dh, 0+dw, -75+dh, 33+dw, -94+dh, 69+dw, -76+dh] d02p = [66+dw, -40+dh, 36+dw, -57+dh, 69+dw, -76+dh, 99+dw, -60+dh] # capa 2 d10p = [-33+dw, -57+dh, -66+dw, -77+dh, -33+dw, -94+dh, 0+dw, -75+dh] d11p = [0+dw, -38+dh, -33+dw, -57+dh, 0+dw, -75+dh, 36+dw, -57+dh] d12p = [33+dw, -20+dh, 0+dw, -38+dh, 36+dw, -57+dh, 66+dw, -40+dh] # capa 3 d20p = [-66+dw, -40+dh, -99+dw, -60+dh, -66+dw, -77+dh, -33+dw, -57+dh] d21p = [-33+dw, -20+dh, -66+dw, -40+dh, -33+dw, -57+dh, 0+dw, -38+dh] d22p = [0+dw, 0+dh, -33+dw, -20+dh, 0+dw, -38+dh, 33+dw, -20+dh] return [[[u00p, u01p, u02p], [u10p, u11p, u12p], [u20p, u21p, u22p]], # Cara superior [[l00p, l01p, l02p], [l10p, l11p, l12p], # cara delantera (se usa l para denotar la cara izquierda que muestra) [l20p, l21p, l22p]], [[r02p, r12p, r22p], [r01p, r11p, r21p], # cara derecha (diferente a otras caras porque está formateada de manera diferente) [r00p, r10p, r20p]], [[d20p, d21p, d22p], [d10p, d11p, d12p], [d00p, d01p, d02p]]] # Cara inferior def clickCanvas(canvas): global isTransparent isTransparent = not isTransparent canvas.delete(ALL) drawCube() # DrawCanvas tomará el root y dibujará un nuevo canvas, también lo devolverá. def drawCanvas(root): canvas = tk.Canvas(root, width=225, height=330, background='white') canvas.grid(row=0, column=0) canvas.bind("", lambda e: clickCanvas(canvas)) return canvas # Se usa para obtener la palabra para cada color, usada en drawCube(canvas) def getColor(element): if element == 'B': return "#06F" # Bonito tono de azul elif element == 'W': return "white" elif element == 'G': return "green" elif element == 'Y': return "yellow" elif element == 'O': return "orange" elif element == 'R': return "#D11" # drawCube() tomará el canvas ya creado y dibujará el cubo con polígonos cuyos puntos están definidos en cubePoints() def drawCube(): global isTransparent, canvas pts = cubePoints() for j in range(3): for k in range(3): canvas.create_polygon(pts[3][j][k], fill=getColor( a[4][j][k]), outline="#000", width=2) for i in range(3): for j in range(3): for k in range(3): if isTransparent: frontTiles = (i == 1) and ((j == 1 and k == 2) or ( j == 2 and k == 2) or (j == 2 and k == 1)) rightTiles = (i == 2) and ((j == 1 and k == 2) or ( j == 2 and k == 2) or (j == 2 and k == 1)) if frontTiles or rightTiles: canvas.create_polygon( pts[i][j][k], fill="", outline="#000", width=2) else: canvas.create_polygon(pts[i][j][k], fill=getColor( a[i][j][k]), outline="#000", width=2) else: canvas.create_polygon(pts[i][j][k], fill=getColor( a[i][j][k]), outline="#000", width=2) # Se usa para crear una nueva instancia de un cubo para resolver, cambia las etiquetas de scramble y solución también def GUInewCube(): global canvas, ScrambleLabel, SolutionLabel, SolutionNumberLabel, a, step_moves_list global PLLNumberLabel, F2LNumberLabel, CrossNumberLabel, OLLNumberLabel, f2l_list, moves_list step_moves_list = [0, 0, 0, 0] a = make_cube() f2l_list = [] moves_list = [] ScrambleLabel.configure(text="El scramble se mostrará aquí") SolutionLabel.configure(text="La solución se mostrará aquí") SolutionNumberLabel.configure(text=0) CrossNumberLabel.configure(text=step_moves_list[0]) F2LNumberLabel.configure(text=step_moves_list[1]) OLLNumberLabel.configure(text=step_moves_list[2]) PLLNumberLabel.configure(text=step_moves_list[3]) canvas.delete(ALL) drawCube() # GUImakeMove se usa para hacer movimientos basados en lo que está en la # EntryBox. Después de hacer clic en Dibujar, redibujará el canvas con el cubo # actualizado def GUImakeMove(move): global canvas if move.get() == "": return m(move.get()) canvas.delete(ALL) drawCube() # GUIScramble hará un scramble de 25 en el cubo, luego actualizará el canvas con el nuevo cubo def GUIScramble(): global ScrambleLabel, canvas scramble(25) ScrambleLabel.configure(text=get_scramble()) canvas.delete(ALL) drawCube() # Se usa para permitir al usuario ingresar su propio scramble en la Entry, # también mostrará el scramble en la etiqueta de scramble def GUIcustomScramble(scram): global ScrambleLabel, canvas if scram.get() == "": ScrambleLabel.configure(text="El scramble se mostrará aquí") return scramble(scram.get()) ScrambleLabel.configure(text=get_scramble()) canvas.delete(ALL) drawCube() # GUISolve resolverá el cubo usando la función solve, luego actualizará el # canvas con el cubo resuelto def GUISolve(): global canvas, SolutionLabel, SolutionNumberLabel, step_moves_list global PLLNumberLabel, F2LNumberLabel, CrossNumberLabel, OLLNumberLabel solve() SolutionLabel.configure(text=get_moves()) SolutionNumberLabel.configure(text=solution_length) CrossNumberLabel.configure(text=step_moves_list[0]) F2LNumberLabel.configure(text=step_moves_list[1]) OLLNumberLabel.configure(text=step_moves_list[2]) PLLNumberLabel.configure(text=step_moves_list[3]) canvas.delete(ALL) drawCube() # Esto permitirá al usuario avanzar por la solución un paso a la vez, el # parámetro step debe ser ya sea cross, f2l, OLL o PLL. Dependiendo de ello, # hará un paso diferente def GUIsetSolve(step): global SolutionLabel, SolutionNumberLabel, canvas, step_moves_list global PLLNumberLabel, F2LNumberLabel, CrossNumberLabel, OLLNumberLabel if step == "cross": cross() simplify_moves() step_moves_list[0] = solution_length elif step == "f2l" or step == "F2L": f2l() simplify_moves() step_moves_list[1] = solution_length - step_moves_list[0] elif step == "OLL": topCross() getfish bOLL() simplify_moves() step_moves_list[2] = solution_length - \ step_moves_list[1] - step_moves_list[0] elif step == "PLL": bPLL() simplify_moves() step_moves_list[3] = solution_length - step_moves_list[2] - \ step_moves_list[1] - step_moves_list[0] assert (isSolved()) SolutionLabel.configure(text=get_moves()) SolutionNumberLabel.configure(text=solution_length) CrossNumberLabel.configure(text=step_moves_list[0]) F2LNumberLabel.configure(text=step_moves_list[1]) OLLNumberLabel.configure(text=step_moves_list[2]) PLLNumberLabel.configure(text=step_moves_list[3]) canvas.delete(ALL) drawCube() # Esto se usa para copiar el string dado al portapapeles del usuario def GUItoClipboard(word): r = Tk() r.withdraw() r.clipboard_clear() r.clipboard_append(word) r.destroy() ''' Este era el intento de usar un temporizador para automatizar una solución, para usarlo, asegúrate de reactivar el botón e importar time #Esto se usa para una solución lenta pero automática. Utiliza las funciones del temporizador para hacer un par de movimientos por segundo o algo así def GUIautomateSolve(): global canvas, a b = copy.deepcopy(a) solve() simplify_moves() a = b for i in moves_list: move(i) canvas.after(200, drawCube()) ''' # Esto se usa para exportar la solución y resolución a alg.cubing.net. Verificará si puede abrirse con # Google Chrome, si no puede, intentará Firefox, de lo contrario, usará el navegador web predeterminado del sistema def GUIexportSolve(): sCopy = copy.deepcopy(get_scramble()) mCopy = copy.deepcopy(get_moves()) sCopy = str.replace(sCopy, "'", "-") sCopy = str.replace(sCopy, " ", "_") mCopy = str.replace(mCopy, "'", "-") mCopy = str.replace(mCopy, " ", "_") url = "alg.cubing.net/?setup=" + sCopy + "&alg=" + mCopy chrome_path = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe" firefox_path = "C:/Program Files/Mozilla Firefox/Firefox.exe" if os.path.exists(chrome_path): webbrowser.get(chrome_path + " %s").open(url) elif os.path.exists(firefox_path): webbrowser.get(firefox_path + " %s").open(url) else: webbrowser.open_new(url) # Esto se usa para la rotación del cubo con botones. Toma un movimiento Yi o Y # para ser ejecutado def GUIyRotation(given): global canvas if given == "Y" or given == "y": move('y') elif given == "Yi" or given == "Y'" or given == "yi" or given == "y'": move('yi') canvas.delete(ALL) drawCube() # Esto creará una nueva GUI de Información, después de cerrarla, se ejecutará la GUI principal() def InfoGUI(): rt = tk.Tk() rt.geometry("600x140+50+50") # tamaño del marco inicial rt.wm_title("Información del Resolutor de Cubos") # Solo permite que se ajuste la altura, no el ancho rt.resizable(True, True) InfoGUIy(rt) rt.mainloop() GUI() # Esto se llamará dentro de InfoGUI(), creará una bonita GUI con instrucciones def InfoGUIy(rt): frame = Frame(rt) frame.grid(row=0, column=0) wel = "Bienvenido al resolutor de cubos, aquí tienes algunas características:" instruct1 = "* Introduce tus propios movimientos, luego haz clic en 'ejecutar' para ejecutarlos" instruct2 = "* Haz clic en scramble para generar uno, o crea uno propio seleccionando 'scramble personalizado'" instruct3 = "* Haz clic en los dos botones de resolver para resolver, o resuelve paso a paso con los botones azules" instruct4 = "* Puedes copiar el scramble o la solución al portapapeles, o exportar a alg.cubing.net para visualizarlo" instruct5 = "* Ejecuta algunas simulaciones ingresando el número de scrambles a simular" InfoLabel = Label(frame, text=wel + "\n"+instruct1+"\n"+instruct2 + "\n"+instruct3+"\n"+instruct4+"\n"+instruct5, justify=LEFT) InfoLabel.grid(row=0, column=0) InfoQuitButton = Button(frame, text="Empezar a Resolver", fg="red", command=lambda: rt.destroy()) InfoQuitButton.grid(row=1, column=0) # Esto se usa para ejecutar simulaciones, utiliza la función de simulación. # Esta es la parte de la GUI para las simulaciones def GUISimulation(simNum): global SimulateBestLabel, SimulateWorstLabel simResults = simulation(simNum) s = StringVar(value=simResults[2]) GUIcustomScramble(s) GUISolve() SimulateBestLabel.configure(text=str(simResults[1] + 1) + " de " + str( simNum.get()) + " con " + str(solution_length) + " movimientos") SimulateWorstLabel.configure(text=str(simResults[4] + 1) + " de " + str( simNum.get()) + " con " + str(simResults[3]) + " movimientos") # GUI es la GUI principal que se creará, llama a GUIy que en realidad hace todo # el trabajo para la GUI def GUI(): global root root = tk.Tk() root.geometry("700x600+50+50") # tamaño del marco inicial root.wm_title("Resolutor de Cubos") # Solo permite que se ajuste la altura, no el ancho root.resizable(True, True) GUIy() root.mainloop() # GUIy, después de que la GUI misma se cree con GUI(), esto creará todos los # botones, etiquetas, etc., y los añadirá a un marco. Este es el trabajo detrás # de escenas para la propia GUI. def GUIy(): global root, canvas, ScrambleLabel, SolutionLabel, SolutionNumberLabel, frame, isTransparent global PLLNumberLabel, F2LNumberLabel, CrossNumberLabel, OLLNumberLabel, SimulateBestLabel, SimulateWorstLabel isTransparent = False canvas = drawCanvas(root) drawCube() # locales move = StringVar(value="") scram = StringVar(value="Introduce el Scramble Aquí") simNum = IntVar() # Número de Simulación # Marco para controles frame = Frame(root) frame.grid(row=0, column=1, sticky="n") # Marco para rotaciones del cubo Rframe = Frame(root) Rframe.grid(row=0, column=0, sticky="n") # fila 1 - etiqueta de bienvenida y botón de nuevo cubo Welcome = Label(frame, text="Bienvenido al Resolutor de Cubos").grid( row=1, column=0) NewCubeButton = Button(frame, text="Nuevo Cubo", command=lambda: GUInewCube()) NewCubeButton.grid(row=1, column=1) # fila 2 - etiqueta para indicar que introduzcas un movimiento para ejecutar EnterMove = Label(frame, text="Introduce movimiento(s):").grid(row=2, column=0) # fila 3 - Tiene entrada para movimientos personalizados así como botón para ejecutarlos MoveEntry = Entry(frame, textvariable=move).grid(row=3, column=0) DrawCubeButton = Button(frame, text="Ejecutar", command=lambda: GUImakeMove( move)).grid(row=3, column=1, sticky="w") # fila 4 - La etiqueta que imprimirá el scramble actual después de la generación ScrambleLabel = Label(frame, text="El scramble se mostrará aquí", wraplength=180, justify=CENTER, height=2) ScrambleLabel.grid(row=4, column=0, columnspan=2) # fila 5 - El botón scramble para generar un nuevo scramble y copiar scramble al portapapeles ScrambleButton = Button(frame, text="Scramble", bg="lightgreen", command=lambda: GUIScramble()).grid(row=5, column=0) CopyScrambleButton = Button(frame, text="Copiar Scramble", bg="#EF9", command=lambda: GUItoClipboard(get_scramble())).grid(row=5, column=1) # fila 6 - entrada para scramble personalizado y botón para aplicar scramble personalizado al cubo CustomScramEntry = Entry(frame, textvariable=scram) CustomScramEntry.grid(row=6, column=0, sticky="w") CustomScramButton = Button(frame, text="Scramble Personalizado", bg="lightgreen", command=lambda: GUIcustomScramble(scram)) CustomScramButton.grid(row=6, column=1) # fila 7 - Solución lenta (usando temporizador para hacerlo lentamente), solución instantánea (solución rápida e instantánea), copiar solución al portapapeles # SolveTimerButton = Button(frame, text="Solución Lenta", bg="#D53", command = lambda: GUIautomateSolve()).grid(row=7, column=0, sticky="w", pady=5) SolveButton = Button(frame, text="Resolver Cubo", bg="#D53", command=lambda: GUISolve( )).grid(row=7, column=0) # sticky="e" si también se usa el botón de temporizador CopyScrambleButton = Button(frame, text="Copiar Solución", bg="#EF9", command=lambda: GUItoClipboard(get_moves())).grid(row=7, column=1) # fila 8 - Botones de solución para hacer pasos independientemente CrossButton = Button(frame, text="Cross", bg="lightblue", command=lambda: GUIsetSolve("cross")) CrossButton.grid(row=8, column=0) F2LButton = Button(frame, text="F2l", bg="lightblue", command=lambda: GUIsetSolve("F2L")) F2LButton.grid(row=8, column=0, sticky="e", padx=15) OLLButton = Button(frame, text="OLL", bg="lightblue", command=lambda: GUIsetSolve("OLL")) OLLButton.grid(row=8, column=1, sticky="w") PLLButton = Button(frame, text="PLL", bg="lightblue", command=lambda: GUIsetSolve("PLL")) PLLButton.grid(row=8, column=1, sticky="e", padx=30) # fila 9 - la etiqueta que contiene la solución que se generará SolutionLabel = Label(frame, text="La solución se mostrará aquí", wraplength=250, justify=CENTER, height=8) SolutionLabel.grid(row=9, column=0, columnspan=2) # fila 10 - Etiquetas para el número de movimientos necesarios para resolver SolutionNumberInfoLabel = Label(frame, text="Número total de movimientos utilizados:") SolutionNumberInfoLabel.grid(row=10, column=0, sticky="e") SolutionNumberLabel = Label(frame, text="0") SolutionNumberLabel.grid(row=10, column=1, sticky="w") # fila 11, 12, 13, 14 - Etiquetas para el número de movimientos para los diferentes pasos CrossInfoLabel = Label(frame, text="Movimientos necesarios para Cross:") CrossInfoLabel.grid(row=11, column=0, sticky="e") CrossNumberLabel = Label(frame, text="0") CrossNumberLabel.grid(row=11, column=1, sticky="w") F2LInfoLabel = Label(frame, text="Movimientos necesarios para F2L:") F2LInfoLabel.grid(row=12, column=0, sticky="e") F2LNumberLabel = Label(frame, text="0") F2LNumberLabel.grid(row=12, column=1, sticky="w") OLLInfoLabel = Label(frame, text="Movimientos necesarios para OLL:") OLLInfoLabel.grid(row=13, column=0, sticky="e") OLLNumberLabel = Label(frame, text="0") OLLNumberLabel.grid(row=13, column=1, sticky="w") PLLInfoLabel = Label(frame, text="Movimientos necesarios para PLL:") PLLInfoLabel.grid(row=14, column=0, sticky="e") PLLNumberLabel = Label(frame, text="0") PLLNumberLabel.grid(row=14, column=1, sticky="w") # fila 15 - Exportando a alg.cubing.net ExportSolveButton = Button( frame, text="Exportar a alg.cubing.net", command=lambda: GUIexportSolve()) ExportSolveButton.grid(row=15, column=0) # fila 16 - Simulaciones para la mejor solución SimulateEntry = Entry(frame, textvariable=simNum) SimulateEntry.grid(row=16, column=0) SimulateButton = Button(frame, text="Iniciar Simulaciones", command=lambda: GUISimulation(simNum)) SimulateButton.grid(row=16, column=1) # fila 17 - Qué mejor se encontró SimulateBestInfo = Label(frame, text="Mejor Simulación: ") SimulateBestInfo.grid(row=17, column=0) SimulateBestLabel = Label(frame, text="") SimulateBestLabel.grid(row=17, column=1, sticky="w") # fila 18 Qué peor se encontró SimulateWorstInfo = Label(frame, text="Peor Simulación: ") SimulateWorstInfo.grid(row=18, column=0) SimulateWorstLabel = Label(frame, text="") SimulateWorstLabel.grid(row=18, column=1) # En Rframe, botones para rotación RotationLabel = Label(Rframe, text="Usa los botones de abajo para rotar el cubo").grid( row=0, column=0, columnspan=2) YrotationButton = Button(Rframe, text="<---- Y", command=lambda: GUIyRotation("Y")) YrotationButton.grid(row=1, column=0) YirotationButton = Button(Rframe, text="Y' ---->", command=lambda: GUIyRotation("Yi")) YirotationButton.grid(row=1, column=1) InfoGUI()