mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
judge: hack
This commit is contained in:
1 parent
7f967060cd
commit
15ea8232a8
5 files changed
+165
-49
No files matched your search
+1
-1
@@ -73,7 +73,7 @@ rules:
|
|||||||
- selector: enum
|
- selector: enum
|
||||||
format: [PascalCase]
|
format: [PascalCase]
|
||||||
- selector: [class, interface, typeAlias, enumMember]
|
- selector: [class, interface, typeAlias, enumMember]
|
||||||
format: [PascalCase, camelCase]
|
format: [PascalCase, camelCase, UPPER_CASE]
|
||||||
- selector: typeParameter
|
- selector: typeParameter
|
||||||
format: [camelCase, PascalCase, UPPER_CASE]
|
format: [camelCase, PascalCase, UPPER_CASE]
|
||||||
|
|
||||||
|
|||||||
@@ -42,15 +42,12 @@ export default async function compile(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
|
const testlibFile = {
|
||||||
|
src: findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h')
|
||||||
|
};
|
||||||
|
|
||||||
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
|
const guessLanguage = (filename: string, getLang: Function) => {
|
||||||
if (['default', 'strict'].includes(checkerType)) {
|
const s = filename.replace('@', '.').split('.');
|
||||||
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
|
||||||
}
|
|
||||||
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
|
|
||||||
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc };
|
|
||||||
const s = checker.replace('@', '.').split('.');
|
|
||||||
let lang;
|
let lang;
|
||||||
let langId = s.pop();
|
let langId = s.pop();
|
||||||
while (s.length) {
|
while (s.length) {
|
||||||
@@ -58,7 +55,24 @@ export async function compileChecker(getLang: Function, checkerType: string, che
|
|||||||
if (lang) break;
|
if (lang) break;
|
||||||
langId = `${s.pop()}.${langId}`;
|
langId = `${s.pop()}.${langId}`;
|
||||||
}
|
}
|
||||||
|
return lang;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
|
||||||
|
if (['default', 'strict'].includes(checkerType)) {
|
||||||
|
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
||||||
|
}
|
||||||
|
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
|
||||||
|
if (checkerType === 'testlib') copyIn['testlib.h'] = testlibFile;
|
||||||
|
const lang = guessLanguage(checker, getLang);
|
||||||
if (!lang) throw new FormatError('Unknown checker language.');
|
if (!lang) throw new FormatError('Unknown checker language.');
|
||||||
// TODO cache compiled checker
|
// TODO cache compiled checker
|
||||||
return await compile(lang, { src: checker }, copyIn);
|
return await compile(lang, { src: checker }, copyIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function compileValidator(getLang: Function, validator: string, copyIn: any): Promise<Execute> {
|
||||||
|
const lang = guessLanguage(validator, getLang);
|
||||||
|
if (!lang) throw new FormatError('Unknown validator language.');
|
||||||
|
// TODO cache compiled checker
|
||||||
|
return await compile(lang, { src: validator }, { ...copyIn, 'testlib.h': testlibFile });
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/* eslint-disable no-sequences */
|
||||||
|
import { basename } from 'path';
|
||||||
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
|
import checkers from '../checkers';
|
||||||
|
import compile, { compileChecker, compileValidator } from '../compile';
|
||||||
|
import { Execute } from '../interface';
|
||||||
|
import { CmdFile, del, run } from '../sandbox';
|
||||||
|
import signals from '../signals';
|
||||||
|
import { parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
|
import { Context } from './interface';
|
||||||
|
|
||||||
|
export async function judge(ctx: Context) {
|
||||||
|
const { config, session } = ctx;
|
||||||
|
ctx.next({ status: STATUS.STATUS_COMPILING, progress: 0 });
|
||||||
|
const userExtraFiles = Object.fromEntries(
|
||||||
|
(config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
|
||||||
|
);
|
||||||
|
const judgeExtraFiles = Object.fromEntries(
|
||||||
|
(config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
|
||||||
|
);
|
||||||
|
const markCleanup = (i: Execute) => (ctx.clean.push(i.clean), i);
|
||||||
|
const [execute, checker, validator, input] = await Promise.all([
|
||||||
|
compile(session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
|
||||||
|
compileChecker(session.getLang, config.checker_type, config.checker, judgeExtraFiles).then(markCleanup),
|
||||||
|
compileValidator(session.getLang, config.validator, judgeExtraFiles).then(markCleanup),
|
||||||
|
ctx.session.fetchFile(ctx.input),
|
||||||
|
]);
|
||||||
|
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
||||||
|
const validateResult = await run(
|
||||||
|
validator.execute,
|
||||||
|
{
|
||||||
|
stdin: { src: input },
|
||||||
|
copyIn: judgeExtraFiles,
|
||||||
|
time: parseTimeMS(ctx.config.time || '1s') * execute.time,
|
||||||
|
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (validateResult.status !== STATUS.STATUS_ACCEPTED) {
|
||||||
|
const message = `${validateResult.files.stdout || ''}\n${validateResult.files.stderr || ''}`.trim();
|
||||||
|
return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message });
|
||||||
|
}
|
||||||
|
const copyIn = { ...ctx.execute.copyIn };
|
||||||
|
const { filename } = ctx.config;
|
||||||
|
if (filename) copyIn[`${filename}.in`] = { src: input };
|
||||||
|
const res = await run(
|
||||||
|
execute.execute,
|
||||||
|
{
|
||||||
|
stdin: filename ? null : { src: input },
|
||||||
|
copyIn,
|
||||||
|
copyOutCached: [filename ? `${filename}.out?` : 'stdout'],
|
||||||
|
time: parseTimeMS(ctx.config.time || '1s') * execute.time,
|
||||||
|
memory: parseMemoryMB(ctx.config.memory || '256m'),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const { code, time, memory } = res;
|
||||||
|
let { status } = res;
|
||||||
|
let stdout: CmdFile = { fileId: res.fileIds[filename ? `${filename}.out` : 'stdout'] };
|
||||||
|
const stderr = { fileId: res.fileIds['stderr'] };
|
||||||
|
if (!stdout.fileId) stdout = { content: '' };
|
||||||
|
let message: any = '';
|
||||||
|
if (status === STATUS.STATUS_ACCEPTED) {
|
||||||
|
if (time > ctx.config.time * execute.time) {
|
||||||
|
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
||||||
|
} else if (memory > ctx.config.memory * 1024) {
|
||||||
|
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
|
||||||
|
} else {
|
||||||
|
({ status, message } = await checkers[ctx.config.checker_type]({
|
||||||
|
execute: checker.execute,
|
||||||
|
copyIn: checker.copyIn || {},
|
||||||
|
input: { src: input },
|
||||||
|
output: null,
|
||||||
|
user_stdout: stdout,
|
||||||
|
user_stderr: stderr,
|
||||||
|
score: 100,
|
||||||
|
detail: ctx.config.detail ?? true,
|
||||||
|
env: { ...ctx.env, HYDRO_TESTCASE: '0' },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
||||||
|
if (code < 32) message = signals[code];
|
||||||
|
else message = { message: 'Your program returned {0}.', params: [code] };
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
Object.values(res.fileIds).map((id) => del(id)),
|
||||||
|
).catch(() => { /* Ignore file doesn't exist */ });
|
||||||
|
|
||||||
|
if (message) ctx.next({ message });
|
||||||
|
|
||||||
|
return ctx.end({
|
||||||
|
status: status === STATUS.STATUS_ACCEPTED ? STATUS.STATUS_HACK_UNSUCCESSFUL : STATUS.STATUS_HACK_SUCCESSFUL,
|
||||||
|
score: 0,
|
||||||
|
time,
|
||||||
|
memory,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as def from './default';
|
import * as def from './default';
|
||||||
|
import * as hack from './hack';
|
||||||
import * as interactive from './interactive';
|
import * as interactive from './interactive';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
import * as objective from './objective';
|
import * as objective from './objective';
|
||||||
@@ -6,5 +7,5 @@ import * as run from './run';
|
|||||||
import * as submit_answer from './submit_answer';
|
import * as submit_answer from './submit_answer';
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
default: def, interactive, run, submit_answer, objective,
|
default: def, interactive, run, submit_answer, objective, hack,
|
||||||
} as Record<string, { judge(ctx: Context): Promise<void> }>;
|
} as Record<string, { judge(ctx: Context): Promise<void> }>;
|
||||||
@@ -1,24 +1,26 @@
|
|||||||
export const STATUS = {
|
export enum STATUS {
|
||||||
STATUS_WAITING: 0,
|
STATUS_WAITING = 0,
|
||||||
STATUS_ACCEPTED: 1,
|
STATUS_ACCEPTED = 1,
|
||||||
STATUS_WRONG_ANSWER: 2,
|
STATUS_WRONG_ANSWER = 2,
|
||||||
STATUS_TIME_LIMIT_EXCEEDED: 3,
|
STATUS_TIME_LIMIT_EXCEEDED = 3,
|
||||||
STATUS_MEMORY_LIMIT_EXCEEDED: 4,
|
STATUS_MEMORY_LIMIT_EXCEEDED = 4,
|
||||||
STATUS_OUTPUT_LIMIT_EXCEEDED: 5,
|
STATUS_OUTPUT_LIMIT_EXCEEDED = 5,
|
||||||
STATUS_RUNTIME_ERROR: 6,
|
STATUS_RUNTIME_ERROR = 6,
|
||||||
STATUS_COMPILE_ERROR: 7,
|
STATUS_COMPILE_ERROR = 7,
|
||||||
STATUS_SYSTEM_ERROR: 8,
|
STATUS_SYSTEM_ERROR = 8,
|
||||||
STATUS_CANCELED: 9,
|
STATUS_CANCELED = 9,
|
||||||
STATUS_ETC: 10,
|
STATUS_ETC = 10,
|
||||||
STATUS_HACKED: 11,
|
STATUS_HACKED = 11,
|
||||||
STATUS_JUDGING: 20,
|
STATUS_JUDGING = 20,
|
||||||
STATUS_COMPILING: 21,
|
STATUS_COMPILING = 21,
|
||||||
STATUS_FETCHED: 22,
|
STATUS_FETCHED = 22,
|
||||||
STATUS_IGNORED: 30,
|
STATUS_IGNORED = 30,
|
||||||
STATUS_FORMAT_ERROR: 31,
|
STATUS_FORMAT_ERROR = 31,
|
||||||
};
|
STATUS_HACK_SUCCESSFUL = 32,
|
||||||
|
STATUS_HACK_UNSUCCESSFUL = 33,
|
||||||
|
}
|
||||||
|
|
||||||
export const STATUS_TEXTS = {
|
export const STATUS_TEXTS: Record<STATUS, string> = {
|
||||||
[STATUS.STATUS_WAITING]: 'Waiting',
|
[STATUS.STATUS_WAITING]: 'Waiting',
|
||||||
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
||||||
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
||||||
@@ -36,9 +38,11 @@ export const STATUS_TEXTS = {
|
|||||||
[STATUS.STATUS_FETCHED]: 'Fetched',
|
[STATUS.STATUS_FETCHED]: 'Fetched',
|
||||||
[STATUS.STATUS_IGNORED]: 'Ignored',
|
[STATUS.STATUS_IGNORED]: 'Ignored',
|
||||||
[STATUS.STATUS_FORMAT_ERROR]: 'Format Error',
|
[STATUS.STATUS_FORMAT_ERROR]: 'Format Error',
|
||||||
|
[STATUS.STATUS_HACK_SUCCESSFUL]: 'Hack Successful',
|
||||||
|
[STATUS.STATUS_HACK_UNSUCCESSFUL]: 'Hack Unsuccessful',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const STATUS_SHORT_TEXTS = {
|
export const STATUS_SHORT_TEXTS: Partial<Record<STATUS, string>> = {
|
||||||
[STATUS.STATUS_ACCEPTED]: 'AC',
|
[STATUS.STATUS_ACCEPTED]: 'AC',
|
||||||
[STATUS.STATUS_WRONG_ANSWER]: 'WA',
|
[STATUS.STATUS_WRONG_ANSWER]: 'WA',
|
||||||
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'TLE',
|
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'TLE',
|
||||||
@@ -53,24 +57,26 @@ export const STATUS_SHORT_TEXTS = {
|
|||||||
[STATUS.STATUS_FORMAT_ERROR]: 'FE',
|
[STATUS.STATUS_FORMAT_ERROR]: 'FE',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const STATUS_CODES = {
|
export const STATUS_CODES: Record<STATUS, string> = {
|
||||||
0: 'pending',
|
[STATUS.STATUS_WAITING]: 'pending',
|
||||||
1: 'pass',
|
[STATUS.STATUS_ACCEPTED]: 'pass',
|
||||||
2: 'fail',
|
[STATUS.STATUS_WRONG_ANSWER]: 'fail',
|
||||||
3: 'fail',
|
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'fail',
|
||||||
4: 'fail',
|
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'fail',
|
||||||
5: 'fail',
|
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'fail',
|
||||||
6: 'fail',
|
[STATUS.STATUS_RUNTIME_ERROR]: 'fail',
|
||||||
7: 'fail',
|
[STATUS.STATUS_COMPILE_ERROR]: 'fail',
|
||||||
8: 'fail',
|
[STATUS.STATUS_SYSTEM_ERROR]: 'fail',
|
||||||
9: 'ignored',
|
[STATUS.STATUS_CANCELED]: 'ignored',
|
||||||
10: 'fail',
|
[STATUS.STATUS_ETC]: 'fail',
|
||||||
11: 'fail',
|
[STATUS.STATUS_HACKED]: 'fail',
|
||||||
20: 'progress',
|
[STATUS.STATUS_JUDGING]: 'progress',
|
||||||
21: 'progress',
|
[STATUS.STATUS_COMPILING]: 'progress',
|
||||||
22: 'progress',
|
[STATUS.STATUS_FETCHED]: 'progress',
|
||||||
30: 'ignored',
|
[STATUS.STATUS_IGNORED]: 'ignored',
|
||||||
31: 'ignored',
|
[STATUS.STATUS_FORMAT_ERROR]: 'ignored',
|
||||||
|
[STATUS.STATUS_HACK_SUCCESSFUL]: 'pass',
|
||||||
|
[STATUS.STATUS_HACK_UNSUCCESSFUL]: 'fail',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getScoreColor(score: number | string): string {
|
export function getScoreColor(score: number | string): string {
|
||||||
|
|||||||
Reference in new issue
Block a user