|
| 1 | +""" |
| 2 | +* EJERCICIO: |
| 3 | + * ¡Disney ha presentado un montón de novedades en su D23! |
| 4 | + * Pero... ¿Dónde está Mickey? |
| 5 | + * Mickey Mouse ha quedado atrapado en un laberinto mágico |
| 6 | + * creado por Maléfica. |
| 7 | + * Desarrolla un programa para ayudarlo a escapar. |
| 8 | + * Requisitos: |
| 9 | + * 1. El laberinto está formado por un cuadrado de 6x6 celdas. |
| 10 | + * 2. Los valores de las celdas serán: |
| 11 | + * - ⬜️ Vacío |
| 12 | + * - ⬛️ Obstáculo |
| 13 | + * - 🐭 Mickey |
| 14 | + * - 🚪 Salida |
| 15 | + * Acciones: |
| 16 | + * 1. Crea una matriz que represente el laberinto (no hace falta |
| 17 | + * que se genere de manera automática). |
| 18 | + * 2. Interactúa con el usuario por consola para preguntarle hacia |
| 19 | + * donde se tiene que desplazar (arriba, abajo, izquierda o derecha). |
| 20 | + * 3. Muestra la actualización del laberinto tras cada desplazamiento. |
| 21 | + * 4. Valida todos los movimientos, teniendo en cuenta los límites |
| 22 | + * del laberinto y los obtáculos. Notifica al usuario. |
| 23 | + * 5. Finaliza el programa cuando Mickey llegue a la salida. |
| 24 | +""" |
| 25 | +#A pesar de que en el ejercicio no se pide, el programa genera el laberinto de manera automática |
| 26 | +#Esto tiene varias limitaciones a nivel de cuántos obstáculos se pueden colocar para que el juego tenga solución |
| 27 | + |
| 28 | +from abc import ABC,abstractmethod |
| 29 | +from random import randint,shuffle |
| 30 | +from math import ceil |
| 31 | + |
| 32 | +#Clases Abstractas |
| 33 | +class AbstractBoardGenerator(ABC): |
| 34 | + @abstractmethod |
| 35 | + def create_board(self): |
| 36 | + pass |
| 37 | + |
| 38 | +class AbstractPlaceMickeyAndExit(ABC): |
| 39 | + @abstractmethod |
| 40 | + def place_mickey_and_exit(self,board:AbstractBoardGenerator): |
| 41 | + pass |
| 42 | + |
| 43 | +class AbstractObstaclesGenerator(ABC): |
| 44 | + @abstractmethod |
| 45 | + def place_obstacles(self,board:AbstractBoardGenerator): |
| 46 | + pass |
| 47 | + |
| 48 | +class AbstractBoardChecker(ABC): |
| 49 | + @abstractmethod |
| 50 | + def confirm_board(self,board:AbstractBoardGenerator): |
| 51 | + pass |
| 52 | + |
| 53 | +class AbstractBoardPrinter(ABC): |
| 54 | + @abstractmethod |
| 55 | + def print_board(self,board:AbstractBoardGenerator): |
| 56 | + pass |
| 57 | + |
| 58 | +class AbstractMickeyMove(ABC): |
| 59 | + @abstractmethod |
| 60 | + def __init__(self): |
| 61 | + pass |
| 62 | + |
| 63 | + @abstractmethod |
| 64 | + def move_up(self): |
| 65 | + pass |
| 66 | + |
| 67 | + @abstractmethod |
| 68 | + def move_down(self): |
| 69 | + pass |
| 70 | + |
| 71 | + @abstractmethod |
| 72 | + def move_left(self): |
| 73 | + pass |
| 74 | + |
| 75 | + @abstractmethod |
| 76 | + def move_right(self): |
| 77 | + pass |
| 78 | + |
| 79 | +class AbstractMoveChecker(ABC): |
| 80 | + @abstractmethod |
| 81 | + def check_move(self,board:AbstractBoardGenerator): |
| 82 | + pass |
| 83 | + |
| 84 | +#Clases del programa que implementan las abstractas |
| 85 | + |
| 86 | +#Clase que se ocupa de colocar a Mickey y la Salida |
| 87 | +class PlaceMickeyAndExit(AbstractPlaceMickeyAndExit): |
| 88 | + def place_mickey_and_exit(self,board:AbstractBoardGenerator): |
| 89 | + while True: |
| 90 | + mickey_row = randint(0,board.number_of_rows-1) |
| 91 | + mickey_column = randint(0,board.number_of_columns-1) |
| 92 | + if mickey_row == 0: |
| 93 | + out_row = 5 |
| 94 | + out_column = randint(0,board.number_of_columns-1) |
| 95 | + break |
| 96 | + elif mickey_row == 5: |
| 97 | + out_row = 0 |
| 98 | + out_column = randint(0,board.number_of_columns-1) |
| 99 | + break |
| 100 | + else: |
| 101 | + if mickey_column == 0: |
| 102 | + out_column = board.number_of_columns-1 |
| 103 | + out_row = randint(0,board.number_of_rows-1) |
| 104 | + break |
| 105 | + elif mickey_column == board.number_of_columns-1: |
| 106 | + out_column = 0 |
| 107 | + out_row = randint(0,board.number_of_rows-1) |
| 108 | + break |
| 109 | + |
| 110 | + board.board[mickey_row][mickey_column] = "🐭" |
| 111 | + board.board[out_row][out_column] = "🚪" |
| 112 | + return mickey_row,mickey_column,out_row,out_column |
| 113 | + |
| 114 | +class ObstaclesGenerator(AbstractObstaclesGenerator): |
| 115 | + #Método privado que genera 2 obstáculos por cuadrante de manera aleatoria |
| 116 | + def __set_obstacles_in_cuadrant(self,board:AbstractBoardGenerator,rows_start:int,rows_limit:int,columns_start:int,columns_limit:int,number_of_obstacles:int): |
| 117 | + index = 0 |
| 118 | + positions = [(row, col) for row in range(rows_start, rows_limit + 1) for col in range(columns_start, columns_limit + 1)] |
| 119 | + shuffle(positions) # Mezclar posiciones para seleccionar aleatoriamente |
| 120 | + while index < number_of_obstacles: |
| 121 | + row, column = positions.pop() |
| 122 | + if board.board[row][column] == "⬜️": |
| 123 | + board.board[row][column] = "⬛️" |
| 124 | + index +=1 |
| 125 | + #método que calcula cuántos obstáculos se han de colocar por cuadrante según el tamaño del tablero (2 en este ejercicio) y usa __set_obstacles_in_cuadrant para generarlos |
| 126 | + def place_obstacles(self,board:AbstractBoardGenerator): |
| 127 | + NUMBER_OF_OBSTACLES = ceil((board.number_of_rows*board.number_of_columns)*2/9/4) #la proporcion es 9/2, por cada 9 casillas se ponen un máximo de 2 obstáculos para que el laberinto tenga solución. El resultado se divide entre 4 cuadrantes |
| 128 | + #primer cuadrante |
| 129 | + rows_start = 0 |
| 130 | + rows_limit = int(board.number_of_rows/2-1) |
| 131 | + colunms_start = 0 |
| 132 | + columns_limit = int(board.number_of_columns/2-1) |
| 133 | + self.__set_obstacles_in_cuadrant(board,rows_start,rows_limit,colunms_start,columns_limit,NUMBER_OF_OBSTACLES) |
| 134 | + #segundo cuadrante |
| 135 | + rows_start = int(board.number_of_rows/2) |
| 136 | + rows_limit = board.number_of_rows-1 |
| 137 | + colunms_start = 0 |
| 138 | + columns_limit = int(board.number_of_columns/2-1) |
| 139 | + self.__set_obstacles_in_cuadrant(board,rows_start,rows_limit,colunms_start,columns_limit,NUMBER_OF_OBSTACLES) |
| 140 | + #tercer cuadrante |
| 141 | + rows_start = 0 |
| 142 | + rows_limit = int(board.number_of_rows/2-1) |
| 143 | + colunms_start = int(board.number_of_columns/2) |
| 144 | + columns_limit = board.number_of_columns-1 |
| 145 | + self.__set_obstacles_in_cuadrant(board,rows_start,rows_limit,colunms_start,columns_limit,NUMBER_OF_OBSTACLES) |
| 146 | + #cuarto cuadrante |
| 147 | + rows_start = int(board.number_of_rows/2) |
| 148 | + rows_limit = board.number_of_rows-1 |
| 149 | + colunms_start = int(board.number_of_columns/2) |
| 150 | + columns_limit = board.number_of_columns-1 |
| 151 | + self.__set_obstacles_in_cuadrant(board,rows_start,rows_limit,colunms_start,columns_limit,NUMBER_OF_OBSTACLES) |
| 152 | + |
| 153 | +class BoardChecker(AbstractBoardChecker): #comprueba que ni Mickey ni la salida están bloqueados por obstáculos |
| 154 | + def __check_position(self,board:AbstractBoardGenerator,row:int,column:int): #este método privado revisa que Mickey o la Salida no estén rodeados por obstáculos devolviendo un boolean |
| 155 | + valid_position = True |
| 156 | + if row == 5: |
| 157 | + if board.board[row-1][column] == "⬛️" and board.board[row][column-1] == "⬛️" and board.board[row][column+1] == "⬛️": |
| 158 | + valid_position = False |
| 159 | + elif column == 5: |
| 160 | + if board.board[row-1][column] == "⬛️" and board.board[row+1][column] == "⬛️" and board.board[row][column-1] == "⬛️": |
| 161 | + valid_position = False |
| 162 | + elif row == 0: |
| 163 | + if board.board[row][column+1] == "⬛️" and board.board[row+1][column] == "⬛️" and board.board[row][column-1] == "⬛️": |
| 164 | + valid_position = False |
| 165 | + elif column == 0: |
| 166 | + if board.board[row-1][column] == "⬛️" and board.board[row+1][column] == "⬛️" and board.board[row][column+1] == "⬛️": |
| 167 | + valid_position = False |
| 168 | + elif row == 0 and column == 0: |
| 169 | + if board.board[row+1][column] == "⬛️" and board.board[row][column+1] == "⬛️": |
| 170 | + valid_position = False |
| 171 | + elif row == 5 and column == 5: |
| 172 | + if board.board[row-1][column] == "⬛️" and board.board[row][column-1] == "⬛️": |
| 173 | + valid_position = False |
| 174 | + elif row == 0 and column == 5: |
| 175 | + if board.board[row+1][column] == "⬛️" and board.board[row][column-1] == "⬛️": |
| 176 | + valid_position = False |
| 177 | + elif row == 5 and column == 0: |
| 178 | + if board.board[row-1][column] == "⬛️" and board.board[row][column+1] == "⬛️": |
| 179 | + valid_position = False |
| 180 | + else: |
| 181 | + if board.board[row-1][column] == "⬛️" and board.board[row+1][column] == "⬛️" and board.board[row][column-1] == "⬛️" and board.board[row][column+1] == "⬛️": |
| 182 | + valid_position = False |
| 183 | + |
| 184 | + return valid_position |
| 185 | + |
| 186 | + #Este método confirma el tablero apoyándose en __check_position. Si Mickey o la salida están rodeados, genera un nuevo tablero y lo comprueba de manera recursiva |
| 187 | + def confirm_board(self, board: AbstractBoardGenerator,place_mickey_and_exit:AbstractPlaceMickeyAndExit,obstacle_generator:AbstractObstaclesGenerator): |
| 188 | + if not self.__check_position(board,board.mickey_row,board.mickey_column) or not self.__check_position(board,board.out_row,board.out_column): |
| 189 | + board.create_blank_board() |
| 190 | + board.mickey_row,board.mickey_column,board.out_row,board.out_column = place_mickey_and_exit.place_mickey_and_exit(board) |
| 191 | + obstacle_generator.place_obstacles(board) |
| 192 | + self.confirm_board(board,place_mickey_and_exit,obstacle_generator) |
| 193 | + else: |
| 194 | + pass |
| 195 | + |
| 196 | +class BoardPrinter(AbstractBoardPrinter): #imprime el tablero por consola |
| 197 | + def print_board(self,board:AbstractBoardGenerator): |
| 198 | + row_printed = "" |
| 199 | + index = 0 |
| 200 | + for row in board.board: |
| 201 | + for index,column in enumerate(row): |
| 202 | + if index == len(row)-1: |
| 203 | + row_printed += f"{row[index]}\n" |
| 204 | + else: |
| 205 | + row_printed += f"{row[index]} " |
| 206 | + print (row_printed) |
| 207 | + row_printed = "" |
| 208 | + |
| 209 | +class BoardGenerator(AbstractBoardGenerator): #genera el tablero en blanco y usa el resto de métodos para generar los obstáculos y colocar a Mickey y la salida |
| 210 | + def create_blank_board(self): |
| 211 | + for i in range (0,self.number_of_rows): |
| 212 | + row = [] |
| 213 | + for index in range(0,self.number_of_columns): |
| 214 | + row.append("⬜️") |
| 215 | + self.board.append(row) |
| 216 | + |
| 217 | + def create_board(self,number_of_rows:int,number_of_columns:int,place_mickey_and_exit:AbstractPlaceMickeyAndExit,place_obstacles:AbstractObstaclesGenerator,confirm_board:AbstractBoardChecker): |
| 218 | + self.number_of_rows:int = number_of_rows |
| 219 | + self.number_of_columns:int = number_of_columns |
| 220 | + self.board:list = [] |
| 221 | + self.create_blank_board() |
| 222 | + self.mickey_row,self.mickey_column,self.out_row,self.out_column = place_mickey_and_exit.place_mickey_and_exit(self) |
| 223 | + place_obstacles.place_obstacles(self) |
| 224 | + confirm_board.confirm_board(self,place_mickey_and_exit,place_obstacles) |
| 225 | + return self.mickey_row,self.mickey_column,self.out_row,self.out_column |
| 226 | + |
| 227 | +class MoveChecker(AbstractMoveChecker): #con este método comprueba que el movimiento es válido y si ha llegado a la salida o no. |
| 228 | + def check_move(self,board:AbstractBoardGenerator,move:AbstractMickeyMove): |
| 229 | + exit = False |
| 230 | + if move.up: |
| 231 | + if board.mickey_row == 0: |
| 232 | + print("Mickey no puede avanzar en esa dirección, se chocaría con la pared del laberinto\n") |
| 233 | + move.up = False |
| 234 | + else: |
| 235 | + row = board.mickey_row |
| 236 | + board.mickey_row -= 1 |
| 237 | + if board.board[board.mickey_row][board.mickey_column] == "🚪": |
| 238 | + board.board[row][board.mickey_column] ="⬜️" |
| 239 | + print("¡Mickey ha conseguido salir del laberinto!\n") |
| 240 | + exit = True |
| 241 | + elif board.board[board.mickey_row][board.mickey_column] != "⬛️": |
| 242 | + board.board[board.mickey_row][board.mickey_column] = "🐭" |
| 243 | + board.board[row][board.mickey_column] ="⬜️" |
| 244 | + move.up = False |
| 245 | + else: |
| 246 | + print("Mickey no se puede mover ahi, hay un obstáculo en el camino\n") |
| 247 | + board.mickey_row = row |
| 248 | + move.up = False |
| 249 | + elif move.down: |
| 250 | + row = board.mickey_row |
| 251 | + try: |
| 252 | + board.mickey_row +=1 |
| 253 | + if board.board[board.mickey_row][board.mickey_column] == "🚪": |
| 254 | + board.board[row][board.mickey_column] ="⬜️" |
| 255 | + print("¡Mickey ha conseguido salir del laberinto!\n") |
| 256 | + exit = True |
| 257 | + elif board.board[board.mickey_row][board.mickey_column] != "⬛️": |
| 258 | + board.board[board.mickey_row][board.mickey_column] = "🐭" |
| 259 | + board.board[row][board.mickey_column] ="⬜️" |
| 260 | + move.down = False |
| 261 | + else: |
| 262 | + print("Mickey no se puede mover ahi, hay un obstáculo en el camino\n") |
| 263 | + board.mickey_row = row |
| 264 | + move.down = False |
| 265 | + except IndexError: |
| 266 | + print("Mickey no puede avanzar en esa dirección, se chocaría con la pared del laberinto\n") |
| 267 | + move.down = False |
| 268 | + elif move.left: |
| 269 | + if board.mickey_column == 0: |
| 270 | + print("Mickey no puede avanzar en esa dirección, se chocaría con la pared del laberinto\n") |
| 271 | + move.left = False |
| 272 | + else: |
| 273 | + column = board.mickey_column |
| 274 | + board.mickey_column -= 1 |
| 275 | + if board.board[board.mickey_row][board.mickey_column] == "🚪": |
| 276 | + board.board[board.mickey_row][column] ="⬜️" |
| 277 | + print("¡Mickey ha conseguido salir del laberinto!\n") |
| 278 | + exit = True |
| 279 | + elif board.board[board.mickey_row][board.mickey_column] != "⬛️": |
| 280 | + board.board[board.mickey_row][board.mickey_column] = "🐭" |
| 281 | + board.board[board.mickey_row][column] ="⬜️" |
| 282 | + move.left = False |
| 283 | + else: |
| 284 | + print("Mickey no se puede mover ahi, hay un obstáculo en el camino\n") |
| 285 | + board.mickey_column = column |
| 286 | + move.left = False |
| 287 | + elif move.right: |
| 288 | + column = board.mickey_column |
| 289 | + try: |
| 290 | + board.mickey_column += 1 |
| 291 | + if board.board[board.mickey_row][board.mickey_column] == "🚪": |
| 292 | + board.board[board.mickey_row][column] ="⬜️" |
| 293 | + print("¡Mickey ha conseguido salir del laberinto!\n") |
| 294 | + exit = True |
| 295 | + elif board.board[board.mickey_row][board.mickey_column] != "⬛️": |
| 296 | + board.board[board.mickey_row][board.mickey_column] = "🐭" |
| 297 | + board.board[board.mickey_row][column] ="⬜️" |
| 298 | + move.right = False |
| 299 | + else: |
| 300 | + print("Mickey no se puede mover ahi, hay un obstáculo en el camino\n") |
| 301 | + board.mickey_column = column |
| 302 | + move.right = False |
| 303 | + except IndexError: |
| 304 | + print("Mickey no puede avanzar en esa dirección, se chocaría con la pared del laberinto\n") |
| 305 | + move.right = False |
| 306 | + return exit |
| 307 | + |
| 308 | +class MickeyMove(AbstractMickeyMove): #clase para el movimiento de Mickey |
| 309 | + def __init__(self): |
| 310 | + self.up:bool = False |
| 311 | + self.down:bool = False |
| 312 | + self.right:bool = False |
| 313 | + self.left:bool = False |
| 314 | + |
| 315 | + def move_up(self): |
| 316 | + self.up = True |
| 317 | + return self |
| 318 | + |
| 319 | + def move_down(self): |
| 320 | + self.down = True |
| 321 | + return self |
| 322 | + |
| 323 | + def move_left(self): |
| 324 | + self.left = True |
| 325 | + return self |
| 326 | + |
| 327 | + def move_right(self): |
| 328 | + self.right = True |
| 329 | + return self |
| 330 | + |
| 331 | +exit = False |
| 332 | +NUMBER_OF_ROWS = 6 #se podría solicitar el tamaño del tablero por consola. |
| 333 | +NUMBER_OF_COLUMNS = 6 |
| 334 | +board = BoardGenerator() |
| 335 | +mickey_row,mickey_column,out_row,out_column = board.create_board(NUMBER_OF_ROWS,NUMBER_OF_COLUMNS,PlaceMickeyAndExit(),ObstaclesGenerator(),BoardChecker()) |
| 336 | +board_printer = BoardPrinter() |
| 337 | +print("Te doy la bienvenida al Laberinto del D23 en el que tendrás que ayudar a Mickey a escapar\n") |
| 338 | +board_printer.print_board(board) |
| 339 | +print("\n") |
| 340 | +mickey_move = MickeyMove() |
| 341 | +move_checker = MoveChecker() |
| 342 | +while not exit: |
| 343 | + input_move = input("¿Hacia donde quieres mover a Mickey?\n- Arriba (U)\n- Abajo (D)\n- Izquierda (L)\n- Derecha (R)\n Introduce el movimiento por favor: ").upper() |
| 344 | + if input_move == "U": |
| 345 | + move = mickey_move.move_up() |
| 346 | + elif input_move == "D": |
| 347 | + move = mickey_move.move_down() |
| 348 | + elif input_move == "L": |
| 349 | + move = mickey_move.move_left() |
| 350 | + elif input_move == "R": |
| 351 | + move = mickey_move.move_right() |
| 352 | + else: |
| 353 | + print("Vaya, parece que Mickey no puede hacer ese movimiento, probemos de nuevo...") |
| 354 | + exit = move_checker.check_move(board,move) |
| 355 | + board_printer.print_board(board) |
| 356 | + print("\n") |
| 357 | + |
| 358 | +print("Enhorabuena y gracias por ayudar a Mickey a salir del laberinto. Hasta pronto.") |
0 commit comments