Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 87: Proyecto de portafolio — Breakout, el juego de romper ladrillos

100 Días de Código en Python — Día 87: Proyecto de portafolio — Breakout, el juego de romper ladrillos

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 87, proyecto de portafolio profesional (categoría Game).

Este tutorial adapta pedagógicamente el Día 87. La sección 📝 Contenido original del curso proviene del único archivo de la carpeta: dia87-tarea6.md (enunciado y pregunta de reflexión, en inglés; aquí traducido). El curso pide expresamente no seguir un tutorial de Breakout, sino especificar el proyecto, hacer una lista de tareas y, si ayuda, un diagrama de flujo. Por eso este tutorial se centra en esa especificación y en las piezas difíciles (colisiones y rebotes), verificadas como funciones puras; no entrega el juego. Todo lo que no está en ese archivo (especificación de ejemplo, geometría de colisiones, código de ejemplo, retos 🟢🟡🟠🔴, proyecto extra y la etapa 6 de Mi Portafolio Profesional) es creación de esta adaptación.

Nota sobre las verificaciones: el 28 de septiembre de 2026, con Python 3.14, se ejecutaron las funciones choca, lado_del_choque, crear_filas y angulo_rebote de las secciones 4 y 5 con casos de prueba (por ejemplo, 5 filas × 10 columnas generan 50 ladrillos entre x = −270 y x = 270). La parte gráfica con turtle se describe a partir de lo trabajado en el Pong (Día 22) y no se ejecutó en pantalla.


Tabla de contenidos

  1. La tarea: Breakout
  2. Especificar antes de programar (lo que pide el curso)
  3. 📖 Qué se reutiliza del Pong y qué es nuevo
  4. 📖 Colisiones con rectángulos
  5. 📖 Rebotes: ¿horizontal o vertical?, ¿con qué ángulo?
  6. 🚀 Proyecto del día: construcción por versiones
  7. 🌟 Proyecto extra: Recicla-Breakout
  8. 🏗️ Mi Portafolio Profesional — Etapa 6
  9. 🧩 Comprueba lo aprendido
  10. 🤖 Programar con apoyo de IA
  11. 🛠️ Observaciones sobre el material original
  12. 📋 Matriz de trazabilidad
  13. 📦 Evidencias de aprendizaje del Día 87

1. La tarea: Breakout

(Fuente: dia87-tarea6.md)

📝 Contenido original del curso

Breakout fue un juego exitoso programado originalmente por Steve Wozniak antes de que él y Jobs fundaran Apple. Es un juego sencillo, parecido al Pong, en el que usas una pelota y una paleta para derribar un muro (de ladrillos). (El enunciado enlaza la página de Wikipedia del juego, una imagen y una versión jugable en elgoog.im/breakout.)

Un buen punto de partida es repasar las lecciones del Día 22, cuando construimos el Pong. Pero tendrás muchas cosas que buscar en Google, descifrar y pelear para completar este proyecto. Evita ir a un tutorial de cómo construir Breakout: en su lugar, especifica el proyecto, descubre cómo va a funcionar, escribe una lista de tareas (checklist of todos) y dibuja un diagrama de flujo (si te ayuda).

Pregunta de la tarea — Momento de reflexión: cómo abordaste el proyecto; qué fue difícil y qué fácil; cómo mejorarías; tu mayor aprendizaje; qué harías distinto.

Prerequisitos: turtle y coordenadas (Días 18–19); clases, herencia y el ciclo de juego con tracer/update (Días 16, 20 y 21); el Pong (Día 22) y el cruce de la tortuga (Día 23); listas (Día 4).


2. Especificar antes de programar (lo que pide el curso)

(Desarrolla la indicación de dia87-tarea6.md. La especificación es un ejemplo de esta adaptación: la tuya puede ser distinta.)

🔍 Antes de programar

Juega Breakout durante 5 minutos (en la versión del enunciado) y responde por escrito:

Pregunta Tu respuesta
¿Qué objetos hay en pantalla? …
¿Qué controla el jugador? ¿Con qué teclas? …
¿Qué hace la pelota al tocar paredes, techo, paleta, ladrillo, suelo? …
¿Cuándo se gana? ¿Cuándo se pierde? …
¿Cómo se ganan puntos? ¿Todos los ladrillos valen igual? …
¿Cambia algo con el tiempo (velocidad, tamaño de la paleta)? …

Ejemplo de especificación y lista de tareas

Texto
OBJETOS: Paleta, Pelota, Ladrillo (muchos), Marcador
REGLAS:
  - La pelota rebota en paredes laterales y techo.
  - Rebota en la paleta; el ángulo depende de dónde golpea.
  - Al tocar un ladrillo: el ladrillo desaparece, suma puntos, la pelota rebota.
  - Si la pelota cae por debajo de la paleta: se pierde una vida (3 vidas).
  - Se gana al romper todos los ladrillos.

LISTA DE TAREAS (checklist)
[ ] Ventana y ciclo de juego (tracer(0) + update)
[ ] Paleta que se mueve con ← → sin salirse
[ ] Pelota que se mueve y rebota en paredes y techo
[ ] Rebote en la paleta
[ ] Muro de ladrillos (filas de colores)
[ ] Colisión pelota-ladrillo: borrar ladrillo y rebotar
[ ] Vidas y fin de juego
[ ] Marcador (puntos y vidas)
[ ] Victoria al romper todo
[ ] Extras: niveles, velocidad creciente, récord

Diagrama de flujo de un "cuadro" (frame) del juego

Texto
┌─► mover pelota
│      ▼
│   ¿toca pared lateral? ── sí ─► invertir dx
│   ¿toca techo? ────────── sí ─► invertir dy
│   ¿toca paleta? ───────── sí ─► rebotar con ángulo (sección 5)
│   ¿toca un ladrillo? ──── sí ─► ocultar ladrillo, sumar puntos, rebotar
│   ¿cayó al suelo? ─────── sí ─► restar vida ─► ¿vidas = 0? ─► GAME OVER
│   ¿quedan ladrillos? ──── no ─► ¡VICTORIA!
│      ▼
│   actualizar pantalla y esperar un instante
└──────┘

📝 Este diagrama ya es casi el código de tu ciclo while. Cuando te pierdas programando, vuelve a él.


3. 📖 Qué se reutiliza del Pong y qué es nuevo

(Sección creada para esta adaptación.)

Del Pong (Día 22) En Breakout
Clase Paddle que hereda de Turtle, con shapesize para estirar el cuadrado Paleta horizontal abajo que se mueve a izquierda y derecha
Clase Ball con x_move, y_move, bounce_x(), bounce_y() Casi igual; ahora rebota en el techo y en las paredes
Ciclo con screen.tracer(0), screen.update() y time.sleep() Igual
Scoreboard Igual, más un contador de vidas
Nuevo Muchos objetos (ladrillos) en una lista; colisión con rectángulos; saber por qué lado chocó; eliminar objetos

⚠️ Eliminar un ladrillo: ladrillo.hideturtle() lo oculta, pero el objeto sigue en la lista y la pelota seguiría chocando con él. Además, quítalo de la lista. Y cuidado: no lo quites mientras recorres esa misma lista con for (se saltan elementos). Recorre una copia (for l in ladrillos[:]) o guarda el ladrillo golpeado y elimínalo después del ciclo.

Nota sobre time.sleep en turtle: en el Pong del curso se usa time.sleep dentro del while, y funciona porque screen.update() redibuja en cada vuelta. Una alternativa es screen.ontimer(funcion, ms), equivalente al after de Tkinter (Día 86). Ambas son válidas en este proyecto.


4. 📖 Colisiones con rectángulos

(Sección creada para esta adaptación. Funciones verificadas.)

🎯 Objetivo de aprendizaje

Detectar si la pelota toca un rectángulo usando distancias en cada eje.

📖 Explicación conceptual

En el Pong se usaba ball.distance(paddle) < 50: mide la distancia al centro, como si la paleta fuera un círculo. Con ladrillos anchos y bajitos esa aproximación falla: la pelota "choca" con el aire encima del ladrillo, o atraviesa sus esquinas.

Mejor: comprobar cada eje por separado. Un cuadrado de turtle mide 20 × 20 píxeles; shapesize(stretch_wid=1, stretch_len=3) lo convierte en 60 × 20. Su medio ancho es 30 y su medio alto, 10. La pelota (radio 10) toca el ladrillo si:

  • la distancia horizontal entre centros es menor que medio ancho + radio, y
  • la distancia vertical es menor que medio alto + radio.
Texto
        ◄──── 30 ────►◄10►
        ┌─────────────┐
        │  ladrillo ● │ centro
        └─────────────┘
                         ○  pelota: choca si |dx| < 40 y |dy| < 20

🔎 Ejemplo explicado

Python
RADIO_BOLA = 10

def choca(bola_x, bola_y, rect_x, rect_y, medio_ancho, medio_alto):
    return (abs(bola_x - rect_x) < medio_ancho + RADIO_BOLA and
            abs(bola_y - rect_y) < medio_alto + RADIO_BOLA)
  • Recibe números, no objetos Turtle: así se prueba sin abrir ventanas (choca(0, 0, 0, 15, 30, 10) → True; choca(0, 40, 0, 15, 30, 10) → False).
  • abs() da la distancia sin importar si la pelota está a la izquierda o a la derecha.
  • En el juego se llamaría con bola.xcor(), bola.ycor(), ladrillo.xcor(), ladrillo.ycor().

Crear el muro

Python
def crear_filas(filas, columnas, ancho=60, alto=20, x0=-270, y0=250):
    return [(x0 + c * ancho, y0 - f * alto)
            for f in range(filas) for c in range(columnas)]

Devuelve las posiciones (comprensión de listas anidada, Día 26). Con 5 filas y 10 columnas: 50 posiciones, de (-270, 250) a (270, 170). Tu turno: crea un Ladrillo (clase que hereda de Turtle) en cada posición, con un color por fila.


5. 📖 Rebotes: ¿horizontal o vertical?, ¿con qué ángulo?

(Sección creada para esta adaptación. Funciones verificadas.)

¿Por qué lado chocó?

Si la pelota golpea un ladrillo desde abajo, debe invertir su movimiento vertical. Si lo golpea de lado, el horizontal. Una forma de decidirlo: comparar cuánto se "meten" (solapan) la pelota y el ladrillo en cada eje. El eje con menos solapamiento es el lado por el que entró.

Python
def lado_del_choque(bola_x, bola_y, rect_x, rect_y, medio_ancho, medio_alto):
    solape_x = medio_ancho + RADIO_BOLA - abs(bola_x - rect_x)
    solape_y = medio_alto + RADIO_BOLA - abs(bola_y - rect_y)
    return "lateral" if solape_x < solape_y else "vertical"

Verificado: pelota justo debajo del ladrillo → "vertical"; pelota en su borde izquierdo → "lateral".

Alternativa más simple: invertir siempre dy. Funciona "bastante bien" y muchos clones lo hacen. Compara ambas jugando y decide.

El ángulo en la paleta

Si la pelota siempre rebota igual en la paleta, el jugador no tiene control. En Breakout, dónde golpea la pelota cambia el ángulo: en el centro sale recta hacia arriba; en los extremos, inclinada.

Python
def angulo_rebote(bola_x, paleta_x, medio_ancho=50, max_grados=60):
    relativo = (bola_x - paleta_x) / medio_ancho   # -1 (borde izq.) a 1 (borde der.)
    relativo = max(-1, min(1, relativo))
    return 90 - relativo * max_grados

Verificado: centro → 90° (arriba); borde derecho → 30°; borde izquierdo → 150°. El max/min "recorta" el valor si la pelota golpea un poco fuera del borde.

Prerequisito — de ángulo a movimiento: con turtle puedes usar bola.setheading(angulo) y bola.forward(velocidad). Si guardas dx y dy, necesitas trigonometría: dx = velocidad * math.cos(math.radians(angulo)) y dy = velocidad * math.sin(math.radians(angulo)). Elige un enfoque y úsalo en todo el juego.

⚠️ Errores frecuentes

Error Qué ocurre Por qué Solución
La pelota "vibra" dentro de la paleta Rebota muchas veces seguidas En el siguiente cuadro sigue tocándola y vuelve a invertir Rebota solo si baja (dy < 0) o recolócala encima
Rompe dos ladrillos a la vez y no rebota Invierte dy dos veces en el mismo cuadro Cada choque invierte la dirección Máximo un rebote por cuadro (break tras el primer choque)
Atraviesa ladrillos a alta velocidad "Salta" sobre ellos Se mueve más píxeles por cuadro que el alto del ladrillo Velocidad máxima menor que el alto, o mover en pasos pequeños
Ladrillo invisible sigue chocando Rebotes "fantasma" Se ocultó pero sigue en la lista Quítalo de la lista
La paleta sale de la pantalla Desaparece No hay límites Comprueba xcor() antes de moverla

6. 🚀 Proyecto del día: construcción por versiones

(Desarrolla la tarea dia87-tarea6.md. Las versiones siguen la lista de tareas de la sección 2.)

Problema

Construir el juego Breakout con Python turtle.

Contexto

La sala de informática tiene una "semana de juegos clásicos" y cada grupo presenta un juego programado por sus integrantes.

Objetivo

Un Breakout jugable, con muro de ladrillos, vidas, puntaje, victoria y derrota, organizado en clases.

Requisitos funcionales

  1. Paleta controlada con las flechas, sin salir de la pantalla.
  2. Pelota que rebota en paredes, techo, paleta y ladrillos.
  3. Al menos 4 filas de ladrillos; cada ladrillo golpeado desaparece y suma puntos.
  4. 3 vidas; mensaje de "Game over" y de victoria.

Restricciones técnicas

  • Una clase por tipo de objeto (Paleta, Pelota, Ladrillo o Muro, Marcador) en archivos separados, como en el Pong.
  • Colisiones en funciones puras probadas con assert.
  • Sin copiar un tutorial de Breakout (lo pide el curso): documenta en el diario qué buscaste y dónde.

Planificación

La especificación, la lista de tareas y el diagrama de la sección 2 son tu planificación. Añade: 8. probar cada casilla de la lista antes de marcarla; 9. corregir; 10. mejorar con los retos.

Construcción paso a paso

  • Versión 1 — Paleta: ventana, ciclo de juego y paleta con límites.
  • Versión 2 — Pelota: movimiento y rebotes en paredes y techo; si cae, reaparece en el centro.
  • Versión 3 — Paleta y pelota: rebote con ángulo (sección 5).
  • Versión 4 — Muro: dibujar los ladrillos con colores por fila.
  • Versión 5 — Romper: colisión con ladrillos, eliminarlos, sumar puntos (las filas de arriba valen más).
  • Versión 6 — Reglas: vidas, game over, victoria.
  • Versión final — Pulido: velocidad creciente, README.md con captura o GIF, diario.

Pruebas

# Caso Resultado esperado
1 choca(0, 0, 0, 15, 30, 10) True
2 choca(0, 40, 0, 15, 30, 10) False
3 lado_del_choque(0, -12, 0, 10, 30, 10) "vertical"
4 angulo_rebote(0, 0) y angulo_rebote(80, 0) 90.0 y 30
5 Mantener pulsada la flecha derecha La paleta se detiene en el borde
6 Dejar caer la pelota 3 veces "Game over" y el juego se detiene
7 Romper el último ladrillo Mensaje de victoria
8 Golpear dos ladrillos a la vez La pelota rebota (una sola vez)

🧪 Prueba y depuración

Python
for ladrillo in ladrillos:
    if pelota.distance(ladrillo) < 20:
        ladrillo.hideturtle()
        pelota.bounce_y()

Encuentra tres problemas: distance < 20 trata el ladrillo de 60 px como un punto (los extremos no chocan); el ladrillo oculto sigue en la lista; y si toca dos a la vez, rebota dos veces (se anula).

⭐ Extensión del proyecto

  • 🟢 Reto 1 — Aplicación: la velocidad de la pelota aumenta cada 10 ladrillos.
  • 🟡 Reto 2 — Ampliación: ladrillos "duros" que necesitan dos golpes y cambian de color tras el primero.
  • 🟠 Reto 3 — Desafío: niveles: al ganar, se carga un muro con otra forma leído desde un archivo de texto (por ejemplo, # = ladrillo, . = vacío).
  • 🔴 Reto 4 — Pensamiento computacional: "poderes" que caen de algunos ladrillos (paleta más ancha, pelota extra). Diseña cómo manejar varias pelotas a la vez sin duplicar código, y qué pasa con las vidas si cae una de ellas.

7. 🌟 Proyecto extra: Recicla-Breakout

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

El comité ambiental del colegio quiere un juego para la semana del reciclaje. En lugar de ladrillos, el muro está formado por residuos de tres colores (blanco = aprovechables, verde = orgánicos, negro = no aprovechables, según el código de colores de separación en la fuente usado en Colombia). La paleta tiene el color de una caneca, que el jugador cambia con la barra espaciadora.

Requisitos

  1. Un residuo solo se rompe si la paleta tenía el color correcto en el último rebote; si no, resta puntos.
  2. Al final, un resumen: cuántos residuos se clasificaron bien por color.
  3. Reutiliza tus clases del Breakout (herencia: Residuo(Ladrillo)).

Decisiones que debes tomar (y justificar)

  • ¿Cómo sabe el residuo cuál fue el último color de la paleta? ¿Dónde guardas ese dato?
  • ¿Cómo haces que el juego sea educativo sin ser frustrante?

Casos de prueba

  • Golpe con color correcto: el residuo desaparece y suma.
  • Golpe con color incorrecto: el residuo sigue y resta.

8. 🏗️ Mi Portafolio Profesional — Etapa 6

(Proyecto integrador creado para esta adaptación; etapas anteriores en los Días 82 a 86.)

Etapa 6 (hoy):

  1. Repositorio del Breakout con la especificación y la lista de tareas de la sección 2 (en PLAN.md), marcando lo completado.
  2. README.md con un GIF o captura del juego y las teclas de control.
  3. DIARIO.md del Día 87: incluye qué buscaste en Internet sin recurrir a un tutorial de Breakout.
  4. Ficha en proyectos.json.

9. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Por qué distance() no es adecuada para detectar colisiones con ladrillos?
  2. ¿Qué ventaja tiene escribir choca con números en lugar de objetos Turtle?
  3. ¿Por qué no conviene eliminar elementos de una lista mientras la recorres con for?
  4. ¿Por qué el ángulo de rebote en la paleta mejora la jugabilidad?

Análisis de código

Python
t = Turtle("square")
t.shapesize(stretch_wid=1, stretch_len=4)

¿Qué medidas tiene el rectángulo? ¿Qué medio_ancho y medio_alto usarías en choca?

Predicción

¿Qué devuelve angulo_rebote(-25, 0)? ¿Hacia dónde sale la pelota?

Depuración

"Cuando la pelota golpea la paleta de lado, se queda pegada vibrando." Propón dos soluciones.

Modificación

Cambia crear_filas para que el muro quede centrado en la pantalla para cualquier número de columnas.

Creación

Escribe puntos_por_fila(y) que devuelva 7 puntos para las filas más altas, 5, 3 y 1 para las más bajas.


10. 🤖 Programar con apoyo de IA

El enunciado pide no seguir un tutorial de Breakout; lo mismo aplica a pedirle a una IA "hazme un Breakout".

Ciclo de trabajo: formula el problema → inténtalo tú primero → consulta a la IA sobre un punto concreto → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.

Actividad 1 — Preguntar por conceptos. "¿Cómo detecto la colisión entre un círculo y un rectángulo alineado con los ejes?" Compara la respuesta con la sección 4 y pruébala con los casos de la sección 6.

Actividad 2 — Revisar tu especificación. Pega tu lista de tareas (sin código) y pregunta qué casos límite olvidaste. Decide cuáles agregar.

Actividad 3 — Documentación. Pregunta la diferencia entre screen.ontimer y time.sleep en turtle, y compruébalo en la documentación oficial del módulo turtle.

Declara el uso: sección "Uso de IA" en el README.md.


11. 🛠️ Observaciones sobre el material original

Archivo Observación Tratamiento
dia87-tarea6.md Pide especificar, listar tareas y hacer un diagrama, sin seguir tutoriales La sección 2 modela ese proceso; el tutorial no entrega el juego
dia87-tarea6.md Remite al Pong del Día 22 Sección 3 compara lo reutilizable con lo nuevo
dia87-tarea6.md Menciona una versión jugable en un sitio espejo (elgoog.im) Se conserva como referencia; cualquier versión clásica sirve para comprender las reglas
dia87-tarea6.md No menciona la detección de colisiones con rectángulos Secciones 4 y 5, porque el método distance() del Pong no basta

12. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Breakout: pelota y paleta para derribar un muro; similar a Pong dia87-tarea6.md Secciones 3 a 5 Juego Breakout Retos 1 a 4 Secciones 6 y 8
Historia: Steve Wozniak dia87-tarea6.md Sección 1 — — —
Repasar el Día 22 (Pong) dia87-tarea6.md Sección 3 — — Sección 6
Especificar, checklist de tareas y diagrama de flujo; no seguir tutoriales dia87-tarea6.md Sección 2 Planificación del proyecto — Secciones 6 y 8 (PLAN.md)
Momento de reflexión dia87-tarea6.md Sección 6 (versión final) Pregunta de reflexión — Sección 8
(No existe en el curso original) — Colisiones rectangulares, ángulos — Recicla-Breakout Sección 7

13. 📦 Evidencias de aprendizaje del Día 87

  • PLAN.md con la especificación, la lista de tareas y el diagrama de flujo.
  • El juego funcionando (GIF o video corto con victoria y game over).
  • Pruebas de las funciones de colisión superadas.
  • DIARIO.md del Día 87, repositorio y ficha en el portafolio.
  • Cuaderno de errores: distance() con rectángulos, ladrillos ocultos que siguen en la lista, doble rebote, pelota pegada a la paleta, efecto túnel a alta velocidad, paleta sin límites.