- 1 Cambios
- 1.1 De 1.0.1 a 1.0.2
- 1.2 De 1.0.0 a 1.0.1
- 2 Objetivos
- 3 Enunciado
- 3.1 Presentación
- 3.2 Práctica
- 3.3 Diagrama de clases UML
- 4 División del código fuente en archivos
- 5 Entrega. Requisitos técnicos
- 6 Detección de plagios/copias
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 devuelveCellularAutomaton::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 archivoMakefile
de esta versión incluye el ficherocontainer.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:
- Herencia de clases.
- Zonas de visibilidad.
- Clases abstractas.
- Genericidad.
- Espacios de nombres.
- Redefinición de operadores.
- Funciones borradas y funciones miembro especiales por defecto.
- Entrada/Salida, etc…
-
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:
- 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.
- 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.
- 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:
- Una vez creado un automáta celular este ya se encuentra en su primera generación.
- 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:
- Crear una clase
Automaton
abstracta (automaton.h
,automaton.cc
). - Crear una clase
CellularAutomaton
(ca.h
,ca.cc
) que derive de la clase anterior. - Crear una clase genérica
Container
que nos permitirá gestionar varios objetos del mismo tipo o clase como un todo (container.h
ycontainer.tcc
). Fíjate que la extensión que empleamos en este caso es.tcc
. Tienes más información más adelante. - De la clase
Container<CellularAutomatonPtr>
derivará la claseCaContainer
(cacontainer.h
ycacontainer.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 nombresP4
.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 llamadasrc
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 llamadasrc
dentro de otra carpeta llamadairp2-p4
. A su vez, esta carpeta estará comprimida en el fichero llamadoirp2-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 nombreirp2-p4
(todo en minúsculas). -
Dentro del directorio
irp2-p4
existirá la carpetasrc
la cual contendrá los ficherosautomaton.cc
,ca.cc
,container.tcc
ycacontainer.cc
(todos en minúsculas).Recuerda que los ficheros ‘.h’ no 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.
-