- Hydro
我找到了!!!!实现赛制的代码!!!!
- 2024-10-5 20:19:30 @
如题
从Hydro官方的Github里找到的
就在contest.ts这个文件里!!
(第450行)
可以看到这就是实现IOI和IOI严格赛制的代码
下面是实现ACM/ICPC赛制
const acm = buildContestRule({
TEXT: 'ACM/ICPC',
check: () => { },
statusSort: { accept: -1, time: 1 },
submitAfterAccept: false,
showScoreboard: (tdoc, now) => now > tdoc.beginAt,
showSelfRecord: () => true,
// eslint-disable-next-line @typescript-eslint/no-use-before-define
showRecord: (tdoc, now) => now > tdoc.endAt && !isLocked(tdoc),
stat(tdoc, journal: AcmJournal[]) {
const naccept = Counter<number>();
const npending = Counter<number>();
const display: Record<number, AcmDetail> = {};
const detail: Record<number, AcmDetail> = {};
let accept = 0;
let time = 0;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const lockAt = isLocked(tdoc) ? tdoc.lockAt : null;
for (const j of journal) {
if (!this.submitAfterAccept && display[j.pid]?.status === STATUS.STATUS_ACCEPTED) continue;
if (![STATUS.STATUS_ACCEPTED, STATUS.STATUS_COMPILE_ERROR, STATUS.STATUS_FORMAT_ERROR].includes(j.status)) {
naccept[j.pid]++;
}
const real = Math.floor((j.rid.getTimestamp().getTime() - tdoc.beginAt.getTime()) / 1000);
const penalty = 20 * 60 * naccept[j.pid];
detail[j.pid] = {
...j, naccept: naccept[j.pid], time: real + penalty, real, penalty,
};
if (lockAt && j.rid.getTimestamp() > lockAt) {
npending[j.pid]++;
// FIXME this is tricky
// @ts-ignore
display[j.pid] ||= {};
display[j.pid].npending = npending[j.pid];
continue;
}
display[j.pid] = detail[j.pid];
}
for (const d of Object.values(display).filter((i) => i.status === STATUS.STATUS_ACCEPTED)) {
accept++;
time += d.time;
}
return {
accept, time, detail, display,
};
},
async scoreboardHeader(config, _, tdoc, pdict) {
const columns: ScoreboardRow = [
{ type: 'rank', value: '#' },
{ type: 'user', value: _('User') },
];
if (config.isExport && config.showDisplayName) {
columns.push({ type: 'email', value: _('Email') });
columns.push({ type: 'string', value: _('School') });
columns.push({ type: 'string', value: _('Name') });
columns.push({ type: 'string', value: _('Student ID') });
}
columns.push({ type: 'solved', value: `${_('Solved')}\n${_('Total Time')}` });
for (let i = 1; i <= tdoc.pids.length; i++) {
const pid = tdoc.pids[i - 1];
pdict[pid].nAccept = pdict[pid].nSubmit = 0;
if (config.isExport) {
columns.push(
{
type: 'string',
value: '#{0} {1}'.format(i, pdict[pid].title),
},
{
type: 'time',
value: '#{0} {1}'.format(i, _('Penalty (Minutes)')),
},
);
} else {
columns.push({
type: 'problem',
value: String.fromCharCode(65 + i - 1),
raw: pid,
});
}
}
return columns;
},
async scoreboardRow(config, _, tdoc, pdict, udoc, rank, tsdoc, meta) {
const row: ScoreboardRow = [
{ type: 'rank', value: rank.toString() },
{ type: 'user', value: udoc.uname, raw: tsdoc.uid },
];
if (config.isExport && config.showDisplayName) {
row.push({ type: 'email', value: udoc.mail });
row.push({ type: 'string', value: udoc.school || '' });
row.push({ type: 'string', value: udoc.displayName || '' });
row.push({ type: 'string', value: udoc.studentId || '' });
}
row.push({
type: 'time',
value: `${tsdoc.accept || 0}\n${formatSeconds(tsdoc.time || 0.0, false)}`,
hover: formatSeconds(tsdoc.time || 0.0),
});
const accepted = {};
for (const s of tsdoc.journal || []) {
if (!pdict[s.pid]) continue;
if (config.lockAt && s.rid.getTimestamp() > config.lockAt) continue;
pdict[s.pid].nSubmit++;
if (s.status === STATUS.STATUS_ACCEPTED && !accepted[s.pid]) {
pdict[s.pid].nAccept++;
accepted[s.pid] = true;
}
}
const tsddict = (config.lockAt ? tsdoc.display : tsdoc.detail) || {};
for (const pid of tdoc.pids) {
const doc = tsddict[pid] || {} as Partial<AcmDetail>;
const accept = doc.status === STATUS.STATUS_ACCEPTED;
const colTime = accept ? formatSeconds(doc.real, false).toString() : '';
const colPenalty = doc.rid ? Math.ceil(doc.penalty / 60).toString() : '';
if (config.isExport) {
row.push(
{ type: 'string', value: colTime },
{ type: 'string', value: colPenalty },
);
} else {
let value = '';
if (doc.rid) value = `-${doc.naccept}`;
if (accept) value = `${doc.naccept ? `+${doc.naccept}` : '<span class="icon icon-check"></span>'}\n${colTime}`;
else if (doc.npending) value += `${value ? ' ' : ''}<span style="color:orange">+${doc.npending}</span>`;
row.push({
type: 'record',
score: accept ? 100 : 0,
value,
hover: accept ? formatSeconds(doc.time) : '',
raw: doc.rid,
style: accept && doc.rid.getTimestamp().getTime() === meta?.first?.[pid]
? 'background-color: rgb(217, 240, 199);'
: undefined,
});
}
}
return row;
},
async scoreboard(config, _, tdoc, pdict, cursor) {
const rankedTsdocs = await db.ranked(cursor, (a, b) => a.score === b.score && a.time === b.time);
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
const udict = await user.getListForRender(tdoc.domainId, uids, config.showDisplayName ? ['displayName'] : []);
// Find first accept
const first = {};
const data = await document.collStatus.aggregate([
{
$match: {
domainId: tdoc.domainId,
docType: document.TYPE_CONTEST,
docId: tdoc.docId,
accept: { $gte: 1 },
},
},
{ $project: { r: { $objectToArray: '$detail' } } },
{ $unwind: '$r' },
{ $match: { 'r.v.status': STATUS.STATUS_ACCEPTED } },
{ $group: { _id: '$r.v.pid', first: { $min: '$r.v.rid' } } },
]).toArray() as any[];
for (const t of data) first[t._id] = t.first.getTimestamp().getTime();
const columns = await this.scoreboardHeader(config, _, tdoc, pdict);
const rows: ScoreboardRow[] = [
columns,
...await Promise.all(rankedTsdocs.map(
([rank, tsdoc]) => this.scoreboardRow(
config, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc, { first },
),
)),
];
return [rows, udict];
},
async ranked(tdoc, cursor) {
return await db.ranked(cursor, (a, b) => a.accept === b.accept && a.time === b.time);
},
applyProjection(tdoc, rdoc) {
if (isDone(tdoc)) return rdoc;
delete rdoc.time;
delete rdoc.memory;
rdoc.testCases = [];
rdoc.judgeTexts = [];
delete rdoc.subtasks;
delete rdoc.score;
return rdoc;
},
});
IOI(OFS)的应该不会很难,套上IOI的代码再把成绩表的显示方式改一改
AI写的:
const ioiObjectiveFirstSubmit = buildContestRule({
TEXT: 'IOI(ObjectiveFirstSubmit)',
submitAfterAccept: false, // 提交之后立即出结果
showScoreboard: (tdoc, now) => now > tdoc.endAt,
showSelfRecord: (tdoc, now) => now > tdoc.endAt,
showRecord: (tdoc, now) => now > tdoc.endAt && !isLocked(tdoc),
// 统计提交情况
stat(tdoc, journal) {
const display: Record<number, OiDetail> = {};
const detail: Record<number, OiDetail> = {};
const accepted: Record<number, boolean> = {};
let score = 0;
const lockAt = isLocked(tdoc) ? tdoc.lockAt : null;
for (const j of journal.filter((i) => tdoc.pids.includes(i.pid))) {
// 跳过已锁定的提交
if (lockAt && j.rid.getTimestamp() > lockAt) continue;
// 记录第一次接受的提交
if (!accepted[j.pid]) {
accepted[j.pid] = true; // 标记为已接受
detail[j.pid] = j; // 记录提交细节
display[j.pid] ||= {};
display[j.pid] = j; // 显示提交的分数
}
}
// 计算总分
for (const i in display) {
score += ((tdoc.score?.[i] || 100) * (display[i].score || 0)) / 100;
}
return { score, detail, display };
},
async scoreboardHeader(config, _, tdoc, pdict) {
const columns: ScoreboardNode[] = [
{ type: 'rank', value: '#' },
{ type: 'user', value: _('User') },
];
if (config.isExport && config.showDisplayName) {
columns.push({ type: 'email', value: _('Email') });
columns.push({ type: 'string', value: _('School') });
columns.push({ type: 'string', value: _('Name') });
columns.push({ type: 'string', value: _('Student ID') });
}
columns.push({ type: 'total_score', value: _('Total Score') });
for (let i = 1; i <= tdoc.pids.length; i++) {
const pid = tdoc.pids[i - 1];
pdict[pid].nAccept = pdict[pid].nSubmit = 0;
if (config.isExport) {
columns.push({
type: 'string',
value: '#{0} {1}'.format(i, pdict[pid].title),
});
} else {
columns.push({
type: 'problem',
value: String.fromCharCode(65 + i - 1),
raw: pid,
});
}
}
return columns;
},
async scoreboardRow(config, _, tdoc, pdict, udoc, rank, tsdoc) {
const row: ScoreboardNode[] = [
{ type: 'rank', value: rank.toString() },
{ type: 'user', value: udoc.uname, raw: tsdoc.uid },
];
if (config.isExport && config.showDisplayName) {
row.push({ type: 'email', value: udoc.mail });
row.push({ type: 'string', value: udoc.school || '' });
row.push({ type: 'string', value: udoc.displayName || '' });
row.push({ type: 'string', value: udoc.studentId || '' });
}
row.push({ type: 'total_score', value: tsdoc.score || 0 });
const tsddict = tsdoc.display || {};
for (const pid of tdoc.pids) {
const doc = tsddict[pid] || {} as Partial<OiDetail>;
const colScore = doc.score ? doc.score : 0;
row.push({
type: 'record',
value: colScore.toString(),
raw: doc.rid,
score: colScore,
});
}
return row;
},
async scoreboard(config, _, tdoc, pdict, cursor) {
const rankedTsdocs = await db.ranked(cursor, (a, b) => b.score - a.score); // 按分数降序排列
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
const udict = await user.getListForRender(tdoc.domainId, uids, config.showDisplayName ? ['displayName'] : []);
const columns = await this.scoreboardHeader(config, _, tdoc, pdict);
const rows: ScoreboardRow[] = [
columns,
...await Promise.all(rankedTsdocs.map(
([rank, tsdoc]) => this.scoreboardRow(
config, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc,
),
)),
];
return [rows, udict];
},
async ranked(tdoc, cursor) {
return await db.ranked(cursor, (a, b) => b.score - a.score); // 按分数降序排列
},
applyProjection(tdoc, rdoc) {
if (isDone(tdoc)) return rdoc;
delete rdoc.time;
delete rdoc.memory;
rdoc.testCases = [];
rdoc.judgeTexts = [];
delete rdoc.subtasks;
delete rdoc.score;
return rdoc;
},
});
2 comments
-
homo LV 4 @ 2024-10-6 9:49:11
所以我是要在插件里直接复制IOI(OFS)的代码还是修改原来的,还有所有比赛的HTML要不要修改
-
2024-10-5 21:13:38@
呃
- 1