2012 lines
69 KiB
Python
2012 lines
69 KiB
Python
"""
|
|
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("<Button-1>", 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()
|