mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
1 parent
e0db6c6d47
commit
b7dc35d795
6 files changed
+177
-3
No files matched your search
@@ -81,8 +81,10 @@ Hydro 同时支持 VJudge,这意味着你可以直接在系统内导入其他
|
|||||||
- [UOJ](https://uoj.ac):国内知名 OJ,国家集训队常用;
|
- [UOJ](https://uoj.ac):国内知名 OJ,国家集训队常用;
|
||||||
- [SPOJ](https://www.spoj.com):国内连接很不稳定,不推荐;
|
- [SPOJ](https://www.spoj.com):国内连接很不稳定,不推荐;
|
||||||
- [洛谷](https://www.luogu.com.cn):使用此功能需要向洛谷购买授权;
|
- [洛谷](https://www.luogu.com.cn):使用此功能需要向洛谷购买授权;
|
||||||
|
- [HDUOJ](https://acm.hdu.edu.cn):杭州电子科技大学在线评测系统,其中包含多校训练题;
|
||||||
- [CSGOJ](https://cpc.csgrandeur.cn):广东省赛与湖南省赛赛题评测平台;
|
- [CSGOJ](https://cpc.csgrandeur.cn):广东省赛与湖南省赛赛题评测平台;
|
||||||
- [POJ](https://poj.org):较为古董,服务器稳定性差;
|
- [POJ](https://poj.org):较为古董,服务器稳定性差;
|
||||||
|
- [YACS](https://iai.sh.cn)上海市计算机学会竞赛平台;
|
||||||
- HUSTOJ:理论上支持所有 HUSTOJ 驱动的系统,但由于各个系统中 UI 有差异,通常需要手动适配。
|
- HUSTOJ:理论上支持所有 HUSTOJ 驱动的系统,但由于各个系统中 UI 有差异,通常需要手动适配。
|
||||||
|
|
||||||
### 多赛制支持
|
### 多赛制支持
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export async function next(body: Partial<JudgeResultBody> & { rdoc: RecordDoc })
|
|||||||
if (!rdoc) return null;
|
if (!rdoc) return null;
|
||||||
}
|
}
|
||||||
const { $set, $push } = processPayload(rdoc, body);
|
const { $set, $push } = processPayload(rdoc, body);
|
||||||
rdoc = await record.update(body.rdoc.domainId, body.rid, $set, $push, {}, body.addProgress ? { progress: body.addProgress } : {});
|
rdoc = await record.update(rdoc.domainId, body.rid, $set, $push, {}, body.addProgress ? { progress: body.addProgress } : {});
|
||||||
bus.broadcast('record/change', rdoc, $set, $push, body);
|
bus.broadcast('record/change', rdoc, $set, $push, body);
|
||||||
return rdoc;
|
return rdoc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class AccountService {
|
|||||||
async judge(task) {
|
async judge(task) {
|
||||||
const rdoc = await RecordModel.get(task.domainId, task.rid);
|
const rdoc = await RecordModel.get(task.domainId, task.rid);
|
||||||
task = Object.assign(rdoc, task);
|
task = Object.assign(rdoc, task);
|
||||||
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid });
|
const next = (payload) => JudgeHandler.next({ ...payload, rid: task.rid, rdoc });
|
||||||
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid });
|
const end = (payload) => JudgeHandler.end({ ...payload, rid: task.rid, rdoc });
|
||||||
await next({ status: STATUS.STATUS_FETCHED });
|
await next({ status: STATUS.STATUS_FETCHED });
|
||||||
try {
|
try {
|
||||||
const langConfig = SettingModel.langs[task.lang];
|
const langConfig = SettingModel.langs[task.lang];
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import luogu from './luogu';
|
|||||||
import poj from './poj';
|
import poj from './poj';
|
||||||
import spoj from './spoj';
|
import spoj from './spoj';
|
||||||
import uoj from './uoj';
|
import uoj from './uoj';
|
||||||
|
import yacs from './yacs';
|
||||||
|
|
||||||
const vjudge: Record<string, any> = {
|
const vjudge: Record<string, any> = {
|
||||||
codeforces,
|
codeforces,
|
||||||
@@ -23,5 +24,6 @@ const vjudge: Record<string, any> = {
|
|||||||
ybt,
|
ybt,
|
||||||
ybtbas,
|
ybtbas,
|
||||||
hduoj,
|
hduoj,
|
||||||
|
yacs,
|
||||||
};
|
};
|
||||||
export default vjudge;
|
export default vjudge;
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
import {
|
||||||
|
Logger, md5, sleep, STATUS, yaml,
|
||||||
|
} from 'hydrooj';
|
||||||
|
import { BasicFetcher } from '../fetch';
|
||||||
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
import { VERDICT } from '../verdict';
|
||||||
|
|
||||||
|
const logger = new Logger('remote/yacs');
|
||||||
|
|
||||||
|
export default class YACSProvider extends BasicFetcher implements IBasicProvider {
|
||||||
|
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||||
|
super(account, 'https://api.iai.sh.cn', 'form', logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData(url: string) {
|
||||||
|
const { text } = await this.get(new URL(url, 'https://iai.sh.cn').toString());
|
||||||
|
const { window: { document } } = new JSDOM(text);
|
||||||
|
return JSON.parse(document.querySelector('script#__NEXT_DATA__').innerHTML).props.pageProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
get loggedIn(): Promise<boolean> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.get('/user/fetchByToken').query({ token: this.token })
|
||||||
|
.end((err, { status }) => resolve(status === 200));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ensureLogin() {
|
||||||
|
if (await this.loggedIn) return true;
|
||||||
|
logger.info('retry login');
|
||||||
|
try {
|
||||||
|
const { body: { token } } = await this.post('/user/login')
|
||||||
|
.send({
|
||||||
|
username: this.account.handle,
|
||||||
|
password: /^[a-f0-9]{32}$/.test(this.account.password)
|
||||||
|
? this.account.password
|
||||||
|
: md5(`${this.account.password}yacs`),
|
||||||
|
});
|
||||||
|
this.token = token;
|
||||||
|
} catch (e) { }
|
||||||
|
return await this.loggedIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProblem(id: string) {
|
||||||
|
logger.info(id);
|
||||||
|
const { problem } = await this.getData(`/problem/${id.split('P')[1]}`);
|
||||||
|
const tag = [];
|
||||||
|
if (problem.level) tag.push(problem.level);
|
||||||
|
if (problem.contest) tag.push(problem.contest.name);
|
||||||
|
let content = '';
|
||||||
|
if (problem.background.trim()) content += `## 题目背景\n\n${problem.background.trim()}\n\n`;
|
||||||
|
content += `## 题目描述\n\n${problem.description.trim()}\n\n`;
|
||||||
|
content += `## 输入格式\n\n${problem.inputFormat.trim()}\n\n`;
|
||||||
|
content += `## 输出格式\n\n${problem.outputFormat.trim()}\n\n`;
|
||||||
|
content += problem.exampleList.map((sample, index) => {
|
||||||
|
const sampleId = index + 1;
|
||||||
|
let ret = '';
|
||||||
|
ret += `\`\`\`input${sampleId}\n${sample.input.trim()}\n\`\`\`\n\n`;
|
||||||
|
ret += `\`\`\`output${sampleId}\n${sample.output.trim()}\n\`\`\`\n\n`;
|
||||||
|
if (sample.note && sample.note.trim()) ret += `## 样例解释 ${sampleId}\n\n${sample.note.trim()}\n\n`;
|
||||||
|
return ret;
|
||||||
|
}).join('');
|
||||||
|
content += `## 数据范围\n\n${problem.dataRange.trim()}\n\n`;
|
||||||
|
if (problem.source.trim()) content += `## 来源\n\n${problem.source.trim()}\n\n`;
|
||||||
|
return {
|
||||||
|
title: problem.title,
|
||||||
|
data: {
|
||||||
|
'config.yaml': Buffer.from(yaml.dump({
|
||||||
|
time: `${problem.limitTime}ms`,
|
||||||
|
memory: `${problem.limitMemory}m`,
|
||||||
|
type: 'remote_judge',
|
||||||
|
subType: 'yacs',
|
||||||
|
target: id,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
files: {},
|
||||||
|
tag,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async listProblem(page: number, resync = false) {
|
||||||
|
if (resync && page > 1) return [];
|
||||||
|
const data = await this.getData(`/problem?pi=${page}`);
|
||||||
|
return data.problemList
|
||||||
|
.filter((problem) => problem.contest.status === '榜单已公布')
|
||||||
|
.map((problem) => `P${problem.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitProblem(id: string, lang: string, code: string, info, next, end) {
|
||||||
|
if (!['C++', 'Python 3.6'].includes(lang)) {
|
||||||
|
end({ status: STATUS.STATUS_COMPILE_ERROR, message: `Language not supported: ${lang}` });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { body } = await this.post('/submission/submit')
|
||||||
|
.set('Yacs-Token', this.token)
|
||||||
|
.send({
|
||||||
|
code,
|
||||||
|
language: lang,
|
||||||
|
problemId: +id.split('P')[1],
|
||||||
|
});
|
||||||
|
return `${body.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForSubmission(id: string, next, end) {
|
||||||
|
const done = {};
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
await sleep(3000);
|
||||||
|
const { submission } = await this.getData(`/submission/${id}`);
|
||||||
|
const status = VERDICT[submission.finalStatus];
|
||||||
|
if (status === STATUS.STATUS_COMPILE_ERROR) {
|
||||||
|
return await end({
|
||||||
|
status,
|
||||||
|
compilerText: submission.finalErrorInfo,
|
||||||
|
score: 0,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const cases = [];
|
||||||
|
let taskId = 0;
|
||||||
|
for (const task of submission.finalTaskList) {
|
||||||
|
taskId++;
|
||||||
|
let pointId = 0;
|
||||||
|
for (const point of (task as any).dataPointList) {
|
||||||
|
pointId++;
|
||||||
|
if (done[`${taskId}.${pointId}`]) continue;
|
||||||
|
if (VERDICT[point.status] === STATUS.STATUS_JUDGING) continue;
|
||||||
|
done[`${taskId}.${pointId}`] = true;
|
||||||
|
cases.push({
|
||||||
|
id: pointId,
|
||||||
|
subtaskId: taskId,
|
||||||
|
score: point.scoreGet,
|
||||||
|
time: 0,
|
||||||
|
memory: 0,
|
||||||
|
status: VERDICT[point.status],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await next({
|
||||||
|
status,
|
||||||
|
score: submission.finalScoreGet,
|
||||||
|
cases,
|
||||||
|
});
|
||||||
|
if (status !== STATUS.STATUS_JUDGING) {
|
||||||
|
return await end({
|
||||||
|
status,
|
||||||
|
score: submission.finalScoreGet,
|
||||||
|
time: submission.finalTimeUsage,
|
||||||
|
memory: submission.finalMemoryUsage / 1024,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,16 @@ export const VERDICT = new Proxy<Record<string, STATUS>>({
|
|||||||
SUBMIT_FAILED_PERM: STATUS.STATUS_SYSTEM_ERROR,
|
SUBMIT_FAILED_PERM: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
SUBMIT_FAILED_TEMP: STATUS.STATUS_SYSTEM_ERROR,
|
SUBMIT_FAILED_TEMP: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
|
|
||||||
|
// YACS
|
||||||
|
正在评测: STATUS.STATUS_JUDGING,
|
||||||
|
答案正确: STATUS.STATUS_ACCEPTED,
|
||||||
|
编译失败: STATUS.STATUS_COMPILE_ERROR,
|
||||||
|
部分正确: STATUS.STATUS_WRONG_ANSWER,
|
||||||
|
运行超时: STATUS.STATUS_TIME_LIMIT_EXCEEDED,
|
||||||
|
内存超出: STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
|
||||||
|
暂未公布: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
|
评测机故障: STATUS.STATUS_SYSTEM_ERROR,
|
||||||
|
|
||||||
// HustOJ
|
// HustOJ
|
||||||
WAITING: STATUS.STATUS_WAITING,
|
WAITING: STATUS.STATUS_WAITING,
|
||||||
等待: STATUS.STATUS_WAITING,
|
等待: STATUS.STATUS_WAITING,
|
||||||
|
|||||||
Reference in new issue
Block a user