Skip to content

Commit a48833a

Browse files
authored
feat(judge): add testlib checker mode (#260)
1 parent f46846d commit a48833a

8 files changed

Lines changed: 238 additions & 40 deletions

File tree

src/core/judgingthread.cpp

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <QDir>
2020
#include <QFile>
2121
#include <QFileInfo>
22+
#include <QRegularExpression>
2223
#include <QTextStream>
2324
#include <QTime>
2425
#include <QUuid>
@@ -449,7 +450,7 @@ void JudgingThread::compareRealNumbers(const QString &contestantOutput) {
449450
fclose(standardOutputFile);
450451
}
451452

452-
void JudgingThread::specialJudge(const QString &fileName) {
453+
void JudgingThread::lemonSpecialJudge(const QString &fileName) {
453454
if (! QFileInfo::exists(inputFile)) {
454455
score = 0;
455456
result = FileError;
@@ -564,6 +565,128 @@ void JudgingThread::specialJudge(const QString &fileName) {
564565
result = CorrectAnswer;
565566
}
566567

568+
void JudgingThread::testlibSpecialJudge(const QString &fileName) {
569+
if (! QFileInfo::exists(inputFile)) {
570+
score = 0;
571+
result = FileError;
572+
message = tr("Cannot find standard input file");
573+
return;
574+
}
575+
576+
if (! QFileInfo::exists(fileName)) {
577+
score = 0;
578+
result = FileError;
579+
message = tr(R"(Cannot find contestant's output file)");
580+
return;
581+
}
582+
583+
if (! QFileInfo::exists(outputFile)) {
584+
score = 0;
585+
result = FileError;
586+
message = tr("Cannot find standard output file");
587+
return;
588+
}
589+
590+
auto judge = std::make_unique<QProcess>(this);
591+
QStringList arguments;
592+
arguments << inputFile << fileName << outputFile;
593+
judge->setStandardErrorFile(workingDirectory + "_score");
594+
judge->start(Settings::dataPath() + task->getSpecialJudge(), arguments);
595+
596+
if (! judge->waitForStarted(-1)) {
597+
score = 0;
598+
result = InvalidSpecialJudge;
599+
return;
600+
}
601+
602+
QFile scoreFile(workingDirectory + "_score");
603+
604+
auto removeTempFiles = qScopeGuard([&] { scoreFile.remove(); });
605+
606+
QElapsedTimer timer;
607+
timer.start();
608+
bool flag = false;
609+
610+
while (timer.elapsed() < specialJudgeTimeLimit) {
611+
if (judge->state() != QProcess::Running) {
612+
flag = true;
613+
break;
614+
}
615+
616+
QCoreApplication::processEvents();
617+
618+
if (stopJudging) {
619+
judge->kill();
620+
return;
621+
}
622+
623+
msleep(10);
624+
}
625+
626+
if (! flag) {
627+
judge->kill();
628+
score = 0;
629+
result = SpecialJudgeTimeLimitExceeded;
630+
return;
631+
}
632+
633+
if (! scoreFile.open(QFile::ReadOnly)) {
634+
score = 0;
635+
result = InvalidSpecialJudge;
636+
return;
637+
}
638+
639+
QTextStream scoreStream(&scoreFile);
640+
message = scoreStream.readAll();
641+
642+
score = 0;
643+
644+
if (message.startsWith("ok")) {
645+
score = fullScore;
646+
}
647+
648+
if (message.startsWith("FAIL")) {
649+
score = 0;
650+
result = InvalidSpecialJudge;
651+
return;
652+
}
653+
654+
QRegularExpressionMatch m = QRegularExpression(R"(^partially correct \((\d+)\))").match(message);
655+
if (m.hasMatch()) {
656+
int val = m.captured(1).toInt();
657+
score = val * fullScore / 100;
658+
}
659+
660+
m = QRegularExpression(R"(^points ([0-9]*\.[0-9]+|[0-9]+))").match(message);
661+
if (m.hasMatch()) {
662+
double val = m.captured(1).toDouble();
663+
score = val * fullScore;
664+
}
665+
666+
if (scoreStream.status() == QTextStream::ReadCorruptData) {
667+
score = 0;
668+
result = InvalidSpecialJudge;
669+
return;
670+
}
671+
672+
scoreFile.close();
673+
674+
if (score < 0) {
675+
score = 0;
676+
result = InvalidSpecialJudge;
677+
return;
678+
}
679+
680+
if (score == 0)
681+
result = WrongAnswer;
682+
683+
if (0 < score && score < fullScore)
684+
result = PartlyCorrect;
685+
686+
if (score >= fullScore)
687+
result = CorrectAnswer;
688+
}
689+
567690
#ifdef Q_OS_WIN
568691

569692
QString getRandomString(int length) {
@@ -1152,8 +1275,12 @@ void JudgingThread::judgeOutput() {
11521275
compareRealNumbers(fileName);
11531276
break;
11541277

1155-
case Task::SpecialJudgeMode:
1156-
specialJudge(fileName);
1278+
case Task::LemonSpecialJudgeMode:
1279+
lemonSpecialJudge(fileName);
1280+
break;
1281+
1282+
case Task::TestlibSpecialJudgeMode:
1283+
testlibSpecialJudge(fileName);
11571284
break;
11581285
}
11591286
}
@@ -1227,8 +1354,12 @@ void JudgingThread::judgeAnswersOnlyTask() {
12271354
compareRealNumbers(answerFile);
12281355
break;
12291356

1230-
case Task::SpecialJudgeMode:
1231-
specialJudge(answerFile);
1357+
case Task::LemonSpecialJudgeMode:
1358+
lemonSpecialJudge(answerFile);
1359+
break;
1360+
1361+
case Task::TestlibSpecialJudgeMode:
1362+
testlibSpecialJudge(answerFile);
12321363
break;
12331364
}
12341365
}

src/core/judgingthread.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ class JudgingThread : public QThread {
8181
void compareIgnoreSpaces(const QString &);
8282
void compareWithDiff(const QString &);
8383
void compareRealNumbers(const QString &);
84-
void specialJudge(const QString &);
84+
void lemonSpecialJudge(const QString &);
85+
void testlibSpecialJudge(const QString &);
8586
void runProgram();
8687
void judgeOutput();
8788
void judgeTraditionalTask();

src/core/task.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class Task : public QObject {
2525
IgnoreSpacesMode,
2626
ExternalToolMode,
2727
RealNumberMode,
28-
SpecialJudgeMode
28+
LemonSpecialJudgeMode,
29+
TestlibSpecialJudgeMode
2930
};
3031

3132
explicit Task(QObject *parent = nullptr, TaskType taskType = Traditional,

0 commit comments

Comments
 (0)