@halooj/common 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cases.ts +50 -0
- package/index.ts +5 -0
- package/lang.ts +60 -0
- package/package.json +13 -0
- package/permission.ts +210 -0
- package/status.ts +122 -0
- package/subtask.ts +180 -0
- package/tests/subtask.spec.ts +62 -0
- package/tsconfig.json +1 -0
- package/types.ts +172 -0
package/cases.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { findFileSync, parseMemoryMB, parseTimeMS } from '@halooj/utils';
|
|
2
|
+
import { CompilableSource, ProblemConfigFile } from './types';
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line ts/no-unused-vars
|
|
5
|
+
export async function readYamlCases(cfg: ProblemConfigFile = {}, checkFile = (s: CompilableSource, errMsg: string) => s) {
|
|
6
|
+
const config: any = {
|
|
7
|
+
checker_type: cfg.checker_type || 'default',
|
|
8
|
+
judge_extra_files: [],
|
|
9
|
+
user_extra_files: [],
|
|
10
|
+
};
|
|
11
|
+
const checkFileWithLang = (s: CompilableSource, errMsg: string) => {
|
|
12
|
+
if (typeof s === 'string') return { lang: 'auto', file: checkFile(s, errMsg) };
|
|
13
|
+
return { lang: s.lang, file: checkFile(s.file, errMsg) };
|
|
14
|
+
};
|
|
15
|
+
if (cfg.type === 'objective') {
|
|
16
|
+
if (cfg.checker || cfg.interactor || cfg.validator) {
|
|
17
|
+
throw new Error('You cannot use checker, interactor or validator for objective questions');
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
if (cfg.checker) {
|
|
21
|
+
const [name, lang] = typeof cfg.checker === 'string' ? [cfg.checker, 'auto'] : [cfg.checker.file, cfg.checker.lang || 'auto'];
|
|
22
|
+
if (!name.includes('.')) {
|
|
23
|
+
config.checker = {
|
|
24
|
+
lang,
|
|
25
|
+
file: findFileSync(`@halooj/hydrojudge/vendor/testlib/checkers/${name}.cpp`, false),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
config.checker ||= checkFileWithLang(name, 'Cannot find checker {0}.');
|
|
29
|
+
}
|
|
30
|
+
if (cfg.interactor) config.interactor = checkFileWithLang(cfg.interactor, 'Cannot find interactor {0}.');
|
|
31
|
+
if (cfg.manager) config.manager = checkFileWithLang(cfg.manager, 'Cannot find Manager {0}.');
|
|
32
|
+
if (cfg.validator) config.validator = checkFileWithLang(cfg.validator, 'Cannot find validator {0}.');
|
|
33
|
+
for (const n of ['judge', 'user']) {
|
|
34
|
+
const conf = cfg[`${n}_extra_files`];
|
|
35
|
+
if (!conf) continue;
|
|
36
|
+
if (conf instanceof Array) {
|
|
37
|
+
config[`${n}_extra_files`] = conf.map((file) => checkFile(file, `Cannot find ${n} extra file {0}.`));
|
|
38
|
+
} else throw new Error(`Invalid ${n}_extra_files config.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (cfg.cases?.length) {
|
|
42
|
+
config.subtasks = [{
|
|
43
|
+
cases: cfg.cases,
|
|
44
|
+
type: 'sum',
|
|
45
|
+
}];
|
|
46
|
+
}
|
|
47
|
+
if (cfg.time) config.time = parseTimeMS(cfg.time);
|
|
48
|
+
if (cfg.memory) config.memory = parseMemoryMB(cfg.memory);
|
|
49
|
+
return Object.assign(cfg, config);
|
|
50
|
+
}
|
package/index.ts
ADDED
package/lang.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
export interface LangConfig {
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
compile?: string;
|
|
6
|
+
execute: string;
|
|
7
|
+
code_file: string;
|
|
8
|
+
highlight: string;
|
|
9
|
+
monaco: string;
|
|
10
|
+
time_limit_rate: number;
|
|
11
|
+
memory_limit_rate: number;
|
|
12
|
+
address_space_limit?: boolean;
|
|
13
|
+
process_limit?: number;
|
|
14
|
+
display: string;
|
|
15
|
+
target?: string;
|
|
16
|
+
key: string;
|
|
17
|
+
hidden: boolean;
|
|
18
|
+
isBinary?: boolean;
|
|
19
|
+
analysis?: string;
|
|
20
|
+
remote: boolean;
|
|
21
|
+
validAs?: Record<string, string>;
|
|
22
|
+
/** @deprecated */
|
|
23
|
+
pretest?: string | false;
|
|
24
|
+
comment?: string | [string, string];
|
|
25
|
+
compile_time_limit?: number;
|
|
26
|
+
compile_memory_limit?: number;
|
|
27
|
+
version?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseLang(config: string): Record<string, LangConfig> {
|
|
31
|
+
const file = yaml.load(config) as Record<string, LangConfig>;
|
|
32
|
+
if (typeof file === 'undefined' || typeof file === 'string' || typeof file === 'number') throw new Error();
|
|
33
|
+
for (const key of Object.keys(file)) if (key.startsWith('_')) delete file[key];
|
|
34
|
+
for (const key in file) {
|
|
35
|
+
const entry = file[key];
|
|
36
|
+
if (key.includes('.')) {
|
|
37
|
+
const base = key.split('.')[0];
|
|
38
|
+
const baseCfg = file[base];
|
|
39
|
+
for (const bkey in baseCfg || {}) {
|
|
40
|
+
if (!(bkey in entry)) entry[bkey] = baseCfg[bkey];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const key in file) {
|
|
45
|
+
const entry = file[key];
|
|
46
|
+
entry.highlight ||= key;
|
|
47
|
+
entry.monaco ||= entry.highlight;
|
|
48
|
+
entry.time_limit_rate ||= 1;
|
|
49
|
+
entry.memory_limit_rate ||= 1;
|
|
50
|
+
entry.code_file ||= `foo.${key}`;
|
|
51
|
+
entry.execute ||= '/w/foo';
|
|
52
|
+
entry.key = key;
|
|
53
|
+
entry.remote = !!entry.remote;
|
|
54
|
+
entry.hidden ||= false;
|
|
55
|
+
entry.disabled ||= false;
|
|
56
|
+
entry.isBinary ||= false;
|
|
57
|
+
entry.validAs ||= {};
|
|
58
|
+
}
|
|
59
|
+
return file;
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@halooj/common",
|
|
3
|
+
"main": "./index.ts",
|
|
4
|
+
"version": "0.0.10",
|
|
5
|
+
"repository": "https://github.com/hydro-dev/Hydro",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@halooj/utils": "^1.5.6"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
}
|
|
13
|
+
}
|
package/permission.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export const PERM = {
|
|
2
|
+
PERM_NONE: 0n,
|
|
3
|
+
|
|
4
|
+
// Domain Settings
|
|
5
|
+
PERM_VIEW: 1n << 0n,
|
|
6
|
+
PERM_EDIT_DOMAIN: 1n << 1n,
|
|
7
|
+
/** @deprecated use PERM_VIEW_USER_PRIVATE_INFO instead */
|
|
8
|
+
PERM_VIEW_DISPLAYNAME: 1n << 67n,
|
|
9
|
+
PERM_VIEW_USER_PRIVATE_INFO: 1n << 67n,
|
|
10
|
+
PERM_MOD_BADGE: 1n << 2n,
|
|
11
|
+
|
|
12
|
+
// Problem
|
|
13
|
+
PERM_CREATE_PROBLEM: 1n << 4n,
|
|
14
|
+
PERM_EDIT_PROBLEM: 1n << 5n,
|
|
15
|
+
PERM_EDIT_PROBLEM_SELF: 1n << 6n,
|
|
16
|
+
PERM_VIEW_PROBLEM: 1n << 7n,
|
|
17
|
+
PERM_VIEW_PROBLEM_HIDDEN: 1n << 8n,
|
|
18
|
+
PERM_SUBMIT_PROBLEM: 1n << 9n,
|
|
19
|
+
PERM_READ_PROBLEM_DATA: 1n << 10n,
|
|
20
|
+
|
|
21
|
+
// Record
|
|
22
|
+
PERM_VIEW_RECORD: 1n << 70n,
|
|
23
|
+
PERM_READ_RECORD_CODE: 1n << 12n,
|
|
24
|
+
PERM_READ_RECORD_CODE_ACCEPT: 1n << 66n,
|
|
25
|
+
PERM_REJUDGE_PROBLEM: 1n << 13n,
|
|
26
|
+
PERM_REJUDGE: 1n << 14n,
|
|
27
|
+
|
|
28
|
+
// Problem Solution
|
|
29
|
+
PERM_VIEW_PROBLEM_SOLUTION: 1n << 15n,
|
|
30
|
+
PERM_VIEW_PROBLEM_SOLUTION_ACCEPT: 1n << 65n,
|
|
31
|
+
PERM_CREATE_PROBLEM_SOLUTION: 1n << 16n,
|
|
32
|
+
PERM_VOTE_PROBLEM_SOLUTION: 1n << 17n,
|
|
33
|
+
PERM_EDIT_PROBLEM_SOLUTION: 1n << 18n,
|
|
34
|
+
PERM_EDIT_PROBLEM_SOLUTION_SELF: 1n << 19n,
|
|
35
|
+
PERM_DELETE_PROBLEM_SOLUTION: 1n << 20n,
|
|
36
|
+
PERM_DELETE_PROBLEM_SOLUTION_SELF: 1n << 21n,
|
|
37
|
+
PERM_REPLY_PROBLEM_SOLUTION: 1n << 22n,
|
|
38
|
+
PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF: 1n << 24n,
|
|
39
|
+
PERM_DELETE_PROBLEM_SOLUTION_REPLY: 1n << 25n,
|
|
40
|
+
PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF: 1n << 26n,
|
|
41
|
+
|
|
42
|
+
// Discussion
|
|
43
|
+
PERM_VIEW_DISCUSSION: 1n << 27n,
|
|
44
|
+
PERM_CREATE_DISCUSSION: 1n << 28n,
|
|
45
|
+
PERM_HIGHLIGHT_DISCUSSION: 1n << 29n,
|
|
46
|
+
PERM_PIN_DISCUSSION: 1n << 61n,
|
|
47
|
+
PERM_EDIT_DISCUSSION: 1n << 30n,
|
|
48
|
+
PERM_EDIT_DISCUSSION_SELF: 1n << 31n,
|
|
49
|
+
PERM_DELETE_DISCUSSION: 1n << 32n,
|
|
50
|
+
PERM_DELETE_DISCUSSION_SELF: 1n << 33n,
|
|
51
|
+
PERM_REPLY_DISCUSSION: 1n << 34n,
|
|
52
|
+
PERM_ADD_REACTION: 1n << 62n,
|
|
53
|
+
PERM_EDIT_DISCUSSION_REPLY_SELF: 1n << 36n,
|
|
54
|
+
PERM_DELETE_DISCUSSION_REPLY: 1n << 38n,
|
|
55
|
+
PERM_DELETE_DISCUSSION_REPLY_SELF: 1n << 39n,
|
|
56
|
+
PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION: 1n << 40n,
|
|
57
|
+
PERM_LOCK_DISCUSSION: 1n << 64n,
|
|
58
|
+
|
|
59
|
+
// Contest
|
|
60
|
+
PERM_VIEW_CONTEST: 1n << 41n,
|
|
61
|
+
PERM_VIEW_CONTEST_SCOREBOARD: 1n << 42n,
|
|
62
|
+
PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD: 1n << 43n,
|
|
63
|
+
PERM_CREATE_CONTEST: 1n << 44n,
|
|
64
|
+
PERM_ATTEND_CONTEST: 1n << 45n,
|
|
65
|
+
PERM_EDIT_CONTEST: 1n << 50n,
|
|
66
|
+
PERM_EDIT_CONTEST_SELF: 1n << 51n,
|
|
67
|
+
PERM_VIEW_HIDDEN_CONTEST: 1n << 68n,
|
|
68
|
+
|
|
69
|
+
// Homework
|
|
70
|
+
PERM_VIEW_HOMEWORK: 1n << 52n,
|
|
71
|
+
PERM_VIEW_HOMEWORK_SCOREBOARD: 1n << 53n,
|
|
72
|
+
PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD: 1n << 54n,
|
|
73
|
+
PERM_CREATE_HOMEWORK: 1n << 55n,
|
|
74
|
+
PERM_ATTEND_HOMEWORK: 1n << 56n,
|
|
75
|
+
PERM_EDIT_HOMEWORK: 1n << 57n,
|
|
76
|
+
PERM_EDIT_HOMEWORK_SELF: 1n << 58n,
|
|
77
|
+
PERM_VIEW_HIDDEN_HOMEWORK: 1n << 69n,
|
|
78
|
+
|
|
79
|
+
// Training
|
|
80
|
+
PERM_VIEW_TRAINING: 1n << 46n,
|
|
81
|
+
PERM_CREATE_TRAINING: 1n << 47n,
|
|
82
|
+
PERM_EDIT_TRAINING: 1n << 48n,
|
|
83
|
+
PERM_PIN_TRAINING: 1n << 63n,
|
|
84
|
+
PERM_EDIT_TRAINING_SELF: 1n << 49n,
|
|
85
|
+
|
|
86
|
+
// Ranking
|
|
87
|
+
PERM_VIEW_RANKING: 1n << 59n,
|
|
88
|
+
|
|
89
|
+
// HaloOJ Advanced Domain
|
|
90
|
+
PERM_EDIT_DOMAIN_ADVANCED: 1n << 71n,
|
|
91
|
+
PERM_IMPORT_USER: 1n << 72n,
|
|
92
|
+
PERM_VIEW_USER_LIST: 1n << 73n,
|
|
93
|
+
PERM_VIEW_COURSE: 1n << 74n,
|
|
94
|
+
PERM_EDIT_COURSE: 1n << 75n,
|
|
95
|
+
PERM_VIEW_EXAM: 1n << 76n,
|
|
96
|
+
PERM_EDIT_EXAM: 1n << 77n,
|
|
97
|
+
PERM_USE_AI: 1n << 78n,
|
|
98
|
+
PERM_USE_BLOCKLY: 1n << 79n,
|
|
99
|
+
PERM_DOWNLOAD_FIRST_WA: 1n << 80n,
|
|
100
|
+
PERM_CONTEST_PDF: 1n << 81n,
|
|
101
|
+
PERM_CONTEST_BULK_SUBMIT: 1n << 82n,
|
|
102
|
+
|
|
103
|
+
// Placeholder
|
|
104
|
+
PERM_ALL: -1n,
|
|
105
|
+
PERM_BASIC: 0n,
|
|
106
|
+
PERM_DEFAULT: 0n,
|
|
107
|
+
PERM_ADMIN: -1n,
|
|
108
|
+
|
|
109
|
+
PERM_NEVER: 1n << 60n,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
PERM.PERM_BASIC = PERM.PERM_VIEW
|
|
113
|
+
| PERM.PERM_VIEW_PROBLEM
|
|
114
|
+
| PERM.PERM_VIEW_PROBLEM_SOLUTION
|
|
115
|
+
| PERM.PERM_VIEW_PROBLEM_SOLUTION_ACCEPT
|
|
116
|
+
| PERM.PERM_VIEW_DISCUSSION
|
|
117
|
+
| PERM.PERM_VIEW_CONTEST
|
|
118
|
+
| PERM.PERM_VIEW_CONTEST_SCOREBOARD
|
|
119
|
+
| PERM.PERM_VIEW_HOMEWORK
|
|
120
|
+
| PERM.PERM_VIEW_HOMEWORK_SCOREBOARD
|
|
121
|
+
| PERM.PERM_VIEW_TRAINING
|
|
122
|
+
| PERM.PERM_VIEW_RANKING
|
|
123
|
+
| PERM.PERM_VIEW_COURSE
|
|
124
|
+
| PERM.PERM_USE_BLOCKLY;
|
|
125
|
+
|
|
126
|
+
PERM.PERM_DEFAULT = PERM.PERM_VIEW
|
|
127
|
+
| PERM.PERM_VIEW_USER_PRIVATE_INFO
|
|
128
|
+
| PERM.PERM_VIEW_PROBLEM
|
|
129
|
+
| PERM.PERM_EDIT_PROBLEM_SELF
|
|
130
|
+
| PERM.PERM_SUBMIT_PROBLEM
|
|
131
|
+
| PERM.PERM_VIEW_PROBLEM_SOLUTION
|
|
132
|
+
| PERM.PERM_VIEW_PROBLEM_SOLUTION_ACCEPT
|
|
133
|
+
| PERM.PERM_CREATE_PROBLEM_SOLUTION
|
|
134
|
+
| PERM.PERM_VOTE_PROBLEM_SOLUTION
|
|
135
|
+
| PERM.PERM_EDIT_PROBLEM_SOLUTION_SELF
|
|
136
|
+
| PERM.PERM_DELETE_PROBLEM_SOLUTION_SELF
|
|
137
|
+
| PERM.PERM_REPLY_PROBLEM_SOLUTION
|
|
138
|
+
| PERM.PERM_EDIT_PROBLEM_SOLUTION_REPLY_SELF
|
|
139
|
+
| PERM.PERM_DELETE_PROBLEM_SOLUTION_REPLY_SELF
|
|
140
|
+
| PERM.PERM_VIEW_DISCUSSION
|
|
141
|
+
| PERM.PERM_CREATE_DISCUSSION
|
|
142
|
+
| PERM.PERM_EDIT_DISCUSSION_SELF
|
|
143
|
+
| PERM.PERM_REPLY_DISCUSSION
|
|
144
|
+
| PERM.PERM_ADD_REACTION
|
|
145
|
+
| PERM.PERM_EDIT_DISCUSSION_REPLY_SELF
|
|
146
|
+
| PERM.PERM_DELETE_DISCUSSION_REPLY_SELF
|
|
147
|
+
| PERM.PERM_DELETE_DISCUSSION_REPLY_SELF_DISCUSSION
|
|
148
|
+
| PERM.PERM_VIEW_CONTEST
|
|
149
|
+
| PERM.PERM_VIEW_CONTEST_SCOREBOARD
|
|
150
|
+
| PERM.PERM_ATTEND_CONTEST
|
|
151
|
+
| PERM.PERM_EDIT_CONTEST_SELF
|
|
152
|
+
| PERM.PERM_VIEW_HOMEWORK
|
|
153
|
+
| PERM.PERM_VIEW_HOMEWORK_SCOREBOARD
|
|
154
|
+
| PERM.PERM_ATTEND_HOMEWORK
|
|
155
|
+
| PERM.PERM_EDIT_HOMEWORK_SELF
|
|
156
|
+
| PERM.PERM_VIEW_TRAINING
|
|
157
|
+
| PERM.PERM_CREATE_TRAINING
|
|
158
|
+
| PERM.PERM_EDIT_TRAINING_SELF
|
|
159
|
+
| PERM.PERM_SUBMIT_PROBLEM
|
|
160
|
+
| PERM.PERM_CREATE_PROBLEM_SOLUTION
|
|
161
|
+
| PERM.PERM_VOTE_PROBLEM_SOLUTION
|
|
162
|
+
| PERM.PERM_REPLY_PROBLEM_SOLUTION
|
|
163
|
+
| PERM.PERM_CREATE_DISCUSSION
|
|
164
|
+
| PERM.PERM_REPLY_DISCUSSION
|
|
165
|
+
| PERM.PERM_ATTEND_CONTEST
|
|
166
|
+
| PERM.PERM_CREATE_TRAINING
|
|
167
|
+
| PERM.PERM_ATTEND_HOMEWORK
|
|
168
|
+
| PERM.PERM_VIEW_RANKING
|
|
169
|
+
| PERM.PERM_VIEW_RECORD
|
|
170
|
+
| PERM.PERM_VIEW_COURSE
|
|
171
|
+
| PERM.PERM_VIEW_EXAM
|
|
172
|
+
| PERM.PERM_USE_AI
|
|
173
|
+
| PERM.PERM_USE_BLOCKLY
|
|
174
|
+
| PERM.PERM_DOWNLOAD_FIRST_WA
|
|
175
|
+
| PERM.PERM_CONTEST_PDF;
|
|
176
|
+
|
|
177
|
+
PERM.PERM_ADMIN = PERM.PERM_ALL;
|
|
178
|
+
|
|
179
|
+
export const PRIV = {
|
|
180
|
+
PRIV_NONE: 0,
|
|
181
|
+
PRIV_MOD_BADGE: 1 << 25,
|
|
182
|
+
PRIV_EDIT_SYSTEM: 1 << 0, // renamed from PRIV_SET_PRIV
|
|
183
|
+
PRIV_SET_PERM: 1 << 1,
|
|
184
|
+
PRIV_USER_PROFILE: 1 << 2,
|
|
185
|
+
PRIV_REGISTER_USER: 1 << 3,
|
|
186
|
+
PRIV_READ_PROBLEM_DATA: 1 << 4,
|
|
187
|
+
PRIV_READ_RECORD_CODE: 1 << 7,
|
|
188
|
+
PRIV_VIEW_HIDDEN_RECORD: 1 << 8,
|
|
189
|
+
PRIV_JUDGE: 1 << 9, // (renamed)
|
|
190
|
+
PRIV_CREATE_DOMAIN: 1 << 10,
|
|
191
|
+
PRIV_VIEW_ALL_DOMAIN: 1 << 11,
|
|
192
|
+
PRIV_MANAGE_ALL_DOMAIN: 1 << 12,
|
|
193
|
+
PRIV_REJUDGE: 1 << 13,
|
|
194
|
+
PRIV_VIEW_USER_SECRET: 1 << 14,
|
|
195
|
+
PRIV_VIEW_JUDGE_STATISTICS: 1 << 15,
|
|
196
|
+
PRIV_UNLIMITED_ACCESS: 1 << 22,
|
|
197
|
+
PRIV_VIEW_SYSTEM_NOTIFICATION: 1 << 23,
|
|
198
|
+
PRIV_SEND_MESSAGE: 1 << 24,
|
|
199
|
+
PRIV_CREATE_FILE: 1 << 16,
|
|
200
|
+
PRIV_UNLIMITED_QUOTA: 1 << 17,
|
|
201
|
+
PRIV_DELETE_FILE: 1 << 18,
|
|
202
|
+
|
|
203
|
+
PRIV_ALL: -1,
|
|
204
|
+
PRIV_DEFAULT: 0,
|
|
205
|
+
PRIV_NEVER: 1 << 20,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
PRIV.PRIV_DEFAULT = PRIV.PRIV_USER_PROFILE
|
|
209
|
+
+ PRIV.PRIV_CREATE_FILE
|
|
210
|
+
+ PRIV.PRIV_SEND_MESSAGE;
|
package/status.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export enum STATUS {
|
|
2
|
+
STATUS_WAITING = 0,
|
|
3
|
+
STATUS_ACCEPTED = 1,
|
|
4
|
+
STATUS_WRONG_ANSWER = 2,
|
|
5
|
+
STATUS_TIME_LIMIT_EXCEEDED = 3,
|
|
6
|
+
STATUS_MEMORY_LIMIT_EXCEEDED = 4,
|
|
7
|
+
STATUS_OUTPUT_LIMIT_EXCEEDED = 5,
|
|
8
|
+
STATUS_RUNTIME_ERROR = 6,
|
|
9
|
+
STATUS_COMPILE_ERROR = 7,
|
|
10
|
+
STATUS_SYSTEM_ERROR = 8,
|
|
11
|
+
STATUS_CANCELED = 9,
|
|
12
|
+
STATUS_ETC = 10,
|
|
13
|
+
STATUS_HACKED = 11,
|
|
14
|
+
STATUS_JUDGING = 20,
|
|
15
|
+
STATUS_COMPILING = 21,
|
|
16
|
+
STATUS_FETCHED = 22,
|
|
17
|
+
STATUS_IGNORED = 30,
|
|
18
|
+
STATUS_FORMAT_ERROR = 31,
|
|
19
|
+
STATUS_HACK_SUCCESSFUL = 32,
|
|
20
|
+
STATUS_HACK_UNSUCCESSFUL = 33,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const STATUS_TEXTS: Record<STATUS, string> = {
|
|
24
|
+
[STATUS.STATUS_WAITING]: 'Waiting',
|
|
25
|
+
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
|
26
|
+
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
|
27
|
+
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'Time Exceeded',
|
|
28
|
+
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'Memory Exceeded',
|
|
29
|
+
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'Output Exceeded',
|
|
30
|
+
[STATUS.STATUS_RUNTIME_ERROR]: 'Runtime Error',
|
|
31
|
+
[STATUS.STATUS_COMPILE_ERROR]: 'Compile Error',
|
|
32
|
+
[STATUS.STATUS_SYSTEM_ERROR]: 'System Error',
|
|
33
|
+
[STATUS.STATUS_CANCELED]: 'Cancelled',
|
|
34
|
+
[STATUS.STATUS_ETC]: 'Unknown Error',
|
|
35
|
+
[STATUS.STATUS_HACKED]: 'Hacked',
|
|
36
|
+
[STATUS.STATUS_JUDGING]: 'Running',
|
|
37
|
+
[STATUS.STATUS_COMPILING]: 'Compiling',
|
|
38
|
+
[STATUS.STATUS_FETCHED]: 'Fetched',
|
|
39
|
+
[STATUS.STATUS_IGNORED]: 'Ignored',
|
|
40
|
+
[STATUS.STATUS_FORMAT_ERROR]: 'Format Error',
|
|
41
|
+
[STATUS.STATUS_HACK_SUCCESSFUL]: 'Hack Successful',
|
|
42
|
+
[STATUS.STATUS_HACK_UNSUCCESSFUL]: 'Hack Unsuccessful',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const STATUS_SHORT_TEXTS: Partial<Record<STATUS, string>> = {
|
|
46
|
+
[STATUS.STATUS_ACCEPTED]: 'AC',
|
|
47
|
+
[STATUS.STATUS_WRONG_ANSWER]: 'WA',
|
|
48
|
+
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'TLE',
|
|
49
|
+
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'MLE',
|
|
50
|
+
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'OLE',
|
|
51
|
+
[STATUS.STATUS_RUNTIME_ERROR]: 'RE',
|
|
52
|
+
[STATUS.STATUS_COMPILE_ERROR]: 'CE',
|
|
53
|
+
[STATUS.STATUS_SYSTEM_ERROR]: 'SE',
|
|
54
|
+
[STATUS.STATUS_CANCELED]: 'IGN',
|
|
55
|
+
[STATUS.STATUS_HACKED]: 'HK',
|
|
56
|
+
[STATUS.STATUS_IGNORED]: 'IGN',
|
|
57
|
+
[STATUS.STATUS_FORMAT_ERROR]: 'FE',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const STATUS_CODES: Record<STATUS, string> = {
|
|
61
|
+
[STATUS.STATUS_WAITING]: 'pending',
|
|
62
|
+
[STATUS.STATUS_ACCEPTED]: 'pass',
|
|
63
|
+
[STATUS.STATUS_WRONG_ANSWER]: 'fail',
|
|
64
|
+
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'fail',
|
|
65
|
+
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'fail',
|
|
66
|
+
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'fail',
|
|
67
|
+
[STATUS.STATUS_RUNTIME_ERROR]: 'fail',
|
|
68
|
+
[STATUS.STATUS_COMPILE_ERROR]: 'fail',
|
|
69
|
+
[STATUS.STATUS_SYSTEM_ERROR]: 'fail',
|
|
70
|
+
[STATUS.STATUS_CANCELED]: 'ignored',
|
|
71
|
+
[STATUS.STATUS_ETC]: 'fail',
|
|
72
|
+
[STATUS.STATUS_HACKED]: 'fail',
|
|
73
|
+
[STATUS.STATUS_JUDGING]: 'progress',
|
|
74
|
+
[STATUS.STATUS_COMPILING]: 'progress',
|
|
75
|
+
[STATUS.STATUS_FETCHED]: 'progress',
|
|
76
|
+
[STATUS.STATUS_IGNORED]: 'ignored',
|
|
77
|
+
[STATUS.STATUS_FORMAT_ERROR]: 'ignored',
|
|
78
|
+
[STATUS.STATUS_HACK_SUCCESSFUL]: 'pass',
|
|
79
|
+
[STATUS.STATUS_HACK_UNSUCCESSFUL]: 'fail',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const NORMAL_STATUS: STATUS[] = [
|
|
83
|
+
STATUS.STATUS_ACCEPTED,
|
|
84
|
+
STATUS.STATUS_WRONG_ANSWER,
|
|
85
|
+
STATUS.STATUS_TIME_LIMIT_EXCEEDED,
|
|
86
|
+
STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
|
|
87
|
+
STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED,
|
|
88
|
+
STATUS.STATUS_RUNTIME_ERROR,
|
|
89
|
+
STATUS.STATUS_COMPILE_ERROR,
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
export function getScoreColor(score: number | string): string {
|
|
93
|
+
if (score === null || score === undefined || !Number.isFinite(+score)) return '#000000';
|
|
94
|
+
return [
|
|
95
|
+
'#ff4f4f',
|
|
96
|
+
'#ff694f',
|
|
97
|
+
'#f8603a',
|
|
98
|
+
'#fc8354',
|
|
99
|
+
'#fa9231',
|
|
100
|
+
'#f7bb3b',
|
|
101
|
+
'#ecdb44',
|
|
102
|
+
'#e2ec52',
|
|
103
|
+
'#b0d628',
|
|
104
|
+
'#93b127',
|
|
105
|
+
'#25ad40',
|
|
106
|
+
][Math.floor((Number(score) || 0) / 10)];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const USER_GENDER_MALE = 0;
|
|
110
|
+
export const USER_GENDER_FEMALE = 1;
|
|
111
|
+
export const USER_GENDER_OTHER = 2;
|
|
112
|
+
export const USER_GENDERS = [USER_GENDER_MALE, USER_GENDER_FEMALE, USER_GENDER_OTHER];
|
|
113
|
+
export const USER_GENDER_RANGE = {
|
|
114
|
+
[USER_GENDER_MALE]: 'Boy ♂',
|
|
115
|
+
[USER_GENDER_FEMALE]: 'Girl ♀',
|
|
116
|
+
[USER_GENDER_OTHER]: 'Other',
|
|
117
|
+
};
|
|
118
|
+
export const USER_GENDER_ICONS = {
|
|
119
|
+
[USER_GENDER_MALE]: '♂',
|
|
120
|
+
[USER_GENDER_FEMALE]: '♀',
|
|
121
|
+
[USER_GENDER_OTHER]: '?',
|
|
122
|
+
};
|
package/subtask.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { parseMemoryMB, parseTimeMS, sortFiles } from '@halooj/utils/lib/common';
|
|
2
|
+
|
|
3
|
+
export function convertIniConfig(ini: string) {
|
|
4
|
+
const f = ini.split('\n');
|
|
5
|
+
const count = Number.parseInt(f[0], 10);
|
|
6
|
+
const res = { subtasks: [] };
|
|
7
|
+
for (let i = 1; i <= count; i++) {
|
|
8
|
+
if (!f[i]?.trim()) throw new Error('Testdata count incorrect.');
|
|
9
|
+
const [input, output, time, score, memory] = f[i].split('|');
|
|
10
|
+
const cur = {
|
|
11
|
+
cases: [{ input, output }],
|
|
12
|
+
score: Number.parseInt(score, 10),
|
|
13
|
+
time: `${time}s`,
|
|
14
|
+
memory: '256m',
|
|
15
|
+
};
|
|
16
|
+
if (!Number.isNaN(Number.parseInt(memory, 10))) cur.memory = `${Math.floor(Number.parseInt(memory, 10) / 1024)}m`;
|
|
17
|
+
res.subtasks.push(cur);
|
|
18
|
+
}
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface MatchRule {
|
|
23
|
+
regex: RegExp;
|
|
24
|
+
output: (a: RegExpExecArray) => string[];
|
|
25
|
+
id: (a: RegExpExecArray) => number;
|
|
26
|
+
subtask: (a: RegExpExecArray) => number;
|
|
27
|
+
preferredScorerType: (a: RegExpExecArray) => 'min' | 'max' | 'sum';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SubtaskMatcher: MatchRule[] = [
|
|
31
|
+
{
|
|
32
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
33
|
+
regex: /^(([\w.-]*?)(?:(\d*)[-_])?(\d+))\.(in|IN|txt|TXT|in\.txt|IN\.TXT)$/,
|
|
34
|
+
output: (a) => ['out', 'ans']
|
|
35
|
+
.flatMap((i) => [i, i.toUpperCase(), `${i}.txt`, `${i.toUpperCase()}.TXT`])
|
|
36
|
+
.flatMap((i) => [`${a[1]}.${i}`, `${a[1]}.${i}`.replace(/input/g, 'output').replace(/INPUT/g, 'OUTPUT')])
|
|
37
|
+
.concat(a[1].includes('input') ? `${a[1]}.txt`.replace(/input/g, 'output') : null),
|
|
38
|
+
id: (a) => +a[4],
|
|
39
|
+
subtask: (a) => +(a[3] || 1),
|
|
40
|
+
preferredScorerType: (a) => (a[3] ? 'min' : 'sum'),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
regex: /^(\D*)\.(in|IN)(\d+)$/,
|
|
44
|
+
output: (a) => [
|
|
45
|
+
`${a[1]}.${a[2] === 'in' ? 'ou' : 'OU'}${a[3]}`,
|
|
46
|
+
`${a[1]}.${a[2] === 'in' ? 'out' : 'OUT'}${a[3]}`,
|
|
47
|
+
].flatMap((i) => [i, i.replace(/input/g, 'output').replace(/INPUT/g, 'OUTPUT')]),
|
|
48
|
+
id: (a) => +a[2],
|
|
49
|
+
subtask: () => 1,
|
|
50
|
+
preferredScorerType: () => 'sum',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
regex: /^(\D*)([0-9]+)([-_])([0-9]+)\.(in|IN)$/,
|
|
54
|
+
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}${a[2]}${a[3]}${a[4]}.${i}`),
|
|
55
|
+
id: (a) => +a[4],
|
|
56
|
+
subtask: (a) => +a[2],
|
|
57
|
+
preferredScorerType: () => 'min',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
regex: /^(([0-9]+)[-_].*)\.(in|IN)$/,
|
|
61
|
+
output: (a) => ['out', 'ans', 'OUT', 'ANS'].flatMap((i) => `${a[1]}.${i}`),
|
|
62
|
+
id: (a) => +a[2],
|
|
63
|
+
subtask: () => 1,
|
|
64
|
+
preferredScorerType: () => 'sum',
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
function* getScore(totalScore: number, count: number) {
|
|
69
|
+
const base = Math.floor(totalScore / count);
|
|
70
|
+
const extra = count - (totalScore % count);
|
|
71
|
+
for (let i = 0; i < count; i++) {
|
|
72
|
+
if (i >= extra) yield base + 1;
|
|
73
|
+
else yield base;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface ParsedCase {
|
|
78
|
+
id?: number;
|
|
79
|
+
time?: number | string;
|
|
80
|
+
memory?: number | string;
|
|
81
|
+
score?: number;
|
|
82
|
+
input?: string;
|
|
83
|
+
output?: string;
|
|
84
|
+
}
|
|
85
|
+
interface ParsedSubtask {
|
|
86
|
+
cases: ParsedCase[];
|
|
87
|
+
type: 'min' | 'max' | 'sum';
|
|
88
|
+
time?: number | string;
|
|
89
|
+
memory?: number | string;
|
|
90
|
+
score?: number;
|
|
91
|
+
id?: number;
|
|
92
|
+
if?: number[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function readSubtasksFromFiles(files: string[], config) {
|
|
96
|
+
const subtask: Record<number, ParsedSubtask> = {};
|
|
97
|
+
for (const s of config.subtasks || []) if (s.id && Number.isSafeInteger(s.id)) subtask[s.id] = s;
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
let match = false;
|
|
100
|
+
for (const rule of SubtaskMatcher) {
|
|
101
|
+
const data = rule.regex.exec(file);
|
|
102
|
+
if (!data) continue;
|
|
103
|
+
const sid = rule.subtask(data);
|
|
104
|
+
const c = { input: file, output: '', id: rule.id(data) };
|
|
105
|
+
const type = rule.preferredScorerType(data);
|
|
106
|
+
const outputs = (config.noOutputFile ? ['/dev/null'] : rule.output(data)).filter((i) => i);
|
|
107
|
+
for (const output of outputs) {
|
|
108
|
+
if (output === file) continue;
|
|
109
|
+
if (output === '/dev/null' || files.includes(output)) {
|
|
110
|
+
match = true;
|
|
111
|
+
c.output = output;
|
|
112
|
+
if (!subtask[sid]) {
|
|
113
|
+
subtask[sid] = {
|
|
114
|
+
time: config.time,
|
|
115
|
+
memory: config.memory,
|
|
116
|
+
type,
|
|
117
|
+
cases: [c],
|
|
118
|
+
id: sid,
|
|
119
|
+
};
|
|
120
|
+
} else if (!subtask[sid].cases) subtask[sid].cases = [c];
|
|
121
|
+
else if (!subtask[sid].cases.find((i) => i.input === c.input && i.output === c.output)) {
|
|
122
|
+
subtask[sid].cases.push(c);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (match) break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const id in subtask) subtask[id].cases = sortFiles(subtask[id].cases, 'input');
|
|
131
|
+
return Object.values(subtask);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface NormalizedCase extends Required<ParsedCase> {
|
|
135
|
+
time: number;
|
|
136
|
+
memory: number;
|
|
137
|
+
}
|
|
138
|
+
export interface NormalizedSubtask extends Required<ParsedSubtask> {
|
|
139
|
+
cases: NormalizedCase[];
|
|
140
|
+
time: number;
|
|
141
|
+
memory: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function normalizeSubtasks(
|
|
145
|
+
subtasks: ParsedSubtask[], checkFile: (name: string, errMsg: string) => string,
|
|
146
|
+
time: number | string = '1000ms', memory: number | string = '256m', ignoreParseError = false,
|
|
147
|
+
timeRate = 1, memoryRate = 1,
|
|
148
|
+
): NormalizedSubtask[] {
|
|
149
|
+
subtasks.sort((a, b) => (a.id - b.id));
|
|
150
|
+
const subtaskScore = getScore(
|
|
151
|
+
Math.max(100 - Math.sum(subtasks.map((i) => i.score || 0)), 0),
|
|
152
|
+
subtasks.filter((i) => !i.score).length,
|
|
153
|
+
);
|
|
154
|
+
return subtasks.map((s, id) => {
|
|
155
|
+
s.cases.sort((a, b) => (a.id - b.id));
|
|
156
|
+
const score = s.score || subtaskScore.next().value as number;
|
|
157
|
+
const caseScore = getScore(
|
|
158
|
+
Math.max(score - Math.sum(s.cases.map((i) => i.score || 0)), 0),
|
|
159
|
+
s.cases.filter((i) => !i.score).length,
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
id: id + 1,
|
|
163
|
+
type: 'min',
|
|
164
|
+
if: [],
|
|
165
|
+
...s,
|
|
166
|
+
score,
|
|
167
|
+
time: parseTimeMS(s.time || time, !ignoreParseError) * timeRate,
|
|
168
|
+
memory: parseMemoryMB(s.memory || memory, !ignoreParseError) * memoryRate,
|
|
169
|
+
cases: s.cases.map((c, index) => ({
|
|
170
|
+
id: index + 1,
|
|
171
|
+
...c,
|
|
172
|
+
score: c.score || (s.type === 'sum' ? caseScore.next().value as number : score),
|
|
173
|
+
time: parseTimeMS(c.time || s.time || time, !ignoreParseError) * timeRate,
|
|
174
|
+
memory: parseMemoryMB(c.memory || s.memory || memory, !ignoreParseError) * memoryRate,
|
|
175
|
+
input: c.input ? checkFile(c.input, 'Cannot find input file {0}.') : '/dev/null',
|
|
176
|
+
output: c.output ? checkFile(c.output, 'Cannot find output file {0}.') : '/dev/null',
|
|
177
|
+
})) as NormalizedCase[],
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { readSubtasksFromFiles } from '../subtask';
|
|
4
|
+
|
|
5
|
+
interface Info {
|
|
6
|
+
subtask: number;
|
|
7
|
+
id: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function shouldFindTestcase(files: string[], info?: Info) {
|
|
11
|
+
const subtasks = readSubtasksFromFiles(files, {});
|
|
12
|
+
if (files.includes('a2_1.in')) console.log(subtasks);
|
|
13
|
+
expect(subtasks).to.be.lengthOf(1);
|
|
14
|
+
if (info?.subtask) expect(subtasks[0].id).to.equal(info.subtask);
|
|
15
|
+
const cases = subtasks.map((i) => i.cases).flat();
|
|
16
|
+
expect(cases).to.be.lengthOf(1);
|
|
17
|
+
if (info?.id) expect(cases[0].id).to.deep.equal(info.id);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('single case', () => {
|
|
21
|
+
it('1.in/1.out', () => {
|
|
22
|
+
shouldFindTestcase(['1.in', '1.out']);
|
|
23
|
+
});
|
|
24
|
+
it('1.in/2.ans', () => {
|
|
25
|
+
shouldFindTestcase(['1.in', '1.ans']);
|
|
26
|
+
});
|
|
27
|
+
it('file1.in/file1.out', () => {
|
|
28
|
+
shouldFindTestcase(['file1.in', 'file1.out']);
|
|
29
|
+
});
|
|
30
|
+
it('file.in1/file.ou1', () => {
|
|
31
|
+
shouldFindTestcase(['file.in1', 'file.ou1']);
|
|
32
|
+
});
|
|
33
|
+
it('input1.txt/output1.txt', () => {
|
|
34
|
+
shouldFindTestcase(['input1.txt', 'output1.txt']);
|
|
35
|
+
});
|
|
36
|
+
it('data.1.in/data.1.out', () => {
|
|
37
|
+
shouldFindTestcase(['data.1.in', 'data.1.out']);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('subtask', () => {
|
|
41
|
+
it('1_1.in/1_1.out', () => {
|
|
42
|
+
shouldFindTestcase(['1_1.in', '1_1.out'], { subtask: 1, id: 1 });
|
|
43
|
+
});
|
|
44
|
+
it('a1_1.in/a1_1.out', () => {
|
|
45
|
+
shouldFindTestcase(['a1_1.in', 'a1_1.out'], { subtask: 1, id: 1 });
|
|
46
|
+
});
|
|
47
|
+
it('a01_01.in/a01_01.out', () => {
|
|
48
|
+
shouldFindTestcase(['a01_01.in', 'a01_01.out'], { subtask: 1, id: 1 });
|
|
49
|
+
});
|
|
50
|
+
it('1-1.in/1-1.out', () => {
|
|
51
|
+
shouldFindTestcase(['1-1.in', '1-1.out'], { subtask: 1, id: 1 });
|
|
52
|
+
});
|
|
53
|
+
it('a1-1.in/a1-1.out', () => {
|
|
54
|
+
shouldFindTestcase(['a1-1.in', 'a1-1.out'], { subtask: 1, id: 1 });
|
|
55
|
+
});
|
|
56
|
+
it('subtask_1_1.in/subtask_1_1.out', () => {
|
|
57
|
+
shouldFindTestcase(['subtask_1_1.in', 'subtask_1_1.out'], { subtask: 1, id: 1 });
|
|
58
|
+
});
|
|
59
|
+
it('01sample-01.in/01sample-01.out', () => {
|
|
60
|
+
shouldFindTestcase(['01sample-01.in', '01sample-01.out'], { subtask: 1, id: 1 });
|
|
61
|
+
});
|
|
62
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"compilerOptions":{"target":"es2022","lib":["esnext"],"module":"preserve","esModuleInterop":true,"moduleResolution":"bundler","jsx":"react-jsx","sourceMap":false,"composite":true,"strict":false,"strictBindCallApply":true,"resolveJsonModule":true,"experimentalDecorators":true,"incremental":true,"outDir":"/Users/mac/Desktop/HaloTeam/HaloOJ/未命名/.cache/ts-out/packages/common","rootDir":".","paths":{"vj/*":["../../packages/ui-default/*"]}},"include":["**/*.ts","**/*.tsx"],"exclude":["**/public","**/frontend","**/node_modules","**/bin","**/dist","**/__mocks__"]}
|
package/types.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
export type CompilableSource = string | {
|
|
2
|
+
file: string;
|
|
3
|
+
lang: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export enum ProblemType {
|
|
7
|
+
Default = 'default',
|
|
8
|
+
SubmitAnswer = 'submit_answer',
|
|
9
|
+
Interactive = 'interactive',
|
|
10
|
+
Communication = 'communication',
|
|
11
|
+
Objective = 'objective',
|
|
12
|
+
Remote = 'remote_judge',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TestCaseConfig {
|
|
16
|
+
input: string;
|
|
17
|
+
output: string;
|
|
18
|
+
time?: string;
|
|
19
|
+
memory?: string;
|
|
20
|
+
score?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export enum SubtaskType {
|
|
24
|
+
min = 'min',
|
|
25
|
+
max = 'max',
|
|
26
|
+
sum = 'sum',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SubtaskConfig {
|
|
30
|
+
time?: string;
|
|
31
|
+
memory?: string;
|
|
32
|
+
score?: number;
|
|
33
|
+
if?: number[];
|
|
34
|
+
id?: number;
|
|
35
|
+
type?: SubtaskType;
|
|
36
|
+
cases?: TestCaseConfig[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type DetailType = 'full' | 'case' | 'none';
|
|
40
|
+
|
|
41
|
+
export interface ProblemConfigFile {
|
|
42
|
+
type?: ProblemType;
|
|
43
|
+
subType?: string;
|
|
44
|
+
target?: string;
|
|
45
|
+
score?: number;
|
|
46
|
+
time?: string;
|
|
47
|
+
memory?: string;
|
|
48
|
+
filename?: string;
|
|
49
|
+
checker_type?: string;
|
|
50
|
+
num_processes?: number;
|
|
51
|
+
user_extra_files?: string[];
|
|
52
|
+
judge_extra_files?: string[];
|
|
53
|
+
detail?: DetailType | boolean;
|
|
54
|
+
answers?: Record<string, [string | string[], number]>;
|
|
55
|
+
redirect?: string;
|
|
56
|
+
cases?: TestCaseConfig[];
|
|
57
|
+
subtasks?: SubtaskConfig[];
|
|
58
|
+
langs?: string[];
|
|
59
|
+
multi_pass?: number;
|
|
60
|
+
checker?: CompilableSource;
|
|
61
|
+
interactor?: CompilableSource;
|
|
62
|
+
manager?: CompilableSource;
|
|
63
|
+
validator?: CompilableSource;
|
|
64
|
+
time_limit_rate?: Record<string, number>;
|
|
65
|
+
memory_limit_rate?: Record<string, number>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface FileInfo {
|
|
69
|
+
/** storage path */
|
|
70
|
+
_id: string;
|
|
71
|
+
/** filename */
|
|
72
|
+
name: string;
|
|
73
|
+
/** file size (in bytes) */
|
|
74
|
+
size: number;
|
|
75
|
+
etag: string;
|
|
76
|
+
lastModified: Date;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface JudgeMeta {
|
|
80
|
+
problemOwner: number;
|
|
81
|
+
hackRejudge?: string;
|
|
82
|
+
rejudge?: boolean | 'controlled';
|
|
83
|
+
// FIXME stricter types
|
|
84
|
+
type?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface RecordJudgeInfo {
|
|
88
|
+
score: number;
|
|
89
|
+
memory: number;
|
|
90
|
+
time: number;
|
|
91
|
+
judgeTexts: (string | JudgeMessage)[];
|
|
92
|
+
compilerTexts: string[];
|
|
93
|
+
testCases: Required<TestCase>[];
|
|
94
|
+
/** judge uid */
|
|
95
|
+
judger: number;
|
|
96
|
+
judgeAt: Date;
|
|
97
|
+
status: number;
|
|
98
|
+
subtasks?: Record<number, SubtaskResult>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface RecordPayload extends RecordJudgeInfo {
|
|
102
|
+
domainId: string;
|
|
103
|
+
pid: number;
|
|
104
|
+
uid: number;
|
|
105
|
+
lang: string;
|
|
106
|
+
code: string;
|
|
107
|
+
rejudged: boolean;
|
|
108
|
+
source?: string;
|
|
109
|
+
progress?: number;
|
|
110
|
+
/** pretest */
|
|
111
|
+
input?: string | string[];
|
|
112
|
+
/** hack target rid */
|
|
113
|
+
hackTarget?: string;
|
|
114
|
+
/** 0 if pretest&script */
|
|
115
|
+
contest?: string;
|
|
116
|
+
|
|
117
|
+
files?: Record<string, string>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface JudgeRequest extends Omit<RecordPayload, 'testCases'> {
|
|
121
|
+
priority: number;
|
|
122
|
+
type: 'judge' | 'generate';
|
|
123
|
+
rid: string;
|
|
124
|
+
config: ProblemConfigFile;
|
|
125
|
+
meta: JudgeMeta;
|
|
126
|
+
data: FileInfo[];
|
|
127
|
+
source: string;
|
|
128
|
+
trusted: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface TestCase {
|
|
132
|
+
id?: number;
|
|
133
|
+
subtaskId?: number;
|
|
134
|
+
score?: number;
|
|
135
|
+
time: number;
|
|
136
|
+
memory: number;
|
|
137
|
+
status: number;
|
|
138
|
+
message: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface JudgeMessage {
|
|
142
|
+
message: string;
|
|
143
|
+
params?: string[];
|
|
144
|
+
stack?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface SubtaskResult {
|
|
148
|
+
type: SubtaskType;
|
|
149
|
+
score: number;
|
|
150
|
+
status: number;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface JudgeResultBody {
|
|
154
|
+
key: string;
|
|
155
|
+
domainId: string;
|
|
156
|
+
rid: string;
|
|
157
|
+
judger?: number;
|
|
158
|
+
progress?: number;
|
|
159
|
+
addProgress?: number;
|
|
160
|
+
case?: TestCase;
|
|
161
|
+
cases?: TestCase[];
|
|
162
|
+
status?: number;
|
|
163
|
+
score?: number;
|
|
164
|
+
/** in miliseconds */
|
|
165
|
+
time?: number;
|
|
166
|
+
/** in kilobytes */
|
|
167
|
+
memory?: number;
|
|
168
|
+
message?: string | JudgeMessage;
|
|
169
|
+
compilerText?: string;
|
|
170
|
+
nop?: boolean;
|
|
171
|
+
subtasks?: Record<number, SubtaskResult>;
|
|
172
|
+
}
|