import React, { useState, useEffect, useRef } from 'react'; import { Settings, Printer, RefreshCw, Upload, Eye, EyeOff, Play, Flag } from 'lucide-react'; // Algoritmo de generación de laberintos (Depth-First Search - Backtracking) const generateMaze = (width, height) => { const grid = []; for (let y = 0; y < height; y++) { const row = []; for (let x = 0; x < width; x++) { row.push({ x, y, walls: { top: true, right: true, bottom: true, left: true }, visited: false, path: false // Parte de la solución }); } grid.push(row); } const stack = []; const start = grid[0][0]; start.visited = true; stack.push(start); while (stack.length > 0) { const current = stack[stack.length - 1]; const neighbors = []; const dirs = [ { x: 0, y: -1, wall: 'top', opp: 'bottom' }, { x: 1, y: 0, wall: 'right', opp: 'left' }, { x: 0, y: 1, wall: 'bottom', opp: 'top' }, { x: -1, y: 0, wall: 'left', opp: 'right' } ]; dirs.forEach(dir => { const nx = current.x + dir.x; const ny = current.y + dir.y; if (nx >= 0 && nx < width && ny >= 0 && ny < height && !grid[ny][nx].visited) { neighbors.push({ cell: grid[ny][nx], dir }); } }); if (neighbors.length > 0) { const chosen = neighbors[Math.floor(Math.random() * neighbors.length)]; current.walls[chosen.dir.wall] = false; chosen.cell.walls[chosen.dir.opp] = false; chosen.cell.visited = true; stack.push(chosen.cell); } else { stack.pop(); } } // Resetear visitados para el solver for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { grid[y][x].visited = false; } } // Resolver para encontrar el camino (BFS simple) const queue = [{ x: 0, y: 0, path: [] }]; const visited = new Set(['0,0']); let solutionPath = []; while (queue.length > 0) { const { x, y, path } = queue.shift(); const currentPath = [...path, { x, y }]; if (x === width - 1 && y === height - 1) { solutionPath = currentPath; break; } const currentCell = grid[y][x]; const moves = [ { x: 0, y: -1, wall: 'top' }, { x: 1, y: 0, wall: 'right' }, { x: 0, y: 1, wall: 'bottom' }, { x: -1, y: 0, wall: 'left' } ]; moves.forEach(move => { if (!currentCell.walls[move.wall]) { const nx = x + move.x; const ny = y + move.y; if (!visited.has(`${nx},${ny}`)) { visited.add(`${nx},${ny}`); queue.push({ x: nx, y: ny, path: currentPath }); } } }); } return { grid, solutionPath }; }; const App = () => { const [width, setWidth] = useState(15); const [height, setHeight] = useState(15); const [title, setTitle] = useState("¡Encuentra el camino!"); const [maze, setMaze] = useState({ grid: [], solutionPath: [] }); const [showSolution, setShowSolution] = useState(false); const [startImg, setStartImg] = useState(null); const [endImg, setEndImg] = useState(null); // Referencias para file inputs const startInputRef = useRef(null); const endInputRef = useRef(null); useEffect(() => { handleGenerate(); }, []); // Generar al montar const handleGenerate = () => { const newMaze = generateMaze(width, height); setMaze(newMaze); setShowSolution(false); }; const handleImageUpload = (e, type) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { if (type === 'start') setStartImg(event.target.result); else setEndImg(event.target.result); }; reader.readAsDataURL(file); } }; const handlePrint = () => { window.print(); }; // Constantes de dibujo const cellSize = 30; // Tamaño base para el SVG const wallWidth = 2; const svgWidth = width * cellSize + wallWidth; const svgHeight = height * cellSize + wallWidth; return (
{/* --- PANEL DE CONTROL (Oculto al imprimir) --- */}

Generador de Laberintos Docente

{/* Configuración Básica */}

Configuración

setTitle(e.target.value)} className="w-full border border-slate-300 rounded-md p-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none" />
{ setWidth(parseInt(e.target.value)); setHeight(parseInt(e.target.value)); // Mantener cuadrado por simplicidad visual }} className="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-indigo-600" />

Desliza para ajustar la dificultad

{/* Imágenes */}

Imágenes Personalizadas

handleImageUpload(e, 'start')} /> {startImg && }
handleImageUpload(e, 'end')} /> {endImg && }
{/* Acciones */}

Acciones

Usa "Guardar como PDF" en el diálogo de impresión.

{/* --- ÁREA IMPRIMIBLE (Vista Previa) --- */}
); }; export default App;