Problema de formato (ZIP manual)
Esta página es para quienes tienen experiencia en resolver problemas y necesitan crear un problema a mano.
.zip, o editar un omegaUp ya implementado, porque necesitan algo que
Las herramientas de apuntar y hacer clic no exponen: problemas de Karel, tareas interactivas,
un validador personalizado o un control preciso sobre agrupaciones y pesos. si lo eres
recién está comenzando, o su problema es una simple tarea de "leer entrada, imprimir salida",
utilice el Creador de problemas (CDP) en su lugar y
Ahórrese el embalaje: hay un tutorial de CDP en YouTube.
Tutoriales en vídeo
Si sigue la ruta manual, será útil ver a alguien hacerlo primero: parte 1 y parte 2 del manual tutorial de creación de problemas.
Lo más importante que debes internalizar antes de construir cualquier cosa: el
.zip que subes no es lo que almacena omegaUp. Cuando subes, omegaUp's
gitserver (el servicio Go en omegaup/gitserver
que mantiene cada problema como su propio repositorio git) descomprime el archivo, lee
su cases/, su testplan opcional y cualquier settings.json que incluya,
y lo compila todo en un settings.json canónico que el
la clasificadora realmente consume. testplan y cualquier settings.json parcial que haya enviado
se eliminan después de plegarlos, precisamente porque ahora son redundantes
con el archivo generado (ver
ziphandler.go).
Así que piense en el .zip como fuente y en el settings.json como el artefacto compilado.
que es exactamente por qué los nombres de directorio y las extensiones de archivo a continuación deben ser
letra perfecta.
Los ajustes configurables (modelo mental)
Ya sea que los configure a través de la interfaz de usuario web o los envíe en metadatos empaquetados, cada El problema conlleva el mismo puñado de perillas. Comprender lo que cada uno significa - y el veredicto que obtiene cuando se excede es lo que le permite empaquetar correctamente.
Validador: cómo se juzga la producción del concursante
El validador decide si un resultado es correcto y otorga una puntuación por caso en
[0.0, 1.0]. omegaUp envía cinco, cuyos nombres canónicos viven en
ProblemParams.php
(lado PHP) y
common/problemsettings.go
(lado de la niveladora):
token: token por token. Lee cada token (una serie de hasta 4,194,304 caracteres imprimibles contiguos: 4 MiB, elMaxTokenLengthen eltokenizer.godel corredor — separados por espacios en blanco) tanto del.outesperado como del concursante salida y requiere que las dos secuencias de tokens sean idénticas. Este es el predeterminado y lo que deseas para casi todo.token-caseless: igual quetoken, pero primero pone en minúsculas cada token, por lo queYesyyescoinciden. Busque esto cuando la capitalización no sea parte del respuesta.token-numeric— lee únicamente tokens numéricos, los interpreta como números, y los acepta cuando el valor del concursante esté dentro de un absoluto o error relativo de 1e-9 del valor esperado (elTolerancepredeterminado, también1e-9, instalado en Configuración predeterminada deProblem.php). Las dos secuencias deben tener la misma longitud. Úselo para punto flotante respuestas donde los últimos dígitos pueden tambalearse.literal(se muestra en la interfaz de usuario como "interpretar la salida estándar como puntuación"): dice la salida estándar del concursante, la analiza como un solo flotante y lo sujeta a[0.0, 1.0]para usar directamente como puntuación del caso. Es casi exclusivamente para Problemas interactivos: el proceso interactuante, no el concursante, imprime el puntuación, lo que evita que el concursante simplemente imprima1.0para hacer trampa.custom(validator.<lang>): incluye un programa que lee el salida estándar del concursante (y, si así lo desea, la entrada y salida esperada del caso) y imprime la partitura misma. Los detalles completos y los ejemplos elaborados se encuentran en Validador personalizado a continuación.
Idiomas: lo que podrá presentar el concursante
- C, C++, Java, Python, … — el concursante envía la fuente en uno de omegaUp Idiomas admitidos.
- Karel — el concursante envía un programa de Karel. Ver el Problemas de Karel para saber cómo crear los casos.
- Solo resultados: el concursante carga un
.zipde respuestas para cada caso. en lugar de un programa. Si quieres también dejarles pegar un solo caso responda como texto plano en lugar de como zip, el problema debe tener exactamente uno caso denominadoMain.in/Main.out. - No hay presentaciones: el concursante no puede enviar nada. Esto existe puramente para mostrar contenido (una lectura, una lección) dentro de un curso.
Límites de tiempo, memoria y salida
Cada uno de estos se asigna a un veredicto específico cuando el programa del concursante lo cruza,
y cada uno tiene un valor predeterminado real. El formulario de creación de problemas actualmente completa previamente estos
valores (ver
Problem.php),
y el propio DefaultLimits de la niveladora
(common/problemsettings.go)
Estoy de acuerdo con ellos, por lo que un paquete que omite límites aún se ejecuta de manera sensata:
- Límite de tiempo:
TimeLimit(ms),1000predeterminado: el tiempo máximo de CPU que El sistema operativo permite que el proceso del concursante se ejecute para cada caso antes de que lo eliminen conTLE. Este es tiempo de CPU, no de reloj de pared, por lo que es tiempo inactivo o bloqueado no cuenta en su contra. - Límite de tiempo total (pared total):
OverallWallTimeLimit(ms), predeterminado60000: el tiempo máximo real que el calificador espera por el problema completo para finalizar antes de detenerlo conTLE. Cualquier caso que no llegó a ejecutarse antes de esta fecha límite simplemente no se evalúa. Para mantener los resultados al menos algo consistente cuando esto se activa, los casos se evalúan en lexicográfico orden, por lo que los casos que se omiten son deterministas y no aleatorios. - Límite de memoria:
MemoryLimit(KiB),32768predeterminado (es decir, 32 MiB): el RAM máxima (montón + pila) que el sistema operativo permite que el programa use antes de eliminarlo conMLE. Se expresa en kibibytes, por lo que32768KiB = 32 MiB. - Límite de salida —
OutputLimit(bytes),10240predeterminado — el máximo del programa puede escribir en stdout o stderr antes de que se elimine conOLE. Para ordinario problemas de token omegaUp normalmente detecta automáticamente esto desde sus archivos.out— toma el más grande y agrega 10 KiB de espacio libre, por lo que rara vez lo configuras por mano. Pero si usas un validador personalizado debes configurarlo explícitamente, porque No existe un.outsencillo con el que comparar. - Límite de entrada:
inputLimit(bytes),10240predeterminado: la longitud máxima de el código fuente del concursante. Baja esto cuando quieras detener a la gente. de pegar una tabla de respuestas precalculada en lugar de resolver realmente la problema. - Límite de tiempo del validador:
validatorTimeLimit(ms),1000predeterminado: cuánto tiempo el evaluador espera a que un validador personalizado emita un veredicto para cada caso antes rendirse conJE. - Tiempo de pared adicional para libinteractive —
ExtraWallTime(ms),0predeterminado — cómo cuánto tiempo espera el evaluador hasta que el programa interactor termine cada caso (más allá los límites normales) antes de detenerlo conTLE. Sólo relevante para problemas interactivos.
Los validadores personalizados obtienen sus propios límites más generosos
En el momento en que cambia el validador a custom, omegaUp genera un conjunto separado
de límites para el proceso validador: actualmente 256 MiB de memoria, 30 s
Límite de tiempo de CPU, 5 s de tiempo total de pared y 10 KiB de salida
(Problem.php).
La idea es que juzgar puede darse el lujo de ser más lento y más hambriento que el
La solución del concursante.
Todo lo demás
- Fuente: atribución/origen de la declaración, mostrada a los concursantes.
- Aparece en el listado público: si el problema se puede mostrar públicamente y utilizado en concursos y cursos de otras personas.
- Enviar aclaraciones por correo electrónico: si omegaUp le envía un correo electrónico a usted (el autor) cuando El usuario pide una aclaración sobre este problema.
- Etiquetas: etiquetas de clasificación.
El diseño ZIP
Guarde todo en un archivo .zip, no en .rar, .tar.bz2, .7z o
.zx. El nombre del zip en sí no importa. Un mínimo problema de idioma
se ve así:
problem.zip
├── cases/ # Required: the .in/.out test data
│ ├── 1.in
│ ├── 1.out
│ └── …
├── statements/ # Required: at least one <locale>.markdown
│ └── es.markdown
├── solutions/ # Optional: editorial / official write-up
├── interactive/ # Optional: libinteractive bundle
├── validator.cpp # Optional: custom validator (one of validator.<lang>)
├── settings.json # Optional: pre-baked settings (usually generated for you)
└── testplan # Optional: per-case weights
frontend/tests/resources/testproblem.zip,
y hay muchos más debajo
frontend/tests/resources.
cases/ y statements/ deben ubicarse en la raíz
Es críticamente importante que cases/ y statements/ estén directamente en
la raíz del .zip, sin ninguna carpeta intermedia que los envuelva; esto
ha picado a suficientes personas como para ganarse su propio virus,
omegaup#310. En Linux/Mac el
Una forma confiable de hacerlo bien es ingresar cd al directorio del problema y ejecutar
zip -r myproblem.zip *, que comprime el contenido en lugar del contenedor
carpeta. Y como omegaUp se ejecuta en Linux, los nombres distinguen entre mayúsculas y minúsculas:
No se encontrará la carpeta llamada Cases, ni tampoco un archivo de entrada que termine
en .In en lugar de .in.
cases/
Esta carpeta contiene todos los casos de prueba como archivos .in/.out emparejados. Los nombres base
debe coincidir — 1.in con 1.out, hola.in con hola.out — pero el nombre base
en sí mismo es arbitrario. Internamente, el implementador solo acepta archivos que coincidan con el
expresión regular ^cases/([^/]+)\.in$
(ziphandler.go),
y si la carpeta falta o está vacía, la carga falla por completo con
cases/ directory missing or empty
(ziphandler.go).
Cada .in que espera envíos debe tener un .out coincidente, o el despliegue
errores con failed to find the output file for cases/<name>.
El . (punto) en un nombre de caso está reservado para agrupación. No coloque un punto en un
nombre del caso a menos que quiera agrupar: el texto antes del primer punto se convierte en el
nombre del grupo (strings.SplitN(caseName, ".", 2)[0] en
common/problemsettings.go).
Entonces grupo1.caso1.in, grupo1.caso1.out, grupo1.caso2.in, grupo1.caso2.out
forme un grupo (grupo1) con dos casos.
Los casos agrupados existen porque a veces el conjunto de respuestas plausibles es pequeño y no desea que un concursante obtenga un crédito parcial por una suposición afortunada: ganar un Los puntos del grupo debes resolver cada caso en ese grupo. No hay límite en el número de grupos, y los grupos pueden tener diferente número de casos.
Tampoco hay un límite estricto en el número de casos, pero mantenga el caso total
carga útil inferior a ~100 MB. Más casos significa que cada envío tarda más en calificarse,
y en un concurso en vivo que se traduce directamente en tiempos de espera en las colas, especialmente
doloroso cuando una solución lenta, vinculada a TLE, está por delante de todos los demás en el
cola.
statements/
Esto contiene la declaración del problema en Markdown (el mismo estilo que usa Wikipedia), uno
archivo por configuración regional: es.markdown, en.markdown, pt.markdown. Al menos uno es
requerido. Puede obtener una vista previa exactamente de cómo se representarán Markdown y LaTeX en
omegaup.com/redaccion.php — por favor, hágalo
esto y confirme que las tablas de entrada/salida se ven bien, porque una declaración confusa
Es una experiencia miserable a mitad de la competencia.
LaTeX es totalmente compatible. Envuelva los nombres de las variables en $…$: escriba $n$, $x$,
$x_i$ para un subíndice, para que se destaquen de la prosa y los concursantes puedan encontrar
ellos de un vistazo. Se lee mejor y evita ambigüedades.
solutions/
Estructuralmente idéntico a statements/: el artículo oficial de la solución en
Markdown, nombrado según la configuración regional (es.markdown y traducciones en.markdown,
pt.markdown). El paquete en
testproblem.zip
Incluye un ejemplo de soluciones.
interactive/ (opcional)
Problemas interactivos: donde el programa del concursante habla de un lado a otro con un
juzgar el proceso en lugar de leer una entrada fija, debe construirse con
libinteractive; esa página documenta el
Formato de la interfaz .idl y cómo se generan las calzas. Para una completa y real
referencia de cómo se estructura el zip de un problema interactivo, utilice
Cueva de IOI 2013 como plantilla.
Una comodidad que el implementador maneja por usted: casos de muestra de libinteractive en
interactive/examples/ no necesita un .out — gitserver genera un vacío
uno automáticamente
(ziphandler.go).
Validador personalizado (validator.<lang>)
Cuando la comparación de tokens no es suficiente: múltiples respuestas correctas, juez especial
puntuación, crédito parcial: envíe exactamente un archivo llamado validator.<lang> a
la raíz del zip, donde <lang> es uno de c, cpp, java, p (Pascal),
o py. Solo necesitas un validador y es independiente del concursante
idioma.
Aquí está el contrato exacto y vale la pena hacerlo bien:
- Su validador lee el resultado del concursante en su propia entrada estándar — simple
scanf/cin/input(). Mentalmente, el evaluador ejecuta el equivalente de./contestant < data.in | ./validator <casename>, donde<casename>es el Nombre.indel caso actual sin la extensión. - Puede abrir un archivo llamado literalmente
data.in: la misma entrada que se envió a el concursante y un archivo llamadodata.out: el resultado esperado emparejado con esedata.in. Lea cualquiera de los dos, ambos o ninguno. - Debe imprimir un solo flotante en
[0.0, 1.0]en la salida estándar: la fracción del caso el concursante acertó. No imprimir nada →JE. Imprimir menos de 0 → el la puntuación se fija en 0; imprimir más de 1 → fijado a 1. - El validador se ejecuta dentro del mismo entorno limitado que los programas de los concursantes. Si el
el propio validador se porta mal (
WA,RFE,RTE,…), se juzga el envíoJE: por lo tanto, un validador con errores falla ruidosamente en lugar de obtener una puntuación errónea en silencio. - De todos modos debes enviar archivos
.outaunque no se vayan a utilizar para comparación. Los archivos vacíos están bien; simplemente tienen que existir para que el emparejamiento de casos tiene éxito.
Un validador para sumas (leer dos
números enteros, imprima su suma) en C++ 17; observe cómo se leen los a y b originales
de data.in, la suma esperada de data.out, la respuesta del concursante de
stdin e imprime 1.0 o 0.0:
#include <iostream>
#include <fstream>
int main() {
// read "data.in" to get the original input.
int64_t a, b;
{
std::ifstream original_input("data.in", std::ifstream::in);
original_input >> a >> b;
}
// you can store anything that helps you evaluate in "data.out".
int64_t sum;
{
std::ifstream original_output("data.out", std::ifstream::in);
original_output >> sum;
}
// read standard input to get the contestant's output.
int64_t contestant_sum;
if (!(std::cin >> contestant_sum)) {
// anything you print to cerr is ignored, but it's useful for debugging.
std::cerr << "Error reading the contestant's output\n";
std::cout << 0.0 << '\n';
return 0;
}
// determine whether the answer is incorrect.
if (sum != contestant_sum && sum != a + b) {
std::cerr << "Incorrect output\n";
std::cout << 0.0 << '\n';
return 0;
}
// If execution reaches here, the contestant's output is correct.
std::cout << 1.0 << '\n';
return 0;
}
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import logging
import sys
def _main():
# read "data.in" to get the original input.
with open('data.in', 'r') as f:
a, b = [int(x) for x in f.read().strip().split()]
# you can store anything that helps you evaluate in "data.out".
with open('data.out', 'r') as f:
expected_sum = int(f.read().strip())
score = 0
try:
# Read the contestant's output.
contestant_sum = int(input().strip())
# Determine whether the output is incorrect.
if contestant_sum not in (expected_sum, a + b):
# Anything printed to sys.stderr is ignored, but useful for debugging.
print('Incorrect output', file=sys.stderr)
return
# If execution reaches here, the contestant's output is correct.
score = 1
except:
logging.exception("Error reading the contestant's output")
finally:
print(score)
if __name__ == '__main__':
_main()
testplan (opcional)
De forma predeterminada cada caso vale 1/number-of-cases: el implementador asigna cada
caso un peso de 1/1 y el clasificador normaliza todos los pesos para que sumen 1
(AddCaseName(caseName, big.NewRat(1, 1), false) en
ziphandler.go,
pesos divididos por el total en
common/literalinput.go).
Cuando desee que los casos se ponderen de manera desigual, suelte un archivo llamado testplan (no
extensión) en la raíz del zip, una línea por caso: el nombre del archivo del caso
sin la extensión, espacios en blanco, luego el número de puntos. por un problema
con estuches cases/caso1.in, cases/grupo2.caso1.in, cases/grupo2.caso2.in:
caso1 5
grupo2.caso1 10
grupo2.caso2 0
NewCaseWeightMappingFromTestplan)
realmente se aplica, haciendo coincidir cada línea con
^\s*([^#[:space:]]+)\s+([0-9.]+)\s*$:
- Sin espacios en los nombres de los archivos de casos: el token del nombre del caso no puede contener espacio en blanco.
#inicia un comentario: una línea cuyo primer carácter que no sea un espacio es#(y cualquier línea que no coincide con el patrón) se omite, para que pueda anotar su plan de prueba.- El
testplany el.zipdeben concordar en el conjunto de cajas. gitserver se ejecuta una diferencia simétrica en ambos sentidos (ziphandler.go): un caso en el plan de prueba pero que falta encases/falla contestplan missing case "<name>", y un caso encases/pero ausente en el El plan de prueba falla con.zip missing case "<name>". No se puede especificar a medias.
Para calificar a todo un grupo sin dividir los puntos entre sus casos, el
La convención es poner la puntuación total del grupo en el primer caso y 0 en todos.
los demás, como lo hace grupo2.caso1 10 / grupo2.caso2 0 arriba.
Esto interactúa con la política de puntuación del grupo, uno de los dos valores en
ProblemParams.php:
sum-if-not-zero (valor predeterminado: un grupo obtiene la suma de las puntuaciones de sus casos,
pero solo si son todos distintos de cero) o min (el grupo obtiene el mínimo de
las puntuaciones de sus casos multiplicadas por el peso del grupo). El valor predeterminado es por qué el
La convención de "puntos en el primer caso, cero en el resto" funciona: resuelve el todo
agrupa y recoges todo el peso; Se pierde cualquier caso y el grupo colapsa.
cero.
settings.json (generalmente generado, ocasionalmente escrito a mano)
La mayoría de las veces nunca escribirás este archivo: es el artefacto compilado de gitserver.
produce desde su cases/, testplan y límites, ordenados en
ziphandler.go.
Su forma es la estructura ProblemSettings.
(common/problemsettings.go):
un bloque Limits, un bloque Validator (Name, Tolerance, opcional
GroupScorePolicy, validador personalizado opcional Limits), una matriz de grupos Cases
cada uno con sus casos ponderados y, para problemas interactivos, un Interactive
bloque. Si envías tu propio settings.json, gitserver lo lee y aún así
permite que un testplan anule los pesos de la caja encima. De cualquier manera, sólo el
El settings.json generado sobrevive en el repositorio de problemas implementado.
Imágenes
omegaUp tiene soporte de imágenes nativas :). Para incrustar una imagen en una declaración, agregue el
archivo de imagen a su zip dentro de statements/ y haga referencia a él desde su
es.markdown con Markdown ordinario:

Ejemplo de zips
Los zips que omegaUp utiliza en sus propias pruebas son las mejores plantillas para copiar:
frontend/tests/resources.
Problemas de Karel
Primero, prueba karel.js: convierte casos para usted y es mucho menos problemático que lo que sigue.
Si ya tiene sus casos y prefiere no volver a hacerlos en karel.js, el
Los pasos siguientes son para Windows y suponemos que tienes Python 2.7 instalado y encendido.
su PATH (la ruta de instalación predeterminada suele ser C:\Python27); verificar que puedes
ejecute python desde la consola de DOS antes de comenzar.
- Tenga estos archivos a mano:
el kit de herramientas de Karel —
karel.exe(ejecuta una solución contra un mundo),kcl.exe(la solución compilador), el script Pythonkarel_mdo_convert.pyy el contenedorkarel-to-omegaup.batque los une. - Coloque sus casos MDO y KEC en una carpeta. Para generarlos puedes utilizar el Karel, creador de casos, de KarelOMI.zip.
- También necesitas tu solución. Si programa en Java, déle a la solución un
Extensión
.JS(por lo quekcl.exela interpreta como karel-java); para Pascal, use.PAS(karel-pascal). - Coloque los archivos ejecutables, el script Python y el
.bat, todos en la misma carpeta. - Ejecute
karel-to-omegaup.batsin argumentos y le solicitará el ruta de la solución y la ruta de los casos, o páselos en la línea de comando: cite rutas que contengan espacios:
karel-to-omegaup.bat "karel vs chuzpa\solucion.js" "karel vs chuzpa\casos"
kcl.exe.
(produciendo un .KX), luego construye los mundos .IN a partir de cada .MDO en los casos
carpeta. Tenga en cuenta que el convertidor de Python necesita que exista el .KEC coincidente: para
caso1.MDO también debes tener caso1.KEC. De aquellos extrae buscas,
orientación y posición en cada .IN.
7. Luego ejecuta karel.exe con cada .IN generado y el .KX compilado.
solución para producir el .OUT correspondiente, por lo que una solución correcta es esencial,
ya que las salidas son tan correctas como son.
8. El .bat coloca una carpeta cases (con los pares .IN/.OUT) dentro de su
directorio de casos.
9. Finalmente, agregue una carpeta statements con es.markdown y comprímala exactamente como
Tendrías un problema de idioma.
Cómo se junta todo
Para cerrar el ciclo: cuando subes, gitserver
ziphandler.go
descomprime el archivo, valida que cases/ existe y que cada caso enviado tiene
su .out, pliega testplan/settings.json en un settings.json canónico,
confirma todo como una nueva revisión del repositorio git del problema, y
elimina el ahora redundante testplan. En el momento de la calificación, la interfaz PHP
(\OmegaUp\Controllers\Run::apiCreate →
\OmegaUp\Grader::grade)
entrega el envío al evaluador Go a través de HTTP, que dice que settings.json,
normaliza los pesos de los casos para que sumen 1, ejecuta cada caso bajo el sandbox contra
sus límites, aplica el validador y acumula las puntuaciones por caso a través del
política de puntuación del grupo. Cada ruta y extensión en este documento existe para hacer que
la canalización se resuelve correctamente, por lo que es importante lograr que sean exactamente correctas.
Documentación relacionada
- Creando problemas: el flujo de trabajo de creación y las rutas de la interfaz de usuario
- Veredictos: qué significan
AC,TLE,MLE,OLE,JEy el resto