Skip to content

Commit ed830e1

Browse files
judge: change config.detail to enum (#1000)
1 parent fb04aaa commit ed830e1

13 files changed

Lines changed: 41 additions & 29 deletions

File tree

packages/common/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface SubtaskConfig {
3636
cases?: TestCaseConfig[];
3737
}
3838

39+
export type DetailType = 'full' | 'case' | 'none';
40+
3941
export interface ProblemConfigFile {
4042
type?: ProblemType;
4143
subType?: string;
@@ -48,7 +50,7 @@ export interface ProblemConfigFile {
4850
num_processes?: number;
4951
user_extra_files?: string[];
5052
judge_extra_files?: string[];
51-
detail?: boolean;
53+
detail?: DetailType | boolean;
5254
answers?: Record<string, [string | string[], number]>;
5355
redirect?: string;
5456
cases?: TestCaseConfig[];

packages/hydrojudge/src/cases.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export default async function readCases(folder: string, cfg: ProblemConfigFile =
8787
throw new SystemError('Cannot parse testdata.', [e.message, ...(e.params || [])]);
8888
}
8989
}
90+
if (result.detail === true) result.detail = 'full';
91+
else if (result.detail === false) result.detail = 'case';
92+
else result.detail ||= 'full';
9093
result.subtasks = normalizeSubtasks(result.subtasks || [], checkFile, config.time, config.memory, false, timeRate, memoryRate);
9194
if (result.key && args.key !== result.key) throw new FormatError('Incorrect secret key');
9295
if (!result.key && !args.trusted) isValidConfig(result);

packages/hydrojudge/src/checkers.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { STATUS } from '@hydrooj/common';
1+
import { DetailType, STATUS } from '@hydrooj/common';
22
import { FormatError, SystemError } from './error';
33
import { CopyInFile, runQueued } from './sandbox';
44
import { parse } from './testlib';
@@ -12,7 +12,7 @@ export interface CheckConfig {
1212
code: CopyInFile;
1313
copyIn: Record<string, CopyInFile>;
1414
score: number;
15-
detail: boolean;
15+
detail: DetailType;
1616
env?: Record<string, string>;
1717
}
1818

@@ -85,7 +85,7 @@ elif [ -n "$result" ]; then
8585
fi
8686
`;
8787

88-
const getDefaultChecker = (strict: boolean) => async (config) => {
88+
const getDefaultChecker = (strict: boolean) => async (config: CheckConfig) => {
8989
const { code, stdout } = await runQueued(`/bin/bash compare.sh${strict ? '' : ' BZ'}`, {
9090
copyIn: {
9191
usrout: config.user_stdout,
@@ -102,7 +102,7 @@ const getDefaultChecker = (strict: boolean) => async (config) => {
102102
message = `Checker returned with status ${code}`;
103103
} else if (stdout) {
104104
status = STATUS.STATUS_WRONG_ANSWER;
105-
if (config.detail && !strict) message = parseDiffMsg(stdout);
105+
if (config.detail === 'full') message = parseDiffMsg(stdout);
106106
} else status = STATUS.STATUS_ACCEPTED;
107107
if (message.length > 1024000) message = '';
108108
return {
@@ -135,7 +135,7 @@ const checkers: Record<string, Checker> = new Proxy({
135135
return {
136136
status,
137137
score: status === STATUS.STATUS_ACCEPTED ? config.score : 0,
138-
message: stdout,
138+
message: config.detail === 'full' ? stdout : '',
139139
};
140140
},
141141

@@ -168,7 +168,7 @@ const checkers: Record<string, Checker> = new Proxy({
168168
const score = Math.floor(+files.score) || 0;
169169
return {
170170
score,
171-
message: files.message,
171+
message: config.detail === 'full' ? files.message : '',
172172
status: score === config.score
173173
? STATUS.STATUS_ACCEPTED
174174
: STATUS.STATUS_WRONG_ANSWER,
@@ -193,7 +193,7 @@ const checkers: Record<string, Checker> = new Proxy({
193193
return {
194194
status: st,
195195
score: (status === STATUS.STATUS_ACCEPTED) ? config.score : 0,
196-
message: stdout,
196+
message: config.detail === 'full' ? stdout : '',
197197
};
198198
},
199199

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

225225
async testlib(config) {
@@ -252,7 +252,7 @@ const checkers: Record<string, Checker> = new Proxy({
252252
message: `Checker exited with code ${code}`,
253253
};
254254
}
255-
return parse(stderr, config.score);
255+
return parse(stderr, config.score, config.detail);
256256
},
257257

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

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

290292
return { status, score, message };
291293
},

packages/hydrojudge/src/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ export const JudgeSettings = Schema.object({
3232
host: Schema.any(),
3333
secret: Schema.string().description('Judge Token Secret').default(String.random(32)),
3434
disable: Schema.boolean().description('Disable builtin judge').default(false),
35-
detail: Schema.boolean().description('Show diff detail').default(true),
35+
detail: Schema.union([
36+
Schema.const('full'),
37+
Schema.const('case'),
38+
Schema.const('none'),
39+
Schema.transform(Schema.boolean(), (v) => (v ? 'full' : 'case')),
40+
]).description('Show diff detail').default('full'),
3641
performance: Schema.boolean().description('Performance mode').default(false),
3742
});
3843

packages/hydrojudge/src/flow.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ function judgeSubtask(subtask: NormalizedSubtask, sid: string, judgeCase: Task['
5353
ctx.total_time += res.time;
5454
ctx.total_memory = Math.max(ctx.total_memory, res.memory);
5555
}
56-
ctx.next({ ...res ? { case: res } : {}, addProgress: 100 / ctx.config.count });
56+
if (ctx.config.detail !== 'none') {
57+
ctx.next({ ...res ? { case: res } : {}, addProgress: 100 / ctx.config.count });
58+
}
5759
}));
5860
}
5961
try {

packages/hydrojudge/src/interface.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
JudgeResultBody, type LangConfig, NormalizedSubtask, ProblemConfigFile,
2+
DetailType, JudgeResultBody, type LangConfig, NormalizedSubtask, ProblemConfigFile,
33
} from '@hydrooj/common';
44
import { CopyInFile } from './sandbox';
55
import type { JudgeTask } from './task';
@@ -14,11 +14,12 @@ export interface Execute {
1414

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

17-
export interface ParsedConfig extends Omit<ProblemConfigFile, 'time' | 'memory' | 'subtasks'> {
17+
export interface ParsedConfig extends Omit<ProblemConfigFile, 'time' | 'memory' | 'subtasks' | 'detail'> {
1818
count: number;
1919
time: number;
2020
memory: number;
2121
subtasks: NormalizedSubtask[];
22+
detail: DetailType;
2223
}
2324

2425
export { JudgeRequest } from '@hydrooj/common';
@@ -28,5 +29,5 @@ export interface Session {
2829
getReporter: (task: JudgeTask) => { next: NextFunction, end: NextFunction };
2930
fetchFile: <T extends null | string>(namespace: T, files: Record<string, string>) => Promise<T extends null ? string : null>;
3031
postFile: (target: string, filename: string, file: string) => Promise<void>;
31-
config: { detail: boolean, host?: string, trusted?: boolean };
32+
config: { detail: DetailType, host?: string, trusted?: boolean };
3233
}

packages/hydrojudge/src/judge/communication.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ function judgeCase(c: NormalizedCase) {
4646
let score = 0;
4747
let status = STATUS.STATUS_ACCEPTED;
4848
let message: any;
49-
const detail = ctx.config.detail ?? true;
5049
for (let i = 0; i < ctx.config.num_processes; i++) {
5150
const result = res[i + 1];
5251
time += result.time;
@@ -55,7 +54,7 @@ function judgeCase(c: NormalizedCase) {
5554
else if (result.memory > c.memory * 1024) status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
5655
else if ((result.code && result.code !== 13 /* Broken Pipe */) || (result.code === 13 && !resManager.code)) {
5756
status = STATUS.STATUS_RUNTIME_ERROR;
58-
if (detail) {
57+
if (ctx.config.detail === 'full') {
5958
if (result.code < 32 && result.signalled) message = signals[result.code];
6059
else message = { message: 'Your program returned {0}.', params: [result.code] };
6160
}

packages/hydrojudge/src/judge/default.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ function judgeCase(c: NormalizedCase) {
2828
let { status } = res;
2929
let message: any = '';
3030
let score = 0;
31-
const detail = ctx.config.detail ?? true;
3231
if (status === STATUS.STATUS_ACCEPTED) {
3332
if (time > c.time) {
3433
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
@@ -44,7 +43,7 @@ function judgeCase(c: NormalizedCase) {
4443
user_stdout: fileIds.stdout ? { fileId: fileIds.stdout } : { content: '' },
4544
user_stderr: fileIds.stderr ? { fileId: fileIds.stderr } : { content: '' },
4645
score: c.score,
47-
detail,
46+
detail: ctx.config.detail,
4847
env: {
4948
...ctx.env,
5049
HYDRO_TESTCASE: c.id.toString(),
@@ -53,7 +52,7 @@ function judgeCase(c: NormalizedCase) {
5352
},
5453
}));
5554
}
56-
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code && detail) {
55+
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code && ctx.config.detail === 'full') {
5756
if (code < 32 && signalled) message = signals[code];
5857
else message = { message: 'Your program returned {0}.', params: [code] };
5958
}

packages/hydrojudge/src/judge/hack.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export async function judge(ctx: Context) {
6666
user_stderr: { fileId: res.fileIds.stderr },
6767
code: ctx.code,
6868
score: 100,
69-
detail: ctx.config.detail ?? true,
69+
detail: ctx.config.detail ?? 'full',
7070
env: { ...ctx.env, HYDRO_TESTCASE: '0' },
7171
}));
7272
}

packages/hydrojudge/src/judge/interactive.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ function judgeCase(c: NormalizedCase) {
3939
let status: number;
4040
let score = 0;
4141
let message: any = '';
42-
const detail = ctx.config.detail ?? true;
4342
if (time > c.time) {
4443
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
4544
} else if (memory > c.memory * 1024) {
4645
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
47-
} else if (detail && ((code && code !== 13/* Broken Pipe */) || (code === 13 && !resInteractor.code))) {
46+
} else if (ctx.config.detail === 'full' && ((code && code !== 13/* Broken Pipe */) || (code === 13 && !resInteractor.code))) {
4847
status = STATUS.STATUS_RUNTIME_ERROR;
4948
if (code < 32 && signalled) message = signals[code];
5049
else message = { message: 'Your program returned {0}.', params: [code] };

0 commit comments

Comments
 (0)