diff --git a/docs/dev-notes/2025-12-10/add_tests_for_contest_table_provider/plan.md b/docs/dev-notes/2025-12-10/add_tests_for_contest_table_provider/plan.md new file mode 100644 index 000000000..b80f5b0b0 --- /dev/null +++ b/docs/dev-notes/2025-12-10/add_tests_for_contest_table_provider/plan.md @@ -0,0 +1,480 @@ +# ABC001~041 & ARC001~057 テスト追加計画 + +**作成日**: 2025-12-10 + +**対象ブランチ**: #2838 + +**優先度**: High + +--- + +## 概要 + +Issue #2838 で実装された `ABC001ToABC041Provider` と `ARC001ToARC057Provider` に対するテストを追加する計画。 + +**対象ファイル**: + +- **Provider実装**: [`src/lib/utils/contest_table_provider.ts`](../../../../../src/lib/utils/contest_table_provider.ts) + - `ABC001ToABC041Provider` (318行目~) + - `ARC001ToARC057Provider` (401行目~) +- **テストファイル**: [`src/test/lib/utils/contest_table_provider.test.ts`](../../../../../src/test/lib/utils/contest_table_provider.test.ts) + +**参照ドキュメント**: + +- [`docs/dev-notes/2025-12-03/add_tests_for_contest_table_provider/plan.md`](../../2025-12-03/add_tests_for_contest_table_provider/plan.md) - ABC042~125 & ARC058~103 のテスト設計(参照パターン) +- [`prisma/tasks.ts`](../../../../../prisma/tasks.ts) - タスク ID のフォーマット確認 + +--- + +## 特徴と仕様 + +### ABC001~041 の仕様 + +- **範囲**: ABC001 ~ ABC041 +- **特徴**: レーティング導入前のコンテスト +- **4問構成**: すべてのラウンドで A・B・C・D の4問 +- **ID フォーマット**: + - ABC001~019: `abc001_1`, `abc001_2`, `abc001_3`, `abc001_4` (数字サフィックス) + - ABC020~041: `abc020_a`, `abc020_b`, `abc020_c`, `abc020_d` (英字サフィックス) +- **共有問題**: なし(ARC001~057 と共有問題がない) + +### ARC001~057 の仕様 + +- **範囲**: ARC001 ~ ARC057 +- **特徴**: レーティング導入前のコンテスト +- **4問構成**: すべてのラウンドで A・B・C・D の4問 +- **ID フォーマット**: + - ARC001~034: `arc001_1`, `arc001_2`, `arc001_3`, `arc001_4` (数字サフィックス) + - ARC035~057: `arc035_a`, `arc035_b`, `arc035_c`, `arc035_d` (英字サフィックス) +- **共有問題**: なし(ABC001~041 と共有問題がない) + +### 表示設定 + +両プロバイダーとも以下で統一: + +- `tableBodyCellsWidth: 'w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1'` +- `isShownHeader: true` +- `isShownRoundLabel: true` +- `roundLabelWidth: 'xl:w-16'` +- `isShownTaskIndex: false` + +--- + +## テスト設計 + +### 2.1 ABC001ToABC041Provider テスト + +**ファイル**: `src/test/lib/utils/contest_table_provider.test.ts` + +**配置**: ARC058ToARC103Provider テストの後、AGC001OnwardsProvider テストの前 + +テストケースは [`docs/dev-notes/2025-12-03/add_tests_for_contest_table_provider/plan.md`](../../2025-12-03/add_tests_for_contest_table_provider/plan.md) の テスト2.1.1~2.1.10 を参照して実装。共有問題がないため、テスト2.1.11・2.1.12 は不要。 + +#### テスト2.1.1: フィルタリング(Range検証) + +ABC001~041 の範囲のみをフィルタリングすることを確認。古い ID フォーマット(数字サフィックス)と新しい ID フォーマット(英字サフィックス)の両方を含める。 + +```typescript +test('expects to filter tasks within ABC001-41 range', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mixed = [ + { contest_id: 'abc000', task_id: 'abc000_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, // 古いフォーマット + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, // 新しいフォーマット + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + { contest_id: 'abc042', task_id: 'abc042_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(3); + expect(filtered.map((t) => t.contest_id)).toEqual(['abc001', 'abc020', 'abc041']); +}); +``` + +#### テスト2.1.2: コンテストタイプ判別(ABC のみ、ARC との分離) + +ABC-type のみをフィルタリングし、ARC のコンテストが混ざらないことを確認。 + +```typescript +test('expects to filter only ABC-type contests and exclude ARC', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mixed = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(2); + expect(filtered.every((t) => t.contest_id.startsWith('abc'))).toBe(true); +}); +``` + +#### テスト2.1.3: メタデータ取得 + +```typescript +test('expects to return correct metadata', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const metadata = provider.getMetadata(); + + expect(metadata.title).toBe('AtCoder Beginner Contest 001 〜 041(レーティング導入前)'); + expect(metadata.abbreviationName).toBe('fromAbc001ToAbc041'); +}); +``` + +#### テスト2.1.4: ディスプレイ設定 + +```typescript +test('expects to return correct display config', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const config = provider.getDisplayConfig(); + + expect(config.isShownHeader).toBe(true); + expect(config.isShownRoundLabel).toBe(true); + expect(config.tableBodyCellsWidth).toBe('w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1'); + expect(config.roundLabelWidth).toBe('xl:w-16'); + expect(config.isShownTaskIndex).toBe(false); +}); +``` + +#### テスト2.1.5: ラウンドラベルフォーマット + +```typescript +test('expects to format contest round label correctly', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + + expect(provider.getContestRoundLabel('abc001')).toBe('001'); + expect(provider.getContestRoundLabel('abc041')).toBe('041'); +}); +``` + +#### テスト2.1.6: テーブル生成(古い ID フォーマット) + +古いフォーマット(数字サフィックス)での正しいテーブル生成を確認。 + +```typescript +test('expects to generate correct table structure with old ID format (numeric suffix)', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_2', task_table_index: 'B' }, + { contest_id: 'abc001', task_id: 'abc001_3', task_table_index: 'C' }, + { contest_id: 'abc001', task_id: 'abc001_4', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('abc001'); + expect(table.abc001).toHaveProperty('A'); + expect(table.abc001).toHaveProperty('B'); + expect(table.abc001).toHaveProperty('C'); + expect(table.abc001).toHaveProperty('D'); +}); +``` + +#### テスト2.1.7: テーブル生成(新しい ID フォーマット) + +新しいフォーマット(英字サフィックス)での正しいテーブル生成を確認。 + +```typescript +test('expects to generate correct table structure with new ID format (alphabet suffix)', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, + { contest_id: 'abc020', task_id: 'abc020_b', task_table_index: 'B' }, + { contest_id: 'abc020', task_id: 'abc020_c', task_table_index: 'C' }, + { contest_id: 'abc020', task_id: 'abc020_d', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('abc020'); + expect(table.abc020).toHaveProperty('A'); + expect(table.abc020).toHaveProperty('B'); + expect(table.abc020).toHaveProperty('C'); + expect(table.abc020).toHaveProperty('D'); +}); +``` + +#### テスト2.1.8: コンテスト ID 取得 + +```typescript +test('expects to return correct contest round ids', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + ]; + + const ids = provider.getContestRoundIds(mockTasks as TaskResult[]); + + expect(ids).toContain('abc001'); + expect(ids).toContain('abc020'); + expect(ids).toContain('abc041'); +}); +``` + +#### テスト2.1.9: ヘッダー ID 取得 + +```typescript +test('expects to return correct header ids', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_2', task_table_index: 'B' }, + { contest_id: 'abc001', task_id: 'abc001_3', task_table_index: 'C' }, + { contest_id: 'abc001', task_id: 'abc001_4', task_table_index: 'D' }, + ]; + + const ids = provider.getHeaderIdsForTask(mockTasks as TaskResult[]); + + expect(ids).toEqual(['A', 'B', 'C', 'D']); +}); +``` + +#### テスト2.1.10: 空入力処理 + +```typescript +test('expects to handle empty input gracefully', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + + const filteredEmpty = provider.filter([] as TaskResult[]); + const tableEmpty = provider.generateTable([] as TaskResult[]); + const idsEmpty = provider.getContestRoundIds([] as TaskResult[]); + const headerIdsEmpty = provider.getHeaderIdsForTask([] as TaskResult[]); + + expect(filteredEmpty).toEqual([]); + expect(tableEmpty).toEqual({}); + expect(idsEmpty).toEqual([]); + expect(headerIdsEmpty).toEqual([]); +}); +``` + +--- + +### 2.2 ARC001ToARC057Provider テスト + +**ファイル**: `src/test/lib/utils/contest_table_provider.test.ts` + +**配置**: ABC001ToABC041Provider テストの後、AGC001OnwardsProvider テストの前 + +テストケースは [`docs/dev-notes/2025-12-03/add_tests_for_contest_table_provider/plan.md`](../../2025-12-03/add_tests_for_contest_table_provider/plan.md) の テスト2.2.1~2.2.10 を参照して実装。共有問題がないため、テスト2.2.11・2.2.12 は不要。 + +各テストはパターンとして ABC001ToABC041Provider と同等ですが、以下の点で異なります: + +- Provider クラス: `ARC001ToARC057Provider` +- コンテスト ID の範囲: `arc001` ~ `arc057` +- ID フォーマット: + - ARC001~034: 数字サフィックス (`arc001_1`, `arc001_2`, ...) + - ARC035~057: 英字サフィックス (`arc035_a`, `arc035_b`, ...) +- メタデータ: + - title: `'AtCoder Regular Contest 001 〜 057(レーティング導入前)'` + - abbreviationName: `'fromArc001ToArc057'` + +#### テスト2.2.1: フィルタリング(Range検証) + +ARC001~057 の範囲のみをフィルタリングすることを確認。古い ID フォーマット(数字サフィックス)と新しい ID フォーマット(英字サフィックス)の両方を含める。 + +```typescript +test('expects to filter tasks within ARC001-57 range', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mixed = [ + { contest_id: 'arc000', task_id: 'arc000_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, // 古いフォーマット + { contest_id: 'arc035', task_id: 'arc035_a', task_table_index: 'A' }, // 新しいフォーマット + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + { contest_id: 'arc058', task_id: 'arc058_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(3); + expect(filtered.map((t) => t.contest_id)).toEqual(['arc001', 'arc035', 'arc057']); +}); +``` + +#### テスト2.2.2: コンテストタイプ判別(ARC のみ、ABC との分離) + +ARC-type のみをフィルタリングし、ABC のコンテストが混ざらないことを確認。 + +```typescript +test('expects to filter only ARC-type contests and exclude ABC', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mixed = [ + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(2); + expect(filtered.every((t) => t.contest_id.startsWith('arc'))).toBe(true); +}); +``` + +#### テスト2.2.3~2.2.10 + +ABC001ToABC041Provider のテスト2.1.3~2.1.10 と同等の構成で実装。 + +詳細は [`docs/dev-notes/2025-12-03/add_tests_for_contest_table_provider/plan.md`](../../2025-12-03/add_tests_for_contest_table_provider/plan.md) のテスト2.2.3~2.2.10 を参照。 + +--- + +## テスト実装時の注意点 + +### 1. モックデータの準備 + +`src/test/lib/utils/test_cases/contest_table_provider.ts` に、以下のモックデータを追加する必要があります: + +- ABC001~041 (古いフォーマット・新しいフォーマットの両方) +- ARC001~057 (古いフォーマット・新しいフォーマットの両方) + +### 2. task_table_index の取り扱い + +古い ID フォーマット(`abc001_1`)でも、`task_table_index` は必ず `'A'`, `'B'`, `'C'`, `'D'` になることに注意。 + +### 3. 共有問題への対応 + +ABC001~041 と ARC001~057 には共有問題がないため、共有問題に関するテストケースは不要です。 + +### 4. テストの配置 + +既存テストの構成を崩さないよう、以下の順序で配置: + +1. ABC 関連テスト(ABCLatest20Rounds → ABC319Onwards → ABC212ToABC318 → ABC126ToABC211 → ABC042ToABC125 → **ABC001ToABC041**) +2. ARC 関連テスト(ARC104Onwards → ARC058ToARC103 → **ARC001ToARC057**) +3. AGC001OnwardsProvider テスト以降 + +--- + +## 実装を開始する前の確認事項(設計パターン) + +### Q1. 参照ドキュメント + +計画では `docs/dev-notes/2025-12-03/add_tests_for_contest_table_provider/plan.md` のテスト設計を参照していますが、このファイルで ABC042ToABC125 & ARC058ToARC103 のテストが既に実装されていますか? + +**A1. YES** - 既に実装済み。参照パターンとして利用可。 + +### Q2. モックデータファイル + +`src/test/lib/utils/test_cases/contest_table_provider.ts` は既に存在していますか? + +**A2. YES** - 既に存在。新しいテストケース用のモックデータを追加する。 + +### Q3. 現在の実装状態 + +`src/lib/utils/contest_table_provider.ts` で ABC001ToABC041Provider と ARC001ToARC057Provider は既に実装されていますか? + +**A3. YES** - 既に実装済み(Issue #2838)。テスト追加のみ。 + +--- + +## 実装完了と教訓 + +### 実装結果 + +**テスト数**: + +- ABC001ToABC041Provider: 10テスト +- ARC001ToARC057Provider: 10テスト +- 合計新規追加: 20テスト +- 全体テストパス数: 186テスト(既存166テスト) + +**実装内容**: + +1. テストケースデータを `src/test/lib/utils/test_cases/contest_table_provider.ts` に追加 + - ABC001, ABC019, ABC020, ABC041(数字・英字サフィックスの両方をカバー) + - ARC001, ARC034, ARC035, ARC057(数字・英字サフィックスの両方をカバー) + +2. テストを `src/test/lib/utils/contest_table_provider.test.ts` に追加 + - ABC042ToABC125 テストの直後に ABC001ToABC041Provider テスト + - ARC058ToARC103 テストの直後に ARC001ToARC057Provider テスト + +### テスト設計パターンの活用 + +参照ドキュメント(2025-12-03のテスト設計)を活用することで、以下のベストプラクティスを実装: + +1. **フィルタリング検証**: 正しい範囲のコンテストのみを抽出していることを確認 +2. **コンテストタイプ判別**: ABC/ARC の分離が正確に機能していることを確認 +3. **メタデータ検証**: title と abbreviationName が正確であることを確認 +4. **ディスプレイ設定**: UI関連の設定が統一されていることを確認 +5. **ラウンドラベルフォーマット**: 3桁のゼロパディングが正確であることを確認 +6. **テーブル生成(両フォーマット)**: 古いフォーマット(数字)と新しいフォーマット(英字)の両方に対応していることを確認 +7. **ID取得**: コンテスト ID とヘッダー ID の取得が正確であることを確認 +8. **エッジケース処理**: 空入力の処理が正確であることを確認 + +### 実装上の重要なポイント + +1. **レーティング導入前コンテストの特性**: ABC001-041 と ARC001-057 はレーティング導入前のコンテストで、以下の特性を持つ + - ID フォーマットの移行期がある(ABC020 から、ARC035 から) + - 共有問題がない + - すべて 4 問構成 + +2. **テストケースデータの設計**: モックデータは以下の観点を網羅 + - 新旧 ID フォーマットの両方を含める + - 複数のステータス(AC, AC_WITH_EDITORIAL, TRYING, PENDING)を含める + - 範囲の開始・中盤・終了を網羅 + +3. **テスト配置の重要性**: テスト実行順序と配置 + - ABC関連: ABCLatest20Rounds → ... → ABC042ToABC125 → **ABC001ToABC041** → ARC104Onwards + - ARC関連: ARC104Onwards → ARC058ToARC103 → **ARC001ToARC057** → AGC001Onwards + - 既存テストの構成を維持することが重要 + +### 学習した設計パターン + +今後、新しいコンテスト期間のプロバイダーをテストする際に活用可能なパターン: + +```typescript +// 1. テストケースデータ: 新旧フォーマットを含める +const [xxx_old_format] = createContestTasks('xxxNNN', [ + { taskId: 'xxxnnn_1', taskTableIndex: 'A', statusName: AC }, + // ... old format +]); + +const [xxx_new_format] = createContestTasks('xxxNNN', [ + { taskId: 'xxxnnn_a', taskTableIndex: 'A', statusName: AC }, + // ... new format +]); + +// 2. テスト: 確認すべき項目を系統的に検証 +describe('XXXToYYYProvider', () => { + test('expects to filter tasks within XXX-YYY range', () => { + /* ... */ + }); + test('expects to filter only appropriate contest type', () => { + /* ... */ + }); + test('expects to return correct metadata', () => { + /* ... */ + }); + test('expects to return correct display config', () => { + /* ... */ + }); + test('expects to format contest round label correctly', () => { + /* ... */ + }); + test('expects to generate correct table structure with old ID format', () => { + /* ... */ + }); + test('expects to generate correct table structure with new ID format', () => { + /* ... */ + }); + test('expects to return correct contest round ids', () => { + /* ... */ + }); + test('expects to return correct header ids', () => { + /* ... */ + }); + test('expects to handle empty input gracefully', () => { + /* ... */ + }); +}); +``` + +このパターンにより、新しいプロバイダーの網羅的なテストを体系的に実装できる。 diff --git a/prisma/tasks.ts b/prisma/tasks.ts index 95231dea3..1a02807b1 100755 --- a/prisma/tasks.ts +++ b/prisma/tasks.ts @@ -4600,6 +4600,118 @@ export const tasks = [ name: 'Iroha and Haiku (ABC Edition)', title: 'A. Iroha and Haiku (ABC Edition)', }, + { + id: 'abc041_d', + contest_id: 'abc041', + problem_index: 'D', + name: '徒競走', + title: 'D. 徒競走', + }, + { + id: 'abc041_c', + contest_id: 'abc041', + problem_index: 'C', + name: '背の順', + title: 'C. 背の順', + }, + { + id: 'abc041_b', + contest_id: 'abc041', + problem_index: 'B', + name: '直方体', + title: 'B. 直方体', + }, + { + id: 'abc041_a', + contest_id: 'abc041', + problem_index: 'A', + name: '添字', + title: 'A. 添字', + }, + { + id: 'abc040_d', + contest_id: 'abc040', + problem_index: 'D', + name: '道路の老朽化対策について', + title: 'D. 道路の老朽化対策について', + }, + { + id: 'abc040_c', + contest_id: 'abc040', + problem_index: 'C', + name: '柱柱柱柱柱', + title: 'C. 柱柱柱柱柱', + }, + { + id: 'abc040_b', + contest_id: 'abc040', + problem_index: 'B', + name: '□□□□□', + title: 'B. □□□□□', + }, + { + id: 'abc040_a', + contest_id: 'abc040', + problem_index: 'A', + name: '赤赤赤赤青', + title: 'A. 赤赤赤赤青', + }, + { + id: 'abc020_d', + contest_id: 'abc020', + problem_index: 'D', + name: 'LCM Rush', + title: 'D. LCM Rush', + }, + { + id: 'abc020_c', + contest_id: 'abc020', + problem_index: 'C', + name: '壁抜け', + title: 'C. 壁抜け', + }, + { + id: 'abc020_b', + contest_id: 'abc020', + problem_index: 'B', + name: '足し算', + title: 'B. 足し算', + }, + { + id: 'abc020_a', + contest_id: 'abc020', + problem_index: 'A', + name: 'クイズ', + title: 'A. クイズ', + }, + { + id: 'abc019_4', + contest_id: 'abc019', + problem_index: 'D', + name: '高橋くんと木の直径', + title: 'D. 高橋くんと木の直径', + }, + { + id: 'abc019_3', + contest_id: 'abc019', + problem_index: 'C', + name: '高橋くんと魔法の箱', + title: 'C. 高橋くんと魔法の箱', + }, + { + id: 'abc019_2', + contest_id: 'abc019', + problem_index: 'B', + name: '高橋くんと文字列圧縮', + title: 'B. 高橋くんと文字列圧縮', + }, + { + id: 'abc019_1', + contest_id: 'abc019', + problem_index: 'A', + name: '高橋くんと年齢', + title: 'A. 高橋くんと年齢', + }, { id: 'abc007_3', contest_id: 'abc007', @@ -4607,7 +4719,62 @@ export const tasks = [ name: '幅優先探索', title: 'C. 幅優先探索', }, - + { + id: 'abc002_4', + contest_id: 'abc002', + problem_index: 'D', + name: '派閥', + title: 'D. 派閥', + }, + { + id: 'abc002_3', + contest_id: 'abc002', + problem_index: 'C', + name: '直訴', + title: 'C. 直訴', + }, + { + id: 'abc002_2', + contest_id: 'abc002', + problem_index: 'B', + name: '罠', + title: 'B. 罠', + }, + { + id: 'abc002_1', + contest_id: 'abc002', + problem_index: 'A', + name: '正直者', + title: 'A. 正直者', + }, + { + id: 'abc001_4', + contest_id: 'abc001', + problem_index: 'D', + name: '感雨時刻の整理', + title: 'D. 感雨時刻の整理', + }, + { + id: 'abc001_3', + contest_id: 'abc001', + problem_index: 'C', + name: '風力観測', + title: 'C. 風力観測', + }, + { + id: 'abc001_2', + contest_id: 'abc001', + problem_index: 'B', + name: '視程の通報', + title: 'B. 視程の通報', + }, + { + id: 'abc001_1', + contest_id: 'abc001', + problem_index: 'A', + name: '積雪深差', + title: 'A. 積雪深差', + }, { id: 'arc209_e', contest_id: 'arc209', @@ -4974,6 +5141,174 @@ export const tasks = [ name: "Iroha's Obsession", title: "C. Iroha's Obsession", }, + { + id: 'arc057_d', + contest_id: 'arc057', + problem_index: 'D', + name: '全域木', + title: 'D. 全域木', + }, + { + id: 'arc057_c', + contest_id: 'arc057', + problem_index: 'C', + name: '2乗根', + title: 'C. 2乗根', + }, + { + id: 'arc057_b', + contest_id: 'arc057', + problem_index: 'B', + name: '高橋君ゲーム', + title: 'B. 高橋君ゲーム', + }, + { + id: 'arc057_a', + contest_id: 'arc057', + problem_index: 'A', + name: '2兆円', + title: 'A. 2兆円', + }, + { + id: 'arc056_d', + contest_id: 'arc056', + problem_index: 'D', + name: 'サケノミ', + title: 'D. サケノミ', + }, + { + id: 'arc056_c', + contest_id: 'arc056', + problem_index: 'C', + name: '部門分け', + title: 'C. 部門分け', + }, + { + id: 'arc056_b', + contest_id: 'arc056', + problem_index: 'B', + name: '駐車場', + title: 'B. 駐車場', + }, + { + id: 'arc056_a', + contest_id: 'arc056', + problem_index: 'A', + name: 'みんなでワイワイみかん', + title: 'A. みんなでワイワイみかん', + }, + { + id: 'arc035_d', + contest_id: 'arc035', + problem_index: 'D', + name: '高橋くんとマラソンコース', + title: 'D. 高橋くんとマラソンコース', + }, + { + id: 'arc035_c', + contest_id: 'arc035', + problem_index: 'C', + name: 'アットコーダー王国の交通事情', + title: 'C. アットコーダー王国の交通事情', + }, + { + id: 'arc035_b', + contest_id: 'arc035', + problem_index: 'B', + name: 'アットコーダー王国のコンテスト事情', + title: 'B. アットコーダー王国のコンテスト事情', + }, + { + id: 'arc035_a', + contest_id: 'arc035', + problem_index: 'A', + name: '高橋くんと回文', + title: 'A. 高橋くんと回文', + }, + { + id: 'arc034_4', + contest_id: 'arc034', + problem_index: 'D', + name: 'インフレゲーム', + title: 'D. インフレゲーム', + }, + { + id: 'arc034_3', + contest_id: 'arc034', + problem_index: 'C', + name: '約数かつ倍数', + title: 'C. 約数かつ倍数', + }, + { + id: 'arc034_2', + contest_id: 'arc034', + problem_index: 'B', + name: '方程式', + title: 'B. 方程式', + }, + { + id: 'arc034_1', + contest_id: 'arc034', + problem_index: 'A', + name: '首席', + title: 'A. 首席', + }, + { + id: 'arc002_4', + contest_id: 'arc002', + problem_index: 'D', + name: 'ボードゲーム', + title: 'D. ボードゲーム', + }, + { + id: 'arc002_3', + contest_id: 'arc002', + problem_index: 'C', + name: 'コマンド入力', + title: 'C. コマンド入力', + }, + { + id: 'arc002_2', + contest_id: 'arc002', + problem_index: 'B', + name: '割り切れる日付', + title: 'B. 割り切れる日付', + }, + { + id: 'arc002_1', + contest_id: 'arc002', + problem_index: 'A', + name: 'うるう年', + title: 'A. うるう年', + }, + { + id: 'arc001_4', + contest_id: 'arc001', + problem_index: 'D', + name: 'レースゲーム', + title: 'D. レースゲーム', + }, + { + id: 'arc001_3', + contest_id: 'arc001', + problem_index: 'C', + name: 'パズルのお手伝い', + title: 'C. パズルのお手伝い', + }, + { + id: 'arc001_2', + contest_id: 'arc001', + problem_index: 'B', + name: 'リモコン', + title: 'B. リモコン', + }, + { + id: 'arc001_1', + contest_id: 'arc001', + problem_index: 'A', + name: 'センター採点', + title: 'A. センター採点', + }, { id: 'agc074_e', contest_id: 'agc074', diff --git a/src/lib/utils/contest_table_provider.ts b/src/lib/utils/contest_table_provider.ts index 7c4d13c7b..d6ac518df 100644 --- a/src/lib/utils/contest_table_provider.ts +++ b/src/lib/utils/contest_table_provider.ts @@ -296,6 +296,45 @@ export class ABC042ToABC125Provider extends ContestTableProviderBase { } } +// ABC001 〜 ABC041 (2013/10/12 〜 2016/07/02) +// 4 tasks per contest +// +// Note: Unrated contests before ABC042. +export class ABC001ToABC041Provider extends ContestTableProviderBase { + protected setFilterCondition(): (taskResult: TaskResult) => boolean { + return (taskResult: TaskResult) => { + if (classifyContest(taskResult.contest_id) !== this.contestType) { + return false; + } + + const contestRound = parseContestRound(taskResult.contest_id, 'abc'); + return contestRound >= 1 && contestRound <= 41; + }; + } + + getMetadata(): ContestTableMetaData { + return { + title: 'AtCoder Beginner Contest 001 〜 041(レーティング導入前)', + abbreviationName: 'fromAbc001ToAbc041', + }; + } + + getDisplayConfig(): ContestTableDisplayConfig { + return { + isShownHeader: true, + isShownRoundLabel: true, + tableBodyCellsWidth: 'w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1', + roundLabelWidth: 'xl:w-16', + isShownTaskIndex: false, + }; + } + + getContestRoundLabel(contestId: string): string { + const contestNameLabel = getContestNameLabel(contestId); + return contestNameLabel.replace('ABC ', ''); + } +} + // ARC104 〜 (2020/10/03 〜 ) // 4 〜 7 tasks per contest export class ARC104OnwardsProvider extends ContestTableProviderBase { @@ -364,6 +403,45 @@ export class ARC058ToARC103Provider extends ContestTableProviderBase { } } +// ARC001 〜 ARC057 (2012/04/12 〜 2016/07/09) +// 4 tasks per contest +// +// Note: Unrated contests before ARC058. +export class ARC001ToARC057Provider extends ContestTableProviderBase { + protected setFilterCondition(): (taskResult: TaskResult) => boolean { + return (taskResult: TaskResult) => { + if (classifyContest(taskResult.contest_id) !== this.contestType) { + return false; + } + + const contestRound = parseContestRound(taskResult.contest_id, 'arc'); + return contestRound >= 1 && contestRound <= 57; + }; + } + + getMetadata(): ContestTableMetaData { + return { + title: 'AtCoder Regular Contest 001 〜 057(レーティング導入前)', + abbreviationName: 'fromArc001ToArc057', + }; + } + + getDisplayConfig(): ContestTableDisplayConfig { + return { + isShownHeader: true, + isShownRoundLabel: true, + tableBodyCellsWidth: 'w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1', + roundLabelWidth: 'xl:w-16', + isShownTaskIndex: false, + }; + } + + getContestRoundLabel(contestId: string): string { + const contestNameLabel = getContestNameLabel(contestId); + return contestNameLabel.replace('ARC ', ''); + } +} + // AGC001 〜 (2016/07/16 〜 ) // 4 〜 7 tasks per contest export class AGC001OnwardsProvider extends ContestTableProviderBase { @@ -854,6 +932,15 @@ export const prepareContestProviderPresets = () => { ariaLabel: 'Filter contests from ABC 042 to ABC 125', }).addProvider(new ABC042ToABC125Provider(ContestType.ABC)), + /** + * Single group for ABC 001-041 + */ + ABC001ToABC041: () => + new ContestTableProviderGroup(`From ABC 001 to ABC 041`, { + buttonLabel: '旧 ABC', + ariaLabel: 'Filter contests from ABC 001 to ABC 041', + }).addProvider(new ABC001ToABC041Provider(ContestType.ABC)), + /** * Single group for ARC 104 onwards */ @@ -872,6 +959,15 @@ export const prepareContestProviderPresets = () => { ariaLabel: 'Filter contests from ARC 058 to ARC 103', }).addProvider(new ARC058ToARC103Provider(ContestType.ARC)), + /** + * Single group for ARC 001-057 + */ + ARC001ToARC057: () => + new ContestTableProviderGroup(`ARC 001 To ARC 057`, { + buttonLabel: '旧 ARC', + ariaLabel: 'Filter contests from ARC 001 to ARC 057', + }).addProvider(new ARC001ToARC057Provider(ContestType.ARC)), + /** * Single group for AGC 001 onwards */ @@ -944,6 +1040,8 @@ export const contestTableProviderGroups = { arc104Onwards: prepareContestProviderPresets().ARC104Onwards(), fromArc058ToArc103: prepareContestProviderPresets().ARC058ToARC103(), agc001Onwards: prepareContestProviderPresets().AGC001Onwards(), + fromAbc001ToAbc041: prepareContestProviderPresets().ABC001ToABC041(), + fromArc001ToArc057: prepareContestProviderPresets().ARC001ToARC057(), typical90: prepareContestProviderPresets().Typical90(), tessokuBook: prepareContestProviderPresets().TessokuBook(), mathAndAlgorithm: prepareContestProviderPresets().MathAndAlgorithm(), diff --git a/src/test/lib/utils/contest_table_provider.test.ts b/src/test/lib/utils/contest_table_provider.test.ts index 84235a158..0930e06ce 100644 --- a/src/test/lib/utils/contest_table_provider.test.ts +++ b/src/test/lib/utils/contest_table_provider.test.ts @@ -9,8 +9,10 @@ import { ABC212ToABC318Provider, ABC126ToABC211Provider, ABC042ToABC125Provider, + ABC001ToABC041Provider, ARC104OnwardsProvider, ARC058ToARC103Provider, + ARC001ToARC057Provider, AGC001OnwardsProvider, EDPCProvider, TDPCProvider, @@ -547,6 +549,144 @@ describe('ContestTableProviderBase and implementations', () => { }); }); + describe('ABC 001 to ABC 041', () => { + test('expects to filter tasks within ABC001-41 range', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mixed = [ + { contest_id: 'abc000', task_id: 'abc000_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + { contest_id: 'abc042', task_id: 'abc042_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(3); + expect(filtered.map((task) => task.contest_id)).toEqual(['abc001', 'abc020', 'abc041']); + }); + + test('expects to filter only ABC-type contests and exclude ARC', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mixed = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(2); + expect(filtered.every((task) => task.contest_id.startsWith('abc'))).toBe(true); + }); + + test('expects to return correct metadata', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const metadata = provider.getMetadata(); + + expect(metadata.title).toBe('AtCoder Beginner Contest 001 〜 041(レーティング導入前)'); + expect(metadata.abbreviationName).toBe('fromAbc001ToAbc041'); + }); + + test('expects to return correct display config', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const config = provider.getDisplayConfig(); + + expect(config.isShownHeader).toBe(true); + expect(config.isShownRoundLabel).toBe(true); + expect(config.tableBodyCellsWidth).toBe('w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1'); + expect(config.roundLabelWidth).toBe('xl:w-16'); + expect(config.isShownTaskIndex).toBe(false); + }); + + test('expects to format contest round label correctly', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + + expect(provider.getContestRoundLabel('abc001')).toBe('001'); + expect(provider.getContestRoundLabel('abc041')).toBe('041'); + }); + + test('expects to generate correct table structure with old ID format (numeric suffix)', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_2', task_table_index: 'B' }, + { contest_id: 'abc001', task_id: 'abc001_3', task_table_index: 'C' }, + { contest_id: 'abc001', task_id: 'abc001_4', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('abc001'); + expect(table.abc001).toHaveProperty('A'); + expect(table.abc001).toHaveProperty('B'); + expect(table.abc001).toHaveProperty('C'); + expect(table.abc001).toHaveProperty('D'); + }); + + test('expects to generate correct table structure with new ID format (alphabet suffix)', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, + { contest_id: 'abc020', task_id: 'abc020_b', task_table_index: 'B' }, + { contest_id: 'abc020', task_id: 'abc020_c', task_table_index: 'C' }, + { contest_id: 'abc020', task_id: 'abc020_d', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('abc020'); + expect(table.abc020).toHaveProperty('A'); + expect(table.abc020).toHaveProperty('B'); + expect(table.abc020).toHaveProperty('C'); + expect(table.abc020).toHaveProperty('D'); + }); + + test('expects to return correct contest round ids', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc020', task_id: 'abc020_a', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + ]; + + const ids = provider.getContestRoundIds(mockTasks as TaskResult[]); + + expect(ids).toContain('abc001'); + expect(ids).toContain('abc020'); + expect(ids).toContain('abc041'); + }); + + test('expects to return correct header ids', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + const mockTasks = [ + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_2', task_table_index: 'B' }, + { contest_id: 'abc001', task_id: 'abc001_3', task_table_index: 'C' }, + { contest_id: 'abc001', task_id: 'abc001_4', task_table_index: 'D' }, + ]; + + const ids = provider.getHeaderIdsForTask(mockTasks as TaskResult[]); + + expect(ids).toEqual(['A', 'B', 'C', 'D']); + }); + + test('expects to handle empty input gracefully', () => { + const provider = new ABC001ToABC041Provider(ContestType.ABC); + + const filteredEmpty = provider.filter([] as TaskResult[]); + const tableEmpty = provider.generateTable([] as TaskResult[]); + const idsEmpty = provider.getContestRoundIds([] as TaskResult[]); + const headerIdsEmpty = provider.getHeaderIdsForTask([] as TaskResult[]); + + expect(filteredEmpty).toEqual([]); + expect(tableEmpty).toEqual({}); + expect(idsEmpty).toEqual([]); + expect(headerIdsEmpty).toEqual([]); + }); + }); + // ARC 104 Onwards only describe('ARC 104 Onwards', () => { test('expects to filter tasks to include only ARC104 and later', () => { @@ -855,6 +995,144 @@ describe('ContestTableProviderBase and implementations', () => { }); }); + describe('ARC 001 to ARC 057', () => { + test('expects to filter tasks within ARC001-57 range', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mixed = [ + { contest_id: 'arc000', task_id: 'arc000_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'arc035', task_id: 'arc035_a', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + { contest_id: 'arc058', task_id: 'arc058_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(3); + expect(filtered.map((task) => task.contest_id)).toEqual(['arc001', 'arc035', 'arc057']); + }); + + test('expects to filter only ARC-type contests and exclude ABC', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mixed = [ + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'abc001', task_id: 'abc001_1', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + { contest_id: 'abc041', task_id: 'abc041_a', task_table_index: 'A' }, + ]; + + const filtered = provider.filter(mixed as TaskResult[]); + + expect(filtered).toHaveLength(2); + expect(filtered.every((task) => task.contest_id.startsWith('arc'))).toBe(true); + }); + + test('expects to return correct metadata', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const metadata = provider.getMetadata(); + + expect(metadata.title).toBe('AtCoder Regular Contest 001 〜 057(レーティング導入前)'); + expect(metadata.abbreviationName).toBe('fromArc001ToArc057'); + }); + + test('expects to return correct display config', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const config = provider.getDisplayConfig(); + + expect(config.isShownHeader).toBe(true); + expect(config.isShownRoundLabel).toBe(true); + expect(config.tableBodyCellsWidth).toBe('w-1/2 md:w-1/3 lg:w-1/4 px-1 py-1'); + expect(config.roundLabelWidth).toBe('xl:w-16'); + expect(config.isShownTaskIndex).toBe(false); + }); + + test('expects to format contest round label correctly', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + + expect(provider.getContestRoundLabel('arc001')).toBe('001'); + expect(provider.getContestRoundLabel('arc057')).toBe('057'); + }); + + test('expects to generate correct table structure with old ID format (numeric suffix)', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mockTasks = [ + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_2', task_table_index: 'B' }, + { contest_id: 'arc001', task_id: 'arc001_3', task_table_index: 'C' }, + { contest_id: 'arc001', task_id: 'arc001_4', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('arc001'); + expect(table.arc001).toHaveProperty('A'); + expect(table.arc001).toHaveProperty('B'); + expect(table.arc001).toHaveProperty('C'); + expect(table.arc001).toHaveProperty('D'); + }); + + test('expects to generate correct table structure with new ID format (alphabet suffix)', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mockTasks = [ + { contest_id: 'arc035', task_id: 'arc035_a', task_table_index: 'A' }, + { contest_id: 'arc035', task_id: 'arc035_b', task_table_index: 'B' }, + { contest_id: 'arc035', task_id: 'arc035_c', task_table_index: 'C' }, + { contest_id: 'arc035', task_id: 'arc035_d', task_table_index: 'D' }, + ]; + + const table = provider.generateTable(mockTasks as TaskResult[]); + + expect(table).toHaveProperty('arc035'); + expect(table.arc035).toHaveProperty('A'); + expect(table.arc035).toHaveProperty('B'); + expect(table.arc035).toHaveProperty('C'); + expect(table.arc035).toHaveProperty('D'); + }); + + test('expects to return correct contest round ids', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mockTasks = [ + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'arc035', task_id: 'arc035_a', task_table_index: 'A' }, + { contest_id: 'arc057', task_id: 'arc057_a', task_table_index: 'A' }, + ]; + + const ids = provider.getContestRoundIds(mockTasks as TaskResult[]); + + expect(ids).toContain('arc001'); + expect(ids).toContain('arc035'); + expect(ids).toContain('arc057'); + }); + + test('expects to return correct header ids', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + const mockTasks = [ + { contest_id: 'arc001', task_id: 'arc001_1', task_table_index: 'A' }, + { contest_id: 'arc001', task_id: 'arc001_2', task_table_index: 'B' }, + { contest_id: 'arc001', task_id: 'arc001_3', task_table_index: 'C' }, + { contest_id: 'arc001', task_id: 'arc001_4', task_table_index: 'D' }, + ]; + + const ids = provider.getHeaderIdsForTask(mockTasks as TaskResult[]); + + expect(ids).toEqual(['A', 'B', 'C', 'D']); + }); + + test('expects to handle empty input gracefully', () => { + const provider = new ARC001ToARC057Provider(ContestType.ARC); + + const filteredEmpty = provider.filter([] as TaskResult[]); + const tableEmpty = provider.generateTable([] as TaskResult[]); + const idsEmpty = provider.getContestRoundIds([] as TaskResult[]); + const headerIdsEmpty = provider.getHeaderIdsForTask([] as TaskResult[]); + + expect(filteredEmpty).toEqual([]); + expect(tableEmpty).toEqual({}); + expect(idsEmpty).toEqual([]); + expect(headerIdsEmpty).toEqual([]); + }); + }); + describe('AGC 001 Onwards', () => { test('expects to filter tasks to include only AGC001 and later', () => { const provider = new AGC001OnwardsProvider(ContestType.AGC); diff --git a/src/test/lib/utils/test_cases/contest_table_provider.ts b/src/test/lib/utils/test_cases/contest_table_provider.ts index 2748177b7..2609ebad8 100644 --- a/src/test/lib/utils/test_cases/contest_table_provider.ts +++ b/src/test/lib/utils/test_cases/contest_table_provider.ts @@ -120,6 +120,62 @@ const [abc212_a, abc212_b, abc212_f, abc212_g, abc212_h] = createContestTasks('a const [abc213_h] = createContestTasks('abc213', [{ taskTableIndex: 'H', statusName: PENDING }]); const [abc232_h] = createContestTasks('abc232', [{ taskTableIndex: 'H', statusName: TRYING }]); +// ABC001 - ABC019: 4 tasks (A, B, C, D) with numeric suffix format +const [abc001_1, abc001_2, abc001_3, abc001_4] = createContestTasks('abc001', [ + { taskId: 'abc001_1', taskTableIndex: 'A', statusName: AC }, + { taskId: 'abc001_2', taskTableIndex: 'B', statusName: AC }, + { taskId: 'abc001_3', taskTableIndex: 'C', statusName: AC_WITH_EDITORIAL }, + { taskId: 'abc001_4', taskTableIndex: 'D', statusName: TRYING }, +]); +const [abc019_1, abc019_2, abc019_3, abc019_4] = createContestTasks('abc019', [ + { taskId: 'abc019_1', taskTableIndex: 'A', statusName: AC }, + { taskId: 'abc019_2', taskTableIndex: 'B', statusName: AC }, + { taskId: 'abc019_3', taskTableIndex: 'C', statusName: PENDING }, + { taskId: 'abc019_4', taskTableIndex: 'D', statusName: PENDING }, +]); + +// ABC020 - ABC041: 4 tasks (A, B, C, D) with alphabet suffix format +const [abc020_a, abc020_b, abc020_c, abc020_d] = createContestTasks('abc020', [ + { taskId: 'abc020_a', taskTableIndex: 'A', statusName: AC }, + { taskId: 'abc020_b', taskTableIndex: 'B', statusName: AC }, + { taskId: 'abc020_c', taskTableIndex: 'C', statusName: AC_WITH_EDITORIAL }, + { taskId: 'abc020_d', taskTableIndex: 'D', statusName: TRYING }, +]); +const [abc041_a, abc041_b, abc041_c, abc041_d] = createContestTasks('abc041', [ + { taskId: 'abc041_a', taskTableIndex: 'A', statusName: AC }, + { taskId: 'abc041_b', taskTableIndex: 'B', statusName: AC }, + { taskId: 'abc041_c', taskTableIndex: 'C', statusName: PENDING }, + { taskId: 'abc041_d', taskTableIndex: 'D', statusName: PENDING }, +]); + +// ARC001 - ARC034: 4 tasks (A, B, C, D) with numeric suffix format +const [arc001_1, arc001_2, arc001_3, arc001_4] = createContestTasks('arc001', [ + { taskId: 'arc001_1', taskTableIndex: 'A', statusName: AC }, + { taskId: 'arc001_2', taskTableIndex: 'B', statusName: AC }, + { taskId: 'arc001_3', taskTableIndex: 'C', statusName: AC_WITH_EDITORIAL }, + { taskId: 'arc001_4', taskTableIndex: 'D', statusName: TRYING }, +]); +const [arc034_1, arc034_2, arc034_3, arc034_4] = createContestTasks('arc034', [ + { taskId: 'arc034_1', taskTableIndex: 'A', statusName: AC }, + { taskId: 'arc034_2', taskTableIndex: 'B', statusName: AC }, + { taskId: 'arc034_3', taskTableIndex: 'C', statusName: PENDING }, + { taskId: 'arc034_4', taskTableIndex: 'D', statusName: PENDING }, +]); + +// ARC035 - ARC057: 4 tasks (A, B, C, D) with alphabet suffix format +const [arc035_a, arc035_b, arc035_c, arc035_d] = createContestTasks('arc035', [ + { taskId: 'arc035_a', taskTableIndex: 'A', statusName: AC }, + { taskId: 'arc035_b', taskTableIndex: 'B', statusName: AC }, + { taskId: 'arc035_c', taskTableIndex: 'C', statusName: AC_WITH_EDITORIAL }, + { taskId: 'arc035_d', taskTableIndex: 'D', statusName: TRYING }, +]); +const [arc057_a, arc057_b, arc057_c, arc057_d] = createContestTasks('arc057', [ + { taskId: 'arc057_a', taskTableIndex: 'A', statusName: AC }, + { taskId: 'arc057_b', taskTableIndex: 'B', statusName: AC }, + { taskId: 'arc057_c', taskTableIndex: 'C', statusName: PENDING }, + { taskId: 'arc057_d', taskTableIndex: 'D', statusName: PENDING }, +]); + // ABC233 - ABC318: 8 tasks (A, B, C, D, E, F, G and Ex) const [abc233_a, abc233_b, abc233_ex] = createContestTasks('abc233', [ { taskTableIndex: 'A', statusName: AC }, @@ -251,6 +307,38 @@ const [ ]); export const taskResultsForContestTableProvider: TaskResults = [ + abc001_1, + abc001_2, + abc001_3, + abc001_4, + abc019_1, + abc019_2, + abc019_3, + abc019_4, + abc020_a, + abc020_b, + abc020_c, + abc020_d, + abc041_a, + abc041_b, + abc041_c, + abc041_d, + arc001_1, + arc001_2, + arc001_3, + arc001_4, + arc034_1, + arc034_2, + arc034_3, + arc034_4, + arc035_a, + arc035_b, + arc035_c, + arc035_d, + arc057_a, + arc057_b, + arc057_c, + arc057_d, abc126_a, abc126_b, abc126_e,