| 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; |