Descarga la aplicación para disfrutar aún más
Vista previa del material en texto
UNIVERSIDAD NACIONAL AUTÓNOMA DE MÉXICO FACULTAD DE ESTUDIOS SUPERIORES ACATLÁN Desarrollo de un juez en línea: una herramienta web para administrar concursos de programación y evaluar la efectividad de programas propuestos como solución a los problemas planteados Tesina QUE PARA OBTENER EL TÍTULO DE Lic. en Matemáticas Aplicadas y Computación PRESENTA: Moroni Silverio Flores Asesor: Dr. Jorge Vasconcelos Santillán Junio 2019 UNAM – Dirección General de Bibliotecas Tesis Digitales Restricciones de uso DERECHOS RESERVADOS © PROHIBIDA SU REPRODUCCIÓN TOTAL O PARCIAL Todo el material contenido en esta tesis esta protegido por la Ley Federal del Derecho de Autor (LFDA) de los Estados Unidos Mexicanos (México). El uso de imágenes, fragmentos de videos, y demás material que sea objeto de protección de los derechos de autor, será exclusivamente para fines educativos e informativos y deberá citar la fuente donde la obtuvo mencionando el autor o autores. Cualquier uso distinto como el lucro, reproducción, edición o modificación, será perseguido y sancionado por el respectivo titular de los Derechos de Autor. Agradecimientos A Saraí, por su apoyo en esta trayectoria. A mis padres, por apoyarme en mis estudios. A mi asesor, por su guía para llevar a cabo esta obra. Al Grupo de Algoritmia de la FES Acatlán, de quienes aprendí muchas cosas. Al Departamento de Servicios de Cómputo de la FES Acatlán, por los recursos facilitados para desarrollar este proyecto. Y a todos aquellos que hicieron esto posible que no puedo mencionar por nombre. Contenido Introducción ............................................................................................................................... 1 1. La programación competitiva ........................................................................................ 3 1.1. ¿Qué es la programación competitiva? .............................................................. 3 1.2. Requerimientos de un juez en línea ..................................................................... 3 1.2.1. Jueces en línea actuales ................................................................................. 4 1.2.2. Requerimientos del Juez GUAPA ................................................................. 5 1.2.3. Limitaciones del Juez GUAPA ....................................................................... 7 1.3. Antecedentes históricos y beneficios ............................................................... 15 1.3.1. Beneficios de la programación competitiva ............................................ 16 2. Desarrollo web con Meteor .......................................................................................... 19 2.1. ¿Qué es Meteor? ..................................................................................................... 19 2.2. El protocolo DDP ..................................................................................................... 20 2.2.1. Optimistic UI ..................................................................................................... 21 2.3. Programación reactiva .......................................................................................... 25 2.3.1. Fuentes reactivas ............................................................................................ 27 2.3.2. Tracker ............................................................................................................... 30 3. Almacenamiento de información con MongoDB .................................................... 32 3.1. SQL vs NoSQL ......................................................................................................... 32 3.2. ¿Qué es MongoDB? ............................................................................................... 34 3.3. Colecciones del juez en línea .............................................................................. 35 4. Desarrollo e implementación del juez ....................................................................... 53 4.1. Desarrollo .................................................................................................................. 53 4.2. Requerimientos para el algoritmo de evaluación........................................... 54 4.2.1. Algoritmos eficientes de evaluación ......................................................... 55 4.2.2. Medir el tiempo de ejecución ....................................................................... 60 4.2.3. Proceso de instalación del juez en línea .................................................. 62 4.3. Uso en competencias ............................................................................................ 63 5. Discusión y trabajo a futuro ......................................................................................... 64 5.1. Idiomas ...................................................................................................................... 65 5.2. Nuevo contenido ..................................................................................................... 66 5.3. Cualidades que no se deberían agregar al juez .............................................. 67 5.4. Mejoras que se podrían incorporar al juez ...................................................... 69 5.5. API ............................................................................................................................... 73 Conclusiones ........................................................................................................................... 75 Bibliografía ............................................................................................................................... 77 Anexos ....................................................................................................................................... 81 A. Esquema en JSON de las colecciones de la base de datos ........................ 81 B. Configuracion de Isolate ....................................................................................... 90 C. Código actualización scoreboard ....................................................................... 94 D. Rutas del Juez GUAPA .......................................................................................... 96 1 Introducción El presente documento mostrará el proceso de creación de una herramienta Web enfocada a evaluar y administrar concursos de programación competitiva. El objetivo de esta obra es desarrollar una herramienta web, llamada “juez en línea”1, que ayude a organizadores de concursos de programación competitiva a evaluar las soluciones propuestas a los problemas planteados durante estos eventos, así como proporcionar una plataforma digital a los interesados en mejorar sus habilidades de programación de manera eficiente, así como profundizar en los algoritmos involucrados y en sus fundamentos matemáticos. También se desea ayudar al Grupo de Algoritmia Avanzada y Programación Competitiva (GUAPA), perteneciente a la Facultad de Estudios Superiores (FES) Acatlán de la Universidad Nacional Autónoma de México (UNAM), a tener un control del progreso de sus miembros, a organizar, principalmente, el concurso de programación de la semana académica de MAC y el concurso interno de programación; también permitirá realizar otros concursos, que, si bien no se realizan de manera periódica, ayudan a los miembros del grupo a prepararse para los concursos regionales del gran premio de México del ICPC2. El ser parte del grupo de algoritmiapermite ver la manera en que este ayuda a los alumnos en su desempeño académico y profesional, permitiendo también ver de qué cosas carece. La idea de tener un juez en línea en la institución fue algo que ha estado presente en la mente de varios miembros del grupo durante algunos años. Con esta contribución se puede ayudar a los miembros del grupo de algoritmia a mejorar sus habilidades de programación y de matemáticas. Surge también idea de esta obra para cubrir deficiencias en otras herramientas en línea existentes en la actualidad, no necesariamente para reemplazarlas, pero para aportar a la formación académica de quienes las utilizan día a día. 1 Este juez en particular se llama Juez GUAPA y se puede acceder a él a través de https://www.juezguapa.com/ 2 International Collegiate Programming Contest (Concurso Internacional Universitario de Programación, traducción libre). 2 A lo largo de cinco capítulos se describe cómo se desarrolla esta herramienta Web, los desafíos que se presentaron y su evolución, también se muestra el uso de distintas herramientas de desarrollo y lenguajes de programación que juntas hacen que este trabajo sea posible. En el capítulo I se hace una exposición sobre el concepto de programación competitiva, los principales concursos de programación en México y el mundo además de la necesidad de jueces en línea y automatizados. También se muestra como ha sido la participación de los alumnos de la FES Acatlán (principales miembros del grupo de algoritmia). Se muestra también los requerimientos que debe satisfacer un juez y sus limitaciones. En el capítulo 2 se explica que es el framework Meteor[js] y por qué se eligió para desarrollar la herramienta Web. También se explican algunos paradigmas de programación relacionados con el desarrollo Web actual. En el capítulo 3 se muestra que son las bases de datos no relacionales y como su uso es el adecuado para el desarrollo de esta herramienta. Una parte importante es el cómo se almacena la información y como se muestra al usuario final, se explica todo este proceso. El capítulo 4 muestra cómo se fue desarrollando el juez, cómo fue evolucionando, cómo instalarlo con sus requerimientos mínimos y se muestra al juez en acción en concursos de programación dentro y fuera de la FES Acatlán. En el último capítulo se muestra el futuro del juez en línea, con posibles líneas de investigación y desarrollo. También se muestran las actualizaciones que se están realizando continuamente y se dan detalles de algunos algoritmos, no implementados aun en el juez en línea, que se agregarán en un tiempo próximo para mejorar la experiencia de los usuarios, tanto concursantes como administradores de concursos. Todo el desarrollo del juez en línea fue hecho por el autor de esta obra, quien lo tomó desde cero con el deseo de tener una herramienta para facilitar la administración de concursos de programación y con el deseo de mejorar el nivel de programación de muchos entusiastas. Este desarrollo abarcó desde la toma de requerimientos, desarrollo backend y frontend (explicados más adelante), diseño de la base de datos y su producción. 3 Capítulo 1 1. La programación competitiva 1.1. ¿Qué es la programación competitiva? La programación competitiva es un deporte que se lleva a cabo en una red local o por internet, puede ser comparado con cualquier deporte de campo: Cuando se es nuevo en este quizá las cosas salgan mal, pero después de un tiempo y práctica se irá mejorando; se anotarán goles si se practica fútbol, en el caso de la programación competitiva el código que se programe será la solución de problemas planteados y quizá al principio sea malo o tenga muchos errores, pero con el estudio y la práctica se tendrán más aciertos. 1.2. Requerimientos de un juez en línea Dada la cantidad de personas que participan en un concurso de programación es evidente que no es tarea para humanos el revisar el código de cada concursante y emitir un veredicto. Como nota se puede decir que en los concursos de programación realizados dentro de la FES Acatlán se han realizado un promedio de dos soluciones por minuto, ahora, ¿Cuántas se realizan en un concurso con más de mil participantes y que dura aproximadamente cinco horas? En los primeros concursos de programación competitiva realizados en Estados Unidos y Canadá los usuarios guardaban su código en un disquete y corrían hacia los jueces (quienes estaban sentados en una mesa para evaluar problemas) y se formaban esperando a que su código fuente fuera evaluado, después empezaron a realizar sus envíos a través de la red local, sin embargo, la cantidad de envíos realizados siempre ha requerido la automatización del proceso de evaluación. Y dado que es una competencia donde el tiempo es uno de los factores importantes, se necesitan entregar resultados de manera rápida, los concursantes deben saber si sus envíos son correctos o 4 no lo son para pasar al siguiente problema o intentar corregir lo que les salió mal (o incluso abandonar el problema). El trabajo de los organizadores durante una competencia, en la época actual, suele limitarse a resolver dudas de los participantes, ya que no se cuenta con el tiempo para validar el código de todas las personas, eso lo debe hacer un juez automatizado. En los concursos clasificatorios de México donde participan alrededor de 300 equipos de distintas universidades suele haber hasta 2000 envíos en un periodo de cinco horas de competencia. Cada región es libre de utilizar el juez en línea de su preferencia para las competencias, algunos de los cuales son software libre y se pueden descargar para correr una copia local durante la competencia. 1.2.1. Jueces en línea actuales El ICPC utiliza el DOM judge [1] para las finales mundiales, algunas regiones de Europa y Estados Unidos también utilizan este juez para sus competencias, para utilizarlo es necesario descargar una copia del juez para ejecutarlo en un servidor local durante la competencia. Este juez está escrito en PHP y requiere una máquina con Linux para funcionar. Otro juez en línea es el Kattis [2], aunque este no es software libre, a diferencia del DOM judge, el Kattis no tiene que descargarse para realizar concursos, el propósito de Kattis es ayudar a universidades con sus cursos de ciencias de la computación, ayudar a empresas a contratar personal y permitir a amantes de la programación practicar y ser contactados por empresas. El uso de Kattis para llevar a cabo concursos tiene un costo, sin embargo, tiene todo listo para que los administradores no tengan que preocuparse por instalar nada o verificar que el sistema de evaluación cumpla con requisitos que se mencionarán más adelante. En Latinoamérica suele utilizarse una copia del Juez Boca [3] que es utilizado principalmente en competencias brasileñas de programación. Para usar el juez es necesario descargarlo y correrlo en un servidor al que tengan acceso los concursantes. A pesar de que el juez boca cumple con el propósito de las competencias, los usuarios deben ser cuidadosos con los envíos que realizan ya que algunas veces el juez da como veredicto que la solución es muy lenta, aunque en otro juez hubiera pasado en tiempo, normalmente requiere métodos de lectura y escritura rápida que, en la 5 opinión de algunos participantes, van en contra del espíritu de la programación competitiva donde se evalúan las complejidades de las soluciones en términos de Big O. La complejidad no varía si se usa un printf o un cout en C++ para imprimir la respuesta, sin embargo, en este juez sí son importantes3. Otro de los problemas que enfrenta este juez es el tiempo de evaluación, en competencias ha habido demoras de más de 20 minutos en la evaluación de problemas, y esto afecta seriamente el desempeño de lacompetencia de los concursantes. Anteriormente su utilizaba el Caribbean Online Judge [4] (un juez en línea cubano de la Universidad de Ciencias Informáticas, UCI), pero se presentaban muchos problemas de conectividad durante las competencias, el firewall de la Universidad de Ciencias Informáticas bloqueaba el acceso cuando veía “demasiadas” solicitudes de la misma IP (todos los concursantes dentro del mismo campus muestran la misma IP) y, aunque esto se arreglaba, algunas veces el servidor se caía a media competencia debido al estrés que recibía por tantos concursantes, sin embargo sus tiempos de evaluación eran buenos (se tardaba alrededor de un minuto en emitir un veredicto). Actualmente sigue siendo usado para competencias del Caribe y algunos países de África. 1.2.2. Requerimientos del Juez GUAPA ¿Qué debe tener un juez en línea para administrar un concurso de programación? A simple vista del lado del usuario es que califique bien sus envíos, es lo que le importa a quien resuelve ejercicios para practicar o durante un concurso, también quiere que sea rápido. A nadie le gustan las cosas lentas, en especial cuando estas en una competencia donde el tiempo es un factor de desempate. Entonces uno quiere saber lo más rápido posible si hubo un error en su código, para que haga los cambios pertinentes. 3 En C++ la función printf() es más rápida que cout para imprimir datos en la salida estándar, en Python 3 se puede ver diferencias al imprimir utilizando stdout.write vs print(), siendo el primero mucho más rápido. Esto ha llevado a que algunos jueces acepten una solución escrita con printf y rechacen una escrita con cout. 6 ¿Qué hay del lado de los que organizan el concurso? ¿Cuál es su juez ideal? ¿Qué requieren para que el juez pueda cumplir su trabajo? Un pequeño recorrido del lado de los jueces (humanos) nos diría que el Juez debe ser rápido. Al igual que los concursantes, un administrador de concurso quiere que las respuestas sean rápidas, no le gusta ver a la gente desesperada preguntando porque no han tenido respuesta del código que enviaron hace 25 minutos cuando el concurso se acaba en 15 minutos. El juez debe permitir una comunicación entre los administradores y los concursantes, es normal que haya dudas, siempre las habrá, a veces porque queda alguna ambigüedad en la redacción de los problemas, a veces porque los concursantes no leen bien los enunciados. Debe haber un canal de comunicación siempre abierto. Otro punto importante es la seguridad, un buen juez estará compilando y ejecutando código desconocido en el servidor; hay personas maliciosas, ¿Qué pasaría si alguien intenta tomar control del servidor? ¿O apagarlo? ¿O buscar las respuestas en alguna parte del sistema de archivos? Ese es un punto crucial del juez, si se planea que el servicio Web esté disponible 24/7, cuando no haya nadie verificando los envíos con la tranquilidad de que al despertar nadie habrá tomado control del servidor o ejecutado código malicioso. Aunque hay jueces que se montan únicamente para concursos de programación y después del periodo de cinco horas o menos que dura el concurso lo ponen fuera de línea. Durante un concurso no suele haber código malicioso, si acaso errores en el código que hacen mal uso de memoria y que harían que una computadora normal dejara de funcionar al ejecutar dicho código, sin embargo, de vez en cuando hay algún usuario que quiere hacer lo que considera una travesura enviando código malicioso. En capítulos posteriores se discutirán los requerimientos en memoria RAM y de almacenamiento para instalar un juez. 7 1.2.3. Limitaciones del Juez GUAPA También se debe especificar qué es lo que no hace un juez. La cosa principal que no hace es decirle al usuario en que está mal, esa es una tarea muy complicada que va más allá del trabajo de un juez, a veces los errores van desde errores en el formato de salida (se imprime un carácter de más o de menos), olvidan limpiar una alguna variable (se llegan a realizar operaciones correctas con información incorrecta), errores en la lectura de datos, etc. Incluso aunque pudiéramos decir, al ver el código, que la respuesta es prácticamente correcta, el juez no puede aceptarla, ya que ese pequeño error hace que la salida del programa enviado por el usuario sea diferente a la salida que se tiene como respuesta correcta. 1.2.3.1. Fraude Otro asunto muy discutido entre los administradores de concursos de programación es la detección de plagios. Surge la pregunta: ¿Cómo se sabe que hubo un plagio, que un archivo fue copiado? Se podría correr un diff [5] para verificar que los archivos no sean iguales, pero esta prueba se pasaría al modificar un carácter del código original, entonces los archivos serían parecidos, pero no iguales. Entonces se podría utilizar un algoritmo para encontrar la subsecuencia común más grande [6], entonces, si se modificaron partes del archivo (se cambiaron nombres de las variables, entre otros) aun habría código en común. Pero ¿qué pasa si la persona que copió el código cambia el orden en que fueron declaradas las funciones dentro del código fuente?: La cantidad de código en común no se vería reflejado por la subsecuencia común más grande. Otra cosa que se podría hacer sería intentar verificar la estructura del árbol de sintaxis de los códigos fuentes. Pero ¿qué tan parecidos deben ser para considerarse fraude, incluso si los códigos fuente fueron escritos en distintos lenguajes de programación? Y también surge el dilema: muchos problemas requieren un algoritmo en específico para resolverse; entonces, dos usuarios de manera independiente seguramente utilizarán un Rolling Hash [7] con búsqueda binaria para encontrar el tamaño de la subcadena común más grande de dos cadenas de texto de 100 mil elementos cada 8 una. Otra manera de solucionarlo es con un suffiex trie, quizá haya más soluciones, pero es seguro que los concursantes se apegarán a esas soluciones. Se utilizará de ejemplo el algoritmo de Euclides (el cual sirve para encontrar el máximo común divisor de dos números a y b) como ejemplo para crear un árbol de sintaxis, el código en Python es el siguiente: Ilustración 1-1 Árbol de sintaxis del algoritmo de Euclides Secuencia de enunciados Mientras Comparar != Variable b Constante 0 Si Comparar > Variable a Variable b Cumple Asignar Variable a Operacion - Variable a Variable b No se cumple Asignar Variable b Operacion - Variable b Variable a Regresar Variable a while b != 0: if a > b: a = a − b else b = b − a return a 9 El árbol de sintaxis es libre del lenguaje de programación, entonces, si alguien programara el algoritmo de Euclides utilizando otro lenguaje de programación sus códigos pasarían la prueba de comparación de texto como subsecuencia común más grande porque la manera de escribir es distinta, sin embargo, sus árboles de sintaxis serían el mismo (la ilustración 1-1 muestra este árbol de sintaxis), pero esto no nos dice nada en cuanto al fraude. Solo nos dice que programaron lo mismo, pero nada más. El hecho de que dos personas hayan sacado diez en su tarea no significa que se hayan copiado. Regresando al problema de la subcadena común más grande, si hay más de dos personas que resolvieron el problema, por el principio de Dirichlet podemos decir que hay al menos dos concursantes con la misma solución. Su árbol de sintaxis será muy parecido, incluso su código. ¿Por qué? Porque muchos equipos usan plantillas para resolver problemas bien conocidos para ahorrarse tiempo en concurso. Y, qué pasa si ambos equipos tuvieron el mismo maestro y ambos programan la solución de un problema que les pide contar el número de permutaciones de un arreglo de tamaño 𝑛 dónde los primeros𝑘 elementos no están en su lugar. Seguramente sus soluciones serán casi idénticas, inclusive con el nombre de las variables; ambos aprendieron el principio de inclusión exclusión y no hay fraude ni malicia en ellos, sin embargo, sus códigos son muy parecidos. ¿Es plagio porque ambos equipos de la misma institución supieron resolver el mismo problema de la misma manera porque de esa manera aprendieron a resolverlo? ¿O, aunque sean de escuelas distintas? La solución4 es: ∑(−1)𝑖 ( 𝑘 𝑖 ) (n − i)! 𝑘 𝑖=0 4 Este es un problema de desarreglos (derangements) y puede resolverse con el principio de inclusión- exclusión, sin embargo, la demostración, aunque hermosa, va más allá de esta obra, pero en un buen libro de matemáticas discretas se puede encontrar la explicación. 10 A continuación, se comparará el código fuente que soluciona el problema hecho por dos personas que lo programaron de manera independiente: Solución 1: #include <iostream> #include <cmath> using namespace std; #define mod 1000000007 typedef long long ll; #define N 1000003 ll facto[N+3]; ll revfacto[N+3]; ll fast_pow(ll x,ll n) { ll res = 1; while(n) { if(n & 1) res = res * x % mod; x = x * x % mod; n >>= 1; } return res; } void llena() { facto[0] = 1; for(int I = 1; I <= N; i++) facto[i]=(facto[I - 1] * i) % mod; revfacto[N - 1] = fast_pow( facto[N],mod - 2) * N % mod; for(int i =N - 2; I >= 0; i--) revfacto[i] = revfacto[I + 1] * (i + 1) % mod; } ll combi(int n,int k) { if(k == 0 or n == k) return 1; return (facto[n] * revfacto[n - k] % mod * revfacto[k]) % mod; } 11 Solución 2: int main() { llena(); int n, k; cin>>n>>k; ll resto = 0; for(int i = 1; i <= k; i++) { ll pro = combi(k, i) * facto[n - i] % mod; i & 1 ? resto += pro : resto -= pro; resto = (resto + mod) % mod; } cout<<(facto[n] – resto + mod) % mod<<endl; return 0; } #include <bits/stdc++.h> using namespace std; typedef long long ll; #define mod 1000000007 #define tam 1000003 ll fastpow( ll a, ll n ) { ll res=1; while ( n ) { if ( n&1 ) res=res*a%mod; a = a*a%mod; n >>=1; } return res; } ll fact[tam + 5], factreverso[tam + 5]; 12 No es un problema trivial, entonces quien lo resolvió o conoce del tema o realizó fraude, en este caso en particular conozco la fuente de ambas soluciones y no hubo fraude, sin embargo se parecen bastante, podríamos pensar que se cambió el nombre de la función combi(int n,int k) a n_en_k(int n,int k). También que la función llena() se metió dentro del main y se cambió el nombre de tam por N. Considerando esos cambios podríamos decir que la solución dos es una ligera modificación de la solución 1, pero no es así. ll n_en_k(int n, int k) { if (n < k) return 0LL; ll res = fact[n]; res = res * factreverso[k] % mod; res = res * factreverso[n-k] % mod; return res; } int main() { fact[0] = 1; for (int I = 1; I <= tam; ++i) fact[i] = fact[i - 1] * i % mod; factreverso[tam] = fastpow(fact[tam], mod - 2); for (int i = tam-1; i >= 0; --i) factreverso[i] = factreverso[i + 1] * ( i + 1) % mod; int n, k; cin>>n>>k; ll res = 0LL; for (int i = 0; i <= k; ++i) { int signo = (i % 2 == 0) ? 1:-1; res = (res + fact[n - i] * n_en_k(k, i) * signo % mod + mod) % mod; } cout<<res<<"\n"; return 0; } 13 Por eso mismo no es posible, para un juez en línea, identificar un fraude en un concurso, lo que se exhorta a hacer es realizar técnicas y reglamentos que disuadan a los concursantes a realizar algún tipo de trampa. 1.2.3.2. Prevención de Fraude ¿Cómo evitamos el plagio en un concurso de programación competitiva? ¿Cómo se evita el plagio en el salón de clases? Si un profesor utiliza un juez en línea para evaluar a sus alumnos podría pensar que hubo plagio porque incluso los comentarios son los mismos, o los códigos tienen los mismos errores, o el código está en una lengua extranjera (buscaron el código en internet). En un concurso de programación hay otras técnicas que se utilizan para evitar el fraude, aunque no son 100% efectivas son lo que se utiliza en la actualidad en la región de México y Centroamérica. Si bien los concursos clasificatorios no son tan estrictos con los concursantes, en la final regional de México y Centroamérica, donde se disputan los lugares para asistir a las Finales Mundiales, se aplican las siguientes reglas: Restricción de acceso a internet Únicamente se puede acceder a las paginas permitidas durante el concurso, que son la IP del servidor donde está el juez en línea y la documentación de los lenguajes oficiales, aunque estas últimas suelen estar ya descargadas, por lo que únicamente se tiene acceso por internet al juez en línea, se bloquean las conexiones a otros puertos para las computadoras no tengan comunicación vía ssh con otros equipos de cómputo. Modo superusuario Los concursantes inician sesión en la computadora que se les asigna con un usuario que no tiene permisos (privilegios) de hacer prácticamente nada, esto impide que el usuario llegue a burlar la restricción de acceso a internet o a la red local para comunicarse con otros equipos durante la competencia. 14 No equipos electrónicos Antes de entrar a la sala donde será el concurso todos los aparatos electrónicos son recogidos a los concursantes, si se llegase a detectar alguno durante la competencia, esto sería motivo de descalificación. Aunque en la final mundial las reglas son aún más estrictas no las abarcaremos en esta obra ya que incluso la región de México no cuenta con la infraestructura para cumplirlas, mucho menos un organizador de un concurso local. 1.2.3.3. Error humano A pesar de las muchas cosas que puede hacer un juez en línea, este no puede detectar errores de los administradores de los concursos, sin embargo, un buen juez debe ayudarlos a corregirlos cuando estos los detectan. En la práctica se ha visto que cuando ocurre un error por parte de los administradores del concurso, son los concursantes quienes se dan cuenta que ocurre un error, o en la mayoría de los casos sospechan que hay un error y preguntan a los organizadores, quienes podrán verificar y darse cuenta si hubo un error o no. Se han recopilado algunos ejemplos de errores que han ocurrido en competencias y que se hace para solucionarlos: Durante el concurso se dan cuenta que el enunciado de un problema esta mal, lo que se suele hacer es responder en la sección de clarificaciones cual debería ser el enunciado correcto, esta sección es utilizada como canal de comunicación para que los concursantes puedan resolver dudas y se aclaren ambigüedades en los enunciados del problema. Durante el concurso se dan cuenta que los archivos de entrada o salida, que sirven para evaluar las soluciones de los concursantes, están mal, Lo que se debe hacer es en ese momento corregir los archivos de entrada o salida y reevaluar el problema. Esto seguramente afectara el marcador, quizá algunos concursantes pierdan puntos (en caso de que se les haya aceptado una solución incorrecta) o ganen puntos (su solución, aunque correcta había sido rechazada porque los archivos con los que el juez en línea verifico la solución estaban mal). Esto quizá retrase el concurso por 15 unos minutos, pero es lo mejor que se puede hacer. Esto ha pasado en concursos de clasificación del Gran Premio de México y Centroamérica.Pero ¿qué sucede si este error es detectado una vez que el concurso ha finalizado? También ha ocurrido y para bien o para mal, si algún usuario recibió puntos con una respuesta incorrecta, no se le pueden quitar esos puntos, la corrección hecha después de acabado el concurso solo puede subir los puntos de los participantes o dejarlos igual, pero no se les quitan los puntos bajo la premisa de que fue error de los jueces no de los usuarios, y no se sabe si hubieran llegado a la respuesta correcta después de haber sido notificados que su solución estaba mal, por lo tanto se les dejan los puntos. Y a los usuarios que tenían la solución correcta se les aumentan los puntos. Estos son los errores más comunes que llegan a pasar en las competencias, aunque no ocurren en cada concurso, se debe estar preparado para ello. Mas adelante se hablará sobre el uso de esas presentaciones en el juez en línea que hacen posible la solución de errores. 1.3. Antecedentes históricos y beneficios Todo comenzó en 1970 cuando la UPE Computer Science Honor Society (Upsilon Pi Epsilon: Sociedad de Honor para las Ciencias de la Computación) llevó a cabo la primera competencia de programación [8]. La iniciativa se extendió rápidamente por Estados Unidos y Canadá con la idea de incrementar la ambición, habilidad de resolución de problemas y para dar oportunidades a estudiantes en el campo de la computación. Actualmente cada año, aproximadamente en abril, se llevan a cabo las finales mundiales de programación competitiva (ICPC World Finals). El ICPC (International Collegiate Programming Contest) se lleva en diferentes etapas alrededor del mundo: concursos locales, regionales y finales mundiales. En las finales mundiales participan alrededor de 120 universidades de distintas regiones del mundo, las cuales obtuvieron su pase en sus respectivas regiones al ganar los concursos regionales, (la Facultad de Estudios Superiores Acatlán pertenece a la región México y Centroamérica). Actualmente hay más de 50 mil estudiantes registrados participando en el ICPC de aproximadamente tres mil universidades [8]. El concurso se realiza en inglés, y aunque cada 16 región decide el idioma de la competencia la mayoría opta por este, aunque no sea la lengua materna de ningún concursante de la región. 1.3.1. Beneficios de la programación competitiva En estos días el ICPC no es el único concurso que se lleva a cabo a nivel mundial, la mayoría de estos se realizan por grandes compañías con el fin de reclutar talento, aunque también hay concursos con el único fin de preparar a estudiantes para el ICPC. Todos los concursos de programación siguen un estilo similar al del ICPC debido a que los creadores de los concursos son en su mayoría personas que se formaron y obtuvieron sus empleos gracias a la programación competitiva. Se mencionan algunos concursos internacionales de programación en los cuales participa el grupo de Algoritmia: • Facebook Hackercup: No es un concurso para hackear cuentas de Facebook, es un concurso en cinco etapas: Ronda de Clasificación, Ronda 1, Ronda 2, Ronda 3 y Ronda Final. En cada ronda se presentan de tres a cuatro problemas de distinta dificultad, excepto en la Ronda Final donde se presentan seis problemas. Es un concurso individual donde el primer lugar se lleva $10,000 USD. Fuera de los primeros 25 lugares el premio es una playera para los primeros 500 lugares, que, a pesar de no ser un premio ostentoso, se ve muy bien poner el lugar obtenido en un Curriculum Vitae. Participan anualmente cerca de 7000 personas alrededor del mundo. En este concurso cualquier lenguaje de programación es permitido. Para la evaluación de los problemas los concursantes, después de que han programado su solución, reciben un archivo de entrada y cuentan con cinco minutos para ejecutar su código con el archivo de entrada que descargaron para posteriormente subir un archivo con la solución del problema que depende del archivo de entrada y el cual fue generado por su solución. Este modo de evaluación puede considerarse arcaico si se compara con el grueso de los concursos de programación de hoy en día. Cada concursante cuenta con una oportunidad para subir su solución de cada problema durante el tiempo del concurso, por lo que, si no sube su archivo de salida a tiempo después de haber solicitado el archivo de entrada, se considera que su solución fue incorrecta. Uno debe estar seguro de lo que ya tiene la solución para no perder su oportunidad además de una buena conexión a internet. 17 • Google Code Jam: Este concurso, obviamente organizado por Google, cuenta con cinco rondas al igual que el Facebook Hacker Cup (incluso se llaman igual), con pequeñas diferencias: Una es que la Ronda 1 está dividida en tres fechas(A, B y C) y no importa en cuál de las tres se participe, solo se necesita quedar dentro de los primeros mil lugares en cualquiera de ellas para asegurar un lugar para clasificar a la siguiente ronda, se hace esto con el fin de facilitar a personas de distintas partes del mundo el participar ya que suele haber conflictos con los husos horarios. Participan poco más de 25000 personas alrededor del mundo. El premio es de igual manera de $10000 USD para el primer lugar. El modo de evaluación es distinto al del Facebook Hacker Cup, los usuarios envían su código con su solución en el lenguaje de programación de su elección (limitado a doce lenguajes de programación permitidos [9]). Entonces en los servidores de Google el código es compilado y ejecutado para verificar si imprime la misma solución que tienen ellos del problema dado. En el año 2018 tuvieron dificultades porque había demasiados usuarios intentando subir sus soluciones provocando que el servicio de evaluación estuviera sin funcionar por algunas horas. • Snack Down: Realizado por CodeChef con sede en la India, en la edición anterior participaron alrededor de 28 mil personas de distintas partes del mundo. El premio es de $10000 USD. La fase final se realiza en la India y busca incluir a estudiantes y profesionales en la competencia. En todos estos concursos los temas a tratar van desde matemáticas discretas a geometría computacional, es un “temario” muy completo, también se abordan paradigmas para la resolución de problemas de programación, estructuras de datos y procesamiento de texto. A excepción del ICPC los demás concursos hacen públicos los códigos fuentes de los envíos realizados por los concursantes una vez finalizadas las rondas. Esto ha contribuido a que concursantes con menos experiencia se fortalezcan aprendiendo de las soluciones de otras personas, en muchos casos desconocidas. La Facultad de Estudios Superiores Acatlán inició su participación en las competencias de programación competitiva en el año 2011 con dos equipos, el grupo de Algoritmia Avanzada y Programación Competitiva fue fundado por un alumno de la Facultad que había participado en la olimpiada mexicana de informática (OMI) y obtuvo medalla de plata en dicha competencia. Esta primera participación por parte de la Facultad se realizó en la región México, Centroamérica y el Caribe, donde un equipo obtuvo el lugar número 11 de la competencia. El siguiente año se obtuvo el lugar 20 en la competencia regional. Aunque la final regional del ICPC no ha sido la única 18 competencia en la que se ha participado por parte del grupo de algoritmia, es la más importante y la que más auge ha tenido en México en los últimos años, aumentando su nivel de dificultad considerablemente (ha pasado de tener 150 equipos concursando a 400 en cinco años). Para el año 2013 ya eran cuatro equipos participando en la Final Regional de México y el Caribe de la FES Acatlán, el mejor de estos obtuvo el lugar 10 de la competencia, los miembros de la Facultad participaron desde Querétaro en dicha competencia. El siguiente año se logró obtenerel sexto lugar de la competencia que tuvo como sede a la Universidad Jesuita de Guadalajara (ITESO), Guadalajara, a partir de este año la final regional empezó a llevarse en un mismo sitio. Anteriormente la final se llevaba en al menos cinco sedes a lo largo de la República, sin embargo, solía haber problemas de conexión y desfaces en los horarios (una sede, la de Querétaro en 2013, comenzó media hora después que el resto de las sedes). Cada sede tenía su propia copia local del juez, entonces para saber el resultado final era esperar al resto de las sedes y juntar los resultados. La ventaja de esto es que podían participar muchos alumnos en la final regional (aproximadamente 170 equipos), actualmente solo se permite la participación de 50 equipos en la final con un máximo de dos equipos por universidad. El 2015 fue el año en que el grupo de Algoritmia ha estado más cerca de clasificar a las Finales Mundiales del ICPC y en el que se empezaron a notar más los frutos de los miembros del grupo. El equipo UNAM1 tuvo el primer lugar en las 4 rondas clasificatorias del gran premio de México y Centroamérica, clasificando a la Final Regional como el equipo favorito para ganar la competencia y obtener su pase a las Finales Mundiales, sin embargo, la fortuna no siempre le sonríe a todos y el equipo quedó en cuarto lugar de la competencia. Uno de los miembros de ese equipo se encuentra actualmente trabajando en Microsoft en el estado de Washington, EUA, otro de los miembros del equipo se encuentra laborando en Guadalajara. Otros exalumnos de la FES Acatlán que fueron miembros del grupo de algoritmia se encuentran trabajando en empresas como Oracle, Wizeline (ambas en Guadalajara) y GoCater (Francia). El verano pasado uno de los miembros de Algoritmia se fue “intern”5 al campus de Microsoft en Redmond, y más miembros actuales del grupo han aplicado para participar de interns en la misma empresa, así como de Facebook. 5 La traducción más cercana es pasantía, un “intern” suele trabajar en verano en una compañía durante 12 semanas con el requisito de que sea un estudiante universitario. 19 Capítulo 2 2. Desarrollo web con Meteor 2.1. ¿Qué es Meteor? Meteor es una plataforma de software libre de desarrollo de aplicaciones Web y móviles (Android y iOS), es una plataforma Full Stack (base de datos, backend y frontend). Esta plataforma isomórfica utiliza el protocolo DDP (siguiendo un patrón de suscribirse-publicar) para la comunicación entre cliente y servidor, está escrita sobre Nodejs y utiliza MongoDB para almacenar la información. Quizá sean demasiados conceptos en un solo párrafo, a continuación, se desglosan: Backend o server side Es el cómo el sitio funciona y actualiza la información, se puede decir que es todo lo que el usuario no ve en el explorador Web, es donde se mantiene la lógica de la aplicación, es donde se trabaja con la seguridad, estructura y administración de la información. Cuando un usuario se conecta al servicio Web, solicita información al servidor. Del lado del servidor se decide qué información mostrar o actualizar después de ser solicitada por el cliente. Frontend Es todo lo que el usuario puede ver, es con lo que interactúa, el diseño. El usuario recibe la información y del lado del frontend se maneja el diseño para mostrar la información de una manera amigable a este, es lo que tiene que ver con la experiencia del usuario (UX). Full Stack: Se dice que una plataforma es considerada Full Stack si tiene herramientas para manejar la base de datos, el frontend y el backend de manera que convivan sin que haya fricciones entre ellas. La más conocida es LAMP la cual usa Linux como Sistema Operativo, Apache como servidor Web, 20 MySQL como el sistema administrador de base de datos y PHP como lenguaje de scripts para manejar el backend; podría decirse que es de las más antiguas y por donde muchos desarrolladores Web empiezan su camino. De las plataformas Full Stack más recientes se encuentra MEAN la cual utiliza MongoDB para administrar las bases de datos (es un sistema NoSQL), Express, Angular que permite el desarrollo del frontend en Javascript (fue desarrollado por Google) y Nodejs el cual permite utilizar Javascript (JS) del lado del servidor (backend). Isomórfica Podemos llamar a Meteor una plataforma isomórfica porque permite usar el mismo código en el lado del servidor y del cliente (backend y frontend respectivamente). Meteor está escrito en JS y ejecuta el mismo código en el cliente y en el servidor a menos que explícitamente se diga de qué lado se debe ejecutar el código. Esto es una gran ventaja porque no se tiene que aprender un nuevo lenguaje de programación para trabajar entre el cliente y el servidor. 2.2. El protocolo DDP Desarrollado originalmente por el MDG (Meteor Developer Group), permite llamadas remotas a procedimientos del cliente al servidor y también otorga al cliente la facilidad de suscribirse a un conjunto de documentos con la premisa de que el cliente siempre estará actualizado y será notificado cuando estos documentos hayan sido modificados en tiempo real. Resuelve muchas de las dificultades que desarrolladores Web enfrentan hoy en día, ya que muchas veces para saber si hubo algún cambio en algún documento tienen que volver a solicitar toda la información al servidor, aumentando la carga sobre este. El protocolo DDP [10] fue la gran noticia cuando se creó Meteor y una de las razones por la cual utilizar Meteor para desarrollar el juez. El Juez Boca, que se mencionó anteriormente, funciona bajo el esquema de entregar páginas Web estáticas que se actualizan cada cierto periodo de tiempo; incluso el DOM Judge, utilizado en la final mundial, trabaja bajo este mismo formato. 21 Lo anterior lo hacen para mantener al usuario siempre actualizado sin que tenga que recargar la página para solicitar la información más reciente al servidor, como el scoreboard, por ejemplo. Sin embargo, esto ha llegado a ser un problema en competencias regionales en México ya que provoca que se aumente la carga al servidor ya que el cliente sigue refrescando el contenido y solicitando, aunque el usuario no esté prestando atención al juez en ese momento, información al servidor, llegando a provocar incluso que debido a la carga de trabajo el servidor no pudiera atender solicitudes en verdad importantes como el envío de soluciones de los problemas del concurso. Incluso estas actualizaciones forzadas no solicitadas llegan a ser molestas cuando alguien está viendo el scoreboard (usualmente una lista con más de 300 equipos) y antes de que el usuario termine de revisar la información la página se refresca regresando al inicio y provocando que el usuario tenga que volver a empezar su búsqueda y resultando en una pérdida de tiempo. El protocolo DDP de Meteor resuelve este inconveniente de manera natural sin complicaciones o la necesidad de escribir código adicional para poder lograrlo. 2.2.1. Optimistic UI La manera en que trabaja Meteor para modificar información o guardar información que viene del cliente es con Methods (métodos) [11] [12]. Si alguien ha trabajado con solicitudes HTTP tipo POST los encontrará similares, sin embargo, los métodos tienen algunos beneficios que las solicitudes HTTP no tienen, por ejemplo, gracias a Optimistic-ui [13], se puede simular el comportamiento del servidor del lado del cliente haciendo parecer al sitio Web más rápido de lo que realmente es. Cuando un administrador de concurso desea crear un nuevo concurso, un nuevo problema, modificar la fecha de inicio de un concurso, modificar el enunciado de un problema; lo normal es mandar la solicitud al servidor, el servidor autoriza la modificación o creación del documento y después manda su respuesta al cliente, posiblemente tambiénenvíe los datos del documento que modificó. 22 Con los métodos de Meteor, el cliente corre una simulación donde se crea el nuevo documento, se elimina o se modifica, entonces el cliente no tiene por qué esperar a que el servidor reciba la solicitud y devuelva una respuesta para seguir trabajando. Aunque esto en realidad sí pasa (se hace una predicción), si está bien programado el cliente no notara la diferencia, la ilustración 2-1 muestra cómo se lleva a cabo este proceso el cual es explicado en los siguientes párrafos. Se mostrará cómo funciona el protocolo DDP usando como ejemplo el envío de una solución por parte de un usuario en un concurso. Suponiendo que nuestro usuario ya ha realizado cierta cantidad de envíos previamente de distintos problemas durante un concurso, el usuario tiene acceso a una tabla que muestra los envíos que ha realizado junto con sus veredictos y se ve en la tabla 2-1: Ilustración 2-1 Funcionamiento de Optimistic UI, obtenido desde [13] 23 Tabla 2-1Envíos de un usuario durante un concurso Problema Veredicto Puntos Lenguaje Fecha Encriptación Alienígena Incorrecto 0 C++ 27/09/2018 04:59:25 pm Barbacoa Parcialmente Aceptado 70 C++11 27/09/2018 04:37:09 pm Fuera de la cama Aceptado 100 Java8 27/09/2018 01:29:22 pm La fuente de información de la herramienta Web es la base de datos, con la cual el servidor obtiene toda la información. Sin embargo, para el cliente, toda la información viene de una mini base de datos llamada minimongo (se explicará sobre Mongo en el siguiente capítulo), la cual ha obtenido toda su información de la base de datos. Entonces cuando un usuario manda un nuevo envío para ser evaluado, lo hace con la siguiente información en un objeto tipo JSON: Y cuando se invoca a un método de Meteor de la siguiente manera: { "usuario": "Nombre de usuario", "idproblema": "_id", "idconcurso": "_id", "codigo": "#include ...", "lenguaje": "C++11" } Meteor.call ('subir_envio', datos, (error, respuesta) => { if (error) { swal( `Error ${error.error}`, error.reason ... toastr['success'](`Felicidades ${Meteor.user().username ... if ( Session.get('slugconcurso') ) Router.go(`/concursos/${Session.get('slugconcurso ... else Router.go('/problemas/veredictos'); } ); 24 Esta solicitud es enviada al servidor, y al mismo tiempo se corre una simulación del método del lado del cliente, y aunque el cliente no cuenta con toda la información con la que cuenta el servidor al poder acceder a la base de datos, la simulación hace un buen trabajo. Dado que Meteor es isomórfico, se utiliza el mismo código para la simulación del lado del cliente que para la llamada del lado del servidor, el cliente obtiene primero la respuesta de la simulación y aun antes de que el servidor termine de procesar la solicitud obtiene una versión actualizada de su tabla con información de la base de datos de minimongo, mostrando casi instantáneamente la nueva tabla con los envíos (ver tabla 2-2). Tabla 2-2Envíos después de subir una solución Problema Veredicto Puntos Lenguaje Fecha Encriptación Alienígena Pendiente -- C++ 27/09/2018 05:04:25 pm Encriptación Alienígena Incorrecto 0 C++ 27/09/2018 04:59:25 pm Barbacoa Parcialmente Aceptado 70 C++11 27/09/2018 04:37:09 pm Fuera de la cama Aceptado 100 Java8 27/09/2018 01:29:22 pm Y aunque el último registro de la tabla (el que tiene veredicto pendiente) aún no existe en la base de datos, el usuario puede ver la información de su ultimo envío como si ya existiera; posteriormente el servidor devolverá la respuesta de la solicitud con la misma información de la predicción que hizo Optimistic UI, pero sin mostrar un cambio aparente para el usuario. Lo que se hizo fue hacerle creer que la herramienta es más rápida de lo que en verdad es. Si alguno es descuidado con las simulaciones podría darse el caso que la simulación agregue un nuevo registro a la base de datos de minimongo y por ende mostrarlo en la tabla de envíos, pero el servidor podría llegar a rechazar la solicitud evitando que se agregue un nuevo documento a la base de datos, eso provocaría que cuando se obtenga la respuesta del servidor el documento se elimine de minimongo y el registro desaparezca de la tabla instantes después de haber aparecido. Afortunadamente este no es el caso. 25 Para poder separar el código que debe ejecutarse únicamente en el cliente o en el servidor debe hacer de la siguiente manera: Y surge la pregunta: ¿cómo es que la tabla de envíos se actualiza automáticamente modificando la información de los envíos (agregando el veredicto una vez que ha sido evaluado o agregando nuevos envíos) sin que el usuario actualice la página Web? 2.3. Programación reactiva Se puede utilizar el cuerpo humano para explicar que es la programación reactiva y cómo funciona: En un día normal a cualquier ser humano le dará hambre, estará lleno, tendrá sed, sueño, ganas de ir al baño, entre otros; el cuerpo, inteligentemente, avisa cuando se requiere o ya no se requiere algo. Una persona no pasa su día preguntándose ¿tengo hambre?, ¿tengo ganas de ir al baño? El cuerpo simplemente avisa y entonces uno toma acciones. Algo similar ocurre con la programación reactiva, la cual es un paradigma de programación relacionada con el flujo de información y propagación del cambio que esta sufre; en el ejemplo del cuerpo humano cuando alguien está lleno, no significa que nunca más volverá a tener hambre, Meteor.methods ({ ... 'subir_envio'(datos) { if (this.isSimulation) { /* Código que corre únicamente del lado del cliente */ } if (!this.isSimulation) { /* Código que corre únicamente del lado del servidor */ } /* Código que corre en ambos lados */ } ... }); 26 cuando el contenido en el estómago cambie el cuerpo eventualmente volverá a tener hambre sin necesidad de que la persona haga algo en especial para saber si tiene hambre o para saber cómo se encuentra su estómago [14]. Para que la programación reactiva suceda se necesita una fuente de información reactiva, la cual nos avisa cuando algo dentro de ella cambia: El estómago nos avisa cuando el contenido, por así decirlo, dentro de él cambia. Por ejemplo, adentrándonos a la herramienta Web, podemos tener una variable llamada puntos, que nos indica cuántos puntos ha acumulado un usuario durante un concurso, el valor de esta variable cambia con el tiempo a medida que se resuelvan más problemas. Pero de nada nos sirve que nuestra variable nos avise que cambió de valor si nadie la escucha. ¿Para qué nos sirve saber cuántos puntos lleva un usuario? Para saber su posición en el scoreboard. Entonces si su puntaje cambia (porque se resolvió un problema no resuelto anteriormente), cambiará también el color que tiene en el marcador dependiendo cual fue el nuevo problema que resolvió e incluso la asignación de una imagen de trofeo junto a su nombre. Entonces, todas estas cosas deben de actualizarse cuando sus puntos cambian. En la ilustración 2-2 se muestran los cambios generados a partir del nuevo valor del puntaje al resolver un problema (el 100 dentro de un rectángulo): el usuario Jiren subió su posición en el Ilustración 2-2 Scoreboard y actualizaciones 27 scoreboard del sexto lugar al tercero, su puntaje general aumentó a 200 puntos, en lugar de aparecer un numero indicando su lugar aparece un trofeo y el problema que resolvió (L) ahora se ve sombreado (verde). Estos cambios inherentes al nuevo valor del puntaje se generan a través de lo que se conoce como computaciones reactivas, que básicamente es un segmento de código dentro de unafunción (no necesariamente la función completa) que será reevaluado cuando la fuente reactiva dentro de esa sección de código cambie. 2.3.1. Fuentes reactivas Meteor tiene, desde su concepción, la programación reactiva como parte de él [15], y tiene tres principales fuentes reactivas: Minimongo Es la principal fuente reactiva de información [16], las consultas a la base de datos son la fuente reactiva, entonces si una colección cambia porque se modificó, agrego o elimino un documento de la colección en minimongo cualquier sección de código, en el cliente, que obtenga o manipulé información obtenida por la consulta en la colección volverá a ejecutarse. Por ejemplo: Cuando se modifica algún documento en la colección Noficicaciones, únicamente se ejecuta la última línea de código (la que tiene el enunciado return), las primeras líneas de código en notificaciones() no se ven afectadas si cambia algún valor en la colección de Template.navbar.helpers ({ ... notificaciones() { let username = Meteor.user(); if (username) username = username.username; return Notificaciones.find({username:username}, {sort:{hora:-1}}); } }); 28 Notificaciones, por eso no se vuelven a ejecutar. En cambio, si el usuario cambia (cierra sesión, inicia sesión otro usuario) las primeras líneas de código en notificaciones() se vuelven a ejecutar porque obtienen información de Meteor.user(), provocando además que la consulta también se vuelva a ejecutar. Pero ¿cómo sabe el cliente que hubo algún cambio en la colección de notificaciones o en alguna otra colección para volver a ejecutar las consultas correspondientes? Esto se hace mediante el modelo de subscripciones-publicaciones. Normalmente el cliente se suscribe a cierta información al entrar a una nueva ruta dentro del sitio Web, al observar algún cambio en alguna colección o al ocurrir un evento. Una vez que el cliente se suscribe, el servidor verifica los privilegios que el usuario tiene y que datos pude ver, entonces publica la información que le es permitida en forma de cursores [17], la cual es recibida por minimongo y eso activa el computo reactivo. La ilustración 2-3 muestra cómo funcionan las publicaciones subscriciones. Ilustración 2-3 Suscripciones y Publicaciones en Meteor 29 El siguiente extracto de código muestra cómo se publica la información cuando un usuario quiere ver los problemas de un concurso: /* Cliente */ Router.route('/concursos/:slugconcurso/problemas', { waitOn: function () { return [Meteor.subscribe('problemas_concurso', this.params.slugconcurso)]; }, ... }); /* Servidor */ Meteor.publish ('problemas_concurso', function(slugconcurso) { /*slugconcurso: es un identificador de concurso*/ /* Verificaciones */ //evitar inyecciones de codigo check(slugconcurso, NonEmptyString); //verificar que el concurso exista let c = Concursos.findOne({slug:slugconcurso}, {fields:{slug:1, inicio:1}}); if (!c) return []; //si aun no empieza no puede ver los problemas const idconcurso = c._id; const d = new Date(); if (c.inicio > d) return []; //Se publica la información del concurso //y los problemas del mismo return [Problemas_concurso.find({idconcurso:idconcurso}), Concursos.find({_id:idconcurso},{fields:{slug:1}})]; } ); 30 Variables de sesión Las variables de sesión son otra fuente de información reactiva, sirven para guardar información en pares del tipo llave-valor. En la herramienta en línea que se desarrolló se utilizó para guardar información para identificar problemas, concursos y ayudar a saber el estado de algunas operaciones (como subir un archivo de entrada de 10 mb). Podrían ser comparadas con variables globales. También son utilizadas para guardar resultados obtenidos en consultas a la base de datos cuya información ya no está disponible para el cliente. Tienen tres funciones básicas: set(), get(), equals(), que sirven para asignarles un valor, obtener su valor y realizar comparaciones, todas de manera inteligente. Variables reactivas Meteor permite crear variables reactivas propias, normalmente para reducir el alcance de las mismas (las variables de sesión son globales) ya que se requieren para casos muy particulares. El juez en línea no las utiliza ya que la herramienta se ha bastado con las variables de sesión y minimongo. 2.3.2. Tracker Hasta el momento la programación reactiva ha sido magia [14], las fuentes reactivas avisan que hay cambios y entonces estos se propagan; pero la magia tiene su explicación. Tracker es la herramienta que utiliza Meteor para rastrear las dependencias de las fuentes reactivas de información, el programador no tiene que preocuparse por declarar o guardar en alguna parte todas las secciones de su código que utilizan fuentes reactivas [18]. Cuando se ejecutan funciones con fuentes reactivas, se guarda un objeto de computo, el cual es invalidado cuando una fuente reactiva actualiza su valor y entonces se vuelve a ejecutar ese objeto de computo. Esto se conoce como computo reactivo, que son las secciones de código re-ejecutadas cada vez que una fuente reactiva cambia su valor. 31 La manera en que Tracker mantiene todo esto es por medio de teoría de gráficas, guardando dependencias como aristas y las fuentes reactivas como nodos, de esta manera los cambios se propagan haciendo algo similar a una búsqueda en una gráfica dirigida. La ilustración 2-4 muestra cómo se comporta Meteor al notificar a los clientes suscritos a cierta información de la base de datos cuando hay cambios en ella. Todo esto genera una reacción en cadena que termina mostrando al usuario la información más reciente sin que este tenga que hacer o presionar algún botón, todo llega de manera automática y natural. Es importante recalcar que en un concurso donde el tiempo es un factor decisivo en desempates es necesario contar con la información más reciente sin contratiempos. Esta es una de las razones principales por las que Meteor fue escogido como el framework para programar el juez en línea, se requiere entregar velocidad, o al menos hacer creer que hay velocidad. Ilustración 2-4 Computo reactivo en clientes suscritos 32 Capítulo 3 3. Almacenamiento de información con MongoDB 3.1. SQL vs NoSQL Cuando se habla de NoSQL (bases de datos no relacionales, a veces también referido como Not Only SQL) se hace mención de distintas tecnologías de bases de datos desarrolladas en los últimos años con características que las bases de datos relacionales (SQL) no tienen. Una manera de decidirse por cual camino tomar es estudiando ambos paradigmas, y decidir cual es el mejor para el momento, aunque sin la ayuda adecuada [19] las voces del mundo dirán que se debe usar MongoDB porque es lo de hoy. Se escogió MongoDB pero no por esas razones, en este capitulo se da el por qué. Actualmente cuando se busca desarrollar y entregar un producto, la información se guarda de manera estructurada, semi estructurada, no estructurada y de manera polimórfica; los tipos de dato cambian frecuentemente conforme se van agregando características al producto. El juez en línea no es la excepción, las bases de datos relacionales son muy estrictas en cuanto al esquema en que se guardará la información, pero cuando se está en desarrollo las ideas van y vienen y lo que fue un modelo aceptado para almacenar la información puede ser modificado completamente al día siguiente, incluso el mismo día. Entonces, ¿Qué se hace cuando ya se pobló coninformación la base de datos, pero se desea cambiar el esquema? Cambiar el esquema en una base de datos ya poblada suele ser un dolor de cabeza en las bases de datos tipo SQL, pero no en las bases de datos no relacionales, cuando se cambia el esquema únicamente se agregan los nuevos “registros” sin hacer ningún alboroto, pueden vivir en la misma “tabla” registros con diferentes “columnas”, incluso aunque no tengan nada en común. 33 Quizá alguien piense que esto lleva al desorden y al caos, pero esta flexibilidad permite hacer mejoras continuas. Por ejemplo, al desarrollar el juez, se planteó el modelo de cómo se almacenaría la información de los marcadores (scoreboard) en los concursos. La idea inicial fue la siguiente: Con este modelo habría únicamente un “registro” (documento) por concurso; sin embargo, esto causaría problemas debido a la alta actualización del scoreboard en un concurso; si se espera que haya más de mil cambios en un corto periodo de tiempo, es necesario reducir la carga de trabajo del servidor, ya que cada vez que hubiera un cambio, gracias al protocolo DDP [10], todos los concursantes y espectadores recibirían de nuevo todo el documento. El esquema de cómo se vería el scoreboard cambio varias veces hasta llegar a la versión actual. Lo que se hizo con MongoDB fue seguir poblando las colecciones con documentos sin borrar la información anterior para seguir haciendo pruebas, algo que no hubiera sido posible con una base de datos relacional. Scoreboard: { "_id": Mongo id, "idconcurso": Mongo id, "info": { <idusuario>: { "puntos": entero, "penalizacion": entero, "problemas": { <idproblema> : { "puntos": entero, "intentos": entero, "primero_en_resolver": booleano }, ... <idproblema>: { ... } } }, ... <idusuario> : {} } } 34 3.2. ¿Qué es MongoDB? MongoDB es un administrador de base de datos de software libre basado en documentos que almacena la información en documentos tipo JSON cuya estructura y campos pueden variar entre documento y documento [20]. Esta es una de las razones principales por las que se decidió trabajar con Mongo, por la continua variación de la estructura de la información en el juez en línea. En [21] se discuten fuertemente los modelos de bases de datos relacionales y los no relacionales, la intención no es que un modelo sustituya al otro, sino que uno se de cuenta que no todos los modelos son para todos los proyectos. A continuación, se dan algunas definiciones antes de seguir el esquema de la base de datos que utiliza el juez en línea. Document (documento) Es un registro en una colección de MongoDB análogo a un objeto JSON, en el sistema SQL su homologo sería un registro en una tabla. JSON (Javascript Object Notation) Es un formato de intercambio de información fácil de leer para humanos y computadoras [22], aunque está basado en Javascript usa convenciones usadas por muchos lenguajes de programación logrando que estos pueden leer este tipo de documentos. Son similares a lo que en otros lenguajes se conocería como objeto, registro, estructura, tabla hash, diccionario, etc. Si bien los objetos JSON no son parte del estándar de algunos lenguajes de programación es muy fácil encontrar librerías para poder utilizarlos o incluso escribir una librería que pueda utilizarlos, la ilustración 3-1 muestra cómo es un objeto JSON. Ilustración 3-1 Representación de una gráfica dirigía de la expresión regular de un JSON, obtenida de [22] 35 Collection (Colección) Es una agrupación de documentos de Mongo, similar a una tabla en SQL. Las colecciones no fuerzan a que se cumpla un esquema en particular (como es el caso en SQL), aunque mucha gente tiende a forzarlo al momento de guardar documentos en las colecciones para mantener cierta consistencia entre todos sus documentos, esto se hace al revisar el formato de los documentos, por fuera de MongoDB, antes de insertarlos en la colección. Los documentos dentro de la misma colección suelen tener campos similares (pero no necesariamente los mismos) [23], lo cual es notorio en la evolución del proyecto cuando se ha ido cambiando el “esquema” del mismo o porque arbitrariamente así se han decido almacenar (el juez en línea explota esta característica de Mongo). Aunque algunas personas usan MongoDB solo porque es algo relativamente nuevo, se debe pensar dos veces antes de utilizarlo, y considerar los pros y los contras de hacerlo. Al mostrar las especificaciones de la base de datos se hará evidente la necesidad de usar MongoBD, aunque otros proyectos quizá requieran otro tipo de base de datos. Se consideró seriamente [24] para decidir si se debía utilizar MongoDB o algún otro manejador de base de datos relacional o no relacional. Como suele decirse en otros campos: MongoDB no es para todos. En [25] se discute claramente porque MongoDB no debió utilizarse en una red social llamada Diaspora* [26] y el vía crucis que se vivió desde que se empezó a utilizar MongoDB hasta que se cambió por una base de datos relacional. Quizá sea importante resaltar que MongoDB no ha sido un dolor de cabeza en el desarrollo del juez en línea, sino algo sencillo y necesario. 3.3. Colecciones del juez en línea Una buena guía para diseñar bases de datos orientadas a objetos (MongoDB) se encuentra en [27]. Algunas personas han llegado a pensar que estos modelos son recientes sin embargo llevan años implementándose, solo que no habían adquirido popularidad. En [27] se encuentran instrucciones para hacer consultas a este tipo de bases de datos de manera eficiente y ha sido de gran utilidad al diseñar las consultas y colecciones del juez en línea. 36 Si bien no todas las colecciones que utiliza el juez en línea se comportan como no relacionales, al describir cada una se hará aún más evidente la necesidad de MongoDB para almacenar la información del juez. La descripción de las colecciones que usa el juez en línea se da a continuación en orden alfabético según sus nombres: Aclaraciones Esta colección registra las dudas que van saliendo durante cada concurso por los participantes, es la vía de comunicación de estos con los organizadores. • _id6: Es la llave primaria del documento [28]. • Idconcurso: Toda aclaración va ligada a un concurso, este id indica en que concurso se hizo la pregunta. Fuera de un concurso no se hacen aclaraciones debido a que no se asegura que haya un humano resolviendo las dudas sobre problemas del juez, sin embargo, hay comentarios que se pueden dejar en cada problema. • Idproblema: También van ligadas a un problema en particular, cuando hay un concurso puede que un participante no tenga claro el enunciado de un problema, entonces hace la pregunta correspondiente, el ligar la pregunta a un problema hace más fácil a los organizadores de un concurso saber de qué está hablando el participante. • Autor: Es el usuario de la persona que hizo la pregunta, este únicamente es visible para los organizadores de un concurso (los demás concursantes no saben quién hizo la pregunta) y ayuda a dar seguimiento a las dudas o para evitar que se abuse del sistema. • Pregunta: Es la duda que el participante del concurso tiene. Se permite a cada usuario hacer una pregunta cada 30 segundos. Las preguntas no son visibles para el público a menos que hayan sido contestadas por algún administrador del concurso. • Respuesta: Es la respuesta que dan los organizadores de concurso a la pregunta en particular, estos pueden decidir no contestar la pregunta en caso de que lo consideren necesario.6 El campo _id está reservado para usarse como llave primaria; su valor debe ser único en la colección, es inmutable y puede ser de cualquier tipo (a excepción de un arreglo) El _id utilizado por MongoDB es una cadena de 12 caracteres que se genera de la siguiente forma: • Cuatro caracteres representando el tiempo transcurrido desde el Unix epoch. • Cinco caracteres creados al azar. • Tres caracteres de un contador inicializado en un valor al azar. Se omite el _id en la en la descripción de futuras colecciones debido a que no cambia la información respecto a este. 37 • Creado_en: Sirve para ordenar las preguntas en orden en que fueron hechas, ayuda a responder en orden para no hacer esperar a concursantes y para llevar una comunicación más amena con los concursantes. Blogposts Mantiene un historial con las noticias más recientes relacionadas con la programación competitiva y el Grupo de Algoritmia de la FES Acatlán. • Título: Es el título del post que será creado. • Imagen: Cada blogpost requiere al menos una imagen, no se permite que sea únicamente texto, esta aparece en la portada del post. Dada la reducida capacidad de almacenamiento con la que dispone el juez en línea se recomienda que se almacene en otra parte la imagen y únicamente se provea con una URL para poder acceder a ella7. • Resumen: Cuando se accede al sitio Web del juez en línea lo primero que se ve es la lista de blogs publicados, el resumen es la parte del texto que se muestra como vista previa del blog para invitar a los visitantes a leerlo. • Texto: Es el texto con el que está compuesto el blogspot, puede estar en markdown, html o una combinación de ambos para hacerlo más atractivo, el juez en línea no tiene un editor de markdown o html dado que este no es su propósito o razón por la que fue creado; sin embargo, es fácil encontrar en internet un editor para estos y pegar el texto en markdown o html en el juez. • Tag: Indica el tema a tratar en el blogpost, sirve para buscar posts por tema y para que el usuario pueda saber de qué va a tratar lo que va a leer, si bien aún no está implementada una búsqueda de blogposts por tag, es algo que se hará a futuro y se menciona en el capítulo cinco de esta obra. • Publicado: Indica si el blogpost está disponible al público o no, si aún está en edición únicamente quienes tienen permitido modificarlo pueden verlo. • Idautor: Liga al blogpost con su autor, únicamente usuarios que tienen rol de blog pueden escribir nuevos blogs o recibir invitación a editarlos 7 Actualmente todas las imágenes a las que tienen acceso el juez, ya sea por blogposts, problemas o concursos son almacenadas en una carpeta publica en Dropbox. 38 • Autor: Esta redundancia con el campo anterior ahorra tiempo de computo al evitar una consulta en la base de datos para averiguar quién es el autor del blogpost. Este tipo de redundancias podrá observarse en otras colecciones de la base de datos. • Creado: Permite ordenar, con fines de visualización, los blogposts para tener acceso a los más recientes primero. Casos Todo problema tiene un conjunto de entradas y salidas secretas con los cuales se califican los envíos hechos por los usuarios dentro y fuera de un concurso. En el modelo original de la colección de Problemas, esta colección [Casos] estaba contenida dentro de la primera; esto se quitó debido a que MongoDB tiene un límite de 16 MB por documento y cada archivo de entrada llega a pesar hasta 10 mb8 (cuando la entrada es muy corta llega a ser de unkb), por lo que cada par de archivo de entrada y salida se almacena como un solo documento. La cantidad de casos que un problema tiene es de aproximadamente cuarenta. • Idproblema: Similar a una llave foránea, indica a que problema pertenece esta entrada y salida. • Entrada: Es la entrada secreta recibe una posible solución del problema al que pertenece. • Salida: Es la salida que debe generar la solución enviada por el usuario, si las salidas son distintas se considera como incorrecta su solución. • Caso: Para mejor manejo de las entradas y salidas, estas se han dividido en tres casos: pequeño, mediano y grande; estos van aumentando en dificultad y normalmente también en tamaño, esto se ha hecho para evaluar distintas soluciones y sus complejidades en un mismo problema. • Creado: Esta fecha ayuda a mantener un orden por caso al momento de evaluarlos y extraerlos de la colección. 8 Si bien no hay un estándar de cuanto debe pesar un archivo de entrada o de salida, el limite de MongoDB ha permitido almacenar el 99.99% de los archivos de entrada. 39 Concursos La colección almacena los detalles del concurso para ser mostrados al público, así como la información necesaria para que cada concurso pueda ser editado y compartido con los organizadores del evento. • Nombre: Es el nombre que llevará el concurso. • Nombre corto: Este campo únicamente es utilizado para generar una ruta corta que pueda ser compartida a través de redes sociales u otro medio electrónico, es similar a un url shortener dentro del juez en línea. • Inicio: Es la fecha9 en que el concurso iniciará, a partir de esta fecha las personas inscritas en el concurso podrán hacer preguntas, subir soluciones y se bloquea la opción de desinscribirse del concurso, también esta fecha permite ver el scoreboard con todos los problemas y puntaje. Antes de esta fecha las acciones anteriores no son permitidas. • Fin: Es la fecha en la que finaliza el concurso; una vez que este ha finalizado ya no es posible hacer más envíos al concurso para intentar subir puntos (es posible resolver los problemas fuera del concurso, pero sin ningún efecto en el scoreboard), tampoco se permite hacer más preguntas y/o aclaraciones. • Descripción: Sirve para dar información adicional sobre el concurso realizado, suele ser la razón por la que se celebra el evento: algún aniversario, tradición anual/semestral o la razón que los organizadores crean conveniente. • Premios: Normalmente los concursos de entrenamiento no llevan ningún premio ya que la mayoría de los participantes no lo hacen desde alguna universidad o sede, si no desde sus casas. Algunos concursos son patrocinados y se entregan premios a los participantes dentro de la sede y un premio especial al mejor concursante que no estuvo presente en una sede. • Reglas: Aunque normalmente todos los concursos de programación competitiva siguen las reglas del ICPC descritas en [29], cada evento tiene la libertad de crear sus propias reglas que suelen referirse a cuestiones administrativas y logística; como hora de acceso, límite de concursantes, lenguajes de programación permitidos (algunos concursos son más 9 Si bien todas las fechas almacenadas en Mongo están en tiempo epoch, en ellas se guarda la información con precisión de día, mes, año, hora, minutos y segundos. 40 flexibles en cuando a los lenguajes de programación permitidos para concursar), costos administrativos, etc. • Bases: En este campo se agregan especificaciones del evento que no vienen en la descripción ni en las reglas, como el sistema operativo a utilizar, software instalado en los equipos de cómputo, etc. Las bases, premios y reglas se pueden escribir en formato html para mayor claridad. • Congelar_en: Cuando se alcanza esta fecha el scoreboard se deja de actualizar parcialmente para los participantes del evento (los organizadores del concurso siempre tienen acceso al scoreboard actualizado en todo momento). Los concursantes desconocen, al llegar a esta fecha, el puntaje actualizado de los demás, se les permite saber de cuales problemas han hecho nuevos envíos, pero no el veredicto de estos, únicamente de sus propios envíos.
Compartir