⚙️

VSCodeでの競プロ環境構築 (C++)

2023/08/24に公開

以前の記事 では Julia での環境構築を紹介しましたが、私自身は基本的にC++で競プロをしているので、その説明をしようと思います。
以前の記事に幾らか追加したような内容になっています。

環境としてはWSLを想定していますが、そうでなくても殆ど問題ないと思います。
また、この記事ではWSLのインストールやVSCodeのインストール等については説明しません。

競プロ用ディレクトリの作成

適当な競プロ用のディレクトリ(contests とか)を作成します。
特に言及しない限り、この記事内では競プロ用ディレクトリをカレントディレクトリとします。

g++ のインストール

clang++ などの他のコンパイラもありますが、競プロでは g++ が一番使われていると思うので、これをインストールします。
そもそも、既にインストールされている人が殆どだとは思いますが…(最初の方に入れるであろう build-essential パッケージに同梱されています)

sudo apt install g++

を実行します。
g++ がきちんとインストール出来ているかを確認するために以下のコマンドを実行します。

g++ --version

うまくいっていれば g++ のバージョン情報が表示されるはずです。

VSCode における C++ の設定

拡張機能 C/C++ Extension Pack のインストール

拡張機能 C/C++ Extension Pack をインストールをします。
C++ は競プロくらいでしか使わない、という方は C/C++ Extension Pack に同梱されている C/C++ だけのインストールでも良いかもしれません。

IntelliSense の設定

コーディングの体験を良くするために IntelliSense の設定をします。
.vscode ディレクトリ内に c_cpp_properties.json ファイルを作成し、以下のように記述します。

c_cpp_properties.json
{
    "configurations": [
        {
            "name": "WSL",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/g++",
            "cStandard": "c17",
            "cppStandard": "c++17",
            "intelliSenseMode": "linux-gcc-x64"
        }
    ],
    "version": 4
}

フォーマッタの設定

ClangFormat を使ってコードの整形をできるようにしていきます。
.clang-format ファイルを作成して、これ に従ってルールを作成していきます。

.clang-format の例

好みが分かれると思うので、自分で作成する方が良いと思います。

.clang-format
---
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: None
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
  - __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
  AfterCaseLabel: false
  AfterClass: false
  AfterControlStatement: Never
  AfterEnum: false
  AfterFunction: false
  AfterNamespace: false
  AfterObjCDeclaration: false
  AfterStruct: false
  AfterUnion: false
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
  BeforeLambdaBody: false
  BeforeWhile: false
  IndentBraces: false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakAfterJavaFieldAnnotations: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: false
ColumnLimit: 88
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IfMacros:
  - KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
  - Regex: ^"(llvm|llvm-c|clang|clang-c)/
    Priority: 2
    SortPriority: 0
    CaseSensitive: false
  - Regex: ^(<|"(gtest|gmock|isl|json)/)
    Priority: 3
    SortPriority: 0
    CaseSensitive: false
  - Regex: .*
    Priority: 1
    SortPriority: 0
    CaseSensitive: false
IncludeIsMainRegex: (Test)?$
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: Indent
IndentGotoLabels: false
IndentPPDirectives: None
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
Language: Cpp
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
QualifierAlignment: Right
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Always
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceAroundPointerQualifiers: Both
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
  AfterControlStatements: true
  AfterForeachMacros: true
  AfterFunctionDeclarationName: false
  AfterFunctionDefinitionName: false
  AfterIfMacros: true
  AfterOverloadedOperator: false
  BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
  Minimum: 1
  Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
StatementAttributeLikeMacros:
  - Q_EMIT
StatementMacros:
  - Q_UNUSED
  - QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: ForContinuationAndIndentation
WhitespaceSensitiveMacros:
  - STRINGIZE
  - PP_STRINGIZE
  - BOOST_PP_STRINGIZE
  - NS_SWIFT_NAME
  - CF_SWIFT_NAME

作成したら .vscode ディレクトリ内の .settings.json"C_Cpp.clang_format_style": "file" を追記します。
また、保存時に整形されるようにすると便利なので .settings.json"editor.formatOnSave": true も追記します。
これにより Ctrl+S で整形されるようになります。

.settings.json
{
    "C_Cpp.clang_format_style": "file",
    "editor.formatOnSave": true
}

online-judge-tools の設定

ここでは online-judge-tools/ojonline-judge-tools/template-generator の2つを導入します。
これらについての説明は、以下のURLを見てください。

導入方法についてもそれぞれの GitHub に書いてありますが、この記事でも一応記述しておきます。
これらのツールの導入のために pippipenv などを使えるようにしてください。
インストールについての説明は省略します。

また、この記事では pipenv を使ってインストールするので、それ以外の場合は適宜読み替えてください。

oj のインストール

下記のコマンドでインストールします。

pipenv install online-judge-tools

oj でのログイン

ログインをしなければ提出などができないので、設定していきます。

pipenv run oj login https://atcoder.jp

上記のコマンドを入力すると、AtCoderでのユーザー名とパスワードを求められるので入力します。

template-generator のインストール

基本的にはここに従います。
下記のコマンドでインストールします。

pipenv install online-judge-template-generator

VSCodeにおけるonline-judge-toolsの設定

シェルスクリプトの作成

まず configs ディレクトリを作成し、その中に submit.sh という名前でファイルを作成します。
そのファイルに以下のように書きます。

submit.sh
#!/bin/bash

submit_file=$1 # relative file path (${service_name}/${contest_id}/${problem_id}/main.cpp)

service_name=${submit_file%%/*}
contest_id=$(basename ${submit_file%/*/*})
problem_id=$(basename ${submit_file%/*})

case "$service_name" in
  "AtCoder" )
    problem_url="https://atcoder.jp/contests/${contest_id}/tasks/${problem_id}"
    ;;
  "Codeforces" )
    problem_url="https://codeforces.com/contest/${contest_id}/problem/${problem_id}"
    ;;
esac

pipenv run oj s -y ${problem_url} ${submit_file}

その後 chmod 755 configs/submit.sh を実行してパーミッションを変更します。

同じように configs ディレクトリの中に dltest.sh という名前でファイルを作成し、以下のように記述して、パーミッションを変更します。

dltest.sh
#!/bin/bash

problem_dir=$1 # relative directory path (${service_name}/${contest_id}/${problem_id})

service_name=${problem_dir%%/*}
contest_id=$(basename ${problem_dir%/*})
problem_id=${problem_dir##*/}

test_dir=${problem_dir}/test

case "$service_name" in
  "AtCoder" )
    problem_url="https://atcoder.jp/contests/${contest_id}/tasks/${problem_id}"
    ;;
  "Codeforces" )
    problem_url="https://codeforces.com/contest/${contest_id}/problem/${problem_id}"
    ;;
esac

if [ ! -e ${test_dir} ]; then
    pipenv run oj d -d ${test_dir} ${problem_url}
fi

テンプレート等の作成

configs/ ディレクトリに prepare.config.toml を以下の内容で作成します。
これ に従って適当に追記すると良いです。

prepare.config.toml
contest_directory = "{service_name}/{contest_id}"
problem_directory = "{problem_id}"

[templates]
"main.cpp" = "my_main.cpp"

そして ~/.config/online-judge-tools/template/ ディレクトリに my_main.cpp を作成します。
これがテンプレートファイルになるので、使いやすいように変更してください。
これ に従ってテンプレートを作成すると、入出力まで自動で書かれたファイルが生成されます。
スタイル等にこだわりがなく、初期のままで良ければ、テンプレートファイルを作る必要はありませんので prepare.config.toml をそのようにしてください。組み込みのテンプレートファイル main.cpp では入出力まで自動で生成されるようになっています。

テンプレートの例(入出力自動化無し)
my_main.cpp
<%!
	import os
	import platform
	from logging import getLogger

	import onlinejudge_template.generator.cplusplus as cplusplus
	import onlinejudge_template.generator.topcoder as topcoder
	import onlinejudge_template.generator.hook as hook
%>\
<%
	logger = getLogger(__name__)
	data["config"]["indent"] = "\t"
	data["config"]["scanner"] = "cin"
	data["config"]["printer"] = "cout"
	if platform.system() == "Linux" and "clang" not in os.environ.get("CXX", "g++"):
		include = "#include <bits/stdc++.h>"
	else:
		include = "\n".join([
			"#include <iostream>",
			"#include <string>",
			"#include <vector>",
		])
	hook.register_filter_command(["sed", "-e", "s/std:://g", "-e", r"s/'\\n'/endl/g"], data=data)
%>\
${include}

using namespace std;

% if topcoder.is_topcoder(data):
<% solve_function = topcoder.class_name(data) + "()." + topcoder.method_name(data) %>\
class ${topcoder.class_name(data)} {
public:
	${cplusplus.return_type(data)} ${topcoder.method_name(data)}(${cplusplus.formal_arguments(data)}) {
		// TODO: edit here
	}
};

% endif
int main() {
	

	return 0;
}
テンプレートの例(入出力自動化有り)
my_main.cpp
<%!
	import os
	import platform
	from logging import getLogger

	import onlinejudge_template.generator.cplusplus as cplusplus
	import onlinejudge_template.generator.topcoder as topcoder
	import onlinejudge_template.generator.hook as hook
%>\
<%
	logger = getLogger(__name__)
	data["config"]["indent"] = "\t"
	data["config"]["scanner"] = "cin"
	data["config"]["printer"] = "cout"
	if platform.system() == "Linux" and "clang" not in os.environ.get("CXX", "g++"):
		include = "#include <bits/stdc++.h>"
	else:
		include = "\n".join([
			"#include <iostream>",
			"#include <string>",
			"#include <vector>",
		])
	hook.register_filter_command(["sed", "-e", "s/std:://g", "-e", r"s/'\\n'/endl/g"], data=data)
%>\
${include}

using namespace std;

% if topcoder.is_topcoder(data):
<% solve_function = topcoder.class_name(data) + "()." + topcoder.method_name(data) %>\
class ${topcoder.class_name(data)} {
public:
	${cplusplus.return_type(data)} ${topcoder.method_name(data)}(${cplusplus.formal_arguments(data)}) {
		// TODO: edit here
	}
};

% endif
int main() {
${cplusplus.read_input(data)}

	${cplusplus.return_type(data)} ${cplusplus.return_value(data)};

${cplusplus.write_output(data)}

	return 0;
}

タスクの登録

.vscode ディレクトリに tasks.json を作成し、以下のように記述して保存します。
自身の環境に合わせてコマンドや引数の部分を書き換えた方が良いです。
特にコンパイルオプションの部分はかなり好みが出ると思うので、見直すと良い(競プロにおいて pedantic-errors は特にいらない)と思います。

tasks.json
{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "build",
			"command": "/usr/bin/g++",
			"args": [
				"-std=c++17",
				"-O2",
				"-pedantic-errors",
				"-Wall",
				"-D_GLIBCXX_DEBUG",
				"-g",
				"${file}",
				"-o",
				"${fileDirname}/a.out"
			],
			"problemMatcher": [
				"$gcc"
			],
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"detail": "compiler: /usr/bin/g++",
			"presentation": {
				"focus": true
			}
		},
		{
			"type": "shell",
			"label": "download test cases",
			"command": "${workspaceFolder}/configs/dltest.sh",
			"args": [
				"${relativeFileDirname}"
			]
		},
		{
			"type": "shell",
			"label": "do oj-prepare",
			"command": "pipenv",
			"args": [
				"run",
				"oj-prepare",
				"--config-file",
				"configs/prepare.config.toml",
				"${input:service_url}/${input:contest_id}"
			],
			"problemMatcher": []
		},
		{
			"type": "shell",
			"label": "do oj test",
			"command": "pipenv",
			"args": [
				"run",
				"oj",
				"t",
				"-c",
				"${fileDirname}/a.out",
				"-d",
				"${fileDirname}/test"
			],
			"dependsOn": [
				"build",
				"download test cases"
			],
			"group": {
				"kind": "test",
				"isDefault": true
			},
			"presentation": {
				"focus": true
			}
		},
		{
			"type": "shell",
			"label": "submit",
			"command": "${workspaceFolder}/configs/submit.sh",
			"args": [
				"${relativeFile}"
			]
		}
	],
	"inputs": [
		{
			"id": "service_url",
			"type": "pickString",
			"description": "Contest site URL",
			"options": [
				"https://atcoder.jp/contests",
				"https://codeforces.com/contest"
			],
			"default": "https://atcoder.jp/contests"
		},
		{
			"id": "contest_id",
			"type": "promptString",
			"description": "Contest ID"
		}
	]
}

Ctrl+Shift+P を押して Tasks: Run Task を選択すると、登録したタスクが出てきて、選択するだけで実行できるようになっています。
一応、使用するタスクを説明しておくと、

  • do oj-prepare: コンテストのディレクトリ作成とテストケースのダウンロード
  • do oj test: 入力例などによるテスト
  • submit: コードの提出

となっています。
他のタスクは直接使用することはないと思います。
始まっていないコンテストで do oj-prepare はできないので注意してください。

タスクをショートカット一発で実行できるようにする場合は keybindings.json に適当に追記すればよいです。

あとは実際に問題を解いて提出してみてください。

オプション: 競プロ用ライブラリの include

競プロ用のライブラリを持っており、使う度にコピペをしているという場合は online-judge-tools/verification-helperoj-bundle を導入するとコーディング体験がかなり良くなると思います。
これを利用すると include guard を考慮した上で include を自動で展開してくれます。

競プロ用ライブラリのコピー

競プロ用のライブラリを git 等で管理している場合は clone をします。
もし、このコンテスト用のディレクトリを git で管理しようと思っているのであれば submodule の方がよいかもしれません。
git 等で管理していないのであれば、適当にリンクを貼ってください。
ここでは競プロ用ライブラリのディレクトリ名を library と置いて説明していきます。

online-judge-tools/verification-helper のインストール

下記のコマンドでインストールします。

pipenv install online-judge-verify-helper

シェルスクリプトの作成と修正

configs ディレクトリの中に bundle.sh という名前でファイルを作成し、以下のように記述して、パーミッションを変更します。
oj-bundle を使うと #line ... のような表示や、余分な空行がでてくるので、それらを消す処理も入れています。

dltest.sh
#!/bin/bash

file_dir_name=$1
file_basename_no_extension=$2
file_extname=$3

file=${file_dir_name}/${file_basename_no_extension}${file_extname}
bundled_file=${file_dir_name}/${file_basename_no_extension}_bundled${file_extname}

pipenv run oj-bundle -I library ${file} \
| grep -v '^#line ' \
| cat -s \
| sed -e '1{/^$/d}' \
| clang-format --style=file \
> ${bundled_file}

configs/submit.sh がバンドル前のファイルを提出するようになっているので、バンドル後のファイルを提出するように修正します。

submit.sh
@@ -1,10 +1,15 @@
 #!/bin/bash
 
-submit_file=$1 # relative file path (${service_name}/${contest_id}/${problem_id}/main.cpp)
+file_dir_name=$1
+file_basename_no_extension=$2
+file_extname=$3
 
-service_name=${submit_file%%/*}
-contest_id=$(basename ${submit_file%/*/*})
-problem_id=$(basename ${submit_file%/*})
+file=${file_dir_name}/${file_basename_no_extension}${file_extname}
+bundled_file=${file_dir_name}/${file_basename_no_extension}_bundled${file_extname}
+
+service_name=${file%%/*}
+contest_id=$(basename ${file%/*/*})
+problem_id=$(basename ${file%/*})
 
 case "$service_name" in
   "AtCoder" )
@@ -15,4 +20,4 @@ case "$service_name" in
     ;;
 esac
 
-pipenv run oj s -y ${problem_url} ${submit_file}
+pipenv run oj s -y ${problem_url} ${bundled_file}
修正後のファイル
submit.sh
#!/bin/bash

file_dir_name=$1
file_basename_no_extension=$2
file_extname=$3

file=${file_dir_name}/${file_basename_no_extension}${file_extname}
bundled_file=${file_dir_name}/${file_basename_no_extension}_bundled${file_extname}

service_name=${file%%/*}
contest_id=$(basename ${file%/*/*})
problem_id=$(basename ${file%/*})

case "$service_name" in
  "AtCoder" )
    problem_url="https://atcoder.jp/contests/${contest_id}/tasks/${problem_id}"
    ;;
  "Codeforces" )
    problem_url="https://codeforces.com/contest/${contest_id}/problem/${problem_id}"
    ;;
esac

pipenv run oj s -y ${problem_url} ${bundled_file}

タスクの修正

tasks.json を修正して、バンドルしたファイルを提出するようにします。

tasks.json
@@ -12,6 +12,8 @@
                                "-Wall",
                                "-D_GLIBCXX_DEBUG",
                                "-g",
+                               "-I",
+                               "./library",
                                "${file}",
                                "-o",
                                "${fileDirname}/a.out"
@@ -79,7 +81,22 @@
                        "label": "submit",
                        "command": "${workspaceFolder}/configs/submit.sh",
                        "args": [
-                               "${relativeFile}"
+                               "${relativeFileDirname}",
+                               "${fileBasenameNoExtension}",
+                               "${fileExtname}"
+                       ],
+                       "dependsOn": [
+                               "do oj-bundle"
+                       ],
+               },
+               {
+                       "type": "shell",
+                       "label": "do oj-bundle",
+                       "command": "${workspaceFolder}/configs/bundle.sh",
+                       "args": [
+                               "${relativeFileDirname}",
+                               "${fileBasenameNoExtension}",
+                               "${fileExtname}"
                        ]
                }
        ],
修正後のファイル
tasks.json
{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "cppbuild",
			"label": "build",
			"command": "/usr/bin/g++",
			"args": [
				"-std=c++17",
				"-O2",
				"-pedantic-errors",
				"-Wall",
				"-D_GLIBCXX_DEBUG",
				"-g",
				"-I",
				"./library",
				"${file}",
				"-o",
				"${fileDirname}/a.out"
			],
			"problemMatcher": [
				"$gcc"
			],
			"group": {
				"kind": "build",
				"isDefault": true
			},
			"detail": "compiler: /usr/bin/g++",
			"presentation": {
				"focus": true
			}
		},
		{
			"type": "shell",
			"label": "download test cases",
			"command": "${workspaceFolder}/configs/dltest.sh",
			"args": [
				"${relativeFileDirname}"
			]
		},
		{
			"type": "shell",
			"label": "do oj-prepare",
			"command": "pipenv",
			"args": [
				"run",
				"oj-prepare",
				"--config-file",
				"configs/prepare.config.toml",
				"${input:service_url}/${input:contest_id}"
			],
			"problemMatcher": []
		},
		{
			"type": "shell",
			"label": "do oj test",
			"command": "pipenv",
			"args": [
				"run",
				"oj",
				"t",
				"-c",
				"${fileDirname}/a.out",
				"-d",
				"${fileDirname}/test"
			],
			"dependsOn": [
				"build",
				"download test cases"
			],
			"group": {
				"kind": "test",
				"isDefault": true
			},
			"presentation": {
				"focus": true
			}
		},
		{
			"type": "shell",
			"label": "submit",
			"command": "${workspaceFolder}/configs/submit.sh",
			"args": [
				"${relativeFileDirname}",
				"${fileBasenameNoExtension}",
				"${fileExtname}"
			],
			"dependsOn": [
				"do oj-bundle"
			],
		},
		{
			"type": "shell",
			"label": "do oj-bundle",
			"command": "${workspaceFolder}/configs/bundle.sh",
			"args": [
				"${relativeFileDirname}",
				"${fileBasenameNoExtension}",
				"${fileExtname}"
			]
		}
	],
	"inputs": [
		{
			"id": "service_url",
			"type": "pickString",
			"description": "Contest site URL",
			"options": [
				"https://atcoder.jp/contests",
				"https://codeforces.com/contest"
			],
			"default": "https://atcoder.jp/contests"
		},
		{
			"id": "contest_id",
			"type": "promptString",
			"description": "Contest ID"
		}
	]
}

これで提出時にバンドルされたファイルが提出されるようになりました。

Discussion