# als-parcial2-2021_23 (c) 2023 Baltasar MIT License <baltasarq@gmail.com>
import json
"""
1. (4 pts.) VideoESEI, un joven estudio de videojuegos,
quiere llegar pronto al mercado con un juego run&gun,
es decir, el jugador, uno de los Pj (personajes),
siempre está corriendo, y disparando a enemigos para
aumentar su puntuación (momento en el que debe ralentizarse).
Como los plazos son muy justos,
deciden utilizar Python como lenguaje de programación,
y llegan a la clase listada más abajo.
La clase debe almacenar el id (__id) y el delta (__delta) del Pj,
de forma que el id es una cadena de caracteres que
se pasa como parámetro, y el delta un número entero que indica
cuántos píxeles avanza de cada vez, inicializado a cero.
El método set_delta(nd: int) acepta un entero para cambiar el delta
por el nd (nuevo delta), y __str_() devuelve la información en el
formato "<nombre_clase> (<id>): <delta>".
Hay que tener en cuenta las siguientes cuestiones:
a) La clase Pj debe ser capaz de responder a las propiedades delta e id,
aunque por razones de compatibilidad con la versión de Python
que se distribuirá con el juego, no pueden ser propiedades como tal.
Así, si p1 = Pj("Corredor"), entonces p1.delta devolverá el
atributo __delta, y p1.id devuelve su atributo __id,
a pesar de que no existan las propiedades id o delta.
b) Cuando se crea p1 como PjCaminante,
su delta debe inicializarse a PjCorredor.MIN_DELTA // 2.
En cambio, cuando se crea como PjCorredor, debe inicializarse a
PjCorredor.MIN_DELTA * 2.
c) Cuando set_delta(nd) es llamado para modificar el delta con
nd (nuevo delta), si el nuevo delta es mayor o igual
que PjCorredor.MIN_DELTA, la clase del objeto debe cambiar
automáticamente a PjCorredor. Si es más pequeño
que PjCorredor.MIN_DELTA, debe cambiar a PjCaminante.
Por ejemplo, si p1 = PjCaminante(“Mario”),
por tanto type(p1) == PjCaminante;
después de p1.set_delta(10), type(p1) == PjCorredor.
d) Cuando dispara() es llamado siendo p1 un PjCorredor,
automáticamente el delta debe cambiar a PjCorredor.MIN_DELTA // 2,
y por tanto la clase de p1 debe pasar a ser PjCaminante.
Así, si p1 = PJCorredor(“Flash”) y p1.dispara() (devuelve “BANG!”),
entonces type(p1) == PJCaminante debe ser cierto.
2. (3pts.) Cree los métodos to_str_json() -> str
y from_str_json(s: str) -> Pj.
El primero escribe la codificación JSON de los datos necesarios
del objeto a una cadena de caracteres, el segundo (estático),
toma una cadena de caracteres con el contenido necesario
en formato JSON y crea un objeto PjCaminante o PjCorredor con dicho contenido,
según sea apropiado.
Estos métodos son utilizados desde to_json(f) y from_json(f)
para volcar o crear los datos desde o hacia un archivo.
"""
class Pj:
def __init__(self, id):
self.__id = id
self.__delta = 0
def __getattr__(self, item):
if item == "delta":
return self.__delta
elif item == "id":
return self.__id
raise AttributeError(item)
def set_delta(self, value):
self.__delta = value
def to_json(self, f) -> str:
f.write(self.to_str_json())
def to_str_json(self) -> str:
return json.dumps(self.__dict__)
@staticmethod
def from_json(f):
return Pj.from_str_json(str.join("\n", f.readlines()))
@staticmethod
def from_str_json(s: str):
toret = PjCaminante("")
toret.__dict__ = json.loads(s)
toret.set_delta(toret.delta)
return toret
def __str__(self):
return f"{self.__class__.__name__} ({self.id}): {self.delta}"
class PjCorredor(Pj):
MIN_DELTA = 4
def __init__(self, id):
super().__init__(id)
self.set_delta(PjCorredor.MIN_DELTA * 2)
def set_delta(self, value):
super().set_delta(value)
if value < PjCorredor.MIN_DELTA:
self.__class__ = PjCaminante
def dispara(self):
self.set_delta(PjCorredor.MIN_DELTA // 2)
return self.dispara()
class PjCaminante(Pj):
def __init__(self, id):
super().__init__(id)
self.set_delta(PjCorredor.MIN_DELTA // 2)
def set_delta(self, value):
super().set_delta(value)
if value > PjCorredor.MIN_DELTA:
self.__class__ = PjCorredor
def dispara(self):
return "BANG!"
"""
3. (3pts.) Escriba la función filtra_pjs(f: callable, pjs: list[Pj) -> list[Pj],
que recibe como parámetros una lista de objetos Pj y
una función que devolverá verdadero o falso según el objeto Pj que se le pase.
Deben utilizarse exclusivamente lambdas como parámetro de este método.
Cree la función busca_pjs_veloces(pjs: list[Pj]) -> list[Pj],
que llama a la función anterior para encontrar a aquellos Pj’s
que tienen un delta superior a 5.
"""
def filtra_pjs(f: callable, pjs: list[Pj]) -> list[Pj]:
return [pj for pj in pjs if f(pj)] # opc1
# return filter(f, pjs) # opc2
"""
def busca_pjs_veloces(pjs: list[Pj]) -> list[Pj]:
return filtra_pjs(lambda pj: pj.delta > 5, pjs) # opc1
"""
busca_pjs_veloces = lambda pjs: \
filtra_pjs(lambda pj: pj.delta > 5, pjs) # opc2
if __name__ == "__main__":
pj1 = PjCaminante("Mario")
print(str(pj1))
pj1.set_delta(8)
print(str(pj1))
print(pj1.dispara())
print(str(pj1))
print("\nGuardando y recuperando pj1 (Mario)")
str_pj1 = pj1.to_str_json()
print(Pj.from_str_json(str_pj1))
print("\nBuscando a los veloces de entre Mario(2), Flash(8) y Sonic(8)")
pj2 = PjCorredor("Flash")
pj3 = PjCorredor("Sonic")
print(", ".join(str(pj) for pj in busca_pjs_veloces([pj1, pj2, pj3])))
"""
# Tests als-parcial2-2021_23 (c) 2023 Baltasar MIT License <baltasarq@gmail.com>
import unittest
from als_parcial2_2021_23 import Pj, PjCorredor, PjCaminante, filtra_pjs, busca_pjs_veloces
class TestPj(unittest.TestCase):
def setUp(self):
self.pj = Pj("Mario")
self.pjcorredor = PjCorredor("Sonic")
self.pjcaminante = PjCaminante("Yoshi")
def test_creacion_pj(self):
self.assertEqual("Mario", self.pj.id)
self.assertEqual(0, self.pj.delta)
self.assertEqual("(Mario): 0", str(self.pj))
def test_creacion_pj_corredor(self):
self.assertEqual("Sonic", self.pjcorredor.id)
self.assertEqual(8, self.pjcorredor.delta)
self.assertEqual("PjCorredor (Sonic): 8", str(self.pjcorredor))
def test_creacion_pj_caminante(self):
self.assertEqual("Yoshi", self.pjcaminante.id)
self.assertEqual(2, self.pjcaminante.delta)
self.assertEqual("PjCaminante (Yoshi): 2", str(self.pjcaminante))
def test_modificacion_delta_pj(self):
self.pj.set_delta(10)
self.assertEqual(10, self.pj.delta)
self.assertEqual(Pj, self.pj.__class__)
def test_modificacion_delta_corredor(self):
self.pjcorredor.set_delta(10)
self.assertEqual(10, self.pjcorredor.delta)
self.assertEqual(PjCorredor, self.pjcorredor.__class__)
def test_modificacion_delta_caminante(self):
self.pjcaminante.set_delta(3)
self.assertEqual(3, self.pjcaminante.delta)
self.assertEqual(PjCaminante, self.pjcaminante.__class__)
def test_modificacion_delta_caminante_a_corredor(self):
self.pjcaminante.set_delta(10)
self.assertEqual(10, self.pjcaminante.delta)
self.assertEqual(PjCorredor, self.pjcaminante.__class__)
def test_modificacion_delta_corredor_a_caminante(self):
self.pjcorredor.set_delta(2)
self.assertEqual(2, self.pjcorredor.delta)
self.assertEqual(PjCaminante, self.pjcorredor.__class__)
def test_bang_caminante(self):
self.assertEqual("BANG!", self.pjcaminante.dispara())
self.assertEqual(PjCaminante, self.pjcaminante.__class__)
def test_bang_corredor(self):
self.assertEqual("BANG!", self.pjcorredor.dispara())
self.assertEqual(PjCaminante, self.pjcorredor.__class__)
self.assertEqual(PjCorredor.MIN_DELTA // 2, self.pjcorredor.delta)
def test_json(self):
str_pj = self.pj.to_str_json()
str_pjcaminante = self.pjcaminante.to_str_json()
str_pjcorredor = self.pjcorredor.to_str_json()
pj2 = Pj.from_str_json(str_pj)
pjcaminante2 = Pj.from_str_json(str_pjcaminante)
pjcorredor2 = Pj.from_str_json(str_pjcorredor)
self.assertEqual(self.pj.id, pj2.id)
self.assertEqual(self.pj.delta, pj2.delta)
self.assertEqual(self.pjcaminante.id, pjcaminante2.id)
self.assertEqual(self.pjcaminante.delta, pjcaminante2.delta)
self.assertEqual(self.pjcorredor.id, pjcorredor2.id)
self.assertEqual(self.pjcorredor.delta, pjcorredor2.delta)
def test_filtra(self):
pjs = [self.pj, self.pjcaminante, self.pjcorredor]
pjs_rapidos = busca_pjs_veloces(pjs)
self.assertEqual([self.pjcorredor], pjs_rapidos)
pjs_lentos1 = filtra_pjs(lambda pj: pj.delta > 0 and pj.delta < PjCorredor.MIN_DELTA, pjs)
pjs_lentos2 = filtra_pjs(lambda pj: type(pj) == PjCaminante, pjs)
self.assertEqual(pjs_lentos1, pjs_lentos2)
if __name__ == "__main__":
unittest.main()
"""