Trabajo práctico integrador · Programación Orientada a Objetos
🎮 ARENA DE
COMBATE
Construí tu propio videojuego con POO
01La historia
Bienvenido a la Arena de Combate. Tu misión es construir un videojuego usando Programación Orientada a Objetos.
Nadie te va a dar el juego terminado. Lo vas a ir construyendo vos, etapa por etapa, y en cada una el juego va a poder hacer una cosa más que antes. Al principio tu personaje solo va a existir. Después va a poder recibir daño. Después va a tener un arma. Después va a aparecer un enemigo. Y en algún momento, sin que te des cuenta, vas a tener una pelea por turnos funcionando en tu computadora.
La parte importante no es el juego. Es que al final vas a poder explicar por qué lo hiciste así, y eso te va a servir para cualquier problema, no solo para este.
02Cómo funciona esta guía
Cada etapa tiene siempre el mismo recorrido. Y siempre, siempre, empieza por pensar.
Los botones de solución están para comprobar, no para copiar. Si mirás la respuesta antes de intentar, la actividad te sale bien y vos no aprendiste nada. Equivocarte es parte del proceso: el error te muestra exactamente qué parte no entendiste todavía.
03Lo que ya sabés (y vas a usar todo)
Nada de lo que viene es nuevo. Todo esto lo vimos en las clases 1 y 2. Acá lo vas a usar junto, en un solo programa.
Las dos preguntas de siempre: ¿qué información tiene? y ¿qué puede hacer?
Un objeto solo no hace un programa: un programa es un montón de objetos hablando entre ellos.
04Las reglas del juego
👥 Solo o en pareja
Se puede hacer individual o en parejas de máximo dos personas. Si son dos, los dos tienen que poder explicar todo el código: no sirve repartirse "vos las clases, yo el menú".
🐍 Python puro
Sin librerías raras, sin cosas que no vimos. Todo lo que necesitás
ya lo sabés: variables, if, while, funciones, listas y clases.
💾 Un archivo
Abrí un archivo nuevo, ponele juego.py y tenelo al lado
mientras avanzás. Cada etapa le agrega algo. No arranques de cero en cada etapa.
🖨️ ¿Lo preferís en papel?
Hay un apunte en PDF con todo el trabajo práctico, la teoría de repaso, las ayudas y la rúbrica. Se puede imprimir y trabajar arriba.
⏱️ A tu ritmo
Tu progreso se guarda solo en esta computadora. Podés cerrar la página y seguir después desde donde estabas.
Estamos aprendiendo a pensar, diseñar y construir programas.
Pensar antes de programar
Mirar la escena de un juego y encontrar los objetos, sin escribir una sola línea de código.
01La situación
Te pasan esta captura de un juego y te dicen: "programalo". Antes de tocar el teclado, mirala bien.
¿Qué objetos necesitamos para construir un juego de combate?
02Buscá los objetos en la escena
Marcá todo lo que te parece que merece ser un objeto del programa (o sea, algo con su propia clase).
- 🧍 El héroe
- 👾 El goblin
- ⚔️ La espada
- 🧪 La poción
- ❤️ La barra de vida
- ⭐ Los 40 puntos de experiencia
- 🌙 El fondo de la arena
🧩 Ver la explicación completa
Un objeto es cualquier cosa que podamos describir con datos y que además haga algo. Por eso el héroe, el goblin, la espada y la poción entran: los cuatro guardan información y los cuatro saben hacer algo.
La vida, la experiencia y el nivel no son objetos: son datos que vive adentro de otro objeto. Es la misma duda que apareció en la clase 1 con el puntaje.
Que algo se te ocurra no significa que merezca ser una clase. Si no tiene información propia y además acciones propias, casi siempre es un atributo de alguien más.
03Las dos preguntas de siempre
Ya elegimos los objetos. Ahora a cada uno le hacemos las mismas dos preguntas que le hicimos al auto, al perro y al cliente de la cafetería. El método no cambia según el problema.
¿Qué información tiene?
Lo que lo describe. Son sustantivos. Pueden cambiar y sigue siendo el mismo objeto.
→ Atributos
¿Qué puede hacer?
Sus acciones. Son verbos, y se escriben con ().
→ Métodos
Le hacemos las dos preguntas al héroe. ¿Cada una de estas cosas es un atributo o un método?
Decilo en voz alta con las dos frases: "el héroe tiene…" y "el héroe puede…".
Una de las dos va a sonar mal. "El héroe tiene atacar" suena horrible: entonces atacar() es un método.
04Un objeto que todavía no viste
Mirá al héroe y al goblin de la escena. Los dos tienen nombre, los dos tienen vida, a los dos les pueden pegar y los dos pueden morir.
Si todo eso lo comparten, ¿qué conviene hacer?
- Escribir
nombreyvidaen Jugador y otra vez en Enemigo - Crear una clase de arriba con lo que comparten, y que las dos hereden de ahí
- Que Jugador herede de Goblin
- Poner todo en una sola clase gigante llamada Juego
ES UN
Este dibujo lo vamos a construir de a poco. Por ahora quedátelo mirando: arriba lo que comparten todos, abajo lo que agrega cada uno.
05El método, que no cambia nunca
Este es el camino que vamos a repetir en todas las etapas, y el que vas a usar el día que te den un problema que no tenga nada que ver con videojuegos.
En el sexto lugar. Cinco pasos antes de tocar el teclado. Y los tres de abajo son igual de importantes: probar, encontrar el error y mejorar no es fracasar, es programar. No memorices: razoná.
06Desafío de la etapa
Sin computadora, en papel. Escribí la lista de objetos de tu juego. Para cada uno, poné al menos 2 atributos y 2 métodos. Mínimo tienen que estar: Personaje, Jugador, Enemigo y Arma.
Control rápido: los atributos tienen que ser sustantivos, los métodos tienen que ser
verbos y llevar (). Si un método te quedó sin paréntesis o un atributo te quedó siendo un verbo, revisalo.
Después: ¿qué información tiene? y ¿qué puede hacer?
Diseñar las clases
Dejar decidido, en una tabla, qué va a tener y qué va a poder hacer cada clase. Recién después, código.
01La situación
Ya sabemos qué objetos hay. Falta lo más importante y lo que más se saltea todo el mundo: decidir qué va adentro de cada uno. Esto se hace en papel, y tarda cinco minutos. Saltearlo tarda dos horas.
¿Qué información tiene cada clase y qué puede hacer cada una?
02Completá la tabla de diseño
Primero la tuya. No mires ninguna solución todavía: escribí lo que vos creés, aunque no estés seguro. Después comparamos.
¿Qué atributos y qué métodos le pondrías a cada clase?
| Clase | Atributos · ¿qué tiene? | Métodos · ¿qué puede hacer? |
|---|---|---|
| Personaje la de arriba |
||
| Jugador | ||
| Enemigo | ||
| Arma |
🔓 Ver una solución posible
| Clase | Atributos | Métodos |
|---|---|---|
| Personaje | nombre, vida | recibir_danio(), esta_vivo() |
| Jugador | nivel, experiencia, arma | atacar(), ganar_experiencia() |
| Enemigo | danio | atacar() |
| Arma | nombre, danio | usar() |
Ojo con una cosa: en Jugador y en Enemigo no repetimos nombre ni vida.
Eso ya está en Personaje y lo van a heredar. En la tabla de un hijo solo se escribe lo que agrega.
Si vos pusiste vida_maxima en Personaje, o un método
mostrar_info(), o le pusiste fuerza al Jugador: está bien, siempre que
puedas justificarlo. Diseñar es tomar decisiones, y casi nunca hay una sola correcta. Lo que sí está mal
es poner el arma en Personaje: no todos los personajes tienen un arma.
03¿Y este atributo dónde va?
La decisión difícil del diseño no es qué atributos existen: es en qué clase va cada uno. La regla es siempre la misma: arriba va solo lo que comparten todos.
Elegí la clase donde tiene que estar escrito cada atributo.
Poner todo arriba "por las dudas". Si le ponés experiencia a
Personaje, entonces las piedras, los cofres y los goblins también tienen experiencia. En la clase de arriba
va solo lo que comparten todos.
04Repaso relámpago del vocabulario
Tocá un término de la izquierda y después su definición.
05Desafío de la etapa
Dibujá el diagrama de tus cuatro clases, con cajas y flechas, como los de la clase 2:
- Arriba Personaje, con lo que comparten todos.
- Abajo Jugador y Enemigo, con una flecha hacia arriba que diga ES UN.
- Al costado Arma, con una flecha desde Jugador que diga TIENE UN.
Guardalo: este dibujo es parte de la entrega final.
Lo que tiene uno solo, va en el hijo.
Crear la clase Personaje
Escribir la primera clase del juego: la de arriba, la que van a heredar todos los demás.
01La situación
Abrí juego.py. Vamos a escribir la clase Personaje, que es el molde del que
van a salir el héroe y todos los enemigos.
¿Por qué conviene empezar por Personaje y no por Jugador?
- Porque va antes en el alfabeto
- Porque los demás van a heredar de ella: primero tiene que existir el padre
- Porque es la clase más larga y difícil
02Paso 1 · los atributos
Arrancamos con lo mínimo: un personaje tiene un nombre y una vida.
class Personaje:
def __init__(self, nombre, vida):
self.nombre = nombre
self.vida = vida
La clase
class Personaje: es el molde. Todavía no existe ningún personaje: solo dijimos cómo se fabrican.
Se escribe con mayúscula, por convención.
__init__
Es el método que se ejecuta solo, una sola vez: justo cuando escribís Personaje("Aria", 100).
Es el momento en que el personaje nace y recibe sus datos. También se lo llama constructor.
self
Quiere decir "yo". self.vida es "mi vida": el personaje hablando de sus propios
datos, para no confundirlos con los de otro personaje.
Los atributos
self.nombre y self.vida son la información que cada personaje guarda adentro.
Sin el self., el dato no se guarda y después "desaparece". Es el error número uno.
03Paso 2 · que pueda recibir daño
Un personaje al que no le podés pegar no sirve para una arena. Le agregamos su primer método.
class Personaje:
def __init__(self, nombre, vida):
self.nombre = nombre
self.vida = vida
def recibir_danio(self, cantidad):
self.vida = self.vida - cantidad
En la clase 2 lo escribimos recibir_daño, con ñ. Acá lo vamos a escribir
recibir_danio, sin ñ. Python acepta las dos, pero sin ñ te evitás problemas al copiar código,
al cambiar de teclado o al compartir el archivo. Es exactamente la misma idea, con otro nombre.
Si la vida es 100 y el personaje recibe 30 de daño, ¿cuánta vida le queda?
04Paso 3 · que pueda contestar si sigue vivo
Ahora el método más importante de todo el juego, aunque parezca el más chiquito.
class Personaje:
def __init__(self, nombre, vida):
self.nombre = nombre
self.vida = vida
def recibir_danio(self, cantidad):
self.vida = self.vida - cantidad
def esta_vivo(self):
return self.vida > 0
Si la vida es 0, ¿qué devuelve esta_vivo()?
TrueFalseNone- Imprime "estás vivo"
¿Por qué esta_vivo() usa return y recibir_danio() no?
- Porque queda más prolijo escribir return
- Porque
esta_vivo()contesta una pregunta que el programa va a usar en unif, yrecibir_danio()solo hace un cambio - Porque con
print()también funcionaría igual
¿Ese resultado lo necesita una persona o lo necesita el programa?
Si lo mira alguien y ahí termina, va print(). Si el programa lo va a guardar, comparar o volver a
usar, va return.
05Probalo: el problema escondido
Ejecutá el simulador con el botón de 500 de daño y mirá bien el número de la vida.
¿Qué pasa si recibe más daño que la vida que tiene? La vida queda en negativo.
Y un personaje con -400 de vida no existe: el dato miente. Esto ya lo arreglaste una vez, en
el ejercicio de la mascota de la clase 1.
06Desafío · arreglalo vos
Hacé que la vida nunca quede por debajo de cero.
def recibir_danio(self, cantidad):
self.vida = self.vida - cantidad
self.vida 0:
self.vida = 💡 Ver pista
Primero restás, y después te fijás si te pasaste. El if va
adentro del método (indentado igual que la línea de arriba), no afuera. Y acordate: el arreglo tiene que vivir
adentro del objeto, porque es el objeto el que decide cómo cambian sus propios datos. Eso es
encapsulación.
🔓 Ver la solución
def recibir_danio(self, cantidad):
self.vida = self.vida - cantidad
if self.vida < 0:
self.vida = 0 # nunca menos de 0Ese if es la única razón por la que la vida no queda en negativo.
Si lo sacás, el pilar de la encapsulación desaparece: cualquier golpe rompería el dato.
Escribí la clase completa en juego.py, creá un personaje y probá esto.
Antes de ejecutarlo, escribí en la carpeta qué esperás que imprima cada línea:
aria = Personaje("Aria", 100)
aria.recibir_danio(30)
print(aria.vida)
aria.recibir_danio(500)
print(aria.vida)
print(aria.esta_vivo())Y el objeto es el único que decide cómo cambian sus propios datos.
Crear el objeto Arma
Fabricar un objeto que no es un personaje, y ver por qué su método devuelve en vez de mostrar.
01La situación
Tenemos personajes que pueden recibir daño… pero nadie que se lo haga. Falta con qué pegar.
¿Un Arma es un Personaje? ¿Debería heredar de Personaje?
- Sí, porque también participa en la pelea
- No. Es otra cosa distinta: una clase aparte, que no hereda de nadie
- Sí, pero habría que crear una clase todavía más arriba
02La clase Arma
Le hacemos las dos preguntas de siempre. ¿Qué tiene? Un nombre y un daño. ¿Qué puede hacer? Usarse: entregar cuánto daño hace.
class Arma:
def __init__(self, nombre, danio):
self.nombre = nombre
self.danio = danio
def usar(self):
return self.danio
En la clase 2 este método se llamaba atacar(). Acá le decimos
usar() porque el arma no ataca sola: la usa alguien. El nombre lo elegís vos; lo que no se
negocia es que devuelva el daño.
¿Por qué usar() hace return self.danio y no print(self.danio)?
- Porque así el número se ve mejor en pantalla
- Porque ese número no es para mirarlo: hay que restárselo a la vida del enemigo
- Porque
returnes más rápido queprint
03Del molde a las armas de verdad
La clase Arma es el molde: describe, pero no existe. Ninguna espada apareció todavía.
Ahora fabricamos armas concretas.
espada = Arma("Espada", 25)
arco = Arma("Arco", 15)
Un solo molde, dos armas independientes. Cada una tiene sus propios valores. Cambiarle el daño a la espada no toca al arco.
¿Qué devuelve espada.usar() y qué devuelve arco.usar()?
🧩 Ver por qué
print(espada.usar()) # 25
print(arco.usar()) # 15
print(espada.nombre) # Espada
resultado = espada.usar() # el 25 queda GUARDADO en resultado
print(resultado - 5) # 20 ← puedo seguir haciendo cuentasFijate la última parte: como usar() devuelve el número, lo puedo
guardar en una variable y seguir usándolo. Con print, resultado valdría
None.
04Entrenamiento: ¿print() o return?
Para cada método del juego, ¿qué corresponde?
¿Lo necesita una persona o lo necesita el programa?
05Desafío · forjá tu propia arma
Inventá el arma con la que va a pelear tu personaje. Ponele el nombre que quieras y decidí cuánto daño hace. El arma que elijas te va a acompañar durante todo el trabajo práctico.
mi_arma = Arma("...", 25)Copiala en juego.py. Y probá esto también:
print(mi_arma.usar())
Un molde, todas las armas que quieras.
El jugador tiene un arma
Conectar dos objetos: herencia para lo que es, composición para lo que tiene.
01La situación
Tenemos un Personaje y tenemos un Arma. Están sueltos: ninguno de los dos sabe que el otro existe. Hay que decidir cómo se relacionan — y esto se decide antes de escribir código.
¿Un Jugador ES un Arma?
Leelo en voz alta: "un jugador es un arma". Suena horrible. Y cuando la frase suena rara, la respuesta es no. Acá no va herencia.
¿Un Jugador TIENE un Arma?
"Un jugador tiene un arma". Esa sí suena natural. Entonces va composición: dos objetos distintos, conectados.
ES UN · herencia
02Paso 1 · la herencia y el super()
Un jugador es un personaje: hereda. Y como hereda, no tiene que volver a escribir
nombre ni vida.
class Jugador(Personaje):
def __init__(self, nombre, vida):
super().__init__(nombre, vida)(Personaje)
Eso es todo lo que hace falta para decir "Jugador ES UN Personaje". Con esa palabra entre paréntesis,
el jugador ya tiene recibir_danio() y esta_vivo() sin escribirlos.
super()
Quiere decir "la clase de arriba". Traducido: "Padre, encargate vos de nombre y vida. Yo me ocupo de lo mío." Así cada dato se escribe en un solo lugar del programa.
Alguien escribió esto y el programa explota en la última línea. ¿Por qué?
class Jugador(Personaje):
def __init__(self, nombre, vida):
self.vida = vida # se olvidó del super()
aria = Jugador("Aria", 100)
print(aria.nombre)- Porque
Jugadorno hereda dePersonaje - Porque nadie ejecutó el
__init__del padre, así quenombrenunca se guardó - Porque
printno puede mostrar atributos
Si ves un AttributeError: ... has no attribute ..., andá derecho al
__init__ del hijo y fijate si está el super(). Casi siempre es eso.
03Paso 2 · el arma entra adentro del jugador
class Jugador(Personaje):
def __init__(self, nombre, vida, arma):
super().__init__(nombre, vida)
self.arma = armaself.arma = arma es la misma línea de siempre:
self.algo = algo. Lo único distinto es qué estamos guardando: no es un texto ni un número,
es un objeto entero. Un atributo también puede ser otro objeto.
espada = Arma("Espada", 25)
aria = Jugador("Aria", 100, espada)Primero fabricamos el arma. Después fabricamos al jugador pasándole esa arma.
El objeto espada entra adentro del objeto aria.
¿Qué imprime print(aria.arma.nombre)?
aria.arma.nombre = "el nombre del arma de Aria".
Cada punto es un paso más adentro. Si te perdés en una línea larga con muchos puntos, leela así y se ordena sola.
04Paso 3 · que ataque (dos objetos colaborando)
class Jugador(Personaje):
def __init__(self, nombre, vida, arma):
super().__init__(nombre, vida)
self.arma = arma
def atacar(self):
return self.arma.usar()Le pregunta al arma.
En return self.arma.usar(), ¿cuántos objetos participan y qué hace cada uno?
- Uno: el jugador calcula el daño él solo
- Dos: el jugador le pide el daño a su arma, y devuelve esa respuesta
- Tres: el jugador, el arma y el enemigo
🧩 ¿Y por qué esto es tan útil? Cambiar de arma
hacha = Arma("Hacha", 40)
aria.arma = hacha
print(aria.atacar()) # 40¿Qué línea de la clase Jugador tuvimos que cambiar? Ninguna. El jugador siempre supo lo
mismo: "tengo un arma y le pido que ataque". Qué arma sea, no es su problema.
Con 50 armas distintas, la clase Jugador sigue igual de corta. Si en
cambio hubiéramos usado herencia, cada arma nueva sería un tipo de jugador nuevo. Un desastre.
05Entrenamiento: ES UN / TIENE UN
¿Herencia (ES UN) o composición (TIENE UN)?
06Desafío de la etapa
En juego.py, escribí la clase Jugador completa y después:
- Creá tu arma y tu jugador, y mostrá
print(jugador.atacar()). - Mostrá el nombre del arma del jugador en una sola línea.
- Creá una segunda arma, cambiásela al jugador y volvé a atacar. Sin tocar la clase
Jugador. - La difícil: hacé que el jugador le pegue a un personaje de verdad, en dos líneas.
💡 Pista
Primero guardás lo que devuelve el ataque, después se lo pasás al otro:
danio = aria.atacar()yorco.recibir_danio(danio). Ahí ya son tres objetos hablando entre ellos.
Si podés decir "TIENE UN" → composición.
Crear enemigos
Escribir una segunda clase hija y ver que la herencia se escribe una vez y se usa muchas.
01La situación
Tu jugador pega, pero del otro lado no hay nadie. Los golpes se los lleva un personaje que no se defiende. Falta el enemigo.
Nuestro goblin pelea a mordiscones: no usa arma. ¿Cómo lo modelamos?
- Con un atributo
daniopropio, sin arma - Obligándolo a tener un objeto
Armaigual que el jugador - Poniéndole
danioa la clasePersonaje
02La clase Enemigo
Es el mismo molde que Jugador, cambiando lo que agrega. Fijate que ya lo podés escribir
casi sin pensar: eso significa que entendiste el patrón.
class Enemigo(Personaje):
def __init__(self, nombre, vida, danio):
super().__init__(nombre, vida)
self.danio = danio
def atacar(self):
return self.danio| Pregunta | Respuesta |
|---|---|
¿Qué recibe de Personaje? |
Los atributos nombre y vida, y los métodos recibir_danio() y
esta_vivo(). Ninguno está escrito adentro de Enemigo: los hereda. |
¿Qué agrega Enemigo? |
El atributo danio y el método atacar(). Eso es lo único suyo. |
¿Por qué usa super()? |
Para que el padre guarde nombre y vida, y no volver a escribirlos.
"Padre, encargate vos de eso. Yo me ocupo del daño." |
| ¿Qué es común y qué es específico? | Común: nombre y vida (los tiene el jugador y el enemigo). Específico: el arma es del jugador, el danio propio es del enemigo. |
Creamos goblin = Enemigo("Goblin", 60, 12). ¿Qué puede hacer o tener ese objeto?
goblin.nombregoblin.vidagoblin.daniogoblin.esta_vivo()goblin.recibir_danio(20)goblin.arma
03Un molde, muchos enemigos
Con la clase escrita una sola vez, ya podés poblar toda la arena:
goblin = Enemigo("Goblin", 60, 12)
esqueleto = Enemigo("Esqueleto", 45, 18)
lobo = Enemigo("Lobo", 70, 10)Tres objetos independientes, salidos del mismo molde. Bajarle la vida a uno no toca a los otros.
04Desafío · creá tu enemigo
Inventá el enemigo contra el que va a pelear tu personaje. Va a aparecer en el simulador de la etapa 9.
mi_enemigo = Enemigo("...", 60, 12)Y después, en la carpeta: ¿qué atributos de tu enemigo vienen de
Personaje y cuáles son propios de Enemigo?
Cada hijo agrega solo lo suyo.
Herencia de varios niveles
Ver qué pasa cuando el hijo tiene a su vez un hijo, y hasta dónde conviene estirar la cadena.
01La situación
Tu juego crece: querés que existan clases de personaje, como en cualquier RPG. Empezamos por el Guerrero, que es un jugador con fuerza.
Hacé el test de siempre, escalón por escalón. ¿Cuál de estas cadenas tiene sentido?
Personaje → Jugador → GuerreroArma → Jugador → GuerreroGuerrero → Jugador → Personaje(en ese orden de herencia)
ES UN
ES UN
Cada escalón suma, no reemplaza. El guerrero no recibe solo lo del jugador: recibe todo lo que hay más arriba en la cadena.
02El código
¿Qué hay de nuevo acá? Nada. Es exactamente lo mismo de la etapa 5, hecho una vez más.
class Guerrero(Jugador):
def __init__(self, nombre, vida, arma, fuerza):
super().__init__(nombre, vida, arma)
self.fuerza = fuerzaFijate en algo importante: Guerrero nunca menciona vida. Eso es asunto de
Personaje. Guerrero le pide a Jugador, y Jugador le pide a
Personaje. Cada clase se ocupa solamente de lo suyo y nadie repite el trabajo de otro.
Cuando ejecutás Guerrero("Aria", 100, espada, 8), ¿en qué orden pasan las cosas?
- Python entra al
__init__de Guerrero con los cuatro datos - El
super()de Guerrero sube al__init__de Jugador - El
super()de Jugador sube al__init__de Personaje, que guardanombreyvida - Vuelve a Jugador, que guarda el
arma - Vuelve a Guerrero, que guarda la
fuerza. El objeto quedó completo
03Predicción
espada = Arma("Espada", 25)
aria = Guerrero("Aria", 100, espada, 8)
print(aria.nombre)
print(aria.arma.nombre)
print(aria.fuerza)
print(aria.esta_vivo())¿Cuáles de esas cuatro líneas funcionan y por qué?
aria.nombre→ funcionaaria.arma.nombre→ funcionaaria.fuerza→ funcionaaria.esta_vivo()→ funcionaaria.danio→ funciona
Los cuatro atributos viven en un solo objeto, y al usarlo no se nota quién puso cada cosa.
04Una advertencia: que se pueda no significa que convenga
Personaje
└─ Jugador
└─ Guerrero
└─ GuerreroDeHielo
└─ GuerreroDeHieloNivel2
└─ GuerreroDeHieloElite
Para entender una clase hay que leer seis. Y para encontrar de dónde sale un dato, hay que subir seis escalones.
Personaje
├─ Jugador
│ └─ Guerrero
└─ Enemigo
├─ Goblin
└─ Dragon
Dos o tres niveles alcanzan casi siempre. Si necesitás ocho, casi seguro no era herencia lo que buscabas.
05Desafío de la etapa
- Escribí
Guerreroy creá uno. Imprimí sus cuatro datos. - Escribí otra clase hija de
Jugador: unMagoque agreguemagia. - En la carpeta, contestá: ¿de quién hereda el Mago directamente? ¿Y qué atributos termina teniendo?
- La difícil: hacé que el
Guerreropegue más fuerte que un jugador común, usando sufuerza.💡 Pista
Escribile su propio
atacar()adentro deGuerrero:return self.arma.usar() + self.fuerza. El hijo puede reemplazar un método del padre. Y eso, sin que lo diga nadie todavía, ya es polimorfismo: justo lo que viene en la etapa 8. 👀
Pero con dos o tres escalones alcanza.
Polimorfismo: misma orden, distinta respuesta
Que todos los enemigos entiendan atacar(), pero que cada uno conteste a su manera.
01La situación
Tu arena ya tiene goblins, esqueletos y lobos… y los tres pegan exactamente igual. Aburridísimo. Queremos que el dragón sea temible y que el goblin sea un estorbo menor.
Una idea es esta. ¿Qué problema tiene?
if enemigo.tipo == "dragon":
danio = enemigo.danio + 15
elif enemigo.tipo == "goblin":
danio = enemigo.danio
elif enemigo.tipo == "esqueleto":
danio = enemigo.danio * 2
# ... y así con cada enemigo nuevo- Que no funciona
- Que crece con cada enemigo nuevo: hay que volver a tocarlo siempre
- Que los
ifhacen lento al programa
02La solución: que cada uno sepa atacar a su manera
Ya sabemos que un hijo puede agregar cosas. Ahora usamos la otra mitad de la herencia: un hijo también puede reemplazar un método del padre.
class Dragon(Enemigo):
def atacar(self):
return self.danio + 15class Goblin(Enemigo):
def atacar(self):
return self.danioFijate qué corto quedó. Ninguno de los dos escribe __init__: lo heredan de
Enemigo tal cual, porque no agregan ningún atributo nuevo. Solo cambian cómo responden a una orden.
La misma orden. Distinta respuesta. Eso es todo el pilar.
Con dragon = Dragon("Dragón", 90, 20), ¿qué devuelve dragon.atacar()?
Y con goblin = Goblin("Goblin", 60, 12), ¿qué devuelve goblin.atacar()?
03Para qué sirve de verdad
Acá se ve el premio. Los metemos a todos en una lista y les damos la misma orden:
dragon = Dragon("Dragón", 90, 20)
goblin = Goblin("Goblin", 60, 12)
lobo = Goblin("Lobo", 70, 10)
for enemigo in [dragon, goblin, lobo]:
print(enemigo.nombre, "ataca con", enemigo.atacar())¿Qué imprime ese for?
- Dragón 35 · Goblin 12 · Lobo 10
- Dragón 20 · Goblin 12 · Lobo 10
- Dragón 35 · Goblin 27 · Lobo 25
if hay en ese for?Cero. El for no sabe ni le importa quién es cada uno:
cada objeto sabe cómo responder. Y si mañana agregás un Fantasma, no tocás una sola línea
del for.
🤔 Una pregunta para pensar: ¿hacía falta escribir atacar() en Goblin?
No. Si borrás ese método, el goblin igual sabe atacar: lo hereda de Enemigo,
que hace exactamente lo mismo (return self.danio).
Lo dejamos escrito solo para poder comparar las dos clases una al lado de la otra. En un programa de verdad, un hijo escribe un método solo cuando lo hace distinto. Si hace lo mismo, no lo escribas: para eso heredaste.
04Desafío de la etapa
- Creá un
Esqueleto(Enemigo)cuyoatacar()devuelvaself.danio * 2. - Creá un
Slime(Enemigo)que pegue la mitad:self.danio // 2. - Metelos a los cuatro en una lista y recorrela con un
for, imprimiendo cuánto pega cada uno. - Sin ejecutar, escribí primero en la carpeta qué esperás que salga. Después comparalo.
La pregunta de reflexión: agregaste dos enemigos nuevos.
¿Tuviste que cambiar una sola línea del for? ¿Qué pasaría si lo hubieras hecho con
if y elif, y mañana quisieras veinte enemigos?
Cada personaje sabe qué significa eso para él.
Construir la pelea
Hacer chocar todo lo que construiste: herencia, composición, return y polimorfismo, en un solo while.
01La situación
Todas las piezas existen y ninguna se toca. Tenés un jugador con arma, un enemigo que pega y dos personajes que saben decir si siguen vivos. Falta el pedazo de programa que los haga pelear.
¿En qué orden tienen que pasar las cosas en cada vuelta del combate?
- El jugador ataca y obtenemos el daño
- El enemigo recibe ese daño
- Se muestra cómo quedó la vida del enemigo
- Si sigue vivo, el enemigo ataca
- El jugador recibe el daño del enemigo
- Se repite todo mientras los dos estén vivos
- Cuando uno cae, se muestra el ganador
02Pedazo 1 · la condición del while
La pelea sigue mientras los dos estén vivos. Y para preguntarles, usamos el método que devuelve
True o False. Acá se cobra todo lo del return.
while aria.esta_vivo() and goblin.esta_vivo():¿Por qué va and y no or?
- Porque la pelea sigue solo si los dos están vivos: si uno cae, se termina
ortambién funcionaría igual- Porque en Python el
whilesiempre llevaand
Es puro return. Los objetos contestan True o
False y el programa decide con eso. Si esta_vivo() hiciera print,
esta pelea sería imposible de escribir.
03Pedazo 2 · el turno del jugador
El jugador ataca y el enemigo se lleva el golpe. Faltan tres pedazos.
danio = aria.
goblin.
print(aria.nombre, "hace", danio, "de daño ·", goblin.nombre, "queda con", goblin.)🧩 Ver esas tres líneas explicadas
- Línea 1.
danio = aria.atacar()— el jugador le pregunta a su arma cuánto pega. Comoatacar()usareturn, ese número le queda en la mano. Si el arma hicieraprint, acá no habría nada que guardar. - Línea 2.
goblin.recibir_danio(danio)— el jugador le manda ese número al otro personaje, que se lo resta a su propia vida. Fijate que el jugador no toca la vida del otro a mano: se lo pide. Cada objeto se ocupa de lo suyo. - Línea 3. Ahora sí un
print: esto lo lee una persona, no lo necesita el programa.
Tres objetos hablando entre ellos en tres líneas. Esto es POO funcionando.
04Pedazo 3 · el contraataque (y un detalle importante)
Si el goblin quedó con 0 de vida y el enemigo igual contraataca, ¿qué pasa?
- Un enemigo muerto le sigue pegando al jugador: hay que preguntarle antes si está vivo
- El programa da error y se cierra
- No pasa nada, el
whileya lo cortó
if goblin.esta_vivo():
golpe = goblin.atacar()
aria.recibir_danio(golpe)
print(goblin.nombre, "hace", golpe, "de daño ·", aria.nombre, "queda con", aria.vida)05Pedazo 4 · el ganador
Cuando el while termina, uno de los dos cayó. Solo hay que preguntar cuál.
if aria.esta_vivo():
print("Ganó", aria.nombre)
else:
print("Ganó", goblin.nombre)06Todo junto
espada = Arma("Espada", 25)
aria = Jugador("Aria", 100, espada)
goblin = Goblin("Goblin", 60, 12)
print("¡Empieza el combate!")
while aria.esta_vivo() and goblin.esta_vivo():
danio = aria.atacar()
goblin.recibir_danio(danio)
print(aria.nombre, "hace", danio, "de daño ·", goblin.nombre, "queda con", goblin.vida)
if goblin.esta_vivo():
golpe = goblin.atacar()
aria.recibir_danio(golpe)
print(goblin.nombre, "hace", golpe, "de daño ·", aria.nombre, "queda con", aria.vida)
if aria.esta_vivo():
print("Ganó", aria.nombre)
else:
print("Ganó", goblin.nombre)07Probalo con tu arma y tu enemigo
Este simulador ejecuta exactamente ese while, con el arma que forjaste en la
etapa 4 y el enemigo que invocaste en la etapa 6.
¿El resultado te sorprendió? Cambiá el daño de tu arma en la etapa 4 o la vida de tu enemigo en la etapa 6 y volvé a probar. Eso es balancear un juego.
08¿Dónde está cada herramienta?
🧬 Herencia
class Jugador(Personaje) y class Goblin(Enemigo).
Nadie volvió a escribir vida.
🧬 super()
super().__init__(nombre, vida). El padre hace su parte,
el hijo agrega lo suyo.
🎁 Composición
self.arma = arma, y después
self.arma.usar(): el jugador le pregunta a su arma.
📦 return
esta_vivo() hace funcionar al while;
atacar() entrega el número que se resta.
🎭 Polimorfismo
Cambiá Goblin por Dragon en una sola línea
y la pelea entera se comporta distinto. Sin tocar el while.
🔐 Encapsulación
El if adentro de recibir_danio(): nadie
toca la vida desde afuera, y nunca queda en negativo.
09Desafío de la etapa
- Escribí la pelea completa en
juego.pyy ejecutala. - Cambiá el goblin por un
Dragonen una sola línea y volvé a jugar. ¿Cambiaste algo delwhile? - Agregá un contador de vueltas y mostrá al final: "El combate duró 5 turnos".
- La difícil. En la clase 2 el ataque estaba empaquetado adentro del jugador, así:
Escribilo en tu
def atacar_a(self, otro): danio = self.arma.usar() otro.recibir_danio(danio) return danioJugadory reemplazá las dos líneas del turno poraria.atacar_a(goblin). Después contestá: ¿por qué ese método igual devuelve el daño, si ya se lo restó al otro?💡 Pista
El golpe ya está dado, pero el programa todavía no sabe de cuánto fue. Con el
returnpodés mostrarlo, sumarlo a un total o guardar el récord del golpe más fuerte. Sin elreturn, esa información se pierde.
Un programa es un montón de objetos hablando entre ellos.
Experiencia y niveles
Agregarle al jugador información nueva (nivel, experiencia) y el comportamiento que la maneja.
01La situación
Ganaste la pelea… y no pasó absolutamente nada. Tu personaje sigue igual de fuerte que al empezar. Falta la recompensa.
¿En qué clase van nivel y experiencia?
- En
Personaje, así los tienen todos - En
Jugador, porque solo él sube de nivel - En
Arma, junto con el daño
02Información nueva y comportamiento nuevo
Fijate que siempre es lo mismo: cuando el juego crece, o le agregamos datos a un objeto, o le agregamos acciones. Acá agregamos las dos cosas.
Los dos primeros son del Jugador. La vida_maxima
la ponemos en Personaje: sirve para saber hasta dónde se puede curar cualquiera.
Son del Jugador. Y ojo: subir_nivel() lo va a
llamar el propio objeto, no vos desde afuera.
class Personaje:
def __init__(self, nombre, vida):
self.nombre = nombre
self.vida = vida
self.vida_maxima = vidaclass Jugador(Personaje):
def __init__(self, nombre, vida, arma):
super().__init__(nombre, vida)
self.arma = arma
self.nivel = 1
self.experiencia = 0
def atacar(self):
return self.arma.usar()nivel y experiencia no se piden por parámetro:
todos los jugadores arrancan en nivel 1 con 0 de experiencia. Un atributo puede tener un valor fijo de arranque,
sin que nadie se lo pase. Es la misma idea del bonus de la clase 2: si todas las motos tienen 2 ruedas,
no hace falta pedir el dato.
03Los dos métodos nuevos
Cuando la experiencia llega a 100, el jugador sube de nivel.
def ganar_experiencia(self, cantidad):
self.experiencia = self.experiencia cantidad
print(self.nombre, "ganó", cantidad, "de experiencia")
if self.experiencia 100:
self. def subir_nivel(self):
self.nivel = self.nivel + 1
self.experiencia = self.experiencia - 100
self.vida_maxima = self.vida_maxima + 20
self.vida = self.vida_maxima
print(self.nombre, "subió al nivel", self.nivel)¿Por qué restar 100 y no poner 0?
Si ganó 130 de experiencia, sube de nivel y le sobran 30.
Poniendo = 0 esos 30 se pierden. Restando, quedan para el próximo nivel. Es una decisión de diseño:
la otra también es válida, si la podés justificar.
¿Quién llama a subir_nivel()?
El propio jugador, desde adentro de ganar_experiencia().
Vos no tenés que acordarte de nada: el objeto se encarga solo. Eso es encapsulación otra vez.
04Predicción
aria.ganar_experiencia(60)
aria.ganar_experiencia(60)
print(aria.nivel)
print(aria.experiencia)¿Qué imprimen esas dos últimas líneas?
- Nivel 2 y experiencia 20
- Nivel 2 y experiencia 120
- Nivel 1 y experiencia 120: nunca llega justo a 100
Probalo acá
05Desafíos de la etapa
- Hacé que, al ganar la pelea, el jugador reciba experiencia:
aria.ganar_experiencia(50). - Pensalo antes de escribirlo: ¿qué más podría mejorar cuando el jugador sube de nivel? Elegí una mejora y programala.
- La difícil: ¿cómo harías para que el arma pegue más fuerte cuando el jugador sube de nivel?
💡 Pista
Hay por lo menos dos caminos, y los dos son válidos:
- Tocar el arma:
self.arma.danio = self.arma.danio + 5adentro desubir_nivel(). El punto se lee "de": el daño del arma del jugador. - Que el arma sepa mejorarse: agregarle un método
mejorar()aArmay que el jugador le digaself.arma.mejorar(). Más largo, pero cada objeto se ocupa de lo suyo. ¿Cuál te parece mejor y por qué? Esa pregunta vale más que el código.
- Tocar el arma:
- Que el enemigo dé más experiencia si es más difícil (por ejemplo, la mitad de su vida inicial).
o información nueva, o comportamiento nuevo.
El menú: jugar de verdad
Ponerle al sistema que construiste una forma de usarlo. Y armar el programa completo.
01La situación
El menú no es el objetivo del trabajo práctico. Es solamente la forma de
usar el sistema que construiste. Lo importante ya está hecho: las clases, la herencia, la composición,
el return y el polimorfismo. El menú son if y elif, y eso ya lo sabías
hacer desde antes de POO.
Igual, sin menú el juego no se juega: se ejecuta y se termina solo. Vamos a darle a la persona la posibilidad de decidir.
02Primero falta un objeto: la poción
Una de las opciones del menú va a ser curarse. Y para curarse hace falta algo con qué.
¿Qué relación hay entre el Jugador y la Poción?
- ES UNA →
class Jugador(Pocion) - TIENE UNA → composición, igual que con el arma
- Ninguna: la poción es solo un número, no hace falta una clase
class Pocion:
def __init__(self, nombre, cura):
self.nombre = nombre
self.cura = cura
def usar(self):
return self.cura def curarse(self, pocion):
self.vida = self.vida + pocion.usar()
if self.vida > self.vida_maxima:
self.vida = self.vida_maximaArma y Pocion son casi idénticas: guardan un número y lo
devuelven cuando se las usa. Y el jugador tampoco calcula nada: le pregunta a la poción cuánto cura.
Cuando entendés un patrón, el siguiente objeto sale solo.
if +20 XP¿Para qué está el if de curarse()?
- Para que la vida nunca supere la
vida_maxima - Para que la poción no cure de más
- Para que no se pueda curar si está muerto
03El menú
=== ARENA DE COMBATE ===
1. Ver información
2. Atacar
3. Usar poción
4. Ver experiencia
5. Salirwhile True:
print("=== ARENA DE COMBATE ===")
print("1. Ver información")
print("2. Atacar")
print("3. Usar poción")
print("4. Ver experiencia")
print("5. Salir")
opcion = input("Elegí una opción: ")
if opcion == "1":
heroe.mostrar_info()
enemigo.mostrar_info()
elif opcion == "5":
print("¡Hasta la próxima!")
breakinput() siempre devuelve texto: por eso comparamos con
"1" entre comillas y no con 1. Y while True repite para siempre: la única
forma de salir es el break de la opción 5.
04El programa completo
Todo lo que construiste en once etapas, junto. Leelo entero antes de copiarlo: tenés que poder explicar cada línea. Si hay alguna que no entendés, volvé a la etapa donde apareció.
# ===== ARENA DE COMBATE =====
# Trabajo práctico integrador de POO
class Personaje:
def __init__(self, nombre, vida):
self.nombre = nombre
self.vida = vida
self.vida_maxima = vida
def recibir_danio(self, cantidad):
self.vida = self.vida - cantidad
if self.vida < 0:
self.vida = 0
def esta_vivo(self):
return self.vida > 0
def mostrar_info(self):
print(self.nombre, "- Vida:", self.vida, "/", self.vida_maxima)
class Arma:
def __init__(self, nombre, danio):
self.nombre = nombre
self.danio = danio
def usar(self):
return self.danio
class Pocion:
def __init__(self, nombre, cura):
self.nombre = nombre
self.cura = cura
def usar(self):
return self.cura
class Jugador(Personaje):
def __init__(self, nombre, vida, arma):
super().__init__(nombre, vida)
self.arma = arma
self.nivel = 1
self.experiencia = 0
def atacar(self):
return self.arma.usar()
def curarse(self, pocion):
self.vida = self.vida + pocion.usar()
if self.vida > self.vida_maxima:
self.vida = self.vida_maxima
def ganar_experiencia(self, cantidad):
self.experiencia = self.experiencia + cantidad
print(self.nombre, "ganó", cantidad, "de experiencia")
if self.experiencia >= 100:
self.subir_nivel()
def subir_nivel(self):
self.nivel = self.nivel + 1
self.experiencia = self.experiencia - 100
self.vida_maxima = self.vida_maxima + 20
self.vida = self.vida_maxima
print(self.nombre, "subió al nivel", self.nivel)
class Enemigo(Personaje):
def __init__(self, nombre, vida, danio):
super().__init__(nombre, vida)
self.danio = danio
def atacar(self):
return self.danio
class Goblin(Enemigo):
def atacar(self):
return self.danio # igual que el padre: podría no escribirse
class Dragon(Enemigo):
def atacar(self):
return self.danio + 15 # el dragón pega más fuerte
# ===== creamos los objetos =====
espada = Arma("Espada", 25)
pocion = Pocion("Poción chica", 30)
heroe = Jugador("Aria", 150, espada) # 150 de vida: el dragón pega fuerte
enemigo = Dragon("Dragón rojo", 90, 20) # 20 + 15 = 35 de daño por golpe
# ===== el menú =====
while True:
print()
print("=== ARENA DE COMBATE ===")
print("1. Ver información")
print("2. Atacar")
print("3. Usar poción")
print("4. Ver experiencia")
print("5. Salir")
opcion = input("Elegí una opción: ")
if opcion == "1":
heroe.mostrar_info()
enemigo.mostrar_info()
elif opcion == "2":
danio = heroe.atacar()
enemigo.recibir_danio(danio)
print(heroe.nombre, "atacó con", heroe.arma.nombre, "e hizo", danio, "de daño")
if enemigo.esta_vivo():
golpe = enemigo.atacar()
heroe.recibir_danio(golpe)
print(enemigo.nombre, "contraatacó e hizo", golpe, "de daño")
else:
print(enemigo.nombre, "fue derrotado")
heroe.ganar_experiencia(50)
elif opcion == "3":
heroe.curarse(pocion)
print(heroe.nombre, "usó", pocion.nombre, "- Vida:", heroe.vida)
elif opcion == "4":
print("Nivel:", heroe.nivel, "- Experiencia:", heroe.experiencia, "/ 100")
elif opcion == "5":
print("¡Hasta la próxima!")
break
else:
print("Esa opción no existe")
if not heroe.esta_vivo():
print("GAME OVER")
breakAntes de seguir, marcá en tu archivo con un comentario dónde está cada cosa:
herencia, super(), composición, polimorfismo, return
y encapsulación. Si alguna no la encontrás, esa es la que tenés que repasar. Esto además te va a servir
para la entrega.
05Desafío de la etapa
- Armá tu
juego.pycompleto, con tu arma, tu personaje y tu enemigo. Jugalo. - Agregá la opción 6. Cambiar de arma. Acordate: cambiar de arma es una sola línea, y no toca la clase
Jugador. - Que el juego avise cuando la vida del héroe está por debajo de 30: "¡Cuidado, estás por morir!"
- Probá cambiar
DragonporGoblinen la línea donde creás el enemigo. ¿Cuántas líneas más del programa tuviste que tocar?
Lo que construiste de verdad son las clases.
Desafíos de ampliación
Hacer crecer tu juego. Cada nivel practica un concepto distinto de POO — y ahí está la nota.
Están ordenados de menor a mayor dificultad. No hace falta hacerlos todos. Vale mucho más hacer dos bien y poder explicarlos, que hacer cinco a medias. Marcá los que vayas terminando.
Hacé lo de siempre, aunque sea en dos líneas: ¿qué objetos aparecen? ¿qué información tienen? ¿qué pueden hacer? ¿cómo se relacionan? Recién después, el código.
Qué practicás: clase vs objetoatributosmétodos print() vs return
La pregunta: mostrar_info(), ¿usa print()
o return? ¿Por qué?
Qué practicás: composición (TIENE UN)encapsulación un objeto le pregunta a otro
La pregunta: el control de la vida máxima, ¿va adentro de
curarse() o afuera, en el menú? ¿Quién tiene que decidir cómo cambia la vida del jugador?
Qué practicás: herencia de varios nivelessuper() polimorfismo
La pregunta: cuando agregaste el tercer personaje al
for, ¿tuviste que cambiar el for? Esa es exactamente la ventaja del polimorfismo.
Qué practicás: composición con listasobjetos adentro de objetos return
La pregunta: el jugador tiene un inventario, y el inventario
tiene armas. ¿Cómo se lee jugador.inventario.armas[0].nombre? (Acordate: el punto se lee "de",
de derecha a izquierda.)
Qué practicás: todo juntovarios objetos colaborando herencia + composición + polimorfismodiseño
La pregunta: el for que recorre los enemigos,
¿necesita saber si el que viene es un goblin, un dragón o el jefe final?
No agregues clases porque sí. Si creás Moneda,
PuntoDeVida y NumeroDeTurno como clases, volviste al desorden del principio.
Preguntate siempre: ¿esto tiene información propia y hace algo? Si no, es un atributo de alguien.
Dos desafíos bien explicados valen más que cinco a medias.
Arena de Combate
🏆 ¡MISIÓN CUMPLIDA!
Empezaste con una pantalla en blanco y terminaste con un videojuego funcionando. Pero lo que de verdad te llevás no es el juego.
01Lo que construiste
7 clases
Personaje, Arma, Pocion, Jugador, Enemigo, Goblin y Dragon. Todas escritas por vos, todas con un motivo.
2 relaciones
Herencia donde podías decir ES UN, composición donde podías decir TIENE UN.
1 sistema
Objetos que se pasan objetos, se preguntan cosas y deciden. Eso es un programa orientado a objetos.
02La comprobación: ¿lo podés explicar?
Marcá cada casilla solo si podés explicarlo en voz alta, sin leer. Si alguna te queda sin marcar, volvé a esa etapa: ahí está lo que te falta.
0 de 11
03La reflexión final
Si mañana te damos un problema completamente diferente —una veterinaria, un cine, una app de música—, ¿qué deberías preguntarte antes de escribir código?
🔓 Comparar con la respuesta esperada
No hay una única forma de decirlo, pero tienen que estar estas preguntas, en este orden:
Y recién después, el código. Si tu respuesta empieza por "abro Python y…", volvé a leerla. Fijate que en ninguna de esas preguntas aparece la palabra Python: por eso este método te sirve para cualquier problema y en cualquier lenguaje.
04La entrega
Cada estudiante o pareja (máximo dos personas) entrega:
💾 El código completo
El archivo juego.py, que tiene que ejecutarse sin
errores. Probalo de cero antes de entregar.
📝 Explicación del diseño
Media carilla: qué objetos identificaste, qué decisiones tomaste y por qué. Las decisiones discutibles, mejor: explicá por qué elegiste esa.
📐 El diagrama
Cajas y flechas, como los de la clase 2. Con las flechas de ES UN y de TIENE UN bien marcadas. Puede ser a mano.
🎬 La demostración
Mostrar el juego andando: una pelea completa, una subida de nivel y el menú funcionando.
🔍 Dónde está cada concepto
Una lista con el número de línea: herencia, super(),
composición, polimorfismo, return y encapsulación.
🚀 Mejoras posibles
Tres cosas que le agregarías si tuvieras más tiempo, y qué clase tocarías para hacer cada una.
Los dos tienen que poder explicar todo el código, no solo la parte que escribieron. En la defensa se le pregunta a cualquiera de los dos, sobre cualquier parte.
05Cómo se evalúa
Se evalúa principalmente el razonamiento y la comprensión, no la cantidad de funcionalidades. Un juego chiquito que entendés a fondo vale más que uno enorme que no sabés explicar.
| Qué se mira | Lo esperado |
|---|---|
| Identificación de objetos | Encontró los objetos del problema y no convirtió en clase cualquier cosa que se le ocurrió. |
| Diseño de clases | Lo común está arriba, lo específico abajo. Cada clase tiene un motivo para existir. |
| Atributos y métodos | Los atributos son datos y están en la clase correcta; los métodos son acciones y hacen una cosa cada uno. |
| Herencia | Se usa donde se puede decir "ES UN", y no para ahorrar líneas porque sí. |
Uso de super() | El hijo no repite los atributos del padre. Puede explicar qué pasa si lo saca. |
| Composición | Hay al menos un objeto adentro de otro, y sabe justificar por qué ahí no iba herencia. |
| Polimorfismo | Distintos objetos responden al mismo método a su manera, y el código que los usa no tiene cadenas de if. |
Uso de return | Devuelve cuando el programa necesita el valor; imprime cuando lo lee una persona. Puede explicar cada caso. |
| Funcionamiento | El programa corre sin errores y hace lo que dice que hace. |
| Explicar las decisiones | Lo que más pesa. Puede contar por qué diseñó así y qué otra opción había. |
| Prolijidad y mejoras | Nombres claros, código ordenado, y una idea concreta de cómo seguiría. |
Que recites la definición de polimorfismo. Lo que importa es que, frente a un problema, sepas encontrar los objetos y hacerles las dos preguntas. El resto es vocabulario y se aprende usándolo.
Aprendimos a pensar, diseñar y construir programas.