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
194 changes: 194 additions & 0 deletions prisma/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4820,6 +4820,56 @@ export const tasks = [
title: 'A01. The First Problem',
grade: 'Q10',
},
{
id: 'joi2025_yo1c_d',
contest_id: 'joi2025yo1c',
problem_index: 'D',
name: '周期文字列 (Cycle String)',
title: 'D. 周期文字列 (Cycle String)',
},
{
id: 'joi2025_yo1c_c',
contest_id: 'joi2025yo1c',
problem_index: 'C',
name: 'いずれか片方 (Either, but Not Both)',
title: 'C. いずれか片方 (Either, but Not Both)',
},
{
id: 'joi2025_yo1c_b',
contest_id: 'joi2025yo1c',
problem_index: 'B',
name: 'ブラックジャック (Blackjack)',
title: 'B. ブラックジャック (Blackjack)',
},
{
id: 'joi2025_yo1c_a',
contest_id: 'joi2025yo1c',
problem_index: 'A',
name: '所持金 (Money On Me)',
title: 'A. 所持金 (Money On Me)',
},
{
id: 'joi2025_yo1b_d',
contest_id: 'joi2025yo1b',
problem_index: 'D',
name: '三角足し算 (Triangle Addition)',
title: 'D. 三角足し算 (Triangle Addition)',
},
{
id: 'joi2025_yo1b_c',
contest_id: 'joi2025yo1b',
problem_index: 'C',
name: 'じゃんけん (Rock-Scissors-Paper)',
title: 'C. じゃんけん (Rock-Scissors-Paper)',
},
{
id: 'joi2025_yo1b_b',
contest_id: 'joi2025yo1b',
problem_index: 'B',
name: '鉄道旅行 3 (Railway Trip 3)',
title: 'B. 鉄道旅行 3 (Railway Trip 3)',
grade: 'Q8',
},
{
id: 'joi2025_yo1b_a',
contest_id: 'joi2025yo1b',
Expand All @@ -4828,6 +4878,22 @@ export const tasks = [
title: 'A. 徒競走 (Footrace)',
grade: 'Q9',
},
{
id: 'joi2025_yo1a_d',
contest_id: 'joi2025yo1a',
problem_index: 'D',
name: 'どら焼き (Dorayaki)',
title: 'D. どら焼き (Dorayaki)',
grade: 'Q7',
},
{
id: 'joi2025_yo1a_c',
contest_id: 'joi2025yo1a',
problem_index: 'C',
name: 'OIJ (OIJ)',
title: 'C. OIJ (OIJ)',
grade: 'Q7',
},
{
id: 'joi2025_yo1a_b',
contest_id: 'joi2025yo1a',
Expand Down Expand Up @@ -4916,6 +4982,38 @@ export const tasks = [
title: 'A. 立方体 (Cube)',
grade: 'Q10',
},
{
id: 'joi2022_yo1a_d',
contest_id: 'joi2022yo1a',
problem_index: 'D',
name: '箱と鍵 (Boxes and Keys)',
title: 'D. 箱と鍵 (Boxes and Keys)',
grade: 'Q6',
},
{
id: 'joi2022_yo1a_c',
contest_id: 'joi2022yo1a',
problem_index: 'C',
name: '複雑な文字列 (Complex String)',
title: 'C. 複雑な文字列 (Complex String)',
grade: 'Q7',
},
{
id: 'joi2022_yo1a_b',
contest_id: 'joi2022yo1a',
problem_index: 'B',
name: '移動 (Moving)',
title: 'B. 移動 (Moving)',
grade: 'Q8',
},
{
id: 'joi2022_yo1a_a',
contest_id: 'joi2022yo1a',
problem_index: 'A',
name: '余り (Remainder)',
title: 'A. 余り (Remainder)',
grade: 'Q9',
},
{
id: 'joi2021_yo1b_a',
contest_id: 'joi2021yo1b',
Expand All @@ -4932,6 +5030,102 @@ export const tasks = [
title: 'A. 金平糖 (Konpeito)',
grade: 'Q8',
},
{
id: 'joi2021_yo1a_c',
contest_id: 'joi2021yo1a',
problem_index: 'C',
name: '共通要素 (Common Elements)',
title: 'C. 共通要素 (Common Elements)',
grade: 'Q6',
},
{
id: 'joi2021_yo1a_b',
contest_id: 'joi2021yo1a',
problem_index: 'B',
name: 'JOI ソート (JOI Sort)',
title: 'B. JOI ソート (JOI Sort)',
grade: 'Q7',
},
{
id: 'joi2021_yo1a_a',
contest_id: 'joi2021yo1a',
problem_index: 'A',
name: '2 番目に大きい整数 (The Second Largest Integer)',
title: 'A. 2 番目に大きい整数 (The Second Largest Integer)',
grade: 'Q8',
},
{
id: 'joi2020_yo1c_c',
contest_id: 'joi2020yo1c',
problem_index: 'C',
name: '最長昇順連続部分列 (Longest Ascending Contiguous Subsequence)',
title: 'C. 最長昇順連続部分列 (Longest Ascending Contiguous Subsequence)',
grade: 'Q6',
},
{
id: 'joi2020_yo1c_b',
contest_id: 'joi2020yo1c',
problem_index: 'B',
name: 'キャピタリゼーション (Capitalization)',
title: 'B. キャピタリゼーション (Capitalization)',
grade: 'Q7',
},
{
id: 'joi2020_yo1c_a',
contest_id: 'joi2020yo1c',
problem_index: 'A',
name: 'X に最も近い値 (The Nearest Value)',
title: 'A. X に最も近い値 (The Nearest Value)',
grade: 'Q7',
},
{
id: 'joi2020_yo1b_c',
contest_id: 'joi2020yo1b',
problem_index: 'C',
name: '最頻値 (Mode)',
title: 'C. 最頻値 (Mode)',
grade: 'Q6',
},
{
id: 'joi2020_yo1b_b',
contest_id: 'joi2020yo1b',
problem_index: 'B',
name: '文字列の反転 (Inversion of a String)',
title: 'B. 文字列の反転 (Inversion of a String)',
grade: 'Q7',
},
{
id: 'joi2020_yo1b_a',
contest_id: 'joi2020yo1b',
problem_index: 'A',
name: '試験 (Exam)',
title: 'A. 試験 (Exam)',
grade: 'Q8',
},
{
id: 'joi2020_yo1a_c',
contest_id: 'joi2020yo1a',
problem_index: 'C',
name: 'マージ (Merge)',
title: 'C. マージ (Merge)',
grade: 'Q5',
},
{
id: 'joi2020_yo1a_b',
contest_id: 'joi2020yo1a',
problem_index: 'B',
name: '母音を数える (Counting Vowels)',
title: 'B. 母音を数える (Counting Vowels)',
grade: 'Q7',
},
{
id: 'joi2020_yo1a_a',
contest_id: 'joi2020yo1a',
problem_index: 'A',
name: '3 つの整数 (Three Integers)',
title: 'A. 3 つの整数 (Three Integers)',
grade: 'Q9',
},
{
id: 'joi2016yo_a',
contest_id: 'joi2016yo',
Expand Down
15 changes: 12 additions & 3 deletions src/lib/components/TaskTables/TaskTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@
}

function getBodyCellClasses(taskResult: TaskResult, totalColumns: number): string {
const baseClasses = 'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1';
const baseClasses =
totalColumns >= 5
? 'w-1/2 xs:w-1/3 sm:w-1/4 md:w-1/5 lg:w-1/6 px-1 py-1'
: 'w-1/2 xs:w-1/3 sm:w-1/4 px-1 py-1';
const additionalClasses = totalColumns > 8 ? '2xl:w-1/7 py-2' : '';
const backgroundColor = getBackgroundColor(taskResult);

Expand Down Expand Up @@ -199,7 +202,12 @@
<div class="w-full sticky top-0 z-20 border-b border-gray-200 dark:border-gray-100">
<Table id="task-table" class="text-md table-fixed w-full" aria-label="Task table">
<TableHead class="text-sm border-gray-200 dark:border-gray-100">
<TableHeadCell class="w-full xl:w-16 px-2 text-center" scope="col">Round</TableHeadCell>
<TableHeadCell
class="w-full {contestTable.displayConfig.roundLabelWidth} px-2 text-center"
scope="col"
>
Round
</TableHeadCell>

{#if contestTable.headerIds}
{#each contestTable.headerIds as taskTableHeaderId (taskTableHeaderId)}
Expand All @@ -224,7 +232,8 @@
<TableBodyRow class={getBodyRowClasses(totalColumns)}>
{#if contestTable.displayConfig.isShownRoundLabel}
<TableBodyCell
class="w-full xl:w-16 truncate px-2 py-2 text-center bg-gray-50 dark:bg-gray-800"
class="w-full {contestTable.displayConfig
.roundLabelWidth} truncate px-2 py-2 text-center bg-gray-50 dark:bg-gray-800"
>
{getContestRoundLabel(provider, contestId)}
</TableBodyCell>
Expand Down
2 changes: 2 additions & 0 deletions src/lib/types/contest_table_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,12 @@ export type ContestTablesMetaData = {
* @interface ContestTableDisplayConfig
* @property {boolean} isShownHeader - Whether to display the table header
* @property {boolean} isShownRoundLabel - Whether to display round labels in the contest table
* @property {string} roundLabelWidth - tailwind CSS width for the round label column, e.g., "w-16" or "w-20"
* @property {boolean} isShownTaskIndex - Whether to display task index in the contest table cells
*/
export interface ContestTableDisplayConfig {
isShownHeader: boolean;
isShownRoundLabel: boolean;
roundLabelWidth: string;
isShownTaskIndex: boolean;
}
45 changes: 45 additions & 0 deletions src/lib/utils/contest_table_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export abstract class ContestTableProviderBase implements ContestTableProvider {
return {
isShownHeader: true,
isShownRoundLabel: true,
roundLabelWidth: 'xl:w-16', // Default width for task index column
isShownTaskIndex: false,
};
}
Expand Down Expand Up @@ -230,6 +231,7 @@ export class EDPCProvider extends ContestTableProviderBase {
return {
isShownHeader: false,
isShownRoundLabel: false,
roundLabelWidth: '', // No specific width for task index in EDPC
isShownTaskIndex: true,
};
}
Expand Down Expand Up @@ -261,6 +263,7 @@ export class TDPCProvider extends ContestTableProviderBase {
return {
isShownHeader: false,
isShownRoundLabel: false,
roundLabelWidth: '', // No specific width for task index in TDPC
isShownTaskIndex: true,
};
}
Expand All @@ -270,6 +273,41 @@ export class TDPCProvider extends ContestTableProviderBase {
}
}

const regexForJoiFirstQualRound = /^(joi)(\d{4})(yo1)(a|b|c)$/i;

export class JOIFirstQualRoundProvider extends ContestTableProviderBase {
protected setFilterCondition(): (taskResult: TaskResult) => boolean {
return (taskResult: TaskResult) => {
if (classifyContest(taskResult.contest_id) !== this.contestType) {
return false;
}

return regexForJoiFirstQualRound.test(taskResult.contest_id);
};
}

getMetadata(): ContestTableMetaData {
return {
title: 'JOI 一次予選',
abbreviationName: 'joiFirstQualRound',
};
}

getDisplayConfig(): ContestTableDisplayConfig {
return {
isShownHeader: true,
isShownRoundLabel: true,
isShownTaskIndex: false,
roundLabelWidth: 'xl:w-28',
};
}

getContestRoundLabel(contestId: string): string {
const contestNameLabel = getContestNameLabel(contestId);
return contestNameLabel.replace('JOI 一次予選 ', '');
}
}

/**
* A class that manages individual provider groups
* Manages multiple ContestTableProviders as a single group,
Expand Down Expand Up @@ -415,6 +453,12 @@ export const prepareContestProviderPresets = () => {
{ contestType: ContestType.EDPC, provider: new EDPCProvider(ContestType.EDPC) },
{ contestType: ContestType.TDPC, provider: new TDPCProvider(ContestType.TDPC) },
),

JOIFirstQualRound: () =>
new ContestTableProviderGroup(`JOI 一次予選`, {
buttonLabel: 'JOI 一次予選',
ariaLabel: 'Filter JOI First Qualifying Round',
}).addProvider(ContestType.JOI, new JOIFirstQualRoundProvider(ContestType.JOI)),
};
};

Expand All @@ -423,6 +467,7 @@ export const contestTableProviderGroups = {
abc319Onwards: prepareContestProviderPresets().ABC319Onwards(),
fromAbc212ToAbc318: prepareContestProviderPresets().ABC212ToABC318(),
dps: prepareContestProviderPresets().dps(), // Dynamic Programming (DP) Contests
joiFirstQualRound: prepareContestProviderPresets().JOIFirstQualRound(),
};

export type ContestTableProviderGroups = keyof typeof contestTableProviderGroups;
Loading
Loading