Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface SubtaskConfig {
cases?: TestCaseConfig[];
}

export type DetailType = 'full' | 'case' | 'none';

export interface ProblemConfigFile {
type?: ProblemType;
subType?: string;
Expand All @@ -48,7 +50,7 @@ export interface ProblemConfigFile {
num_processes?: number;
user_extra_files?: string[];
judge_extra_files?: string[];
detail?: boolean;
detail?: DetailType | boolean;
answers?: Record<string, [string | string[], number]>;
redirect?: string;
cases?: TestCaseConfig[];
Expand Down
3 changes: 3 additions & 0 deletions packages/hydrojudge/src/cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ export default async function readCases(folder: string, cfg: ProblemConfigFile =
throw new SystemError('Cannot parse testdata.', [e.message, ...(e.params || [])]);
}
}
if (result.detail === true) result.detail = 'full';
else if (result.detail === false) result.detail = 'case';
else result.detail ||= 'full';
result.subtasks = normalizeSubtasks(result.subtasks || [], checkFile, config.time, config.memory, false, timeRate, memoryRate);
if (result.key && args.key !== result.key) throw new FormatError('Incorrect secret key');
if (!result.key && !args.trusted) isValidConfig(result);
Expand Down
22 changes: 12 additions & 10 deletions packages/hydrojudge/src/checkers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { STATUS } from '@hydrooj/common';
import { DetailType, STATUS } from '@hydrooj/common';
import { FormatError, SystemError } from './error';
import { CopyInFile, runQueued } from './sandbox';
import { parse } from './testlib';
Expand All @@ -12,7 +12,7 @@ export interface CheckConfig {
code: CopyInFile;
copyIn: Record<string, CopyInFile>;
score: number;
detail: boolean;
detail: DetailType;
env?: Record<string, string>;
}

Expand Down Expand Up @@ -85,7 +85,7 @@ elif [ -n "$result" ]; then
fi
`;

const getDefaultChecker = (strict: boolean) => async (config) => {
const getDefaultChecker = (strict: boolean) => async (config: CheckConfig) => {
const { code, stdout } = await runQueued(`/bin/bash compare.sh${strict ? '' : ' BZ'}`, {
copyIn: {
usrout: config.user_stdout,
Expand All @@ -102,7 +102,7 @@ const getDefaultChecker = (strict: boolean) => async (config) => {
message = `Checker returned with status ${code}`;
} else if (stdout) {
status = STATUS.STATUS_WRONG_ANSWER;
if (config.detail && !strict) message = parseDiffMsg(stdout);
if (config.detail === 'full') message = parseDiffMsg(stdout);
} else status = STATUS.STATUS_ACCEPTED;
if (message.length > 1024000) message = '';
return {
Expand Down Expand Up @@ -135,7 +135,7 @@ const checkers: Record<string, Checker> = new Proxy({
return {
status,
score: status === STATUS.STATUS_ACCEPTED ? config.score : 0,
message: stdout,
message: config.detail === 'full' ? stdout : '',
};
},

Expand Down Expand Up @@ -168,7 +168,7 @@ const checkers: Record<string, Checker> = new Proxy({
const score = Math.floor(+files.score) || 0;
return {
score,
message: files.message,
message: config.detail === 'full' ? files.message : '',
status: score === config.score
? STATUS.STATUS_ACCEPTED
: STATUS.STATUS_WRONG_ANSWER,
Expand All @@ -193,7 +193,7 @@ const checkers: Record<string, Checker> = new Proxy({
return {
status: st,
score: (status === STATUS.STATUS_ACCEPTED) ? config.score : 0,
message: stdout,
message: config.detail === 'full' ? stdout : '',
};
},

Expand All @@ -219,7 +219,7 @@ const checkers: Record<string, Checker> = new Proxy({
if (status !== STATUS.STATUS_ACCEPTED) throw new SystemError('Checker returned {0}.', [status]);
const score = +stdout;
status = score === 100 ? STATUS.STATUS_ACCEPTED : STATUS.STATUS_WRONG_ANSWER;
return { status, score: Math.floor((score * config.score) / 100), message: stderr };
return { status, score: Math.floor((score * config.score) / 100), message: config.detail === 'full' ? stderr : '' };
},

async testlib(config) {
Expand Down Expand Up @@ -252,7 +252,7 @@ const checkers: Record<string, Checker> = new Proxy({
message: `Checker exited with code ${code}`,
};
}
return parse(stderr, config.score);
return parse(stderr, config.score, config.detail);
},

// https://www.kattis.com/problem-package-format/spec/2023-07-draft.html#output-validator
Expand Down Expand Up @@ -285,7 +285,9 @@ const checkers: Record<string, Checker> = new Proxy({

const message = status === STATUS.STATUS_SYSTEM_ERROR
? files['feedback_dir/judgeerror.txt'] || `Checker exited with code ${code}`
: files['feedback_dir/teammessage.txt'] || files['feedback_dir/judgemessage.txt'] || '';
: config.detail === 'full'
? files['feedback_dir/teammessage.txt'] || files['feedback_dir/judgemessage.txt'] || ''
: '';

return { status, score, message };
},
Expand Down
7 changes: 6 additions & 1 deletion packages/hydrojudge/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ export const JudgeSettings = Schema.object({
host: Schema.any(),
secret: Schema.string().description('Judge Token Secret').default(String.random(32)),
disable: Schema.boolean().description('Disable builtin judge').default(false),
detail: Schema.boolean().description('Show diff detail').default(true),
detail: Schema.union([
Schema.const('full'),
Schema.const('case'),
Schema.const('none'),
Schema.transform(Schema.boolean(), (v) => (v ? 'full' : 'case')),
]).description('Show diff detail').default('full'),
performance: Schema.boolean().description('Performance mode').default(false),
});

Expand Down
4 changes: 3 additions & 1 deletion packages/hydrojudge/src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ function judgeSubtask(subtask: NormalizedSubtask, sid: string, judgeCase: Task['
ctx.total_time += res.time;
ctx.total_memory = Math.max(ctx.total_memory, res.memory);
}
ctx.next({ ...res ? { case: res } : {}, addProgress: 100 / ctx.config.count });
if (ctx.config.detail !== 'none') {
ctx.next({ ...res ? { case: res } : {}, addProgress: 100 / ctx.config.count });
}
}));
}
try {
Expand Down
7 changes: 4 additions & 3 deletions packages/hydrojudge/src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
JudgeResultBody, type LangConfig, NormalizedSubtask, ProblemConfigFile,
DetailType, JudgeResultBody, type LangConfig, NormalizedSubtask, ProblemConfigFile,
} from '@hydrooj/common';
import { CopyInFile } from './sandbox';
import type { JudgeTask } from './task';
Expand All @@ -14,11 +14,12 @@ export interface Execute {

export type NextFunction = (body: Partial<JudgeResultBody>) => Promise<void> | void;

export interface ParsedConfig extends Omit<ProblemConfigFile, 'time' | 'memory' | 'subtasks'> {
export interface ParsedConfig extends Omit<ProblemConfigFile, 'time' | 'memory' | 'subtasks' | 'detail'> {
count: number;
time: number;
memory: number;
subtasks: NormalizedSubtask[];
detail: DetailType;
}

export { JudgeRequest } from '@hydrooj/common';
Expand All @@ -28,5 +29,5 @@ export interface Session {
getReporter: (task: JudgeTask) => { next: NextFunction, end: NextFunction };
fetchFile: <T extends null | string>(namespace: T, files: Record<string, string>) => Promise<T extends null ? string : null>;
postFile: (target: string, filename: string, file: string) => Promise<void>;
config: { detail: boolean, host?: string, trusted?: boolean };
config: { detail: DetailType, host?: string, trusted?: boolean };
}
3 changes: 1 addition & 2 deletions packages/hydrojudge/src/judge/communication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ function judgeCase(c: NormalizedCase) {
let score = 0;
let status = STATUS.STATUS_ACCEPTED;
let message: any;
const detail = ctx.config.detail ?? true;
for (let i = 0; i < ctx.config.num_processes; i++) {
const result = res[i + 1];
time += result.time;
Expand All @@ -55,7 +54,7 @@ function judgeCase(c: NormalizedCase) {
else if (result.memory > c.memory * 1024) status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
else if ((result.code && result.code !== 13 /* Broken Pipe */) || (result.code === 13 && !resManager.code)) {
status = STATUS.STATUS_RUNTIME_ERROR;
if (detail) {
if (ctx.config.detail === 'full') {
if (result.code < 32 && result.signalled) message = signals[result.code];
else message = { message: 'Your program returned {0}.', params: [result.code] };
}
Expand Down
5 changes: 2 additions & 3 deletions packages/hydrojudge/src/judge/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ function judgeCase(c: NormalizedCase) {
let { status } = res;
let message: any = '';
let score = 0;
const detail = ctx.config.detail ?? true;
if (status === STATUS.STATUS_ACCEPTED) {
if (time > c.time) {
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
Expand All @@ -44,7 +43,7 @@ function judgeCase(c: NormalizedCase) {
user_stdout: fileIds.stdout ? { fileId: fileIds.stdout } : { content: '' },
user_stderr: fileIds.stderr ? { fileId: fileIds.stderr } : { content: '' },
score: c.score,
detail,
detail: ctx.config.detail,
env: {
...ctx.env,
HYDRO_TESTCASE: c.id.toString(),
Expand All @@ -53,7 +52,7 @@ function judgeCase(c: NormalizedCase) {
},
}));
}
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code && detail) {
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code && ctx.config.detail === 'full') {
if (code < 32 && signalled) message = signals[code];
else message = { message: 'Your program returned {0}.', params: [code] };
}
Expand Down
2 changes: 1 addition & 1 deletion packages/hydrojudge/src/judge/hack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export async function judge(ctx: Context) {
user_stderr: { fileId: res.fileIds.stderr },
code: ctx.code,
score: 100,
detail: ctx.config.detail ?? true,
detail: ctx.config.detail ?? 'full',
env: { ...ctx.env, HYDRO_TESTCASE: '0' },
}));
}
Expand Down
3 changes: 1 addition & 2 deletions packages/hydrojudge/src/judge/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@
let status: number;
let score = 0;
let message: any = '';
const detail = ctx.config.detail ?? true;
if (time > c.time) {
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
} else if (memory > c.memory * 1024) {
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
} else if (detail && ((code && code !== 13/* Broken Pipe */) || (code === 13 && !resInteractor.code))) {
} else if (ctx.config.detail === 'full' && ((code && code !== 13/* Broken Pipe */) || (code === 13 && !resInteractor.code))) {
status = STATUS.STATUS_RUNTIME_ERROR;
if (code < 32 && signalled) message = signals[code];
else message = { message: 'Your program returned {0}.', params: [code] };
} else {
const result = parse(resInteractor.stderr, c.score);

Check failure on line 51 in packages/hydrojudge/src/judge/interactive.ts

View workflow job for this annotation

GitHub Actions / build

Expected 3 arguments, but got 2.

Check failure on line 51 in packages/hydrojudge/src/judge/interactive.ts

View workflow job for this annotation

GitHub Actions / build

Expected 3 arguments, but got 2.
status = result.status;
score = result.score;
message = result.message;
Expand Down
2 changes: 1 addition & 1 deletion packages/hydrojudge/src/judge/submit_answer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function judgeCase(c: NormalizedCase) {
user_stderr: { content: '' },
code: ctx.code,
score: c.score,
detail: ctx.config.detail ?? true,
detail: ctx.config.detail,
env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() },
}));
}
Expand Down
6 changes: 3 additions & 3 deletions packages/hydrojudge/src/testlib.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { STATUS } from '@hydrooj/common';
import { DetailType, STATUS } from '@hydrooj/common';

const operation = /^\s*(status|score)\((\d+)\)\s*(.*)$/m;

export function parse(output: string, fullscore: number) {
export function parse(output: string, fullscore: number, detail: DetailType) {
let status = STATUS.STATUS_WRONG_ANSWER;
let score = 0;
let builder = (msg: string) => msg;
Expand Down Expand Up @@ -51,5 +51,5 @@ export function parse(output: string, fullscore: number) {
score = +val;
}
}
return { status, score, message: builder(message) };
return { status, score, message: builder(detail === 'full' ? message : '') };
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const problemConfigSchema: JSONSchema7 = {
cases: { $ref: '#/definitions/cases' },
subtasks: { type: 'array', items: { $ref: '#/definitions/subtask' } },
filename: { type: 'string' },
detail: { type: 'boolean' },
detail: { type: 'string', enum: ['full', 'case', 'none'] },
time: { $ref: '#/definitions/time' },
memory: { $ref: '#/definitions/memory' },
score: { $ref: '#/definitions/score' },
Expand Down
Loading