diff --git a/Cubo rubik.py b/Cubo rubik.py new file mode 100644 index 0000000..ba5de22 --- /dev/null +++ b/Cubo rubik.py @@ -0,0 +1,264 @@ +class CuboRubik: + def __init__(self): + # Cada cara del cubo se representa como una matriz de 3x3 + # Cada elemento de la matriz es un color, por ejemplo 'R' para rojo, 'G' para verde, etc. + self.cara_frontal = [['B' for _ in range(3)] for _ in range(3)] # Rojo + self.cara_izquierda = [['O' for _ in range(3)] for _ in range(3)] # Naranja + self.cara_derecha = [['R' for _ in range(3)] for _ in range(3)] # Verde + self.cara_trasera = [['G' for _ in range(3)] for _ in range(3)] # Blanco + self.cara_superior = [['Y' for _ in range(3)] for _ in range(3)] # Amarillo + self.cara_inferior = [['W' for _ in range(3)] for _ in range(3)] # Azul + + # self.cara_frontal = [['A','B','C'],['D','F','G'],['H','I','J']] + # self.cara_izquierda = [['1x','2x','3x'],['4x','5x','6x'],['7x','8x','9x']] + # self.cara_derecha = [['1y','2y','3y'],['4y','5y','6y'],['7y','8y','9y']] + # self.cara_trasera = [['a','b','c'],['d','f','g'],['h','i','j']] + # self.cara_superior = [['1z','2z','3z'],['4z','5z','6z'],['7z','8z','9z']] + # self.cara_inferior = [['1w','2w','3w'],['4w','5w','6w'],['7w','8w','9w']] + + # Pretty print provisional que imprime cada matriz (cara) del cubo + def notSoPrettyPrint(self): + print("\nCara Frontal:") + for fila in self.cara_frontal: + print(' '.join(fila)) + print("\nCara Izquierda:") + for fila in self.cara_izquierda: + print(' '.join(fila)) + print("\nCara Derecha:") + for fila in self.cara_derecha: + print(' '.join(fila)) + print("\nCara Trasera:") + for fila in self.cara_trasera: + print(' '.join(fila)) + print("\nCara Superior:") + for fila in self.cara_superior: + print(' '.join(fila)) + print("\nCara Inferior:") + for fila in self.cara_inferior: + print(' '.join(fila)) + + + # Método para rotar una cara del cubo 90° en sentido horario + # Por ejemplo, si tenemos + # 1 2 3 + # 4 5 6 + # 7 8 9 + # después de rotarla, debería quedar + # 7 4 1 + # 8 5 2 + # 9 6 3 + def rotar_cara(self, cara): + if cara == 'frontal': + self.cara_frontal = [list(x) for x in zip(*self.cara_frontal[::-1])] + elif cara == 'izquierda': + self.cara_izquierda = [list(x) for x in zip(*self.cara_izquierda[::-1])] + elif cara == 'derecha': + self.cara_derecha = [list(x) for x in zip(*self.cara_derecha[::-1])] + elif cara == 'trasera': + self.cara_trasera = [list(x) for x in zip(*self.cara_trasera[::-1])] + elif cara == 'superior': + self.cara_superior = [list(x) for x in zip(*self.cara_superior[::-1])] + elif cara == 'inferior': + self.cara_inferior = [list(x) for x in zip(*self.cara_inferior[::-1])] + else: + print("Cara no válida") + + # Método que con una cara dada, llama a la función rotar_cara con la cara dada + # y luego mueve las piezas adyacentes a esa cara. Es básicamente para manejar + # las piezas adyacentes. + # Por ejemplo, si movemos la cara frontal, las piezas adyacentes a la cara frontal + # deben moverse a la derecha, arriba, izquierda y abajo + def mover(self, movimiento): + if movimiento == 'F': + self.rotar_cara('frontal') + + temp = [self.cara_izquierda[i][2] for i in range(3)] #guarda la columna derecha de la cara izquierda + temp2 = [self.cara_derecha[i][0] for i in range(3)] #guarda la columna izquierda de la cara derecha + temp3 = self.cara_superior[2][:] #guarda la fila inferior de la cara superior + temp4 = self.cara_inferior[0][:] #guarda la fila superior de la cara inferior + + # print("columna derecha de la cara izquierda - temp3: ",temp3) + # print("columna izquierda de la cara derecha - temp4: ",temp4) + # print("fila inferior de la cara superior - temp: ",temp) + # print("fila superior de la cara inferior - temp2: ",temp2) + + # Cambia la cara superior + for i in range(3): + self.cara_superior[2][i] = temp[::-1][i] + # Cambia la cara inferior + for i in range(3): + self.cara_inferior[0][i] = temp2[::-1][i] + # Cambia la cara izquierda + for i in range(3): + self.cara_izquierda[i][2] = temp4[i] + # Cambia la cara derecha + for i in range(3): + self.cara_derecha[i][0] = temp3[i] + + elif movimiento == 'L': + self.rotar_cara('izquierda') + + temp = [self.cara_frontal[i][0] for i in range(3)] #guarda la columna izquierda de la cara frontal + temp2 = [self.cara_trasera[i][2] for i in range(3)] #guarda la columna derecha de la cara trasera + temp3 = [self.cara_superior[i][0] for i in range(3)] #guarda la columna izquierda de la cara superior + temp4 = [self.cara_inferior[i][0] for i in range(3)] #guarda la columna izquierda de la cara inferior + + # print("columna izquierda de la cara frontal - temp: ",temp) + # print("columna derecha de la cara trasera - temp2: ",temp2) + # print("columna izquierda de la cara superior - temp3: ",temp3) + # print("columna izquierda de la cara inferior - temp4: ",temp4) + + # Cambia la cara superior + for i in range(3): + self.cara_superior[i][0] = temp2[::-1][i] + # Cambia la cara inferior + for i in range(3): + self.cara_inferior[i][0] = temp[i] + # Cambia la cara frontal + for i in range(3): + self.cara_frontal[i][0] = temp3[i] + # Cambia la cara trasera + for i in range(3): + self.cara_trasera[i][2] = temp4[::-1][i] + + elif movimiento == 'R': + self.rotar_cara('derecha') + + temp = [self.cara_frontal[i][2] for i in range(3)] #guarda la columna derecha de la cara frontal + temp2 = [self.cara_trasera[i][0] for i in range(3)] #guarda la columna izquierda de la cara trasera + temp3 = [self.cara_superior[i][2] for i in range(3)] #guarda la columna derecha de la cara superior + temp4 = [self.cara_inferior[i][2] for i in range(3)] #guarda la columna derecha de la cara inferior + + # print("columna derecha de la cara frontal - temp: ",temp) + # print("columna izquierda de la cara trasera - temp2: ",temp2) + # print("columna derecha de la cara superior - temp3: ",temp3) + # print("columna derecha de la cara inferior - temp2: ",temp4) + + # Cambia la cara superior + for i in range(3): + self.cara_superior[i][2] = temp[i] + # Cambia la cara inferior + for i in range(3): + self.cara_inferior[i][2] = temp2[::-1][i] + # Cambia la cara frontal + for i in range(3): + self.cara_frontal[i][2] = temp4[i] + # Cambia la cara trasera + for i in range(3): + self.cara_trasera[i][0] = temp3[::-1][i] + + elif movimiento == 'B': + self.rotar_cara('trasera') + + temp = [self.cara_izquierda[i][0] for i in range(3)] #guarda la columna izquierda de la cara izquierda + temp2 = [self.cara_derecha[i][2] for i in range(3)] #guarda la columna derecha de la cara derecha + temp3 = self.cara_superior[0][:] #guarda la fila superior de la cara superior + temp4 = self.cara_inferior[2][:] #guarda la fila inferior de la cara inferior + + # print("columna izquierda de la cara izquierda - temp: ",temp) + # print("columna derecha de la cara derecha - temp2: ",temp2) + # print("fila superior de la cara superior - temp3: ",temp3) + # print("fila inferior de la cara inferior - temp4: ",temp4) + + # Cambia la cara superior + for i in range(3): + self.cara_superior[0][i] = temp2[i] + # Cambia la cara inferior + for i in range(3): + self.cara_inferior[2][i] = temp[i] + # Cambia la cara izquierda + for i in range(3): + self.cara_izquierda[i][0] = temp3[::-1][i] + # Cambia la cara derecha + for i in range(3): + self.cara_derecha[i][2] = temp4[::-1][i] + + elif movimiento == 'U': + self.rotar_cara('superior') + + temp = self.cara_frontal[0][:] #guarda la fila superior de la cara frontal + temp2 = self.cara_izquierda[0][:] #guarda la fila superior de la cara izquierda + temp3 = self.cara_derecha[0][:] #guarda la fila superior de la cara derecha + temp4 = self.cara_trasera[0][:] #guarda la fila superior de la cara trasera + + # print("fila superior de la cara frontal - temp: ",temp) + # print("fila superior de la cara izquierda - temp2: ",temp2) + # print("fila superior de la cara derecha - temp3: ",temp3) + # print("fila superior de la cara trasera - temp4: ",temp4) + + # Cambia la cara izquierda + for i in range(3): + self.cara_izquierda[0][i] = temp[i] + # Cambia la cara derecha + for i in range(3): + self.cara_derecha[0][i] = temp4[i] + # Cambia la cara frontal + for i in range(3): + self.cara_frontal[0][i] = temp3[i] + # Cambia la cara trasera + for i in range(3): + self.cara_trasera[0][i] = temp2[i] + + elif movimiento == 'D': + self.rotar_cara('inferior') + + temp = self.cara_frontal[2][:] #guarda la fila inferior de la cara frontal + temp2 = self.cara_izquierda[2][:] #guarda la fila inferior de la cara izquierda + temp3 = self.cara_derecha[2][:] #guarda la fila inferior de la cara derecha + temp4 = self.cara_trasera[2][:] #guarda la fila inferior de la cara trasera + + # print("fila inferior de la cara frontal - temp: ",temp) + # print("fila inferior de la cara izquierda - temp2: ",temp2) + # print("fila inferior de la cara derecha - temp3: ",temp3) + # print("fila inferior de la cara trasera - temp4: ",temp4) + + # Cambia la cara izquierda + for i in range(3): + self.cara_izquierda[2][i] = temp4[i] + # Cambia la cara derecha + for i in range(3): + self.cara_derecha[2][i] = temp[i] + # Cambia la cara frontal + for i in range(3): + self.cara_frontal[2][i] = temp2[i] + # Cambia la cara trasera + for i in range(3): + self.cara_trasera[2][i] = temp3[i] + +cubo = CuboRubik() +cubo.notSoPrettyPrint() + +cubo.mover('R') +cubo.mover('R') +cubo.mover('R') +cubo.mover('U') +cubo.mover('U') +cubo.mover('U') + +print("\nEstado inicial:") +cubo.notSoPrettyPrint() + +cubo.mover('U') +cubo.mover('R') + +print("\nCubo arreglado despues de girar U y R:") +cubo.notSoPrettyPrint() + + + # """ + # Esta heurística es sobre el tiempo usando el camión máximo posible y + # luego caminando. Es admisible porque asume el mejor escenario posible con el camión. + + # """ + # #problema = nodo.problema + # N = nodo.problema.N + # x_actual = nodo.estado[0] + # if x_actual >= N: + # return 0 + # k = 0 + # while x_actual * (2 ** (k + 1)) <= N: + # k += 1 + # tiempo_camion = 2 * k + # nueva_x = x_actual * (2 ** k) + # restante = N - nueva_x + # return tiempo_camion + restante diff --git a/README.md b/README.md index 4378aee..aac96e7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ y conjuntos (como estructura de datos). ## Instrucciones: -1. En el archivo `busquedas.py` se encuentra la clase abstracta `Problema` (para búsquedas), la clase `Nodo`, para la estructura de datos que nos ayudará en la búsqueda, así como ls 4 búsquedas no informadas más utilizadas: BFS, DFS, IDS y UCS. Estas búsquedas las vimos en clase y no vamos a repetir. +1. En el archivo `busquedas.py` se encuentra la clase abstracta `Problema` (para búsquedas), la clase `Nodo`, para la estructura de datos que nos ayudará en la búsqueda, así como las 4 búsquedas no informadas más utilizadas: BFS, DFS, IDS y UCS. Estas búsquedas las vimos en clase y no vamos a repetir. En clase se dio un ejemplo de el uso de dichas búsquedas. Revisa y familiarizate con dichas clases y funciones. Si es necesario desarrolla un problema pequeñito para que las pruebes (el problema de los *misioneros y los canibales* es un ejemplo fácil de programar y fácil de aplicar). diff --git a/busquedas.py b/busquedas.py index a52165c..bedb950 100755 --- a/busquedas.py +++ b/busquedas.py @@ -106,7 +106,9 @@ def __init__(self, estado, accion=None, padre=None, costo_local=0): Inicializa un nodo como una estructura """ + self.estado = estado + #self.estado = tuple(tuple(tuple(row) for row in cara) for cara in estado) self.accion = accion self.padre = padre self.costo = 0 if not padre else padre.costo + costo_local @@ -125,6 +127,7 @@ def expande(self, modelo): return ( Nodo( modelo.sucesor(self.estado, a), + #tuple(tuple(tuple(row) for row in cara) for cara in modelo.sucesor(self.estado, a)), a, self, modelo.costo_local(self.estado, a)) @@ -283,5 +286,27 @@ def busqueda_A_estrella(problema, heuristica): @return Un objeto tipo Nodo con la estructura completa """ - raise NotImplementedError('Hay que hacerlo de tarea \ - (problema 2 en el archivo busquedas.py)') + raiz = Nodo(problema.x0) + frontera = [] + heapq.heappush(frontera, (heuristica(raiz) + raiz.costo, raiz)) # f(n) = g(n) + h(n) + visitados = {problema.x0: 0} + #visitados = {raiz.estado: 0} + + while frontera: + (_, nodo_actual) = heapq.heappop(frontera) + + if problema.es_meta(nodo_actual.estado): + nodo_actual.nodos_visitados = problema.num_nodos + return nodo_actual + + for hijo in nodo_actual.expande(problema.modelo): + #nuevo_estado = tuple(tuple(tuple(row) for row in cara) for cara in hijo.estado) + + g_n = hijo.costo + h_n = heuristica(hijo) + f_n = g_n + h_n + + if hijo.estado not in visitados or f_n < visitados[hijo.estado]: + visitados[hijo.estado] = f_n + heapq.heappush(frontera, (f_n, hijo)) + return None diff --git a/dos_botes.py b/dos_botes.py index 6cf83fe..6af070b 100644 --- a/dos_botes.py +++ b/dos_botes.py @@ -7,7 +7,7 @@ Una pequeña aplicación de las búsquedas """ -__author__ = 'nombre del estudiante' +__author__ = 'Georgina Salcido' import busquedas diff --git a/ocho_puzzle.py b/ocho_puzzle.py index 982cad6..a073331 100755 --- a/ocho_puzzle.py +++ b/ocho_puzzle.py @@ -155,19 +155,19 @@ def probando(pos_ini): print(solucion) print("Explorando {} nodos\n\n".format(solucion.nodos_visitados)) - # # ------- A* con h1 ----------- - # print("---------- Utilizando A* con h1 -------------") - # problema = Ocho_puzzle(pos_ini) - # solucion = busquedas.busqueda_A_estrella(problema, h_1) - # print(solucion) - # print("Explorando {} nodos".format(solucion.nodos_visitados)) - - # # ------- A* con h2 ----------- - # print("---------- Utilizando A* con h2 -------------") - # problema = Ocho_puzzle(pos_ini) - # solucion = busquedas.busqueda_A_estrella(problema, h_2) - # print(solucion) - # print("Explorando {} nodos".format(solucion.nodos_visitados)) + # ------- A* con h1 ----------- + print("---------- Utilizando A* con h1 -------------") + problema = Ocho_puzzle(pos_ini) + solucion = busquedas.busqueda_A_estrella(problema, h_1) + print(solucion) + print("Explorando {} nodos".format(solucion.nodos_visitados)) + + # ------- A* con h2 ----------- + print("---------- Utilizando A* con h2 -------------") + problema = Ocho_puzzle(pos_ini) + solucion = busquedas.busqueda_A_estrella(problema, h_2) + print(solucion) + print("Explorando {} nodos".format(solucion.nodos_visitados)) if __name__ == "__main__": diff --git a/problemas.py b/problemas.py index b6b0bd1..f0556c1 100755 --- a/problemas.py +++ b/problemas.py @@ -9,15 +9,14 @@ """ import busquedas - - +from collections import deque # ------------------------------------------------------------ # Desarrolla el modelo del Camión mágico # ------------------------------------------------------------ -class CamionMagico.busquedas.ModeloBusqueda): - """ +class CamionMagico(busquedas.ModeloBusqueda): + """x --------------------------------------------------------------------------------- Supongamos que quiero trasladarme desde la posición discreta $1$ hasta la posicion discreta $N$ en una vía recta usando un camión mágico. @@ -31,51 +30,81 @@ class CamionMagico.busquedas.ModeloBusqueda): ---------------------------------------------------------------------------------- """ - def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') + def __init__(self, N): + self.N = N def acciones_legales(self, estado): - raise NotImplementedError('Hay que hacerlo de tarea') + acciones = [] + + if estado + 1 <= self.N: + acciones.append("caminar") + if 2 * estado <= self.N: + acciones.append("camion") + + return acciones def sucesor(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + if accion == 'caminar': + return estado + 1 + elif accion == 'camion': + return 2 * estado + else: + raise ValueError("Juégale bien, caminas o usas el camión, no hay de otra...") def costo_local(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + if accion == "caminar": + return 1 + if accion == "camion": + return 2 @staticmethod - def bonito(estado): + def bonito(self, estado): """ El prettyprint de un estado dado """ - raise NotImplementedError('Hay que hacerlo de tarea') + print("-" * (self.N + 2)) + print(" ".join(str(i) if i <= self.N else " " for i in range(1, self.N + 1))) + print(" " * (2 * (estado - 1)) + "🚛") + print("-" * (self.N + 2)) # ------------------------------------------------------------ # Desarrolla el problema del Camión mágico # ------------------------------------------------------------ - class PblCamionMágico(busquedas.ProblemaBusqueda): """ El problema a resolver es establecer un plan para ir desde el punto $1$ hasta el punto $N$ en el menor tiempo posible. """ - def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') - + def __init__(self, N): + super().__init__(1, lambda estado: estado == N, CamionMagico(N)) + self.N = N + # ------------------------------------------------------------ # Desarrolla una política admisible. # ------------------------------------------------------------ - -def h_1_camion_magico(nodo): +def h_1_camion_magico(nodo, N): """ - DOCUMENTA LA HEURÍSTICA QUE DESARROLLES Y DA UNA JUSTIFICACIÓN - PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - + Heurística 1: Distancia mínima considerando movimientos a pie y el camión + + Justificación: + Utiliza el camión lo más posible para avanzar lo máximo posible y luego + camina, obteniendo así el costo mínimo. """ - return 0 + estado_actual = nodo.estado + if estado_actual >= N: + return 0 # por si ya estamos en la meta + + saltos = 0 + while estado_actual * 2 <= N: + estado_actual *= 2 + saltos += 1 + + distancia_restante = N - estado_actual + + return min(2 * saltos + distancia_restante, N - nodo.estado) # ------------------------------------------------------------ @@ -83,70 +112,291 @@ def h_1_camion_magico(nodo): # Analiza y di porque piensas que es (o no es) dominante una # respecto otra política # ------------------------------------------------------------ - -def h_2_camion_magico(nodo): +def h_2_camion_magico(nodo, N): """ - DOCUMENTA LA HEURÍSTICA DE DESARROLLES Y DA UNA JUSTIFICACIÓN - PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE + Heurística 2: Distancia mínima considerando el camión mágico. + Justificación: + Si usamos el camión, podemos reducir la distancia mucho más rápido. + La idea es contar cuántos saltos de camión son posibles hasta N. """ - return 0 + return N - (2 * nodo.estado) + # ------------------------------------------------------------ # Desarrolla el modelo del cubo de Rubik # ------------------------------------------------------------ - -class CuboRubik.busquedas.ModeloBusqueda): +class CuboRubik(busquedas.ModeloBusqueda): """ - La clase para el modelo de cubo de rubik, documentación, no olvides poner - la documentación de forma clara y concisa. - - https://en.wikipedia.org/wiki/Rubik%27s_Cube - + El problema del cubo rubik. + + El estado es un conjunto de 6 matrices de 3x3 en representación de un cubo, + donde cada elemento de la matriz es un color. Por ejemplo, 'R' para rojo, + 'G' para verde, etc. + + Por ejemplo, la cara frontal iniciaría como una cara completamente azul. + ------------- + | B | B | B | + ------------- + | B | B | B | + ------------- + | B | B | B | + ------------ + + Además, es posible rotar sus caras en sentido horario, afectando así a los + elementos adyacentes. Siendo así, estas rotaciones sus acciones legales. + + Acciones_legales = {'F', 'L', 'R', 'B', 'U', 'D'} + """ def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') + self.cara_frontal = tuple(tuple('B' for _ in range(3)) for _ in range(3)) # Rojo + self.cara_izquierda = [['O' for _ in range(3)] for _ in range(3)] # Naranja + self.cara_derecha = [['R' for _ in range(3)] for _ in range(3)] # Verde + self.cara_trasera = [['G' for _ in range(3)] for _ in range(3)] # Blanco + self.cara_superior = [['Y' for _ in range(3)] for _ in range(3)] # Amarillo + self.cara_inferior = [['W' for _ in range(3)] for _ in range(3)] # Azul + + self.estado = self.cara_frontal, self.cara_izquierda, self.cara_derecha, self.cara_trasera, self.cara_superior, self.cara_inferior def acciones_legales(self, estado): - raise NotImplementedError('Hay que hacerlo de tarea') + return {'F', 'L', 'R', 'B', 'U', 'D'} def sucesor(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + """ + Rota la cara 90° en sentido horario. + + Regresa una matríz con sus elementos traspuestos en sentido del reloj. + """ + def rotar_cara(cara): + return [list(x) for x in zip(*cara[::-1])] + + """ + Mueve las piezas adyacentes. + + Al rotar una cara, se espera que se muevan las piezas adyacentes a esa cara. + + Regresa el conjunto de matrices que conforman al estado actualizado. + """ + def mover(estado, movimiento): + caras = [[list(fila[:]) for fila in cara] for cara in estado] + cara_frontal, cara_izquierda, cara_derecha, cara_trasera, cara_superior, cara_inferior = caras + + #print("Movimiento: ", movimiento) + if movimiento == 'F': + cara_frontal = rotar_cara(cara_frontal) #rota la cara frontal 90° en el sentido del reloj + temp = [cara_izquierda[i][2] for i in range(3)] #guarda la columna derecha de la cara izquierda + temp2 = [cara_derecha[i][0] for i in range(3)] #guarda la columna izquierda de la cara derecha + temp3 = cara_superior[2][:] #guarda la fila inferior de la cara superior + temp4 = cara_inferior[0][:] #guarda la fila superior de la cara inferior + # Cambia la cara superior + for i in range(3): + cara_superior[2][i] = temp[::-1][i] + # Cambia la cara inferior + for i in range(3): + cara_inferior[0][i] = temp2[::-1][i] + # Cambia la cara izquierda + for i in range(3): + cara_izquierda[i][2] = temp4[i] + # Cambia la cara derecha + for i in range(3): + cara_derecha[i][0] = temp3[i] + #print("Cara front dsp de cambio: ", cara_frontal) + + elif movimiento == 'L': + cara_izquierda = rotar_cara(cara_izquierda) #rota la cara izquierda 90° en sentido del reloj + temp = [cara_frontal[i][0] for i in range(3)] #guarda la columna izquierda de la cara frontal + temp2 = [cara_trasera[i][2] for i in range(3)] #guarda la columna derecha de la cara trasera + temp3 = [cara_superior[i][0] for i in range(3)] #guarda la columna izquierda de la cara superior + temp4 = [cara_inferior[i][0] for i in range(3)] #guarda la columna izquierda de la cara inferior + # Cambia la cara superior + for i in range(3): + cara_superior[i][0] = temp2[::-1][i] + # Cambia la cara inferior + for i in range(3): + cara_inferior[i][0] = temp[i] + # Cambia la cara frontal + for i in range(3): + cara_frontal[i][0] = temp3[i] + # Cambia la cara trasera + for i in range(3): + cara_trasera[i][2] = temp4[::-1][i] + #print("Cara izq dsp de cambio: ", cara_izquierda) + + elif movimiento == 'R': + cara_derecha = rotar_cara(cara_derecha) #rota la cara derecha 90° en sentido del reloj + temp = [cara_frontal[i][2] for i in range(3)] #guarda la columna derecha de la cara frontal + temp2 = [cara_trasera[i][0] for i in range(3)] #guarda la columna izquierda de la cara trasera + temp3 = [cara_superior[i][2] for i in range(3)] #guarda la columna derecha de la cara superior + temp4 = [cara_inferior[i][2] for i in range(3)] #guarda la columna derecha de la cara inferior + # Cambia la cara superior + for i in range(3): + cara_superior[i][2] = temp[i] + # Cambia la cara inferior + for i in range(3): + cara_inferior[i][2] = temp2[::-1][i] + # Cambia la cara frontal + for i in range(3): + cara_frontal[i][2] = temp4[i] + # Cambia la cara trasera + for i in range(3): + cara_trasera[i][0] = temp3[::-1][i] + #print("Cara der dsp de cambio: ", cara_derecha) + + elif movimiento == 'B': + cara_trasera = rotar_cara(cara_trasera) + temp = [cara_izquierda[i][0] for i in range(3)] #guarda la columna izquierda de la cara izquierda + temp2 = [cara_derecha[i][2] for i in range(3)] #guarda la columna derecha de la cara derecha + temp3 = cara_superior[0][:] #guarda la fila superior de la cara superior + temp4 = cara_inferior[2][:] #guarda la fila inferior de la cara inferior + # Cambia la cara superior + for i in range(3): + self.cara_superior[0][i] = temp2[i] + # Cambia la cara inferior + for i in range(3): + self.cara_inferior[2][i] = temp[i] + # Cambia la cara izquierda + for i in range(3): + self.cara_izquierda[i][0] = temp3[::-1][i] + # Cambia la cara derecha + for i in range(3): + self.cara_derecha[i][2] = temp4[::-1][i] + #print("Cara tras dsp de cambio: ", cara_trasera) + + elif movimiento == 'U': + cara_superior = rotar_cara(cara_superior) + temp = cara_frontal[0][:] #guarda la fila superior de la cara frontal + temp2 = cara_izquierda[0][:] #guarda la fila superior de la cara izquierda + temp3 = cara_derecha[0][:] #guarda la fila superior de la cara derecha + temp4 = cara_trasera[0][:] #guarda la fila superior de la cara trasera + # Cambia la cara izquierda + for i in range(3): + cara_izquierda[0][i] = temp[i] + # Cambia la cara derecha + for i in range(3): + cara_derecha[0][i] = temp4[i] + # Cambia la cara frontal + for i in range(3): + cara_frontal[0][i] = temp3[i] + # Cambia la cara trasera + for i in range(3): + cara_trasera[0][i] = temp2[i] + #print("Cara sup dsp de cambio: ", cara_superior) + + elif movimiento == 'D': + cara_inferior = rotar_cara(cara_inferior) + temp = cara_frontal[2][:] #guarda la fila inferior de la cara frontal + temp2 = cara_izquierda[2][:] #guarda la fila inferior de la cara izquierda + temp3 = cara_derecha[2][:] #guarda la fila inferior de la cara derecha + temp4 = cara_trasera[2][:] #guarda la fila inferior de la cara trasera + # Cambia la cara izquierda + for i in range(3): + cara_izquierda[2][i] = temp4[i] + # Cambia la cara derecha + for i in range(3): + cara_derecha[2][i] = temp[i] + # Cambia la cara frontal + for i in range(3): + cara_frontal[2][i] = temp2[i] + # Cambia la cara trasera + for i in range(3): + cara_trasera[2][i] = temp3[i] + #print("Cara inf dsp de cambio: ", cara_inferior) + + nuevo_estado = tuple(tuple(tuple(fila) for fila in cara) for cara in [cara_frontal, cara_izquierda, cara_derecha, cara_trasera, cara_superior, cara_inferior]) + return nuevo_estado + + estado_actualizado = mover(estado,accion) + return estado_actualizado def costo_local(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + return 1 @staticmethod def bonito(estado): - """ - El prettyprint de un estado dado - - """ - raise NotImplementedError('Hay que hacerlo de tarea') + cara_frontal, cara_izquierda, cara_derecha, cara_trasera, cara_superior, cara_inferior = estado + + print("\nCara Frontal:") + for fila in cara_frontal: + print(' '.join(fila)) + print("\nCara Izquierda:") + for fila in cara_izquierda: + print(' '.join(fila)) + print("\nCara Derecha:") + for fila in cara_derecha: + print(' '.join(fila)) + print("\nCara Trasera:") + for fila in cara_trasera: + print(' '.join(fila)) + print("\nCara Superior:") + for fila in cara_superior: + print(' '.join(fila)) + print("\nCara Inferior:") + for fila in cara_inferior: + print(' '.join(fila)) - # ------------------------------------------------------------ + +# ------------------------------------------------------------ # Desarrolla el problema del Cubo de Rubik # ------------------------------------------------------------ - class PblCuboRubik(busquedas.ProblemaBusqueda): """ El problema a resolver es establecer un plan para resolver el cubo de rubik. """ def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') - + print("Este estado inicial se giró la cara derecha 3 veces y la cara superior 3 veces.") + print("Estado inicial: ") + + estado_inicial = tuple( + tuple(tuple(row) for row in cara) for cara in [ + [['O', 'O', 'O'], ['B', 'B', 'Y'], ['B', 'B', 'Y']], # Frontal + [['W', 'G', 'G'], ['O', 'O', 'O'], ['O', 'O', 'O']], # Izquierda + [['B', 'B', 'Y'], ['R', 'R', 'R'], ['R', 'R', 'R']], # Derecha + [['R', 'R', 'R'], ['W', 'G', 'G'], ['W', 'G', 'G']], # Trasera + [['G', 'G', 'G'], ['Y', 'Y', 'Y'], ['Y', 'Y', 'Y']], # Superior + [['W', 'W', 'B'], ['W', 'W', 'B'], ['W', 'W', 'B']] # Inferior + ] + ) + + CuboRubik.bonito(estado_inicial) + print("\nSe necesita girar la cara superior 1 vez y luego la cara derecha 1 vez para solucionarlo.") + print("Estado meta: ") + + modelo = CuboRubik() + + #meta = lambda estado: estado == tuple(tuple(tuple(row) for row in cara) for cara in modelo.estado) + meta = tuple(tuple(tuple(row) for row in cara) for cara in modelo.estado) + CuboRubik.bonito(meta) + + super().__init__(estado_inicial, lambda estado: estado == meta, modelo) + # ------------------------------------------------------------ # Desarrolla una política admisible. # ------------------------------------------------------------ def h_1_problema_1(nodo): """ - DOCUMENTA LA HEURÍSTICA QUE DESARROLLES Y DA UNA JUSTIFICACIÓN - PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE + Calcula la heurística basada en la cantidad de movimientos que necesita + cada esquina y cada arista para llegar a su posición y orientación correctas. + @param nodo: Nodo del cubo de Rubik con el estado actual del cubo. + @return: Estimación del número mínimo de movimientos necesarios para resolver el cubo. """ - return 0 + estado = nodo.estado # Obtenemos las 6 caras del cubo + meta = CuboRubik() # Estado resuelto del cubo + meta_estado = meta.estado + + movimientos_necesarios = 0 + + # Comparar cada cara del cubo con la meta + for cara_actual, cara_objetivo in zip(estado, meta_estado): + for i in range(3): + for j in range(3): + if cara_actual[i][j] != cara_objetivo[i][j]: # Si el color no está en su lugar + movimientos_necesarios += 1 # Se asume que al menos un movimiento será necesario + + return movimientos_necesarios / 8 # ------------------------------------------------------------ @@ -156,12 +406,46 @@ def h_1_problema_1(nodo): # ------------------------------------------------------------ def h_2_problema_1(nodo): """ - DOCUMENTA LA HEURÍSTICA DE DESARROLLES Y DA UNA JUSTIFICACIÓN - PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - + Heurística admisible para el Cubo de Rubik: + Cuenta el número de esquinas y aristas mal ubicadas y lo divide por 8. """ - return 0 - + estado = nodo.estado # Extraemos el estado actual del nodo + + # Estado final + estado_meta = ( + (('B', 'B', 'B'), ('B', 'B', 'B'), ('B', 'B', 'B')), # Frontal + (('O', 'O', 'O'), ('O', 'O', 'O'), ('O', 'O', 'O')), # Izquierda + (('R', 'R', 'R'), ('R', 'R', 'R'), ('R', 'R', 'R')), # Derecha + (('G', 'G', 'G'), ('G', 'G', 'G'), ('G', 'G', 'G')), # Trasera + (('Y', 'Y', 'Y'), ('Y', 'Y', 'Y'), ('Y', 'Y', 'Y')), # Superior + (('W', 'W', 'W'), ('W', 'W', 'W'), ('W', 'W', 'W')) # Inferior + ) + + esquinas_mal = 0 + aristas_mal = 0 + + esquinas = [ + (0, 0, 0), (0, 0, 2), (0, 2, 0), (0, 2, 2), # Frontal + (3, 0, 0), (3, 0, 2), (3, 2, 0), (3, 2, 2) # Trasera + ] + + aristas = [ + (0, 0, 1), (0, 1, 0), (0, 1, 2), (0, 2, 1), # Frontal + (1, 0, 1), (1, 1, 0), (1, 1, 2), (1, 2, 1), # Izquierda + (2, 0, 1), (2, 1, 0), (2, 1, 2), (2, 2, 1) # Derecha + ] + + # Contar esquinas incorrectas + for cara, fila, col in esquinas: + if estado[cara][fila][col] != estado_meta[cara][fila][col]: + esquinas_mal += 1 + + # Contar aristas incorrectas + for cara, fila, col in aristas: + if estado[cara][fila][col] != estado_meta[cara][fila][col]: + aristas_mal += 1 + + return (esquinas_mal + aristas_mal) / 8 def compara_metodos(problema, heuristica_1, heuristica_2): @@ -193,17 +477,31 @@ def compara_metodos(problema, heuristica_1, heuristica_2): + str(solucion2.nodos_visitados)) print('-' * 50 + '\n\n') +def crear_heuristicas(N): + def h1(nodo): + current_x = nodo.estado + return max(N - current_x, 0) + + def h2(nodo): + current_x = nodo.estado + if current_x >= N: + return 0 + k = 0 + while current_x * (2 ** (k + 1)) <= N: + k += 1 + return 2 * k + (N - current_x * (2 ** k)) + return h1, h2 if __name__ == "__main__": - - # Compara los métodos de búsqueda para el problema del camión mágico - # con las heurísticas que desarrollaste - problema = PblCamionMágico( XXXXXXXXXX ) # <--- PONLE LOS PARÁMETROS QUE NECESITES - compara_metodos(problema, h_1_camion_magico, h_2_camion_magico) + # # Compara los métodos de búsqueda para el problema del camión mágico + # # con las heurísticas que desarrollaste + N = 15 + problema = PblCamionMágico(N) + h_1, h_2 = crear_heuristicas(N) + compara_metodos(problema, h_1, h_2) # Compara los métodos de búsqueda para el problema del cubo de rubik # con las heurísticas que desarrollaste - problema = PblCuboRubik( XXXXXXXXXX ) # <--- PONLE LOS PARÁMETROS QUE NECESITES + problema = PblCuboRubik() compara_metodos(problema, h_1_problema_1, h_2_problema_1) - \ No newline at end of file