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 obstáculos. Notifica al usuario.
23
+ * 5. Finaliza el programa cuando Mickey llegue a la salida.
24
+ """
25
+
26
+
27
+ """
28
+ He querido realizar el ejercicio con clases para practicar SOLID. Se podía haber resuelto de forma mucho mas sencilla.
29
+ """
30
+
31
+ import os
32
+ from abc import ABC , abstractmethod
33
+
34
+ def clear_console ():# Sierve para borrar la consola y que el tablero quede siempre en el mismo sitio
35
+ os .system ('cls' if os .name == 'nt' else 'clear' )
36
+
37
+
38
+ maze = [["⬜️" ,"⬛️" ,"🚪" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ],
39
+ ["⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ],
40
+ ["⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬛️" ],
41
+ ["⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ],
42
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ],
43
+ ["⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ],
44
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ],
45
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ],
46
+ ["⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ],
47
+ ["⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ],
48
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ],
49
+ ["⬛️" ,"🐭" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬜️" ],
50
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ],
51
+ ["⬜️" ,"⬛️" ,"⬜️" ,"⬛️" ,"⬛️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ,"⬜️" ]]
52
+
53
+ class Maze :#Clase que contiene el laberinto y sus proiedades.
54
+ def __init__ (self , grid ):
55
+ self .grid = grid
56
+ self .obstacles , self .mickey , self .exit = self .update_maze_properties ()
57
+
58
+ def __getitem__ (self , index ):# Este metodo hace que cuando accedo a la instacia de Maze con indices no tengo que acceder a maze.grid
59
+ return self .grid [index ]
60
+
61
+ def __len__ (self ):
62
+ return len (self .grid )# Igualmente dice que cuando acceda a len de la instancia de Maze este accediendo directamente a len de maze.grid
63
+
64
+ def update_maze_properties (self ):
65
+ current_obstacles = []
66
+ mickey = exit = None #Mejora la seguridad
67
+ for i , line in enumerate (self .grid ):
68
+ for j , column in enumerate (line ):
69
+ if column != "⬜️" :
70
+ if column == "⬛️" :
71
+ current_obstacles .append ((i , j ))
72
+ elif column == "🐭" :
73
+ mickey = (i , j )
74
+ elif column == "🚪" :
75
+ exit = (i , j )
76
+ return current_obstacles , mickey , exit
77
+
78
+
79
+ class MazePrinter :#Esta clase sieve para pintar el laberinto
80
+ def __init__ (self , maze : Maze ):
81
+ self .maze = maze
82
+
83
+ def print_maze (self ):
84
+ for line in self .maze :
85
+ for cell in line :
86
+ print (cell , end = "" )
87
+ print ()
88
+
89
+
90
+ class CellChecker :# Esta clase se encarga de comprobar las situaciones en el tablero
91
+ def __init__ (self , maze : Maze ):
92
+ self .maze = maze
93
+
94
+ def is_inside_maze (self , i :int , j :int ):
95
+ return 0 <= i < len (self .maze ) and 0 <= j < len (self .maze [0 ]) # Puedo acceder directamente a self.maze como si fuera grid por la funcion __getitem__
96
+
97
+ def is_obstacle (self , i :int , j :int ):
98
+ return (i , j ) in self .maze .obstacles
99
+
100
+ def is_exit (self , i :int , j : int ):
101
+ return (i , j ) in self .maze .exit
102
+
103
+ def is_mickey (self , i :int , j : int ):
104
+ return (i , j ) in self .maze .mickey
105
+
106
+
107
+ class GameMessenger :# Esta clase se encarga de imprimir los mensajes de feedback
108
+ def invalid_direction (self ):
109
+ print ("Elige una opción válida" )
110
+
111
+ def mickey_blocked (self ):
112
+ print ("Mickey no puede continuar por ahí" )
113
+
114
+ def mickey_wins (self ):
115
+ print ("FELICIDADES! - HAS LIBERADO A MICKEY" )
116
+
117
+
118
+ class MoveStrategy (ABC ):# Interfaz/Clase abstracta de los posibles movimientos.
119
+ @abstractmethod
120
+ def matches (self , direction : str ) -> bool :
121
+ pass
122
+ @abstractmethod
123
+ def get_movement (self ) -> tuple [int , int ]:
124
+ pass
125
+
126
+ class MoveLeft (MoveStrategy ):#Implementaciones concretas de los posibles movimientos
127
+ def matches (self , direction ): return direction == "a"
128
+ def get_movement (self ): return (0 , - 1 )
129
+
130
+ class MoveUpLeft (MoveStrategy ):
131
+ def matches (self , direction ): return direction == "q"
132
+ def get_movement (self ): return (- 1 , - 1 )
133
+
134
+ class MoveUp (MoveStrategy ):
135
+ def matches (self , direction ): return direction == "w"
136
+ def get_movement (self ): return (- 1 , 0 )
137
+
138
+ class MoveUpRight (MoveStrategy ):
139
+ def matches (self , direction ): return direction == "e"
140
+ def get_movement (self ): return (- 1 , 1 )
141
+
142
+ class MoveRight (MoveStrategy ):
143
+ def matches (self , direction ): return direction == "d"
144
+ def get_movement (self ): return (0 , 1 )
145
+
146
+ class MoveDownRight (MoveStrategy ):
147
+ def matches (self , direction ): return direction == "c"
148
+ def get_movement (self ): return (1 , 1 )
149
+
150
+ class MoveDown (MoveStrategy ):
151
+ def matches (self , direction ): return direction == "s"
152
+ def get_movement (self ): return (1 , 0 )
153
+
154
+ class MoveDownLeft (MoveStrategy ):
155
+ def matches (self , direction ): return direction == "z"
156
+ def get_movement (self ): return (1 , - 1 )
157
+
158
+ class MovementManager :# Esta clase se encarga de manejar los posibles movimientos
159
+ def __init__ (self , maze : Maze , cell_checker : CellChecker , strategies ):
160
+ self .maze = maze
161
+ self .cell_checker = cell_checker
162
+ self .strategies = strategies
163
+
164
+ def calculate_movement (self , direction : str ):# Obtiene el movimiento segun la opcion elegida
165
+ for strategie in self .strategies :
166
+ if strategie .matches (direction ):
167
+ return strategie .get_movement ()
168
+ return None
169
+
170
+ def move (self , direction : str ):# Realiza el movimiento y devuelve el estado de lo que ha ocurrido. Movimiento normal, bloqueado, final.
171
+ movement = self .calculate_movement (direction )
172
+ if movement is None :
173
+ return "invalid"
174
+
175
+ new_i = self .maze .mickey [0 ] + movement [0 ]
176
+ new_j = self .maze .mickey [1 ] + movement [1 ]
177
+
178
+ if self .cell_checker .is_inside_maze (new_i , new_j ) and not self .cell_checker .is_obstacle (new_i , new_j ):
179
+ old_i , old_j = self .maze .mickey
180
+ self .maze [old_i ][old_j ]= "⬜️"
181
+ self .maze [new_i ][new_j ] = "🐭"
182
+ self .maze .mickey = (new_i , new_j )
183
+
184
+ if self .maze .mickey == self .maze .exit :
185
+ return "win"
186
+ return "moved"
187
+ else :
188
+ return "blocked"
189
+
190
+
191
+ def main ():# Se encarga del flujo del programa
192
+ my_maze = Maze (maze )
193
+ printer = MazePrinter (my_maze )
194
+ cell_checker = CellChecker (my_maze )
195
+ movement_strategies = [
196
+ MoveLeft (), MoveUpLeft (), MoveUp (), MoveUpRight (), MoveRight (), MoveDownRight (), MoveDown (), MoveDownLeft ()
197
+ ]
198
+ movement_manager = MovementManager (my_maze , cell_checker , movement_strategies )
199
+ messenger = GameMessenger ()
200
+
201
+ finish = False
202
+ print ("--Welcome to Mickey Maze--" )
203
+ printer .print_maze ()
204
+
205
+ while not finish :
206
+ print ("Movimientos:" )
207
+ print ("a. Izquierda" )
208
+ print ("q. Arriba izquierda" )
209
+ print ("w. Arriba" )
210
+ print ("e. Arriba derecha" )
211
+ print ("d. Derecha" )
212
+ print ("c. Abajo derecha" )
213
+ print ("s. Abajo" )
214
+ print ("z. Abajo izquierda" )
215
+
216
+ option = input ("Introduce a donde quieres moverte:" )
217
+ clear_console ()
218
+ result = movement_manager .move (option )
219
+
220
+ printer .print_maze ()
221
+
222
+ match result :
223
+ case "invalid" :
224
+ messenger .invalid_direction ()
225
+ case "blocked" :
226
+ messenger .mickey_blocked ()
227
+ case "win" :
228
+ messenger .mickey_wins ()
229
+ finish = True
230
+
231
+ main ()
0 commit comments