Trabajo Práctico II. Respuestas

En esta actividad tendrán que resolver 2 problemas (5 pts c/u) aplicando los conocimientos adquiridos hasta ahora. Se evaluarán los temas de las unidades 3 y 4.

Reglas de la evaluación:

  1. Para realizar la evaluación debe hacer Remix en el documento y al culminar debe exportarlo como archivo .ipynb o Jupyter Notebook, tal y como se hizo con la primera evaluación.

  2. Desde este mismo instante pueden comenzar a realizar la evaluación y deben entregarla el día viernes 17 de junio.

  3. Para aprobar la evaluación deberá obtener un mínimo de 4 pts.

    1. Si el programa no compila (es decir, si presenta excepciones) el ejercicio tendrá una calificación de 0 pts.

    2. Si el programa compila obtendrán 3 pts (en el caso de lenguajes dinámicos, como Python o Clojure, no vale la regla de la compilación, por lo cual se comprobará que ejecuten correctamente en tiempo de ejecución)

    3. Si el programa compila, arroja el resultado correcto y además: a) está bien organizado; b) las variables y funciones tienen nombres adecuados y se usa el tipo de dato y/o estructura de dato más a propósito y; c ) el uso de las técnicas de programación aprendidos en estas unidades es el correcto, se obtendrá la calificación completa de 5 pts.

  4. El trabajo práctico puede resolverlo en el lenguaje de programación de su elección.

  5. Recuerde que puede elegir uno de los siguientes lenguajes (Python, Julia, Racket, Clojure, Scala o Java). Haga clic en el signo (+) después de la declaración del problema y elija en 'Code Cell' el lenguaje de su preferencia. Sin embargo, es recomendable que utilice el lenguaje que se avoque mejor a la tarea, es decir, un lenguaje orientado a objetos para resolver problemas de este tipo y un lenguaje funcional para el caso respectivo.

  6. Para el primer ejercicio realice pruebas creando instancias de cada objeto y llamando a sus métodos, si aplica el caso. Para el segundo caso, deberá probar cada función individualmente y, luego, pruebe el proceso completo (preferiblemente, una función que llame a todas las demás en el orden correcto).

Problema 1

Se nos encarga el diseño de una plataforma en línea de juegos de azar y envite. Su tarea es modelar el problema de dominio usando las herramientas del paradigma objetos. Aplique todas las técnicas que estime convenientes para resolver el problema (herencia, composición, encapsulamiento, interfaces, mixins, etc.). Cree todas las entidades que necesite para gestionar el funcionamiento del sitio y cree al menos tres juegos.

-------------------------------------------------------------------------------------------

En este ejercicio se nos pide que modelemos un problema de dominio representado por una app de juegos. Los primero que debemos hacer consiste en identificar las entidades básicas que necesitamos.

Resulta muy útil utilizar herramientas como el UML (Unified Modelling Language) para bosquejar nuestro diseño.

Como veremos, nuestro diseño emplea un poco de herencia y otro de composición. Las puntas negras se utilizan para indicar composición. Una apuesta y una partida comprenden las entidades Usuario y Juego. Del lado de la herencia, Juego es una clase abstracta. Sus descendientes son Truco, Ruleta y BlackJack.

Vertamos este esquema a código:

public class Usuario{
  private String nombre;
  private String mail;
  private String medio_de_pago;
  
  public Usuario(String nombre,String mail,String medio_de_pago){
    this.nombre=nombre;
    this.mail=mail;
    this.medio_de_pago=medio_de_pago;
  }
  
  public String getNombre(){
    return nombre;
  }
  public String getMail(){
    return mail;
  }
  public String getMedio_de_pago(){
    return medio_de_pago;
  }
  @Override
    public String toString(){
    return nombre+" "+mail+" "+medio_de_pago; 
  } 
}
public abstract class Juego {
}
public class Apuesta {
  private Juego juego;
  private double monto;
  private Usuario usuario;
  
  public Apuesta(Juego juego, double monto, Usuario usuario){
    this.juego=juego;
    this.monto=monto;
    this.usuario=usuario;
  }
  
  public Juego getJuego(){
    return juego;
  }
  public double getMonto(){
    return monto;
  }
  public Usuario getUsuario(){
    return usuario;
  }
  @Override
    public String toString(){
    return juego+" "+monto+" "+usuario;
  }
} 
public class Partida{
  private Juego juego;
  private List<Usuario> jugadores;
  private List<Apuesta> apuestas;
  
  public Partida(Juego juego,List<Usuario> jugadores, List<Apuesta> apuestas){
    this.juego=juego;
    this.jugadores=jugadores;
    this.apuestas=apuestas;
  }
  
  public Juego getJuego(){
    return juego;
  }
  public List<Usuario> getJugadores(){
    return jugadores;
  }
  public List<Apuesta> getApuestas(){
    return apuestas;
  }
   public boolean entrarEnPartida(Usuario usuario){
    return this.jugadores.add(usuario);
  }
  public boolean entrarEnPartida(List<Usuario> usuarios){
    return this.jugadores.addAll(usuarios);
  }
  public boolean salirDePartida(Usuario usuario){
    if(estaParticipando(usuario)) {
    this.jugadores.remove(usuario);
    }
    return false;
  }
  public boolean estaParticipando(Usuario usuario){
    if(this.jugadores.stream().filter(u-> u.equals(usuario)).findFirst().get() instanceof Usuario) return true;
    return false;
  }  
  public void recibirApuestas(List<Apuesta> apuestas){
    this.apuestas.addAll(apuestas);
  }
  public Usuario obtenerGanador(List<Apuesta> apuestas){
    return apuestas
      .stream()
      .filter(i -> i.getUsuario().getNombre().length() > 5)
      .findFirst()
      .get()
      .getUsuario();
  }
  @Override
    public String toString(){
    return juego+""+jugadores+" "+apuestas;
  }
}
public class Truco extends Juego {
  public int minJugadores;
  public int maxJugadores;
  public double apuestaMin;
  
  public Truco(int minJugadores,int maxJugadores,double apuestaMin){
    this.minJugadores=minJugadores;
    this.maxJugadores=maxJugadores;
    this.apuestaMin=apuestaMin;
  }
}
public class BlackJack extends Juego {
  public String nombre;
  public int minJugadores;
  public int maxJugadores;
  public double apuestaMin;
  
  public BlackJack(int minJugadores,int maxJugadores,double apuestaMin){
    this.minJugadores=minJugadores;
    this.maxJugadores=maxJugadores;
    this.apuestaMin=apuestaMin;
  }
}
public class Ruleta extends Juego {
  public String nombre;
  public int minJugadores;
  public int maxJugadores;
  public double apuestaMin;
  
  public Ruleta(int minJugadores,int maxJugadores,double apuestaMin){
    this.minJugadores=minJugadores;
    this.maxJugadores=maxJugadores;
    this.apuestaMin=apuestaMin;
  }
}
3.4s

Ahora creemos alguno objetos para probar:

var usuario = new Usuario("Juan Verde","juanverde@gmail.com","Efectivo");
var usuario2 = new Usuario("Marina Marin","marianiiita@gmail.com","Credito");
var usuario3 = new Usuario("Virgilio Vigil","elvirgil@gmail.com","Debito");
var usuario4 = new Usuario("Juana Valverde","juanavalverde@gmail.com","Bitcoin");
var usuarios = new ArrayList<Usuario>();
usuarios.add(usuario);
usuarios.add(usuario2);
usuarios.add(usuario3);
usuarios.add(usuario4);
var blackjack = new BlackJack(12,20,1500.25);
var apuesta1 = new Apuesta(blackjack, 1510.00, usuario);
var apuesta2 = new Apuesta(blackjack, 5000.01, usuario2);
var apuestas = new ArrayList<Apuesta>();
apuestas.add(apuesta1);
apuestas.add(apuesta2);
var partida_blackjack = new Partida(blackjack, 
                                    usuarios,
                                    apuestas);
partida_blackjack.entrarEnPartida(usuarios);
partida_blackjack.recibirApuestas(apuestas);
partida_blackjack.obtenerGanador(apuestas);
2.7s

Problema 2

Nuestra compañía está diseñando un robot que es capaz de hacer una pizza de principio a fin. Se nos ordena que diseñemos el software que operará al robot. Utilizando sólo las técnicas del paradigma funcional, escriba las funciones que van a orientar al robot en la realización de la pizza desde que recibe el pedido hasta que entrega la pizza. El programa deberá recibir como input un pedido y deberá expulsar como output una factura detallando el pedido (producto, cantidad, precio) junto con un mensaje que deberán elaborar para el cliente.

¡No de cosas por hechas! ¡Recuerde que se trata de un robot y que debe recibir instrucciones precisas y detalladas, así como la secuencia exacta en que debe realizar cada paso! Toda función debe recibir un input y producir un output, no podrán utilizar ninguna función con retorno void.

----------------------------------------------------------------------------------------

Al igual que en el ejercicio pasado, conviene comenzar con un esquema. Recuerden que estamos diseñando una aplicación y las herramientas visuales son de muchísima ayuda. Conviene también tener en cuenta que al pensar en la arquitectura de una aplicación es menester considerar qué tipo de dato recibe una función y qué tipo de dato entrega. Luego hay que coordinar que cada función ocupe el lugar que le corresponde, no sólo según la lógica de negocio, sino también en función de sus inputs y sus outputs.

Podemos pensar esquemáticamente en la siguiente secuencia:

  • preparar masa y estirarla

  • añadir ingredientes

  • hornear

  • empacar

  • validar orden

  • facturar

Todo esta secuencia será ejecutada por una sola función que tomará la orden y hará que el resto de funciones entren a cumplir su papel en el momento preciso.

Veamos:

(import java.util.UUID)
(def menu {:muzza 600
           :salame 800
           :jamon 850
           :morron_y_aceitunas 500})
(def tamanos {:XG #(+ % 500)
              :G #(+ % 400)
              :P #(+ % 200)})
(defn preparar-y-estirar-masa
  [tamano producto]
  (let [tam (condp = tamano
              :XG {:bandeja "extra-grande"}
              :G {:bandeja "grande"}
              :P {:bandeja "pequeña"})
        piz (condp = producto
              :muzza (assoc tam :pizza :muzza)
              :salame (assoc tam :pizza :salame)
              :jamon (assoc tam :pizza :jamon)
              :morron_y_aceitunas (assoc tam :pizza :morron_y_aceitunas))]
    piz))
(defn anadir-ingredientes
  [{:keys [pizza] :as masa}]
  (condp = pizza
    :muzza (assoc masa :ingredientes '("Queso mozzarela","Salsa de tomate"))
    :salame (assoc masa :ingredientes '("Queso mozzarela","Salame","Salsa de tomate"))
    :jamon (assoc masa :ingredientes '("Queso mozzarela","Salsa de tomate","Jamón"))
    :morron_y_aceitunas (assoc masa :ingredientes '("Queso mozzarela","Salsa de tomate","Morrón","Aceitunas"))))
(defn hornear
  [pizza]
  (assoc pizza :hornear "Pizza horneada"))
(defn empacar
  [pizza]
  (assoc pizza :empacado "Pizza empacada"))
(def preparar-pizza (comp empacar hornear anadir-ingredientes preparar-y-estirar-masa))
(defn facturar
  [producto cantidad tamano menu tamanos]
  {:pedido (java.util.UUID/randomUUID)
   :producto producto
   :cantidad cantidad
   :total (* ((tamano tamanos) (producto menu)) cantidad)})
(defn validar
  [producto tamano cantidad menu tamanos]
  (cond
      (not (int? cantidad)) (str "La cantidad debe ser un número entero")
      (not (contains? menu producto)) (str "No tenemos esa opción. Disponemos de: " (keys menu))
    (not (contains? tamanos tamano)) (str "Los tamaños disponibles son: " (keys tamanos))))
(defn ordenar
  [producto tamano cantidad menu tamanos]
  (if (nil? (validar producto tamano cantidad menu tamanos))
    (do (preparar-pizza tamano producto)
      (facturar producto cantidad tamano menu tamanos))))
0.3s
(ordenar :muzza :P 4 menu tamanos)
0.0s

Como es usual, tómense la libertad de explorar el código y cambiarlo a voluntad.

Runtimes (2)
Runtime Languages (12)