Prerrequisitos:
- Saber hacer problemas para omegaUp.
- Saber usar git, y conocimiento básico de Python y C++.
Usamos git para coordinar el trabajo, así como pruebas automáticas
que revisan que los problemas tienen entradas válidas y que las
soluciones de prueba sacan los puntos que esperamos. Todo se coordina
con el archivo problems.json
en la raíz del proyecto, y los
archivos individuales settings.json
en las carpetas de los problemas.
La descripción del formato de estos archivos y la organización de las carpetas está en este documento. Te recomendamos leerlo después de leer este archivo.
El flujo de trabajo para hacer un problema es estrictamente el siguiente. En todos los casos, se debe hacer un branch y posteriormente un pull request conteniendo una unidad de trabajo. Es importante no mezclar trabajo de distintos problemas en el mismo branch o pull request.
Se escribe en el es.markdown
, pero no es la redacción final. Debe contener:
-
Descripción corta del problema. (Sin historia)
-
Descripción del formato de entrada y salida.
-
Para Karel, ser muy específico con las dos anteriores (e.g. Karel empieza viendo al norte, Karel empieza en la casilla (1,1)).
-
Descripción de las subtareas, de haber. Cada subtarea tiene que tener un identificador único que las distinga. (Más detalles en la sección de pruebas.)
-
Agrega por lo menos un caso de prueba válido (el de ejemplo, probablemente) en
cases/0.in
ycases/0.out
. Agrega untestplan
que contenga solamente ese caso y obtenga 100 puntos. -
El primer caso de ejemplo que se vaya a incluir en la redacción debe ir en
examples/sample.in
yexamples/sample.out
. Todos los demás casos de ejemplo deben ir enexamples/
con nombres que indiquen a qué se refiere. Luego, para incluirlos en la redacción, se puede usar la siguiente sintaxis:||examplefile sample ||description Aquí va la descripción del caso de ejemplo. Nota que arriba no se usó el nombre del directorio `examples/` ni la extensión de los archivos. ||examplefile caso_mas_grande ||examplefile caso_mas_complicado ||description No es necesario agregar una descripción en cada caso, pero se recomienda que se haga. ||end
-
Escribir pruebas que verifiquen los casos de prueba.
-
En el caso de Karel, revisar todas las condiciones del problema y tener cuidado de que el
.in
contenga las condiciones de evaluación correctas para el problema. -
Para C++, el evaluador debe checar que la entrada sea válida y que sigue exactamente el formato descrito, cuidando en particular los espacios en blanco y saltos de línea.
-
Las pruebas tienen que considerar los límites especiales que pueden existir para las distintas subtareas. Esto se logra dándole nombres de archivo a los casos conteniendo el identificador de su subtarea, para poder distinguir a qué subtarea pertenecen. Aquí hay un ejemplo.
-
En este punto se agrega el
settings.json
y el problema aproblems.json
. El alias del problema no se determina hasta que se escribe la redacción final. Puedes usar el alias temporaldummy-omi
mientras tanto. -
Crear un archivo
tests/tests.json
indicando qué validador se usará para los casos de prueba. El archivo lleva este contenido:{ "solutions": [], "inputs": { "filename": "[validador].[lang]" } }
-
A partir de aquí, los pull requests deben pasar las pruebas automáticas para poder ser integrados.
Tanto para Karel como para C++ hay bibliotecas que simplifican el trabajo: revisa ejemplos de otros problemas de OMIs anteriores para ver cómo usarlas.
- La solución oficial debe guardarse en la ruta
solutions/solution.[lang]
. Cuando esté lista la solución oficial, se puede trabajar tanto en soluciones como en casos al mismo tiempo. - Agrega la solucion a
tests/tests.json
:{ "solutions": [ { "filename": "../solutions/solution.[lang]", "verdict": "AC" } ], "inputs": { "filename": "[validador].[lang]" } }
- Si la solución oficial se puede usar para auto-generar las salidas de los
casos (los
.out
), puedes borrar todos los archivos.out
del problema y después agregar un archivo llamado.gitignore
en la raíz del problema con el contenido:**/*.out # Si el problema es de Karel, también son necesarias las siguientes tres # líneas: cases/*.png examples/sample.*.png statements/sample.*.png
- Agrega tu solución en
solutions/[sol].[lang]
, así como entests/tests.json
, con el veredicto o puntaje esperado. Para soluciones parciales en lugar deverdict
debes agregarscore_range
con el rango de puntos (entre 0 y 1) que esperas para esa solución. Si es un score definido, pon el mismo score en las dos posiciones. Por ejemplo, si tienes una solución que esperas que dé 20 puntos, debes agregar:"solutions": [ { "filename": "../solutions/[partial].[lang]", "score_range": [0.2, 0.2] } ]
- Escribe casos ya sea manualmente o autogenerados, siguiendo la especificación de la entrada. No olvides agruparlos de ser necesario, y si hay subtareas, de incluir en su nombre el identificador de la subtarea.
- En caso de que los
.in
de tus casos sean autogenerados, incluye el código que los generó en la raíz del problema, con el nombrecase-generator.[lang]
. - Si la solución oficial no se puede utilizar para auto-generar los archivos
.out
, hay que generarlos manualmente y agregarlos:- Los problemas que tienen un validador personalizado pueden tener
.out
vacíos si el validador no necesita información más allá del.in
original y la salida del concursante para generar el veredicto. También se puede poner información en el.out
que le puede ayudar al validador. Por ejemplo, la cota superior / inferior conocida de lo que se desea optimizar. - Para los problemas interactivos, como el
Main.[lang]
típicamente hace las veces del validador, la misma información aplica. - Si generar los archivos
.out
toma mucho tiempo o es necesario generarlos a mano por cualquier razón, hay que agregar el código que los generó en la raíz del problema, con el nombreoutput-generator.[lang]
.
- Los problemas que tienen un validador personalizado pueden tener
- Haz un
testplan
para tus casos. No puede tener puntos decimales y necesariamente debe sumar 100. - Si es un problema de Karel, agrega las soluciones malas
(
default_bad_solutions
) atests/tests.json
, para asegurarte de que tus casos les dan 0 puntos. - Si es un problema con más de una salida válida, agrega en la raiz el
validator.[lang]
y cambia ensettings.json
la sección devalidator
:"validator": { "name": "custom", "limits": { "TimeLimit": 1000 } }
- Si es un problema interactivo, pueden consultar la documentación de
libinteractive y usar el
problema
OMI-2019-Mobius
como plantilla para empezar. Lo importante es tener la carpetainteractive/
con un archivo[ModuleName].idl
, un archivoMain.cpp
con el programa juez oficial y un archivoMain.distrib.cpp
que es el programa juez que se va a compartir con los concursantes (por si el juez oficial tiene secretos, o pistas de cómo se resuelve el problema). También hay que cambiar ensettings.json
la sección deinteractive
:"interactive": { "ModuleName": "[ModuleName]" }
-
Se escribe la redacción final del problema.
-
Asegurarse de que los casos de ejemplo (
examples/sample.{in,out}
, etc.) existen y se están usando con la sintaxis||examplefile
en la redacción.- Si hay solución oficial y esta puede generar el
.out
, no hace falta el.out
.
- Si hay solución oficial y esta puede generar el
-
Para Karel, hacer un symlink de
statements/sample.{in,out}
haciaexamples/sample.{in,out}
. El sistema de build rehace las imágenes de ejemplo basado en esos archivos: hay que borrar las imágenes generadas manualmente y cambiar eles.markdown
para que use las nuevas imágenes (e.g.sample.in.png
). Si hay varios casos de ejemplo, deben estar tanto enexamples
como enstatements
.Si es necesario recortar la imagen porque el mundo es muy grande, el symlink puede llamarse
sample.RxC.in
, dondeR
yC
son la cantidad de filas y columnas a dibujarse, respectivamente.
-
Hacer un documento que explique la solución oficial.
- Si existen soluciones parciales para subtareas, también hay que explicarlas.
-
Listar las observaciones importantes para poder atacar el problema.
-
Para embeber soluciones en la explicación, se puede usar la siguiente sintaxis (asumiendo que la solución existe en el archivo
solutions/subtarea_1.cpp
):<details><summary>Solución para la subtarea fácil (~4 puntos)</summary> {{subtarea_1.cpp}} </details>
-
Enfocarse en dar valor didáctico y si el problema es un tema conocido explicar dónde se puede aprender mas sobre este.
-
A menos de que la solución oficial no pueda usarse para generar las salidas (ver la sección de Casos (
cases/
), sólamente se hace commit a las entradas de los casos de prueba (.in
). Los.out
se generan automáticamente con la solución oficial. -
Cuando hay casos agrupados, el primer caso en el
testplan
debe tener el valor entero del grupo, y todos los demás 0. Por ejemplo:group.1 40 group.2 0 group.3 0
-
Los tests tienen que tener comentarios explicando qué condiciones están evaluando de la entrada.
-
En Karel, utilizar acentos (
algún-zumbador-en-la-mochila
,inicia-ejecución
) ysi-no
en vez desino
. -
Las dimensiones del mundo de Karel deben ser justas para que quepa la parte accesible por Karel. Es decir, no basta con poner paredes en un área pequeña si el mundo sigue siendo de 100x100. El
.in
debe reflejar el tamaño real de la parte del mundo que Karel puede visitar. (kareltest
tiene la rutinaassertTightWorldSize()
para verificar esta condición.) -
Todos los archivos de texto deben de estar en encoding UTF-8, con fin de línea estilo UNIX (
\n
oLF
, nunca\r\n
oCRLF
). -
Debes correr el linter antes de mandar un PR para asegurarnos que el estilo sea consistente y fácil de leer. Para correrlo localmente puedes ejecutar:
./utils/lint fix --all
Cambiar el directorio a la raíz del proyecto.
La primera vez necesitas bajar el submódulo de utilidades para omegaUp:
git submodule init && git submodule update
Instala Python 3, pipenv, y la versión más reciente de Docker.
Después, corre
./utils/lint fix --all
cd utils
pipenv install
pipenv run python3 runtests.py --all