1 / 8
Jan 2015

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&nbsp;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:&nbsp;%.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&nbsp;of&nbsp;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, "&lt;");
		else if(c == '>')
			fprintf(f, "&gt;");
		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ć.

  • created

    Jan '15
  • last reply

    Feb '15
  • 7

    replies

  • 1.3k

    views

  • 4

    users

23 days later

Czy nie brakuje sędziego- sędziów [ich opisu] FP - sprawdzających dobrotliwie i z większą lub mniejszą tolerancją liczby zmienno przecinkowe?
Warto też wspomnieć o sędziach-sędzim interaktywnym - chociaż, że taki też jest, ale to już każdy rzepke sobie skrobie i to już inna bajka?

Kody wszystkich oficjalnych judgy są dostępne. Ja tutaj wymieniłem te, które nie są, a mogą się przydać. Tak więc FP tutaj nie będę umieszczał.
Co do interaktywnych to póki co też nie będę umieszczał, ale w przyszłości tak. Póki co i tak nie działają zadania interaktywne, bo biblioteka spoj_interactive.h się nie kompiluje. Zgłosiłem to z wysokim priorytetem.

10 days later

Czy jest możliwość, żeby test case judgy zwracał jakieś punkty, które potem masterjudgy przechwytuje i zlicza i wypisuje jako wynik ?
Próbowałem coś wyrzucać za pomocą

fprintf(spoj_score,"...",...);

ale nic to nie daje.

PS. Żeby faktycznie zachęcić Problem Setterów do tworzenia zadań/judgy to przydałoby się stworzyć jakiś porządny tutorial (chyba, że taki gdzieś jest, a nie wiem gdzie).

@Edit:
Udało mi się znaleźć rozwiązanie. Jest nim znak końca linii.

fprintf(spoj_score,"%d\n",wynik);
16 days later

Sforkowałem master judge'a który dokleja kod do zgłoszenia w pythonie (a w nim asserty).

Czy w opcjach uploada mam wybrać C++ (bo w nim jest MJ) czy Python (bo w nim jest zgłoszenie)?

Potrzebuję pomocy

Tak się nie da. Właśnie też nie wierzyłem, że Cię zrozumiałem. Judge tylko operuje na plikach wyjściowych, wejściowych i wyników. Nie można modyfikować programu. Możesz tylko sprawdzić czy wyjście programu spełnia pewne założenia. Masz też dostęp do kodu programu, więc teoretycznie możesz sprawdzić czy jest poprawny, ale musiałbyś napisać własny kompilator.

Dziękuję kokosku. Wyjaśniłeś mi więcej niż sedno samego problemu. smile Wkleję asserty do szablonu i udostępnię użytkownikom.

Suggested Topics

Want to read more? Browse other topics in Tutoriale, poradniki or view latest topics.