@@ -108,7 +108,7 @@ const AGC_LIKE: ContestPrefix = {
108108} as const ;
109109const agcLikePrefixes = getContestPrefixes ( AGC_LIKE ) ;
110110
111- // HACK: As of early November 2024, only UTPC is included.
111+ // HACK: As of November 2024, UTPC, TTPC and TUPC are included.
112112// More university contests may be added in the future.
113113/**
114114 * Maps university contest ID prefixes to their display names.
@@ -190,7 +190,7 @@ export function getContestPrefixes(contestPrefixes: Record<string, string>) {
190190}
191191
192192/**
193- * Contest type priorities (0 = Highest, 19 = Lowest)
193+ * Contest type priorities (0 = Highest, 20 = Lowest)
194194 *
195195 * Priority assignment rationale:
196196 * - Educational contests (0-10): ABS, ABC, APG4B, etc.
@@ -262,7 +262,23 @@ export function getContestPriority(contestId: string): number {
262262 */
263263const regexForAxc = / ^ ( a b c | a r c | a g c ) ( \d { 3 } ) / i;
264264
265+ /**
266+ * Regular expression to match AtCoder University contest identifiers.
267+ *
268+ * The pattern matches strings that:
269+ * - Start with either "ut", "tt", or "tu"
270+ * - Followed by "pc"
271+ * - End with exactly year (four digits)
272+ *
273+ * Example matches:
274+ * - "utpc2014"
275+ * - "ttpc2022"
276+ * - "tupc2023"
277+ */
278+ const regexForAtCoderUniversity = / ^ ( u t | t t | t u ) ( p c ) ( \d { 4 } ) / ;
279+
265280export const getContestNameLabel = ( contestId : string ) => {
281+ // AtCoder
266282 if ( regexForAxc . exec ( contestId ) ) {
267283 return contestId . replace (
268284 regexForAxc ,
@@ -298,10 +314,15 @@ export const getContestNameLabel = (contestId: string) => {
298314 return 'アルゴリズムと数学' ;
299315 }
300316
317+ if ( atCoderUniversityPrefixes . some ( ( prefix ) => contestId . startsWith ( prefix ) ) ) {
318+ return getAtCoderUniversityContestLabel ( contestId ) ;
319+ }
320+
301321 if ( contestId . startsWith ( 'chokudai_S' ) ) {
302322 return contestId . replace ( 'chokudai_S' , 'Chokudai SpeedRun ' ) ;
303323 }
304324
325+ // AIZU ONLINE JUDGE
305326 if ( aojCoursePrefixes . has ( contestId ) ) {
306327 return 'AOJ Courses' ;
307328 }
@@ -317,6 +338,24 @@ export const getContestNameLabel = (contestId: string) => {
317338 return contestId . toUpperCase ( ) ;
318339} ;
319340
341+ /**
342+ * Generates a formatted contest label for AtCoder University contests.
343+ *
344+ * This function takes a contest ID string and replaces parts of it using a regular expression
345+ * to generate a formatted label. The label is constructed by converting the contest type and
346+ * common part to uppercase and appending the contest year.
347+ *
348+ * @param contestId - The ID of the contest to format (ex: utpc2023).
349+ * @returns The formatted contest label (ex: UTPC 2023).
350+ */
351+ export function getAtCoderUniversityContestLabel ( contestId : string ) : string {
352+ return contestId . replace (
353+ regexForAtCoderUniversity ,
354+ ( _ , contestType , common , contestYear ) =>
355+ `${ ( contestType + common ) . toUpperCase ( ) } ${ contestYear } ` ,
356+ ) ;
357+ }
358+
320359/**
321360 * Maps PCK contest type abbreviations to their Japanese translations.
322361 *
0 commit comments