1 Cambios

1.1 De 1.0.1 a 1.0.2

  • Se ha corregido un error en el comentario del método CellularAutomaton::print_current_generation, la cadena que imprime es la que devuelve CellularAutomaton::current_generation.

    Se ha corregido en los archivos de partida.

1.2 De 1.0.0 a 1.0.1

  • Solo afecta al fichero Makefile de los archivos de partida.

    Al crear el fichero TGZ para la entrega el archivo Makefile de esta versión incluye el fichero container.tcc. La versión anterior no lo hacía. Es importante, tenlo en cuenta a la hora de generar el archivo de tu entrega.

2 Objetivos

  • En esta cuarta práctica vamos a emplear algunos de los conceptos de la programación orientada a objetos que estamos estudiando actualmente:

  • También aprenderemos a separar en diferentes carpetas (src, bin) el código fuente del código objeto generado.

3 Enunciado

3.1 Presentación

Es muy importante que para entender el desarrollo de esta práctica veas estos vídeos (al menos el primero), ya que en ellos se explica el funcionamiento de estos modelos matemáticos y al mismo tiempo se lleva a cabo una implementación de los mismos lo cual te ayudará con la implementación que tienes que hacer en esta práctica:

  1. Vídeo reciente del canal The Coding Train donde explica de forma detallada el funcionamiento de este tipo de autómatas y realiza una implementación en javascript usando las bibliotecas del proyecto processing.
  2. Otro vídeo anterior en el tiempo del mismo autor donde explica el funcionamiento como en el caso anterior, pero la implementación del código la realiza con la versión de processing para Java.
  3. En este otro vídeo puedes ver una parte de la entrevista que Lex Fridman realizó a S. Wolfram y donde habla de este tipo de autómatas y en concreto de la regla 30.

El resto del enunciado asume que has visto los vídeos anteriores y entendido el funcionamiento de este tipo de autómatas.

En la implementación asumimos que:

  1. Una vez creado un automáta celular este ya se encuentra en su primera generación.
  2. Durante la evolución el nuevo valor para las celulas de los extremos se calcula:
    • en el caso de la de la izquierda, asumiendo que su vecino de la izquierda es la última célula del vector de celulas.
    • en el caso de la de la derecha, asumiendo que su vecino de la derecha es la primera célula del vector de celulas.

3.2 Práctica

En esta práctica vamos a implementar un autómata celular similar al presentado en los vídeos anteriores, concretamente una variante de los estudiados por S. Wolfram los cuales están basados en una retícula unidimensional con una vecindad de 3 celdas y dos estados posibles (0 o 1).

Para ello debemos:

  1. Crear una clase Automaton abstracta (automaton.h, automaton.cc).
  2. Crear una clase CellularAutomaton (ca.h, ca.cc) que derive de la clase anterior.
  3. Crear una clase genérica Container que nos permitirá gestionar varios objetos del mismo tipo o clase como un todo (container.h y container.tcc). Fíjate que la extensión que empleamos en este caso es .tcc. Tienes más información más adelante.
  4. De la clase Container<CellularAutomatonPtr> derivará la clase CaContainer (cacontainer.h y cacontainer.cc).

3.3 Diagrama de clases UML

4 División del código fuente en archivos

4.1 Pautas

  • Cada una de las clases descritas anteriormente se declarará en el archivo .h y definirá en el .cc / .tcc correspondientes. Estos archivos .cc o .tcc no contendrán ningún programa principal (main) pero sí podrán contener las funciones auxiliares que necesites, en este caso declaralas y definelas en el espacio de nombres P4.

    Puedes generar la documentación de esta práctica con doxygen así: make doc.

  • En el caso de la clase genérica Container seguiremos con el mismo criterio, pero para recalcar que no se compila por separado cambiaremos la extensión del archivo de definición de .cc a .tcc.

    Recuerda de lo visto en clase de teoría que deberemos hacer un include de este .tcc en cualquier archivo .cc donde queramos instanciar esta clase.

  • Para poder probar tus clases y métodos dispones de un programa principal de prueba (mainp4.cc). No es necesario entregarlo, si se hace no pasa nada.

  • Es aconsejable que uses el archivo Makefile para compilar y enlazar tu código así como para crear el archivo .tgz para la entrega.

  • Todos los archivos .cc y .tcc se encontrarán dentro de una carpeta llamada src la cual estará dentro de la carpeta principal de la práctica: irp2-p4.

    Esto permitirá de manera sencilla separar archivos objeto generados de archivos de código fuente.

  • Todo el código fuente escrito se encontrará dentro del espacio de nombres P4.

4.2 Contenido de los archivos .h

  • container.h

    #ifndef CONTAINER_H
    #define CONTAINER_H
    
    #include <vector>
    #include <boost/signals2.hpp>
    #include <string>
    
    // -- Type aliases: -------------------------------------------------------
    using namechange_sig_t = boost::signals2::signal<void (const std::string& oldname, const std::string& newname)>;
    
    // -- Classes: ------------------------------------------------------------
    namespace P4 {
      /// Plantilla de clase que representa la clase base de contenedores
      /// de datos de tipo T
      template <typename T>
      class Container {
      public:
        // -- Constructors/Destructor: --------------------------------------------
        /// Crea un Container con nombre name
        Container(const std::string &name);
        /// Implementación por defecto por si más adelante lo necesitamos redefinir
        virtual ~Container () = default;
    
        // -- Setters & Getters: --------------------------------------------------
        /// Permite cambiar el nombre a este contenedor por name
        void set_name(const std::string &name);
        /// Proporciona el nombre de este contenedor
        const std::string &get_name() const;
        /// Hace que todos elementos de este contenedor valgan d
        void set(const T &d);
        /// Añade un nuevo elemento d al final de este contenedor
        void add(const T &d);
        /// Devuelve el elemento i-esimo del contenedor
        T &operator[](size_t i);
        /// Devuelve el número de elementos en este contenedor
        size_t size() const;
    
        // -- Signals: ------------------------------------------------------------
        /// Señal emitida cuando se cambia el nombre a este contenedor
        namechange_sig_t namechange_sig;
    
      protected:
        /// Nombre del contenedor
        std::string cname;
        /// Almacen de los elementos de este contenedor
        std::vector<T> v;
      };
    } // namespace P4
    
    #endif /* CONTAINER_H */
    
  • cacontainer.h

    #ifndef CACONTAINER_H
    #define CACONTAINER_H
    
    #include "ca.h"
    #include "container.tcc"
    
    namespace P4 {
    
      /// Clase que representa a los contenedores de autómatas celulares
      /// 1-dimensionales
      class CaContainer : public Container<CellularAutomatonPtr>
      {
      public:
        // -- Constructors/Destructor: --------------------------------------------
        /// Crea un CaContainer con nombre name
        CaContainer(const std::string &name);
        /// Implementación por defecto por si más adelante lo necesitamos redefinir
        virtual ~CaContainer() = default;
    
        // -- Action: -------------------------------------------------------------
    
        /// Devuelve en una cadena la generación actual de cada autómata
        /// celular que contiene encerrada entre "[]". La cadena
        /// correspondiente a cada autómata se separa de la siguiente por un
        /// carácter '\n', p.e. el siguiente contenedor de autómatas celulares
        /// contiene 3 de estos automatas, cuya generación actual es:
        /// "[-- -- ----- ---]
        ///  [--- -- ---   --]
        ///  [-- -- -  -- - -]"
        std::string get_all_cas_current_gen();
    
      };
    
    }  // P4
    
    #endif /* CACONTAINER_H */
    
  • automaton.h

    #ifndef AUTOMATON_H
    #define AUTOMATON_H
    
    #include <cstdint>
    #include <iostream>
    
    // -- Namespaces: ---------------------------------------------------------
    namespace P4 {
      // -- Type aliases: -------------------------------------------------------
    
      // -- Classes: ------------------------------------------------------------
    
      /// Clase base que representa a un autómata general.
      class Automaton {
      public:
        // -- Constructors/Destructor: --------------------------------------------
        /// Constructor de un autómata a partir de un caracter c
        Automaton(char c);
        /// Esta clase no tiene constructor por defecto
        Automaton() = delete;
        /// Implementación por defecto por si más adelante lo necesitamos redefinir
        virtual ~Automaton() = default;
    
        // -- Setters: ------------------------------------------------------------
        /// Cambia el carácter de dibujo de este autómata
        void set_drawing_char(char c);
    
        // -- Getters: ------------------------------------------------------------
        /// Devuelve el carácter de dibujo de este autómata
        char get_drawing_char() const;
    
        // -- Action: -------------------------------------------------------------
        /// Método abstracto en esta clase, sirve para evolucionar el
        /// autómata durante ngens generaciones.
        virtual void compute_ngens(uint32_t ngens, std::ostream& os) = 0;
    
      protected:
        char c; ///< El carácter usado para dibujar este autómata
    
      };
    
    }  // P4
    
    #endif /* AUTOMATON_H */
    
  • ca.h

    #ifndef CA_H
    #define CA_H
    
    #include <vector>
    #include <string>
    #include <cstdint>
    #include <iostream>
    #include <boost/signals2.hpp>
    
    #include "automaton.h"
    
    // -- Namespaces: ---------------------------------------------------------
    namespace P4 {
    
      // -- Type aliases: -------------------------------------------------------
      class CellularAutomaton;
      using CellularAutomatonPtr = CellularAutomaton*;
      using Cells_t = std::vector<uint8_t>;
      using nextgen_sig_t = boost::signals2::signal<void (uint32_t, const Cells_t&, const CellularAutomaton&)>;
    
      // -- Classes: ------------------------------------------------------------
      /// Esta clase representa automátas celulares unidimensionales.
      class CellularAutomaton : public Automaton {
      public:
        // -- Constructors/Destructor: --------------------------------------------
        /// Esta clase no tiene constructor por defecto
        CellularAutomaton() = delete;
        /// Constructor de copia
        CellularAutomaton(const CellularAutomaton &);
        /// Constructor a partir de ncells celdas, el carácter de dibujo c
        /// y la regla reset. En el estado inicial de todas las celdas del
        /// autómata solo la central (ncells/2) tendrá el valor 1, el
        /// resto valdrá 0.
        CellularAutomaton(uint64_t ncells, char c, uint8_t rset);
        /// Imprime en la salida estándar que el autómata ha sido
        /// destruido, tal y como puedes ver en la salida de ejemplo
        /// proporcionada en el enunciado.
        ~CellularAutomaton();
    
        // -- Setters: ------------------------------------------------------------
        /// Cambia la regla empleada en la evolución de este autómata
        void rule_set(uint8_t rset);
    
        // -- Output: -------------------------------------------------------------
        /// Redefine el operador de salida para mostrar en el stream os el
        /// número de celdas, la regla empleada en la evolucion y el
        /// carácter de dibujo del autómata ca tal y como puedes ver en la
        /// salida de ejemplo proporcionada en el enunciado.
        friend std::ostream &operator<<(std::ostream &os,
                                        const CellularAutomaton& ca);
    
        // -- Getters: ------------------------------------------------------------
        /// Devuelve como una cadena de 0s y 1s la representacion en
        /// binario del valor rset actual
        std::string get_ruleset() const;
        /// Devuelve un vector de 0s y 1s que representa la generacion
        /// actual de este automata
        const Cells_t &get_current_generation() const;
        /// Devuelve una cadena que representa la generacion actual. Cada
        /// carácter de esta cadena será o un espacio en blanco o el
        /// carácter de dibujo de este autómata dependiendo de si en el
        /// vector de 0s y 1s que representa la generación actual hay un 0
        /// o un 1 en la posición correspondiente
        std::string current_generation() const;
    
        // -- Action: -------------------------------------------------------------
        /// Imprime en el stream os la representacion como como cadena de
        /// la generacion actual. Esta cadena es la que devuelve
        /// CellularAutomaton::current_generation
        void print_current_generation(std::ostream &os) const;
        /// Calcula ngens generaciones de evolución para este automata,
        /// imprimiendo cada una de ellas en el stream os con un salto de
        /// linea al final y emitiendo la señal nxgen_sig despues de
        /// calcular cada nueva generacion
        void compute_ngens(uint32_t ngens, std::ostream& os);
    
        // -- Signals: ------------------------------------------------------------
        /// Señal emitida tras el cálculo de cada nueva generacion. Esta
        /// señal tiene como argumentos la generación actual, el vector de
        /// 0s y 1s que representa la generación actual y el autómata
        /// celular que la emite
        nextgen_sig_t nxgen_sig;
    
      private:
        /// Vector de 0s y 1s que tiene el resultado de la generación
        /// actual
        Cells_t cells;
        /// Cadena de 0s y 1s que contiene la representacion en binario
        /// del valor rset actual
        std::string ruleset;
      };
    }
    
    #endif /* CA_H */
    

4.3 Ejemplo de salida de mainp4.cc

$ make run
Ejecutando practica...
bin/p4
Container ca-Container-1 has 2 elements.

ca:
Cells: 40
RuleSet: 00011110
Drawing char: .


ca2:
Cells: 40
RuleSet: 00100001
Drawing char: @

Current gen. of all cas in cac:
[                    .                   ]
[                    @                   ]

                    .
                   ...
                  ..  .
                 .. ....
                ..  .   .
               .. .... ...
              ..  .    .  .
             .. ....  ......
            ..  .   ...     .
           .. .... ..  .   ...
          ..  .    . .... ..  .
         .. ....  .. .    . ....
        ..  .   ...  ..  .. .   .
       .. .... ..  ... ...  .. ...
      ..  .    . ...   .  ...  .  .
     .. ....  .. .  . .....  .......
    ..  .   ...  .... .    ...      .
   .. .... ..  ...    ..  ..  .    ...
  ..  .    . ...  .  .. ... ....  ..  .
 .. ....  .. .  ......  .   .   ... ....
Container name change from: ca-Container-1 to: NUEVO NOMBRE

Current gen. of all cas in cac:
[ .  .   ...  ....     .... ... ..   .   ]
[                    @                   ]


CellularAutomaton destroyed.

CellularAutomaton destroyed.

5 Entrega. Requisitos técnicos

  • Requisitos que tiene que cumplir este trabajo práctico para considerarse válido y ser evaluado (si no se cumple alguno de los requisitos la calificación será cero):

  • El archivo entregado se llama irp2-p4.tgz (todo en minúsculas), no es necesario entregar ningún programa principal, sólo son necesarios tus archivos .cc y .tcc en una carpeta llamada src dentro de otra carpeta llamada irp2-p4. A su vez, esta carpeta estará comprimida en el fichero llamado irp2-p4.tgz.

  • Este archivo lo puedes crear así en el terminal:

    tar cfz irp2-p4.tgz irp2-p4

    Aunque es recomendable que emplees el archivo Makefile que tienes en la carpeta de la práctica para generar el fichero de la entrega tal y como hemos visto en clase de prácticas: make tgz

  • Al descomprimir el archivo irp2-p4.tgz se crea un directorio de nombre irp2-p4 (todo en minúsculas).

  • Dentro del directorio irp2-p4 existirá la carpeta src la cual contendrá los ficheros automaton.cc, ca.cc, container.tcc y cacontainer.cc (todos en minúsculas).

    Recuerda que los ficheros ‘.hno se deben modificar, de lo contrario tus ficheros ‘.cc’/’.tcc’ no compilarán con el corrector. No es necesario entregarlos, si se hace no pasa nada.

  • Las clases, métodos y funciones implementados se llaman como se indica en el enunciado (respetando en todo caso el uso de mayúsculas y minúsculas). También es imprescindible respetar estrictamente los textos y los formatos de salida que se indican en este enunciado.

  • Al principio de todos los ficheros fuente (.h, .cc y .tcc) entregados y escritos por ti se debe incluir un comentario con el nombre y el NIF (o equivalente) de la persona que entrega la práctica, como en el siguiente ejemplo:

    // NIF: 12345678-Z
    // NOMBRE: GARCIA PEREZ, LAURA
    
  • Un error de compilación/enlace implicará un cero en esa parte de la práctica.

  • Lugar y fecha de entrega : Recuerda que las entregas se realizan siempre en (y sólo en) https://pracdlsi.dlsi.ua.es en las fechas y condiciones allí publicadas. Puedes entregar la práctica tantas veces como quieras, sólo se corregirá la última entrega (las anteriores no se borran). El usuario y contraseña para entregar prácticas es el mismo que se utiliza en UACloud.

6 Detección de plagios/copias

  • En el Grado en Ingeniería Robótica, la Programación es una materia muy importante. La manera más efectiva de aprender a programar es programando y, por tanto, haciendo las prácticas correspondientes además de otros programas (más sencillos o más complicados, eso da igual).

  • Tratar de aprobar las asignaturas de programación sin programar (copiando) es complicado y obviamente, te planteará problemas para seguir otras asignaturas así como en tu futura vida laboral.

  • En una asignatura como Programación-II es difícil que alguien que no ha hecho las prácticas supere los exámenes de teoría o el de recuperación de prácticas.

  • IMPORTANTE:

    • Cada práctica debe ser un trabajo original de la persona que la entrega.

    • En caso de detectarse indicios de copia de una o más prácticas se tomarán las medidas disciplinarias correspondientes, informando a las direcciones de Departamento y de la EPS por si hubiera lugar a otras medidas disciplinarias adicionales.