By zachęcić Problem Setterów do tworzenia nowych zadań umieszczę tutaj wszystkie judge, które mam tak by dało się je łatwo modyfikować.
Masterjudge
Modyfikacja judge'a nr 1001, który wyświetla wynik jako % rozwiązanych testów, ale który daje WA jeśli rozwiąże się zbyt małą liczbę testów. Domyślnie trzeba zaliczyć przynajmniej 1 test.
#include <spoj.h>
#include <cstdio>
const int MIN_TESTS = 1;
int main()
{
spoj_init();
int test, sig, mem, memMax = 0, tc = 0;
double score, scoreAll = 0, time, timeAll = 0;
char status[4];
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(status[0] == 'A')
{
if(mem > memMax)
memMax = mem;
timeAll += time;
++scoreAll;
}
++tc;
}
if(scoreAll >= MIN_TESTS)
fprintf(spoj_score, "AC %.2lf 0 %lf %d\n", 100.0*scoreAll/tc, timeAll, memMax);
else
fprintf(spoj_score, "SE 0 0 %lf %d\n", timeAll, memMax);
}
[color=#FF0000]NEW[/color] [b]Wyświetlanie, dla którego testu program dostał NAC (nie AC).[/b] Możemy chcieć ułatwić userom lekko zadanie i pokazać im jaki nr ma pierwszy test, na którym nie dostali AC. Dzięki temu będą widzieć czy poprawili program chociaż trochę, czy nie.
#include <spoj.h>
#include <cstdio>
int main()
{
spoj_init();
int test, sig, mem, memMax = 0, tc = 0;
double score, scoreAll = 0.0, time, timeAll = 0.0;
char status[4];
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(mem > memMax)
memMax = mem;
scoreAll += score;
if(timeAll >= 0)
{
if(time >= 0)
timeAll += time;
else
timeAll = -1;
}
if(status[0] != 'A')
{
if(status[0] == 'C' && status[1] == 'E')
fprintf(spoj_score, "CE 0 %d %lf %d\n", sig, timeAll, memMax);
else
fprintf(spoj_score, "%s 0 %d %lf %d (#%d)\n", status, sig, timeMax, memMax, test);
return 0;
}
++tc;
}
fprintf(spoj_score, "AC %lf 0 %lf %d\n", scoreAll/tc, timeAll, memMax);
return 0;
}
Wynikiem jest długość kodu. Być może spytacie po co go tu wrzucam, skoro jest już judge, który zwraca długość kodu. Ten judge to jednak test case judge. Oznacza to więc, że liczy on długość kodu dla każdego testu osobno (a kod się nie zmienia). Tak więc dla zadań na skracanie kodu powinno się użyć tego masterjudge'a + zwykłego test case judge'a nr 1 dla każdego testu.
#include <spoj.h>
#include <cstdio>
int main()
{
spoj_init();
int test, sig, mem, memMax = 0, tc = 0;
double score, scoreAll = 0, time, timeAll = 0;
char status[4];
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(mem > memMax)
memMax = mem;
scoreAll += score;
if(timeAll >= 0)
if(time >= 0)
timeAll += time;
else
timeAll = -1;
if(status[0] != 'A')
{
fprintf(spoj_score, "%s 0 %d %lf %d\n", status, sig, timeAll, memMax);
return 0;
}
++tc;
}
fprintf(spoj_score, "AC %u 0 %lf %d\n", spoj_file_length(spoj_t_src), timeAll, memMax);
return 0;
}
Zakazywanie ciągów znaków. Możemy chcieć zakazać np. szybkiego IO albo użycia jakiejś funkcji, by zmusić userów by sami ją zaimplementowali. Wystarczy lekko zmodyfikować tego masterjudge'a poprzez zmianę 2 tablic: keywords oraz occurrences. Dodatkowa informacja - judge zabrania korzystania z hasza w linijkach z #define (bo można było obejść zakaz korzystania z wybranych ciągów znaków).
#include <spoj.h>
#include <cstdio>
#include <cstring>
const int MAX_LENGTH = 50000;
int main()
{
spoj_init();
int test, sig, mem, memMax = 0, len = 0, ch;
bool ok = true;
double score, time, timeAll = 0;
char status[4];
char keywords[][7] = {"read", "getc", "putc", "write", "puts", "gets"};
int occurrences[] = {0, 0, 0, 0, 0, 0};
int NO_KEYWORDS = sizeof(occurrences)/sizeof(int);
char program[MAX_LENGTH+9];
// reading user's program
while((ch = getc(spoj_t_src)) != EOF)
program[len++] = ch;
// checking for banned keywords
for(int i = 0; i < len; ++i)
{
// for each banned keywords
for(int j = 0; j < NO_KEYWORDS; ++j)
// if banned keyword was found and it occurred too many times return WA
if(!strncmp(program+i, keywords[j], strlen(keywords[j])) && !occurrences[j]--)
{
ok = false;
break;
}
// we're banning '#' from lines with define
if(!strncmp(program+i, "define", 6))
for(int j = i+6; j < len && program[j] != '\n'; ++j)
if(program[j] == '#')
{
ok = false;
break;
}
if(!ok)
break;
}
if(!ok)
{
fprintf(spoj_score, "WA 0 0 0 0 banned keyword\n");
return 0;
}
// while there are judges' results
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
// print the result description to ps_info
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(mem > memMax)
memMax = mem;
if(timeAll >= 0)
if(time >= 0)
timeAll += time;
else
timeAll = -1;
if(status[0] != 'A')
{
fprintf(spoj_score, "%s 0 %d %lf %d\n", status, sig, timeAll, memMax);
return 0;
}
}
fprintf(spoj_score, "AC 0 0 %lf %d\n", timeAll, memMax);
return 0;
}
Wyświetlanie tabeli z wynikami użytkownikowi. Czasami chcemy ułatwić userom zadanie i dla każdego testu wyświetlić im status z czasem wykonania, limitem czasu, itp. Jest to judge Dra Michała Małafiejskiego, zmodyfikowany przeze mnie. Ponownie, wystarczy zmienić 2 tablice: timelimit oraz scorept.
#include <stdio.h>
#include <spoj.h>
#include <errno.h>
#include <string.h>
using namespace std;
const char *SIG[] = {"NZEC", "", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO", "SIGPWR", "SIGSYS"};
int main()
{
spoj_init();
double scoreAll = 0.0, timeSum = 0.0;
int memMax = 0;
int timelimit[] = {1, 15, 32};
double scorept[] = {2.0, 8.0, 100.0};
int NOT = sizeof(timelimit)/sizeof(int);
int test[NOT], sig[NOT];
char status[NOT][4], mstatus[NOT][64];
double score[NOT], time[NOT];
double scorelimit = 2.0;
int mem[NOT];
for(int i = 0; fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test[i], status[i], &score[i], &sig[i], &time[i], &mem[i]) == 6; ++i)
{
if(status[i][0] == 'A')
{
scoreAll += scorept[i];
timeSum += time[i];
if(memMax < mem[i])
memMax = mem[i];
}
strcpy(mstatus[i], "internal error");
if(status[i][0] == 'A')
strcpy(mstatus[i], "accepted");
if(status[i][0] == 'R')
{
strcpy(mstatus[i], "runtime error (");
strcat(mstatus[i], SIG[sig[i]+1]);
strcat(mstatus[i], ")");
}
if(status[i][0] == 'T')
strcpy(mstatus[i], "time limit exceeded");
if(status[i][0] == 'W')
strcpy(mstatus[i], "wrong answer");
}
fprintf(spoj_u_info, "<table class='navigation' width='99\%'>");
fprintf(spoj_u_info, "<tr class='headerrow'>");
fprintf(spoj_u_info, "<th class='headerrowleft'><font color=yellow>Test case</font></th><th><font color=yellow>Points</font></th>");
fprintf(spoj_u_info, "<th class='headerrowright'><font color=yellow>Result<br>(running time : time limit)</font></th>");
for(int i = 0; i < NOT; ++i)
{
fprintf(spoj_u_info, "<tr class='problemrow'> <td class=%s align=center width=15%><font color=navy>%d</font></td> <td class=%s align=center width=15%><font color=navy>%.1lf pts</font></td>", (status[i][0] == 'A') ? "kol3" : "kol", i+1, (status[i][0]=='A') ? "kol3" : "kol", scorept[i]);
if(time[i] >= 0)
fprintf(spoj_u_info, "<td class=%s align=center><font color=black>%s<br>(%.2lfs : %ds)</font></td>", (status[i][0] == 'A') ? "kol3" : "kol", mstatus[i], time[i], timelimit[i]);
else
fprintf(spoj_u_info, "<td class=%s align=center><font color=black>%s<br>(> %ds : %ds)</font></td></tr>", (status[i][0] == 'A') ? "kol3" : "kol", mstatus[i], 2*timelimit[i], timelimit[i]);
}
if(scoreAll >= scorelimit)
fprintf(spoj_u_info, "<tr class='problemrow'><td align=center colspan=3 style='background-color: navy; font-size: 16px; font-weight: bold;'><font color=yellow><b>Score: %.0lf</b></font></td></tr>", scoreAll);
else
fprintf(spoj_u_info, "<tr class='problemrow'><td align=center colspan=3 style='background-color: navy; font-size: 16px; font-weight: bold;'><font color=orange><b>Score: %.0lf</b></font></tr></td>", scoreAll);
fprintf(spoj_u_info, "</table>");
if(scoreAll >= scorelimit)
fprintf(spoj_score, "AC %.1lf 0 %lf %d\n", scoreAll, timeSum, memMax);
else
fprintf(spoj_score, "SE %.1lf 0 %lf %d (threshold: %.0lf)\n", scoreAll, timeSum, memMax, scorelimit);
}
Dopasowanie kodu do wzorca. Jeżeli jesteśmy nauczycielami, to możemy chcieć stworzyć zadanie, w którym uczniowie wypełnią tylko ciało jednej funkcji. Wystarczy wtedy podać w tym programie początek i koniec programu wzorcowego, ze środkiem do uzupełnienia przez użytkowników. Jeśli chce się podać więcej fragmentów do uzupełnienia, to niestety trzeba trochę zmodyfikować owego judge'a.
#include <spoj.h>
#include <cstdio>
#include <string>
#include <cstdlib>
using namespace std;
bool isWhite(int ch)
{
return ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r' || ch == EOF;
}
int main()
{
spoj_init();
int test, sig, mem, memMax = 0, ch, last = ' ', i = 0;
double score, time, timeAll = 0;
char status[4];
string beginning = "#include<stdio.h> int main() { int x, y; scanf(\"%d %d\", &x, &y);";
string ending = "printf(\"%d %d\", x, y); return 0; } ";
string program = "";
do
{
ch = getc(spoj_t_src);
if(isWhite(ch))
{
if(!isWhite(last))
program += ' ';
}
else
program += (char)ch;
last = ch;
} while(ch != EOF);
fprintf(spoj_p_info, "%s\n", program.c_str());
fprintf(spoj_p_info, "%s\n", beginning.c_str());
fprintf(spoj_p_info, "%s\n", ending.c_str());
if(program.substr(0, beginning.size()) != beginning || program.substr(program.size()-ending.size()) != ending)
{
fprintf(spoj_score, "WA 0 0 0 0 out of pattern\n");
return 0;
}
// while there are judges' results
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
// print the result description to ps_info
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(mem > memMax)
memMax = mem;
if(timeAll >= 0)
if(time >= 0)
timeAll += time;
else
timeAll = -1;
if(status[0] != 'A')
{
fprintf(spoj_score, "%s 0 %d %lf %d\n", status, sig, timeAll, memMax);
return 0;
}
}
fprintf(spoj_score, "AC 0 0 %lf %d\n", timeAll, memMax);
return 0;
}
Ograniczenie pamięci. Niestety w zadaniu nie ma pola, które pozwoliłoby nam zmodyfikować limit pamięci. Można to jednak zrobić zmieniając w tym judge'u 1 liczbę (w kB). W przypadku jego przekroczenia pojawi się status SIGKILL, czyli taki jak w przypadku przekroczenia standardowego limitu pamięci. Pamiętajcie, żeby podać w treści zadania, że limit pamięci jest inny!
#include <spoj.h>
#include <cstdio>
const int MEM_MAX = 32768;
int main()
{
spoj_init();
int test, sig, mem, memMax = 0;
double score, time, timeAll = 0;
char status[4];
// while there are judges' results
while(fscanf(spoj_p_in, "%d %3s %lf %d %lf %d\n", &test, status, &score, &sig, &time, &mem) == 6)
{
// print the result description to ps_info
fprintf(spoj_p_info, "test %d - %s (score=%lf, sig=%d, time=%lf, mem=%d)\n", test, status, score, sig, time, mem);
if(mem > memMax)
memMax = mem;
if(memMax > MEM_MAX || !memMax)
{
fprintf(spoj_score, "RE 0 9 %lf 96\n", timeAll);
return 0;
}
if(timeAll >= 0)
if(time >= 0)
timeAll += time;
else
timeAll = -1;
if(status[0] != 'A')
{
fprintf(spoj_score, "%s 0 %d %lf %d\n", status, sig, timeAll, memMax);
return 0;
}
}
fprintf(spoj_score, "AC 0 0 %lf %d\n", timeAll, memMax);
return 0;
}
Test case judge
Wyświetlanie każdego testu użytkownikowi nr 2. Ten judge zaznacza na czerwono wyjście użytkownika od pierwszego znaku, który się różni z wyjściem autora. Tutaj każdy test jest wypisany w osobnej tabeli, więc nie trzeba do niego własnego masterjudge'a. Nie jestem autorem tego judge'a (nie wiem czemu ale wydaje mi się, że jest nim Dr Krzysztof Ocetkiewicz, jeśli się mylę to proszę o informację).
#include "spoj.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define MAX 10000
using namespace std;
// <0-255> normal, - 1 white, -2 eof
int getChar(FILE *f, bool ignWhite)
{
bool white;
int ch;
do
{
if((ch = getc(f)) == EOF)
return -2;
if(ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r')
white = true;
else
white = false;
} while(ignWhite && white);
if(white)
return -1;
return ch;
}
int check()
{
// read and compare author's and user's outputs
int ch1 = getChar(spoj_t_out, true);
int ch2 = getChar(spoj_p_out, true);
while(ch1 == ch2)
{
// if EOF return AC
if(ch1 == -2)
return SPOJ_RV_POSITIVE;
bool ignWhite;
// if last character was white, ignore next whites
if(ch1 == -1)
ignWhite = true;
// else don't ignore whites because there has to be spaces between strings and numbers
else
ignWhite = false;
ch1 = getChar(spoj_t_out, ignWhite);
ch2 = getChar(spoj_p_out, ignWhite);
}
if(ch1 == -2 && ch2 == -1 && getChar(spoj_p_out, true) == -2)
return SPOJ_RV_POSITIVE;
if(ch2 == -2 && ch1 == -1 && getChar(spoj_t_out, true) == -2)
return SPOJ_RV_POSITIVE;
return SPOJ_RV_NEGATIVE;
}
// prints char array str to file f
void fprint_safe(FILE *f, char *str, int errorpos = -1, char *errorStart = NULL, char *errorEnd = NULL)
{
int pos = 0;
while(*str)
{
// if from this position outputs start to differ print in HTML that the remaining text should be printed in red
if(pos == errorpos)
fprintf(f, "%s", errorStart);
char c = *(str++);
if(c == '<')
fprintf(f, "<");
else if(c == '>')
fprintf(f, ">");
else
fputc(c, f);
pos++;
}
// if outputs differ at any position print in HTML to stop writing in red
if(errorpos != -1)
fprintf(f, "%s", errorEnd);
}
int main()
{
spoj_init();
int rv = check();
char input[MAX+1], output[MAX+1], answer[MAX+1];
int output_error_pos = -1, answer_error_pos = -1;
// calculate first positions on which outputs differ
if(rv == SPOJ_RV_NEGATIVE)
{
output_error_pos = ftell(spoj_p_out)-1;
answer_error_pos = ftell(spoj_t_out)-1;
}
// setting pointers back to files' beginnings
fseek(spoj_t_out, 0, SEEK_SET);
fseek(spoj_p_out, 0, SEEK_SET);
// read input and two outputs with marking their ends
input[fread(input, 1, MAX, spoj_p_in)] = output[fread(output, 1, MAX, spoj_p_out)] = answer[fread(answer, 1, MAX, spoj_t_out)] = 0;
// print the first two rows of the table
fprintf(spoj_u_info, "<p><table class=\"problems\" width=\"100%\"><tr class=\"headerrow\"><th width=\"33%\" class=\"headerrowleft\"></th><th width=\"33%\">%s</th><th width=\"33%\" class=\"headerrowright\"></th></tr>", (rv == SPOJ_RV_POSITIVE) ? "<h3>Test accepted!</h3>" : "<h3>Wrong answer</h3>");
fprintf(spoj_u_info, "<tr class=\"row\">");
fprintf(spoj_u_info, "<td><b>Input data</b></td><td><b>Expected output</b></td><td><b>Your progam's output</b></td></tr>");
fprintf(spoj_u_info, "<tr class=\"row\" valign=\"top\">");
// print the first cell of the last row (with input)
fprintf(spoj_u_info, "<td align=left><pre>");
fprint_safe(spoj_u_info, input);
fprintf(spoj_u_info, "</pre></td>");
// print the second cell of the last row (with expected output)
fprintf(spoj_u_info, "<td align=left><pre>");
fprint_safe(spoj_u_info, output, output_error_pos, "<font color=\"#FF0000\">", "</font>");
fprintf(spoj_u_info, "</pre></td>");
// print the last cell of the last row (with user's output)
fprintf(spoj_u_info, "<td align=left><pre>");
fprint_safe(spoj_u_info, answer, answer_error_pos, "<font color=\"#FF0000\">", "</font>");
fprintf(spoj_u_info, "</pre></td>");
fprintf(spoj_u_info, "</tr></table>");
// return the status
return rv;
}
To by było na tyle. Wszystkie uwagi mile widziane. Jeśli ktoś chce ulepszyć jakoś któregoś z powyższych judgy to proszę śmiało je wklejać poniżej. Jeśli ktoś ma pomysł na innego przydatnego judge'a to też proszę pisać.