Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Python Loop Quiz - Visualizador Interactivo</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Fira+Code:wght@400;500;600&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script> | |
| tailwind.config = { | |
| darkMode: 'class', | |
| theme: { | |
| extend: { | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| mono: ['Fira Code', 'monospace'], | |
| }, | |
| colors: { | |
| glass: 'rgba(255, 255, 255, 0.05)', | |
| glassBorder: 'rgba(255, 255, 255, 0.1)', | |
| accent: '#06b6d4', | |
| accentGlow: 'rgba(6, 182, 212, 0.3)', | |
| correct: '#10b981', | |
| incorrect: '#f43f5e', | |
| }, | |
| animation: { | |
| 'gradient-x': 'gradient-x 15s ease infinite', | |
| 'float': 'float 6s ease-in-out infinite', | |
| 'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite', | |
| 'slide-up': 'slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards', | |
| 'scale-in': 'scaleIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards', | |
| 'glow': 'glow 2s ease-in-out infinite alternate', | |
| }, | |
| keyframes: { | |
| 'gradient-x': { | |
| '0%, 100%': { | |
| 'background-size': '200% 200%', | |
| 'background-position': 'left center' | |
| }, | |
| '50%': { | |
| 'background-size': '200% 200%', | |
| 'background-position': 'right center' | |
| }, | |
| }, | |
| float: { | |
| '0%, 100%': { transform: 'translateY(0)' }, | |
| '50%': { transform: 'translateY(-20px)' }, | |
| }, | |
| slideUp: { | |
| '0%': { transform: 'translateY(30px)', opacity: '0' }, | |
| '100%': { transform: 'translateY(0)', opacity: '1' }, | |
| }, | |
| scaleIn: { | |
| '0%': { transform: 'scale(0.9)', opacity: '0' }, | |
| '100%': { transform: 'scale(1)', opacity: '1' }, | |
| }, | |
| glow: { | |
| '0%': { boxShadow: '0 0 20px rgba(6, 182, 212, 0.2)' }, | |
| '100%': { boxShadow: '0 0 40px rgba(6, 182, 212, 0.5)' }, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| body { | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%); | |
| background-size: 400% 400%; | |
| animation: gradientMove 15s ease infinite; | |
| } | |
| @keyframes gradientMove { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| .glass-panel { | |
| background: rgba(255, 255, 255, 0.03); | |
| backdrop-filter: blur(16px); | |
| -webkit-backdrop-filter: blur(16px); | |
| border: 1px solid rgba(255, 255, 255, 0.08); | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| } | |
| .code-line { | |
| counter-increment: line; | |
| position: relative; | |
| padding-left: 3rem; | |
| } | |
| .code-line::before { | |
| content: counter(line); | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| width: 2.5rem; | |
| text-align: right; | |
| color: rgba(255, 255, 255, 0.3); | |
| font-size: 0.875rem; | |
| user-select: none; | |
| } | |
| .option-card { | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .option-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: -100%; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); | |
| transition: left 0.5s; | |
| } | |
| .option-card:hover::before { | |
| left: 100%; | |
| } | |
| .option-card:hover { | |
| transform: translateY(-4px) scale(1.02); | |
| border-color: rgba(6, 182, 212, 0.4); | |
| box-shadow: 0 20px 40px -10px rgba(6, 182, 212, 0.2); | |
| } | |
| .option-card.selected-correct { | |
| background: rgba(16, 185, 129, 0.1); | |
| border-color: #10b981; | |
| box-shadow: 0 0 30px rgba(16, 185, 129, 0.2); | |
| } | |
| .option-card.selected-incorrect { | |
| background: rgba(244, 63, 94, 0.1); | |
| border-color: #f43f5e; | |
| box-shadow: 0 0 30px rgba(244, 63, 94, 0.2); | |
| animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both; | |
| } | |
| @keyframes shake { | |
| 10%, 90% { transform: translate3d(-1px, 0, 0); } | |
| 20%, 80% { transform: translate3d(2px, 0, 0); } | |
| 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } | |
| 40%, 60% { transform: translate3d(4px, 0, 0); } | |
| } | |
| .iteration-node { | |
| transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); | |
| opacity: 0; | |
| transform: translateY(20px) scale(0.9); | |
| } | |
| .iteration-node.active { | |
| opacity: 1; | |
| transform: translateY(0) scale(1); | |
| } | |
| .iteration-node::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -2rem; | |
| left: 50%; | |
| width: 2px; | |
| height: 2rem; | |
| background: linear-gradient(to bottom, rgba(6, 182, 212, 0.5), transparent); | |
| transform: translateX(-50%); | |
| } | |
| .iteration-node:last-child::after { | |
| display: none; | |
| } | |
| .highlight-text { | |
| background: linear-gradient(120deg, rgba(6, 182, 212, 0.2) 0%, rgba(6, 182, 212, 0.2) 100%); | |
| background-repeat: no-repeat; | |
| background-size: 100% 40%; | |
| background-position: 0 90%; | |
| transition: background-size 0.3s ease; | |
| } | |
| .highlight-text:hover { | |
| background-size: 100% 100%; | |
| } | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(255, 255, 255, 0.02); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.15); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(255, 255, 255, 0.25); | |
| } | |
| .confetti { | |
| position: absolute; | |
| width: 10px; | |
| height: 10px; | |
| background-color: #f0f; | |
| animation: fall linear forwards; | |
| } | |
| @keyframes fall { | |
| to { | |
| transform: translateY(100vh) rotate(720deg); | |
| opacity: 0; | |
| } | |
| } | |
| .syntax-keyword { color: #c678dd; } | |
| .syntax-var { color: #e06c75; } | |
| .syntax-number { color: #d19a66; } | |
| .syntax-func { color: #61afef; } | |
| .syntax-operator { color: #56b6c2; } | |
| </style> | |
| </head> | |
| <body class="min-h-screen text-slate-200 font-sans overflow-x-hidden selection:bg-cyan-500 selection:text-white"> | |
| <!-- Background Elements --> | |
| <div class="fixed inset-0 pointer-events-none overflow-hidden"> | |
| <div class="absolute -top-40 -left-40 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl animate-float"></div> | |
| <div class="absolute top-1/2 -right-40 w-96 h-96 bg-purple-500/10 rounded-full blur-3xl animate-float" style="animation-delay: 2s;"></div> | |
| <div class="absolute -bottom-40 left-1/3 w-96 h-96 bg-emerald-500/10 rounded-full blur-3xl animate-float" style="animation-delay: 4s;"></div> | |
| </div> | |
| <!-- Header --> | |
| <header class="relative z-10 w-full px-6 py-6 flex flex-col sm:flex-row items-center justify-between gap-4 border-b border-white/5 bg-black/20 backdrop-blur-sm"> | |
| <div class="flex items-center gap-3"> | |
| <div class="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center shadow-lg shadow-cyan-500/20"> | |
| <i class="fa-brands fa-python text-white text-xl"></i> | |
| </div> | |
| <div> | |
| <h1 class="text-xl font-bold tracking-tight text-white">Python Loop Master</h1> | |
| <p class="text-xs text-slate-400">Visualizador de Bucles Interactivo</p> | |
| </div> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="group flex items-center gap-2 px-4 py-2 rounded-full bg-white/5 border border-white/10 hover:bg-white/10 hover:border-cyan-500/30 transition-all duration-300 text-sm font-medium text-slate-300 hover:text-cyan-400"> | |
| <i class="fa-solid fa-code text-xs group-hover:rotate-12 transition-transform"></i> | |
| <span>Built with anycoder</span> | |
| <i class="fa-solid fa-arrow-up-right-from-square text-xs opacity-50 group-hover:opacity-100"></i> | |
| </a> | |
| </header> | |
| <main class="relative z-10 container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Question Section --> | |
| <div class="grid lg:grid-cols-2 gap-8 mb-12"> | |
| <!-- Code Panel --> | |
| <div class="glass-panel rounded-3xl p-1 animate-slide-up" style="animation-delay: 0.1s;"> | |
| <div class="bg-black/40 rounded-[1.3rem] p-6 sm:p-8 h-full relative overflow-hidden group"> | |
| <!-- Decorative top bar --> | |
| <div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-cyan-500 via-purple-500 to-cyan-500 opacity-50"></div> | |
| <div class="flex items-center justify-between mb-6"> | |
| <div class="flex items-center gap-2"> | |
| <div class="w-3 h-3 rounded-full bg-red-500/80"></div> | |
| <div class="w-3 h-3 rounded-full bg-yellow-500/80"></div> | |
| <div class="w-3 h-3 rounded-full bg-green-500/80"></div> | |
| </div> | |
| <span class="text-xs font-mono text-slate-500 uppercase tracking-wider">loop_example.py</span> | |
| </div> | |
| <div class="font-mono text-sm sm:text-base leading-relaxed overflow-x-auto"> | |
| <div class="code-line"><span class="syntax-var">i</span><span class="syntax-operator">=</span><span class="syntax-number">100</span></div> | |
| <div class="code-line"><span class="syntax-keyword">while</span>(<span class="syntax-var">i</span><span class="syntax-operator"><=</span><span class="syntax-number">200</span>):</div> | |
| <div class="code-line pl-12 sm:pl-16"><span class="syntax-func">print</span>(<span class="syntax-var">i</span>)</div> | |
| <div class="code-line pl-12 sm:pl-16"><span class="syntax-var">i</span><span class="syntax-operator">+=</span><span class="syntax-number">20</span></div> | |
| </div> | |
| <div class="mt-6 pt-6 border-t border-white/5 flex items-center gap-3 text-slate-400 text-sm"> | |
| <i class="fa-solid fa-circle-info text-cyan-500"></i> | |
| <span>Analiza el bucle cuidadosamente antes de responder</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Question & Options --> | |
| <div class="flex flex-col gap-6 animate-slide-up" style="animation-delay: 0.2s;"> | |
| <div class="glass-panel rounded-3xl p-6 sm:p-8"> | |
| <div class="flex items-start gap-4 mb-6"> | |
| <span class="flex-shrink-0 w-10 h-10 rounded-xl bg-cyan-500/10 border border-cyan-500/20 flex items-center justify-center text-cyan-400 font-bold text-lg">?</span> | |
| <h2 class="text-xl sm:text-2xl font-semibold text-white leading-snug"> | |
| ¿Cuántas veces se ejecutará el siguiente bucle en Python? | |
| </h2> | |
| </div> | |
| <div class="grid gap-4" id="options-container"> | |
| <button onclick="selectOption('A', true)" class="option-card glass-panel rounded-2xl p-5 text-left group border border-white/10" data-option="A"> | |
| <div class="flex items-center gap-4"> | |
| <span class="flex-shrink-0 w-12 h-12 rounded-xl bg-white/5 border border-white/10 group-hover:border-cyan-500/30 group-hover:bg-cyan-500/10 transition-all flex items-center justify-center font-bold text-lg text-slate-300 group-hover:text-cyan-400">A</span> | |
| <div> | |
| <span class="text-2xl font-bold text-white block">6</span> | |
| <span class="text-sm text-slate-400">El bucle itera seis veces</span> | |
| </div> | |
| <div class="ml-auto opacity-0 group-hover:opacity-100 transition-opacity"> | |
| <i class="fa-solid fa-chevron-right text-cyan-400"></i> | |
| </div> | |
| </div> | |
| </button> | |
| <button onclick="selectOption('B', false)" class="option-card glass-panel rounded-2xl p-5 text-left group border border-white/10" data-option="B"> | |
| <div class="flex items-center gap-4"> | |
| <span class="flex-shrink-0 w-12 h-12 rounded-xl bg-white/5 border border-white/10 group-hover:border-cyan-500/30 group-hover:bg-cyan-500/10 transition-all flex items-center justify-center font-bold text-lg text-slate-300 group-hover:text-cyan-400">B</span> | |
| <div> | |
| <span class="text-2xl font-bold text-white block">100</span> | |
| <span class="text-sm text-slate-400">Iteraciones igual al valor inicial</span> | |
| </div> | |
| <div class="ml-auto opacity-0 group-hover:opacity-100 transition-opacity"> | |
| <i class="fa-solid fa-chevron-right text-cyan-400"></i> | |
| </div> | |
| </div> | |
| </button> | |
| <button onclick="selectOption('C', false)" class="option-card glass-panel rounded-2xl p-5 text-left group border border-white/10" data-option="C"> | |
| <div class="flex items-center gap-4"> | |
| <span class="flex-shrink-0 w-12 h-12 rounded-xl bg-white/5 border border-white/10 group-hover:border-cyan-500/30 group-hover:bg-cyan-500/10 transition-all flex items-center justify-center font-bold text-lg text-slate-300 group-hover:text-cyan-400">C</span> | |
| <div> | |
| <span class="text-2xl font-bold text-white block">200</span> | |
| <span class="text-sm text-slate-400">Iteraciones igual al valor límite</span> | |
| </div> | |
| <div class="ml-auto opacity-0 group-hover:opacity-100 transition-opacity"> | |
| <i class="fa-solid fa-chevron-right text-cyan-400"></i> | |
| </div> | |
| </div> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Answer Feedback (Hidden initially) --> | |
| <div id="feedback-section" class="hidden mb-12 animate-slide-up"> | |
| <div class="glass-panel rounded-3xl p-1 border-2 border-emerald-500/20"> | |
| <div class="bg-emerald-500/5 rounded-[1.3rem] p-6 sm:p-8 relative overflow-hidden"> | |
| <div class="absolute top-0 right-0 w-64 h-64 bg-emerald-500/10 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2"></div> | |
| <div class="relative z-10 flex flex-col sm:flex-row items-start sm:items-center gap-4 mb-6"> | |
| <div class="w-14 h-14 rounded-2xl bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center animate-scale-in"> | |
| <i class="fa-solid fa-check text-2xl text-emerald-400"></i> | |
| </div> | |
| <div> | |
| <h3 class="text-2xl font-bold text-white mb-1">¡Correcto! La respuesta es <span class="text-emerald-400">A) 6</span></h3> | |
| <p class="text-slate-400">Has demostrado un excelente dominio de los bucles <code class="bg-white/10 px-2 py-0.5 rounded text-emerald-300 font-mono text-sm">while</code> en Python.</p> | |
| </div> | |
| </div> | |
| <!-- Detailed Explanation --> | |
| <div class="space-y-6 relative z-10"> | |
| <div class="bg-black/20 rounded-2xl p-6 border border-white/5"> | |
| <h4 class="text-lg font-semibold text-white mb-4 flex items-center gap-2"> | |
| <i class="fa-solid fa-list-ol text-cyan-400"></i> | |
| Explicación Detallada | |
| </h4> | |
| <div class="space-y-4 text-slate-300 leading-relaxed"> | |
| <p> | |
| Para determinar cuántas veces se ejecuta el bucle, debemos analizar paso a paso la evolución de la variable <span class="font-mono text-cyan-300 bg-cyan-500/10 px-1.5 py-0.5 rounded">i</span>: | |
| </p> | |
| <div class="overflow-x-auto"> | |
| <table class="w-full text-left border-collapse"> | |
| <thead> | |
| <tr class="border-b border-white/10"> | |
| <th class="pb-3 pr-4 text-slate-400 font-medium text-sm uppercase tracking-wider">Iteración</th> | |
| <th class="pb-3 pr-4 text-slate-400 font-medium text-sm uppercase tracking-wider">Valor inicial de i</th> | |
| <th class="pb-3 pr-4 text-slate-400 font-medium text-sm uppercase tracking-wider">Condición (i <= 200)</th> | |
| <th class="pb-3 pr-4 text-slate-400 font-medium text-sm uppercase tracking-wider">Output</th> | |
| <th class="pb-3 text-slate-400 font-medium text-sm uppercase tracking-wider">Nuevo valor de i</th> | |
| </tr> | |
| </thead> | |
| <tbody class="font-mono text-sm"> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">1</td> | |
| <td class="py-3 pr-4 text-white">100</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">100</td> | |
| <td class="py-3 text-white">120</td> | |
| </tr> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">2</td> | |
| <td class="py-3 pr-4 text-white">120</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">120</td> | |
| <td class="py-3 text-white">140</td> | |
| </tr> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">3</td> | |
| <td class="py-3 pr-4 text-white">140</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">140</td> | |
| <td class="py-3 text-white">160</td> | |
| </tr> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">4</td> | |
| <td class="py-3 pr-4 text-white">160</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">160</td> | |
| <td class="py-3 text-white">180</td> | |
| </tr> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">5</td> | |
| <td class="py-3 pr-4 text-white">180</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">180</td> | |
| <td class="py-3 text-white">200</td> | |
| </tr> | |
| <tr class="border-b border-white/5 hover:bg-white/5 transition-colors bg-emerald-500/5"> | |
| <td class="py-3 pr-4 text-emerald-400 font-bold">6</td> | |
| <td class="py-3 pr-4 text-white">200</td> | |
| <td class="py-3 pr-4 text-emerald-400">True <span class="text-xs opacity-50">✓</span></td> | |
| <td class="py-3 pr-4 text-cyan-300">200</td> | |
| <td class="py-3 text-white">220</td> | |
| </tr> | |
| <tr class="hover:bg-white/5 transition-colors opacity-60"> | |
| <td class="py-3 pr-4 text-rose-400 font-bold">—</td> | |
| <td class="py-3 pr-4 text-white">220</td> | |
| <td class="py-3 pr-4 text-rose-400">False <span class="text-xs opacity-50">✗</span></td> | |
| <td class="py-3 pr-4 text-slate-500">—</td> | |
| <td class="py-3 text-slate-500">Fin</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="bg-cyan-500/5 border border-cyan-500/20 rounded-xl p-4 flex items-start gap-3 mt-4"> | |
| <i class="fa-solid fa-lightbulb text-cyan-400 mt-1"></i> | |
| <div> | |
| <p class="text-cyan-100 font-medium mb-1">Punto clave</p> | |
| <p class="text-sm text-cyan-200/70"> | |
| La condición se evalúa como <span class="font-mono text-cyan-300">True</span> cuando <span class="font-mono text-cyan-300">i=200</span> porque el operador es <span class="font-mono text-cyan-300"><=</span> (menor o igual). Por eso se incluye el valor 200. Después de esa iteración, <span class="font-mono text-cyan-300">i</span> pasa a valer 220 y el bucle termina. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Interactive Visualizer --> | |
| <div class="glass-panel rounded-3xl p-6 sm:p-8 mb-12 animate-slide-up" style="animation-delay: 0.3s;"> | |
| <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-8"> | |
| <div> | |
| <h3 class="text-xl font-bold text-white flex items-center gap-2"> | |
| <i class="fa-solid fa-microchip text-cyan-400"></i> | |
| Visualizador de Ejecución | |
| </h3> | |
| <p class="text-sm text-slate-400 mt-1">Observa cómo cambia el valor de <span class="font-mono text-cyan-300">i</span> en cada iteración</p> | |
| </div> | |
| <button onclick="runSimulation()" id="sim-btn" class="px-6 py-3 rounded-xl bg-cyan-500 hover:bg-cyan-400 text-slate-900 font-bold text-sm transition-all hover:shadow-lg hover:shadow-cyan-500/25 active:scale-95 flex items-center gap-2"> | |
| <i class="fa-solid fa-play"></i> | |
| Ejecutar Simulación | |
| </button> | |
| </div> | |
| <div class="relative"> | |
| <!-- Progress Bar --> | |
| <div class="absolute top-8 left-0 right-0 h-1 bg-white/5 rounded-full overflow-hidden"> | |
| <div id="progress-bar" class="h-full bg-gradient-to-r from-cyan-500 to-emerald-400 w-0 transition-all duration-700 ease-out"></div> | |
| </div> | |
| <!-- Iteration Nodes --> | |
| <div class="grid grid-cols-3 sm:grid-cols-6 gap-4 relative pt-12 pb-4" id="visualizer-container"> | |
| <!-- Generated by JS --> | |
| </div> | |
| <!-- Console Output --> | |
| <div class="mt-6 bg-black/40 rounded-2xl p-4 border border-white/5 font-mono text-sm"> | |
| <div class="flex items-center gap-2 mb-3 text-slate-500 text-xs uppercase tracking-wider font-sans"> | |
| <i class="fa-solid fa-terminal"></i> | |
| Consola de Salida | |
| </div> | |
| <div id="console-output" class="h-8 flex items-center gap-2 text-slate-300 overflow-x-auto whitespace-nowrap"> | |
| <span class="text-slate-600 italic">Esperando ejecución...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Tips Section --> | |
| <div class="grid sm:grid-cols-3 gap-4 animate-slide-up" style="animation-delay: 0.4s;"> | |
| <div class="glass-panel rounded-2xl p-5 hover:bg-white/5 transition-colors group"> | |
| <div class="w-10 h-10 rounded-lg bg-purple-500/10 border border-purple-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform"> | |
| <i class="fa-solid fa-plus-minus text-purple-400"></i> | |
| </div> | |
| <h4 class="font-semibold text-white mb-1">Incremento Constante</h4> | |
| <p class="text-sm text-slate-400">El valor aumenta 20 unidades por iteración. Siempre verifica el paso (<span class="font-mono text-xs">i += n</span>).</p> | |
| </div> | |
| <div class="glass-panel rounded-2xl p-5 hover:bg-white/5 transition-colors group"> | |
| <div class="w-10 h-10 rounded-lg bg-amber-500/10 border border-amber-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform"> | |
| <i class="fa-solid fa-less-than-equal text-amber-400"></i> | |
| </div> | |
| <h4 class="font-semibold text-white mb-1">Condición Inclusiva</h4> | |
| <p class="text-sm text-slate-400">El operador <span class="font-mono text-xs"><=</span> incluye el límite (200), generando una iteración extra comparado con <span class="font-mono text-xs"><</span>.</p> | |
| </div> | |
| <div class="glass-panel rounded-2xl p-5 hover:bg-white/5 transition-colors group"> | |
| <div class="w-10 h-10 rounded-lg bg-rose-500/10 border border-rose-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform"> | |
| <i class="fa-solid fa-calculator text-rose-400"></i> | |
| </div> | |
| <h4 class="font-semibold text-white mb-1">Cálculo Formal</h4> | |
| <p class="text-sm text-slate-400">Fórmula: <span class="font-mono text-xs">((200-100)/20)+1 = 6</span>. Útil para bucles con muchas iteraciones.</p> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="relative z-10 border-t border-white/5 mt-12 py-8 text-center text-slate-500 text-sm"> | |
| <p>Python Loop Master • Aprende interactivamente</p> | |
| </footer> | |
| <script> | |
| let hasAnswered = false; | |
| let simulationRunning = false; | |
| // Initialize visualizer nodes | |
| const iterations = [ | |
| { i: 100, out: 100, next: 120 }, | |
| { i: 120, out: 120, next: 140 }, | |
| { i: 140, out: 140, next: 160 }, | |
| { i: 160, out: 160, next: 180 }, | |
| { i: 180, out: 180, next: 200 }, | |
| { i: 200, out: 200, next: 220 } | |
| ]; | |
| function initVisualizer() { | |
| const container = document.getElementById('visualizer-container'); | |
| container.innerHTML = ''; | |
| iterations.forEach((iter, idx) => { | |
| const node = document.createElement('div'); | |
| node.className = 'iteration-node flex flex-col items-center relative'; | |
| node.id = `node-${idx}`; | |
| node.innerHTML = ` | |
| <div class="w-16 h-16 sm:w-20 sm:h-20 rounded-2xl bg-white/5 border-2 border-white/10 flex flex-col items-center justify-center relative z-10 transition-all duration-500" id="node-box-${idx}"> | |
| <span class="text-xs text-slate-500 mb-0.5">i =</span> | |
| <span class="text-xl sm:text-2xl font-bold text-white font-mono">${iter.i}</span> | |
| </div> | |
| <div class="mt-4 text-center opacity-0 transition-all duration-500 transform translate-y-2" id="node-info-${idx}"> | |
| <div class="text-xs text-emerald-400 font-mono mb-0.5">print(${iter.out})</div> | |
| <div class="text-[10px] text-slate-500">i → ${iter.next}</div> | |
| </div> | |
| `; | |
| container.appendChild(node); | |
| }); | |
| } | |
| initVisualizer(); | |
| function selectOption(option, isCorrect) { | |
| if (hasAnswered) return; | |
| hasAnswered = true; | |
| const buttons = document.querySelectorAll('.option-card'); | |
| buttons.forEach(btn => { | |
| btn.disabled = true; | |
| btn.style.cursor = 'default'; | |
| const btnOption = btn.dataset.option; | |
| if (btnOption === option) { | |
| btn.classList.add(isCorrect ? 'selected-correct' : 'selected-incorrect'); | |
| } else if (btnOption === 'A' && !isCorrect) { | |
| // Highlight correct answer if wrong was selected | |
| setTimeout(() => { | |
| btn.classList.add('selected-correct'); | |
| }, 500); | |
| } | |
| if (!isCorrect && btnOption !== option && btnOption !== 'A') { | |
| btn.style.opacity = '0.5'; | |
| } | |
| }); | |
| if (isCorrect) { | |
| createConfetti(); | |
| document.getElementById('feedback-section').classList.remove('hidden'); | |
| setTimeout(() => { | |
| document.getElementById('feedback-section').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | |
| }, 300); | |
| } else { | |
| // Show feedback anyway after a delay | |
| setTimeout(() => { | |
| document.getElementById('feedback-section').classList.remove('hidden'); | |
| document.getElementById('feedback-section').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | |
| }, 1000); | |
| } | |
| } | |
| async function runSimulation() { | |
| if (simulationRunning) return; | |
| simulationRunning = true; | |
| const btn = document.getElementById('sim-btn'); | |
| const originalBtnText = btn.innerHTML; | |
| btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Ejecutando...'; | |
| btn.classList.add('opacity-75'); | |
| const consoleOutput = document.getElementById('console-output'); | |
| const progressBar = document.getElementById('progress-bar'); | |
| // Reset | |
| consoleOutput.innerHTML = ''; | |
| progressBar.style.width = '0%'; | |
| iterations.forEach((_, idx) => { | |
| document.getElementById(`node-box-${idx}`).className = 'w-16 h-16 sm:w-20 sm:h-20 rounded-2xl bg-white/5 border-2 border-white/10 flex flex-col items-center justify-center relative z-10 transition-all duration-500'; | |
| document.getElementById(`node-info-${idx}`).classList.remove('opacity-100', 'translate-y-0'); | |
| document.getElementById(`node-info-${idx}`).classList.add('opacity-0', 'translate-y-2'); | |
| }); | |
| await wait(500); | |
| for (let idx = 0; idx < iterations.length; idx++) { | |
| const iter = iterations[idx]; | |
| // Highlight node | |
| const box = document.getElementById(`node-box-${idx}`); | |
| box.classList.remove('border-white/10', 'bg-white/5'); | |
| box.classList.add('border-cyan-400', 'bg-cyan-500/20', 'shadow-lg', 'shadow-cyan-500/20'); | |
| // Show info | |
| const info = document.getElementById(`node-info-${idx}`); | |
| info.classList.remove('opacity-0', 'translate-y-2'); | |
| info.classList.add('opacity-100', 'translate-y-0'); | |
| // Update console | |
| const outputSpan = document.createElement('span'); | |
| outputSpan.className = 'inline-flex items-center gap-2 animate-scale-in'; | |
| outputSpan.innerHTML = `<span class="text-slate-500 text-xs">${idx + 1}.</span><span class="text-cyan-300">${iter.out}</span>`; | |
| consoleOutput.appendChild(outputSpan); | |
| // Update progress | |
| progressBar.style.width = `${((idx + 1) / iterations.length) * 100}%`; | |
| await wait(800); | |
| // Mark as completed | |
| box.classList.remove('border-cyan-400', 'bg-cyan-500/20'); | |
| box.classList.add('border-emerald-500/50', 'bg-emerald-500/10'); | |
| } | |
| // Final state | |
| const finalSpan = document.createElement('span'); | |
| finalSpan.className = 'text-emerald-400 text-xs ml-2 border-l border-white/10 pl-2 animate-scale-in'; | |
| finalSpan.textContent = '✓ Bucle completado (6 iteraciones)'; | |
| consoleOutput.appendChild(finalSpan); | |
| btn.innerHTML = originalBtnText; | |
| btn.classList.remove('opacity-75'); | |
| simulationRunning = false; | |
| } | |
| function wait(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| function createConfetti() { | |
| const colors = ['#06b6d4', '#10b981', '#f43f5e', '#f59e0b', '#8b5cf6']; | |
| for (let i = 0; i < 50; i++) { | |
| setTimeout(() => { | |
| const confetti = document.createElement('div'); | |
| confetti.className = 'confetti'; | |
| confetti.style.left = Math.random() * 100 + 'vw'; | |
| confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; | |
| confetti.style.animationDuration = (Math.random() * 3 + 2) + 's'; | |
| confetti.style.opacity = Math.random(); | |
| confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '0'; | |
| document.body.appendChild(confetti); | |
| setTimeout(() => confetti.remove(), 5000); | |
| }, i * 30); | |
| } | |
| } | |
| // Entrance animations for iteration nodes on scroll | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const nodes = entry.target.querySelectorAll('.iteration-node'); | |
| nodes.forEach((node, idx) => { | |
| setTimeout(() => { | |
| node.classList.add('active'); | |
| }, idx * 100); | |
| }); | |
| } | |
| }); | |
| }, { threshold: 0.2 }); | |
| document.add |