<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
Step2:  Clases de objetos 19 y 22 Octubre
Valkyria Chronicles 4
Vimos el trailer del juego Valkyria Chronicles 4 e intentamos hacer algunas clases en base a esto. 
* Valkyria Chronicles 4 (Launch Trailer)
* Valkyria Chronicles 4 (Announcement Trailer)
* Valkyria Chronicles 4 (Gameplay Trailer)
Clase Soldado
Step3:  Pruebas
Step9:  Clase Escuadron
Step10:  Pruebas
Step13:  Nota de clase
Ayer se nos paso durante la clase notar esto. Tanto los sets como el diccionario necesitan que el objeto tenga definido el metodo magico __hash__ para poder ser utilizados. Podemos pensar que el metodo lo que hace, es devolver "la clave" que representa al objeto. En este caso, por supuestos, podemos asumir que al ser los nombres unicos, podemos usar eso
Step18:  Clase Arma
Step19:  Pruebas
Step22:  Omake (La tarea que no hicieron...)
<ASSISTANT_TASK:>
Python Code:
class Soldier:
    Clase que representa a un soldado
    def __init__(self, name):
        self.name = name
    def get_name(self):
        Devuelve el nombre del soldado
        return self.name
    def __eq__(self,another):
        return self.name == another.name
Explanation: Clases de objetos 19 y 22 Octubre
Valkyria Chronicles 4
Vimos el trailer del juego Valkyria Chronicles 4 e intentamos hacer algunas clases en base a esto. 
* Valkyria Chronicles 4 (Launch Trailer)
* Valkyria Chronicles 4 (Announcement Trailer)
* Valkyria Chronicles 4 (Gameplay Trailer)
Clase Soldado
End of explanation
alicia = Soldier("Alicia Melchiott")
alicia2 = Soldier("Alicia Melchiott")
welkin = Soldier("Welkin Gunther")
assert(alicia.get_name() == "Alicia Melchiott")
assert(welkin.get_name() == "Welkin Gunther")
assert(alicia == alicia)
assert(alicia == alicia2)
assert(alicia != welkin)
Explanation: Pruebas
End of explanation
class Squad:
    Clase que representa un escuadron de soldados
    def __init__(self, leader):
        self.leader = leader
        self.squad = set()
    def add_soldier(self,soldier):
        Agrega un soldado al escuadron
        if not isinstance(soldier,Soldier):
            raise TypeError("El escuadron solo puede agregar soldados")
        self.squad.add(soldier)
    def kick_soldier(self):
        Elimina un soldado del escuadron
        pass
    def get_soldiers(self):
        Devuelve todos los soldados del escuadron, formados
        
        # soldiers = [ soldier for soldier in self.squad ]
        # soldiers.sort()
        # return soldiers
        return sorted(self.squad)
    def get_soldiers_amount(self):
         
        return len(self.squad)
Explanation: Clase Escuadron
End of explanation
escuadron = Squad(welkin)
escuadron.add_soldier(alicia)
escuadron.add_soldier(alicia2)
assert(escuadron.get_soldiers_amount()==1)
Explanation: Pruebas
End of explanation
class Soldier:
    Clase que representa a un soldado
    def __init__(self, name):
        self.name = name
    def get_name(self):
        Devuelve el nombre del soldado
        return self.name
    def __eq__(self,another):
        return self.name == another.name
    
    def __hash__(self):
        return hash(self.name)
alicia = Soldier("Alicia Melchiott")
alicia2 = Soldier("Alicia Melchiott")
welkin = Soldier("Welkin Gunther")
escuadron = Squad(welkin)
escuadron.add_soldier(alicia)
escuadron.add_soldier(alicia2)
assert(escuadron.get_soldiers_amount()==1)
Explanation: Nota de clase
Ayer se nos paso durante la clase notar esto. Tanto los sets como el diccionario necesitan que el objeto tenga definido el metodo magico __hash__ para poder ser utilizados. Podemos pensar que el metodo lo que hace, es devolver "la clave" que representa al objeto. En este caso, por supuestos, podemos asumir que al ser los nombres unicos, podemos usar eso:
End of explanation
class Gun:
    Clase que representa un arma generica
    
    SNIPER = "Sniper"
    MORTAR = "Mortar"
    
    def __init__(self,name,damage,gun_type):
        
        self.name = name
        self.damage = damage
        self.gun_type = gun_type
        
    def attack(self,soldier):
        
        soldier.receive_damage(self.damage,self.gun_type)
        
import random
        
class Soldier:
    Clase que representa a un soldado
    def __init__(self, name,gun):
        self.name = name
        self.gun = gun
        self.hp  =  random.randint(600,1200)
        self.shield = random.randint(1,4)
    def get_name(self):
        Devuelve el nombre del soldado
        return self.name
    def attack(self,enemy):
         
        self.gun.attack(enemy)
        
    def receive_damage(self,damage,gun_type):
        if gun_type == Gun.SNIPER:
            self.hp -= damage / self.shield
            return
        
        self.hp -= damage
        
    def __eq__(self,another):
        return self.name == another.name
    
    def __hash__(self):
        return hash(self.name)
Explanation: Clase Arma
End of explanation
sniper_rifle = Gun("Mk-3",200,Gun.SNIPER)
mortar_launcher = Gun("Elias",700,Gun.MORTAR)
alicia = Soldier("Alicia Melchiott",sniper_rifle)
welkin = Soldier("Welkin Gunther",mortar_launcher)
alicia.attack(welkin)
Explanation: Pruebas
End of explanation
class Calculadora:
    
    def __init__(self):
        
        self.resultado_anterior = 0
        
    def sumar(self,numero_1,numero_2=None):
         
        
        if numero_2 is None:
            numero_2 = self.resultado_anterior
            
        self.resultado_anterior = numero_1 + numero_2
        return self.resultado_anterior
        
    def restar(self,numero_1,numero_2=None):
         
        
        self.resultado_anterior = - self.sumar( - numero_1,numero_2)
        return self.resultado_anterior
Explanation: Omake (La tarea que no hicieron...)
End of explanation
<END_TASK>