-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaze
More file actions
319 lines (254 loc) · 15.7 KB
/
maze
File metadata and controls
319 lines (254 loc) · 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#include <iostream> // libreria estandar de entrada y salida
#include <vector> // libreria estandar de vectores
#include <stack> // libreria estandar de pilas
#include <queue> // libreria estandar de colas
#include <random> // libreria estandar de numeros aleatorios
#include <chrono> // libreria estandar de tiempo
#include <thread> // libreria estandar de hilos
using namespace std;
// Estructura para representar una celda del laberinto
struct Cell {
int x, y;
Cell(int x = 0, int y = 0) : x(x), y(y) {}
};
// Clase principal del Laberinto
class Maze {
private:
int width, height; // Dimensiones del laberinto
vector<vector<int>> grid; // 0 = pared, 1 = camino, 2 = solución
Cell start, end;
// Generador de números aleatorios
mt19937 rng; // mt19937 es un algoritmo de generación de números pseudoaleatorios
// Direcciones: arriba, derecha, abajo, izquierda
const int dx[4] = {-1, 0, 1, 0}; // desplazamientos en x izq y der
const int dy[4] = {0, 1, 0, -1}; // desplazamientos en y arriba y abajo
public:
// Constructor
Maze(int w, int h) : width(w), height(h) {
// Inicializar generador aleatorio con semilla basada en tiempo
rng.seed(chrono::steady_clock::now().time_since_epoch().count()); // chrono::steady_clock::now().time_since_epoch().count() obtiene el tiempo actual en nanosegundos desde una época fija
// Inicializar grid con paredes (todo en 0)
grid.resize(height, vector<int>(width, 0)); // 0 = pared
// Definir entrada y salida
start = Cell(1, 1); // CORREGIDO: Empezar en (1,1) en vez de (0,0)
end = Cell(height - 2, width - 2); // CORREGIDO: Terminar en (h-2, w-2)
}
// Verificar si una celda está dentro de los límites
bool isValid(int x, int y) {
return x >= 0 && x < height && y >= 0 && y < width;
}
// Generar laberinto usando DFS (Depth-First Search) con Backtracking
void generate() {
cout << "\nGenerando laberinto aleatorio...\n";
stack<Cell> path; // Pila para almacenar la ruta o camino actual
vector<vector<bool>> visited(height, vector<bool>(width, false)); // Matriz para rastrear celdas visitadas
// Comenzar desde posición (1,1) - siempre impar
path.push(start); // Inicializar la pila con la celda de inicio
grid[start.x][start.y] = 1; // Marcar la celda como camino
visited[start.x][start.y] = true; // Marcar la celda como visitada
while (!path.empty()) { // Mientras la pila no este vacia
Cell current = path.top(); // Obtener la celda actual sin sacarla de la pila
// Obtener vecinos no visitados
vector<int> directions;
for (int i = 0; i < 4; i++) {
int nx = current.x + dx[i] * 2; // Saltar 2 celdas en la direccion i para obtener la celda vecina
int ny = current.y + dy[i] * 2;
if (isValid(nx, ny) && !visited[nx][ny]) { // Si la celda es valida y no ha sido visitada
directions.push_back(i); // Agregar la direccion a la lista de opciones
} // Fin de la condición
}
if (!directions.empty()) { // Si hay opciones disponibles para avanzar en la pila
// Elegir dirección aleatoria
uniform_int_distribution<int> dist(0, directions.size() - 1); // Generar un numero aleatorio entre 0 y el tamaño de la lista de opciones - 1
int dir = directions[dist(rng)]; // Seleccionar una direccion aleatoria
// Calcular siguiente celda
int nx = current.x + dx[dir] * 2; // Saltar 2 celdas en la direccion seleccionada
int ny = current.y + dy[dir] * 2;
// Marcar el camino intermedio
int mx = current.x + dx[dir]; // Saltar 1 celda en la direccion seleccionada
int my = current.y + dy[dir];
grid[mx][my] = 1; // Marcar la celda intermedia como camino
grid[nx][ny] = 1; // Marcar la nueva celda como camino
visited[nx][ny] = true; // Marcar la nueva celda como visitada
path.push(Cell(nx, ny)); // Agregar la nueva celda a la pila
} else {
// Backtracking: retroceder
path.pop(); // Sacar la celda actual de la pila
}
}
// CORREGIDO: Crear camino desde esquina (0,0) hasta start (1,1)
grid[0][0] = 1; // Marcar la celda (0,0) como camino
grid[0][1] = 1; // Marcar la celda (0,1) como camino
grid[1][0] = 1; // Marcar la celda (1,0) como camino
// CORREGIDO: Crear camino desde end hasta esquina (h-1, w-1)
grid[height-1][width-1] = 1; // Marcar la celda (h-1,w-1) como camino
grid[height-1][width-2] = 1; // puente a la izquierda
grid[height-2][width-1] = 1; // puente arriba
cout << "Laberinto generado!\n";
}
// Resolver laberinto usando BFS (Breadth-First Search)
bool solve() {
cout << "\nResolviendo laberinto con BFS...\n";
queue<Cell> q; // Cola para BFS FIFO. Explorar nodos por niveles
vector<vector<bool>> visited(height, vector<bool>(width, false)); // Matriz de visitados
vector<vector<Cell>> parent(height, vector<Cell>(width, Cell(-1, -1))); // Matriz de padres, reconstrucción de camino
//Empezar desde (0,0)
Cell realStart(0, 0);
q.push(realStart);
visited[0][0] = true;
bool found = false; // Bandera para indicar si se encontró la solución
Cell realEnd(height-1, width-1);
// BFS principal. Bucle de exploración
while (!q.empty() && !found) { // Mientras la cola no este vacia y no se haya encontrado la solucion
Cell current = q.front(); // Obtener la celda actual de la cola
q.pop(); // Sacar la celda actual de la cola
// Si llegamos al final (esquina inferior derecha)
if (current.x == realEnd.x && current.y == realEnd.y) { // Si la celda actual es la celda final
found = true; // Marcar que se encontró la solución
break;
}
// Explorar vecinos (solo 1 celda de distancia)
for (int i = 0; i < 4; i++) { // Para cada una de las 4 direcciones
int nx = current.x + dx[i]; // Calcular la nueva posición en x
int ny = current.y + dy[i];
if (isValid(nx, ny) && grid[nx][ny] == 1 && !visited[nx][ny]) { // Si la nueva posición es válida, es un camino y no ha sido visitada
visited[nx][ny] = true; // Marcar la celda como visitada
parent[nx][ny] = current; // Guardar el padre de la celda actual
q.push(Cell(nx, ny)); // Agregar la nueva celda a la cola
}
}
}
if (!found) { // Si no se encontró solución
cout << "X No se encontro solucion.\n";
return false; // Devolver false para indicar que no se encontró solución
}
// Reconstruir el camino desde el final hasta el inicio
Cell current = realEnd; // Empezar desde la celda final
while (!(current.x == 0 && current.y == 0)) { // Mientras no lleguemos al inicio (0,0)
// No marcar inicio ni fin
if (!(current.x == realEnd.x && current.y == realEnd.y) && // No marcar la celda final
!(current.x == 0 && current.y == 0)) { // No marcar la celda inicio
grid[current.x][current.y] = 2; // Marcar solución
}
current = parent[current.x][current.y]; // Moverse al padre de la celda actual
}
cout << "Laberinto resuelto!\n";
return true; // Devolver true para indicar que se encontró solución
}
// Visualizar laberinto
void display(bool showSolution = false) { // Mostrar el laberinto con o sin solución
cout << "\n";
// Borde superior
for (int j = 0; j < width + 2; j++) cout << "#"; // +2 por los bordes laterales
cout << "\n";
for (int i = 0; i < height; i++) { // Recorrer cada fila del laberinto
cout << "#"; // Borde izquierdo
for (int j = 0; j < width; j++) { // Recorrer cada celda del laberinto
if (i == 0 && j == 0) {
cout << "S"; // Start (entrada en esquina superior izquierda)
} else if (i == height-1 && j == width-1) {
cout << "E"; // End (salida en esquina inferior derecha)
} else if (showSolution && grid[i][j] == 2) {
cout << "*"; // Solución
} else if (grid[i][j] == 1 || grid[i][j] == 2) {
cout << " "; // Camino
} else {
cout << "#"; // Pared
}
}
cout << "#\n"; // Borde derecho
}
// Borde inferior
for (int j = 0; j < width + 2; j++) cout << "#";
cout << "\n";
}
// Animación de la solución paso a paso
void animateSolution() {
cout << "\nAnimando solucion...\n";
this_thread::sleep_for(chrono::milliseconds(800)); // Esperar 500ms antes de empezar
for (int i = 0; i < height; i++) { // Recorrer cada celda del laberinto
for (int j = 0; j < width; j++) { // Recorrer cada celda del laberinto
if (grid[i][j] == 2) { // Si la celda es solución
// Limpiar consola (funciona en la mayoría de sistemas)
cout << "\033[2J\033[1;1H"; // Limpiar consola.codigo ANSI
// Temporalmente marcar hasta este punto
int temp = grid[i][j]; // Guardar el valor de la celda
grid[i][j] = 3; // Marcador temporal
display(true); // Mostrar laberinto con solución hasta este punto
grid[i][j] = temp;
this_thread::sleep_for(chrono::milliseconds(100));
}
}
}
}
};
// Función para medir tiempo de ejecución
void benchmark() { // Función de medición de rendimiento
cout << "\n BENCHMARK DE RENDIMIENTO\n";
cout << "================================\n";
vector<int> sizes = {10, 20, 30, 50};
for (int size : sizes) { // Recorrer tamaños de laberintos
auto start = chrono::high_resolution_clock::now(); // Iniciar temporizador
Maze maze(size, size); // Crear laberinto
maze.generate(); // Generar laberinto
maze.solve(); // Resolver laberinto
auto end = chrono::high_resolution_clock::now(); // Detener temporizador
auto duration = chrono::duration_cast<chrono::milliseconds>(end - start); // Calcular duración
cout << "Tamaño " << size << "x" << size << ": " // Mostrar resultado
<< duration.count() << " ms\n";
}
}
// Función principal del programa
int main(int argc, char* argv[]) {
// Tamaño por defecto
int size = 15;
// Leer tamaño desde argumentos de línea de comandos
if (argc > 1) { // Si se proporcionaron argumentos de línea de comandos
size = atoi(argv[1]); // Convertir el primer argumento a entero. atoi convierte una cadena a un entero
if (size < 5) size = 5; // Tamaño mínimo. Valida valores muy pequeños
if (size > 100) size = 100; // Tamaño máximo. Valida valores muy grandes
}
cout << "GENERADOR Y RESOLVEDOR DE LABERINTOS\n";
cout << "========================================\n";
// Crear laberinto
Maze maze(size, size); // Crear laberinto de tamaño size x size
// Medir tiempo de generación
auto t1 = chrono::high_resolution_clock::now();
maze.generate();
auto t2 = chrono::high_resolution_clock::now();
// Mostrar laberinto sin solución
cout << "\nLaberinto generado:\n";
maze.display(false);
// Medir tiempo de resolución
auto t3 = chrono::high_resolution_clock::now();
bool solved = maze.solve();
auto t4 = chrono::high_resolution_clock::now();
if (solved) {
// Mostrar laberinto con solución
cout << "\nLaberinto resuelto:\n";
maze.display(true);
// Tiempos
auto gen_time = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
auto solve_time = chrono::duration_cast<chrono::milliseconds>(t4 - t3);
cout << "\n Tiempos de ejecucion:\n";
cout << " Generación: " << gen_time.count() << " ms\n";
cout << " Resolución: " << solve_time.count() << " ms\n";
cout << " Total: " << (gen_time + solve_time).count() << " ms\n";
}
// Preguntar si desea ver animación
cout << "\n¿Deseas ver la animación de la solución? (s/n): ";
char choice;
cin >> choice;
if (choice == 's' || choice == 'S') {
maze.animateSolution();
cout << "\nLaberinto resuelto (final):\n";
maze.display(true);
}
// Preguntar si desea ejecutar benchmark
cout << "\n¿Deseas ejecutar benchmark de rendimiento? (s/n): ";
cin >> choice;
if (choice == 's' || choice == 'S') {
benchmark();
}
return 0;
}