Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "ContestType" ADD VALUE 'AOJ_UNIVERSITY';
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ enum ContestType {
AOJ_PCK // All-Japan High School Programming Contest (PCK)
AOJ_ICPC // ICPC (International Collegiate Programming Contest)
AOJ_JAG // ACM-ICPC Japan Alumni Group Contest (JAG)
AOJ_UNIVERSITY // University Programming Contest (RUPC, HUPC, UAPC)
}

// 11Q(最も簡単)〜6D(最難関)。
Expand Down
21 changes: 21 additions & 0 deletions prisma/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11973,4 +11973,25 @@ export const tasks = [
name: 'World Trip',
title: '2349. World Trip',
},
{
id: '3058',
contest_id: 'AOJ-UAPC2019-in-RUPC2019-day2',
problem_index: '3058',
name: 'Ghost',
title: '3058. Ghost',
},
{
id: '2903',
contest_id: 'AOJ-RUPC2018-in-ACPC2018-day1',
problem_index: '2903',
name: 'Board',
title: '2903. Board',
},
{
id: '3171',
contest_id: 'AOJ-HUPC2020-in-HUPC2020-day1',
problem_index: '3171',
name: 'Traditional Company',
title: '3171. Traditional Company',
},
];
1 change: 1 addition & 0 deletions src/lib/types/contest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const ContestType: { [key in ContestTypeOrigin]: key } = {
AOJ_PCK: 'AOJ_PCK', // All-Japan High School Programming Contest (PCK)
AOJ_ICPC: 'AOJ_ICPC', // ICPC (International Collegiate Programming Contest)
AOJ_JAG: 'AOJ_JAG', // ACM-ICPC Japan Alumni Group Contest (JAG)
AOJ_UNIVERSITY: 'AOJ_UNIVERSITY', // University Programming Contest (RUPC, HUPC, UAPC)
} as const;

// Re-exporting the original type with the original name.
Expand Down
30 changes: 26 additions & 4 deletions src/lib/utils/contest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ContestType, type ContestPrefix, type ContestLabelTranslations } from '$lib/types/contest';

const regexForJag = /^JAG(Prelim|Regional|Summer|Winter|Spring)\d{4}(-day\d+[A-Z]?)?$/;
export const regexForJag = /^JAG(Prelim|Regional|Summer|Winter|Spring)\d{4}(-day\d+[A-Z]?)?$/;
export const regexForAojUniversity = /^AOJ-[A-Z]+PC\d{4}/;

// See:
// https://github.com/kenkoooo/AtCoderProblems/blob/master/atcoder-problems-frontend/src/utils/ContestClassifier.ts
Expand Down Expand Up @@ -107,6 +108,10 @@ export const classifyContest = (contest_id: string) => {
return ContestType.AOJ_JAG;
}

if (regexForAojUniversity.test(contest_id)) {
return ContestType.AOJ_UNIVERSITY;
}

return null;
};

Expand Down Expand Up @@ -271,13 +276,13 @@ export function getContestPrefixes(contestPrefixes: Record<string, string>) {
}

/**
* Contest type priorities (0 = Highest, 24 = Lowest)
* Contest type priorities (0 = Highest, 25 = Lowest)
*
* Priority assignment rationale:
* - Educational contests (0-11, 17): ABS, ABC, APG4B and AWC etc.
* - Contests for genius (12-16): ARC, AGC, and their variants
* - Special contests (18-20): UNIVERSITY, FPS_24, OTHERS
* - External platforms (21-24): AOJ_COURSES, AOJ_PCK, AOJ_ICPC, AOJ_JAG
* - External platforms (21-25): AOJ_COURSES, AOJ_PCK, AOJ_ICPC, AOJ_JAG, AOJ_UNIVERSITY
*
* @remarks
* HACK: The priorities for ARC, AGC, UNIVERSITY, AOJ_COURSES, and AOJ_PCK are temporary
Expand Down Expand Up @@ -312,6 +317,7 @@ export const contestTypePriorities: Map<ContestType, number> = new Map([
[ContestType.AOJ_PCK, 22],
[ContestType.AOJ_ICPC, 23],
[ContestType.AOJ_JAG, 24],
[ContestType.AOJ_UNIVERSITY, 25],
]);

export function getContestPriority(contestId: string): number {
Expand Down Expand Up @@ -462,6 +468,10 @@ export const getContestNameLabel = (contestId: string) => {
return getAojContestLabel(JAG_TRANSLATIONS, contestId);
}

if (regexForAojUniversity.test(contestId)) {
return getAojUniversityContestLabel(contestId);
}

return contestId.toUpperCase();
};

Expand Down Expand Up @@ -689,6 +699,17 @@ const PCK_TRANSLATIONS = {
Final: ' 本選 ',
};

function getAojUniversityContestLabel(contestId: string): string {
const label = contestId
.replace(/^AOJ-/, '')
.replace(/UAPC/g, 'ACPC')
.replace(/([A-Z]{2,})(\d{4})/g, '$1 $2')
.replace(/-in-/, ' in ')
.replace(/-day(\d+)/, ' Day$1')
.replace(/-summer/, ' Summer');
return '(' + label + ')';
}

/**
* Maps JAG contest type abbreviations to their Japanese translations.
*
Expand Down Expand Up @@ -740,6 +761,7 @@ function isAojContest(contestId: string): boolean {
aojCoursePrefixes.has(contestId) ||
contestId.startsWith('PCK') ||
regexForJag.test(contestId) ||
contestId.startsWith('ICPC')
contestId.startsWith('ICPC') ||
regexForAojUniversity.test(contestId)
);
}
12 changes: 9 additions & 3 deletions src/lib/utils/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import type { UrlGenerator, UrlGenerators } from '$lib/types/url';
import { type WorkBookTaskBase } from '$features/workbooks/types/workbook';

import { ATCODER_BASE_CONTEST_URL, AOJ_TASKS_URL } from '$lib/constants/urls';
import { getPrefixForAojCourses, getContestPriority } from '$lib/utils/contest';
import {
getPrefixForAojCourses,
getContestPriority,
regexForAojUniversity,
regexForJag,
} from '$lib/utils/contest';

// TODO: Codeforces、yukicoder、BOJなどに対応できるようにする
/**
Expand Down Expand Up @@ -36,8 +41,9 @@ class AojGenerator implements UrlGenerator {
return (
getPrefixForAojCourses().includes(contestId) ||
contestId.startsWith('PCK') ||
contestId.startsWith('JAG') ||
contestId.startsWith('ICPC')
regexForJag.test(contestId) ||
contestId.startsWith('ICPC') ||
regexForAojUniversity.test(contestId)
);
}

Expand Down
36 changes: 36 additions & 0 deletions src/test/lib/utils/contest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ describe('Contest', () => {
});
});
});

describe('when contest_id means AOJ University (RUPC, HUPC, UAPC)', () => {
TestCasesForContestType.aojUniversity.forEach(({ name, value }) => {
runTests(`${name}`, [value], ({ contestId, expected }: TestCaseForContestType) => {
expect(classifyContest(contestId)).toEqual(expected);
});
});
});
});
});

Expand Down Expand Up @@ -435,6 +443,14 @@ describe('Contest', () => {
});
});
});

describe('when contest_id means AOJ University (RUPC, HUPC, UAPC)', () => {
TestCasesForContestType.aojUniversity.forEach(({ name, value }) => {
runTests(`${name}`, [value], ({ contestId, expected }: TestCaseForContestType) => {
expect(getContestPriority(contestId)).toEqual(contestTypePriorities.get(expected));
});
});
});
});
});

Expand Down Expand Up @@ -530,6 +546,14 @@ describe('Contest', () => {
});
});
});

describe('when contest_id means AOJ University (RUPC, HUPC, UAPC)', () => {
TestCasesForContestNameLabel.aojUniversity.forEach(({ name, value }) => {
runTests(`${name}`, [value], ({ contestId, expected }: TestCaseForContestNameLabel) => {
expect(getContestNameLabel(contestId)).toEqual(expected);
});
});
});
});
});

Expand Down Expand Up @@ -725,6 +749,18 @@ describe('Contest', () => {
);
});
});

describe('when contest_id means AOJ University (RUPC, HUPC, UAPC)', () => {
TestCasesForContestNameAndTaskIndex.aojUniversity.forEach(({ name, value }) => {
runTests(
`${name}`,
[value],
({ contestId, taskTableIndex, expected }: TestCaseForContestNameAndTaskIndex) => {
expect(addContestNameToTaskIndex(contestId, taskTableIndex)).toEqual(expected);
},
);
});
});
});
});

Expand Down
8 changes: 8 additions & 0 deletions src/test/lib/utils/task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ describe('Task', () => {
});
});
});

describe('when contest ids and task ids for AOJ University (RUPC, HUPC, UAPC) are given', () => {
TestCasesForTaskUrl.aojUniversity.forEach(({ name, value }) => {
runTests(`${name}`, [value], ({ contestId, taskId, expected }: TestCaseForTaskUrl) => {
expect(getTaskUrl(contestId, taskId)).toBe(expected);
});
});
});
});

describe('count accepted tasks', () => {
Expand Down
38 changes: 38 additions & 0 deletions src/test/lib/utils/test_cases/contest_name_and_task_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,3 +888,41 @@ const generateAojIcpcTestCases = (
export const aojIcpc = Object.entries(AOJ_ICPC_TEST_DATA).flatMap(([contestId, tasks]) =>
generateAojIcpcTestCases(Array(tasks.tasks.length).fill(contestId), tasks.tasks),
);

const AOJ_UNIVERSITY_TEST_DATA = [
{
contestId: 'AOJ-RUPC2018-in-ACPC2018-day1',
taskTableIndex: '2903',
expected: 'AOJ 2903(RUPC 2018 in ACPC 2018 Day1)',
},
{
contestId: 'AOJ-UAPC2019-in-RUPC2019-day2',
taskTableIndex: '3058',
expected: 'AOJ 3058(ACPC 2019 in RUPC 2019 Day2)',
},
{
contestId: 'AOJ-HUPC2020-in-HUPC2020-day1',
taskTableIndex: '3171',
expected: 'AOJ 3171(HUPC 2020 in HUPC 2020 Day1)',
},
{ contestId: 'AOJ-UAPC2003', taskTableIndex: '1000', expected: 'AOJ 1000(ACPC 2003)' },
{
contestId: 'AOJ-UAPC2011-summer',
taskTableIndex: '1001',
expected: 'AOJ 1001(ACPC 2011 Summer)',
},
{
contestId: 'AOJ-UAPC2012-day1',
taskTableIndex: '1002',
expected: 'AOJ 1002(ACPC 2012 Day1)',
},
];

export const aojUniversity = AOJ_UNIVERSITY_TEST_DATA.map(
({ contestId, taskTableIndex, expected }) =>
createTestCaseForContestNameAndTaskIndex(`AOJ, ${contestId} - ${taskTableIndex}`)({
contestId,
taskTableIndex,
expected,
}),
);
27 changes: 27 additions & 0 deletions src/test/lib/utils/test_cases/contest_name_labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,30 @@ export const aojIcpc = [
expected: '(ICPC 地区予選 2023)',
}),
];

export const aojUniversity = [
createTestCaseForContestNameLabel('AOJ, RUPC 2018 in ACPC 2018 Day1')({
contestId: 'AOJ-RUPC2018-in-ACPC2018-day1',
expected: '(RUPC 2018 in ACPC 2018 Day1)',
}),
createTestCaseForContestNameLabel('AOJ, HUPC 2020 in HUPC 2020 Day1')({
contestId: 'AOJ-HUPC2020-in-HUPC2020-day1',
expected: '(HUPC 2020 in HUPC 2020 Day1)',
}),
createTestCaseForContestNameLabel('AOJ, UAPC 2019 in RUPC 2019 Day2')({
contestId: 'AOJ-UAPC2019-in-RUPC2019-day2',
expected: '(ACPC 2019 in RUPC 2019 Day2)',
}),
createTestCaseForContestNameLabel('AOJ, UAPC 2003')({
contestId: 'AOJ-UAPC2003',
expected: '(ACPC 2003)',
}),
createTestCaseForContestNameLabel('AOJ, UAPC 2011 Summer')({
contestId: 'AOJ-UAPC2011-summer',
expected: '(ACPC 2011 Summer)',
}),
createTestCaseForContestNameLabel('AOJ, UAPC 2012 Day1')({
contestId: 'AOJ-UAPC2012-day1',
expected: '(ACPC 2012 Day1)',
}),
];
16 changes: 16 additions & 0 deletions src/test/lib/utils/test_cases/contest_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,3 +665,19 @@ export const aojIcpc = aojIcpcContestData.map(({ name, contestId }) =>
expected: ContestType.AOJ_ICPC,
}),
);

const aojUniversityContestData = [
{ name: 'AOJ, RUPC 2018 in ACPC 2018 Day1', contestId: 'AOJ-RUPC2018-in-ACPC2018-day1' },
{ name: 'AOJ, HUPC 2020 in HUPC 2020 Day1', contestId: 'AOJ-HUPC2020-in-HUPC2020-day1' },
{ name: 'AOJ, UAPC 2019 in RUPC 2019 Day2', contestId: 'AOJ-UAPC2019-in-RUPC2019-day2' },
{ name: 'AOJ, UAPC 2003', contestId: 'AOJ-UAPC2003' },
{ name: 'AOJ, UAPC 2011 Summer', contestId: 'AOJ-UAPC2011-summer' },
{ name: 'AOJ, UAPC 2012 Day1', contestId: 'AOJ-UAPC2012-day1' },
];

export const aojUniversity = aojUniversityContestData.map(({ name, contestId }) =>
createTestCaseForContestType(name)({
contestId,
expected: ContestType.AOJ_UNIVERSITY,
}),
);
17 changes: 17 additions & 0 deletions src/test/lib/utils/test_cases/task_url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,20 @@ export const aojIcpc = icpcContests.flatMap((icpc) =>
});
}),
);

// AOJ University contests: contest ID = AOJ-{NAME}PC{YEAR}[-...], task ID = numeric problem ID
const aojUniversityContests = [
{ contestId: 'AOJ-RUPC2018-in-ACPC2018-day1', tasks: ['2903', '2904'] },
{ contestId: 'AOJ-UAPC2019-in-RUPC2019-day2', tasks: ['3058', '3059'] },
{ contestId: 'AOJ-HUPC2020-in-HUPC2020-day1', tasks: ['3171', '3172'] },
];

export const aojUniversity = aojUniversityContests.flatMap((contest) =>
contest.tasks.map((task) => {
return createTestCaseForTaskUrl(`AOJ University, ${contest.contestId} ${task}`)({
contestId: contest.contestId,
taskId: task,
expected: `${AOJ_TASKS_URL}/${task}`,
});
}),
);
Loading