C++ で AtCoder やり始めたのでローカル環境の構築方法をメモしておく
2023-12-18今年の 10 月から AtCoder で競プロを始めました。初めてから 2 ヶ月、コンテストに参加したり過去問をやっていたりする中で、その場では解けてもあとからコードを見て何をやっているかわからない という状況が発生することが多いと気づきました。なので、コンテスト・過去問問わず自分が解いた問題については未来の自分がわかるように解説を書いておこうと思った次第です。
初回の今回は、とりあえずローカル環境の構築方法について書いておきます。
AtCoder への参加状況
現在の参加状況は以下の通りです。
- 2023 年 10 月から参加
- コンテストはできる限り毎週 ABC に参加
- 過去問は ABC の C 問題までを中心に解いている
- 上記に加えて、 AtCoder Problems の Recommendation も解いている
- 灰色 michimani - AtCoder
- 言語は C++ 20 (Clang 16.0.6)
ローカル環境
下記のマシン、環境で解いています。
- MacBook Pro 2023 (M2 Max, 96 GB)
- VSCode
- C++
❯ clang --version Apple clang version 15.0.0 (clang-1500.0.40.1) Target: arm64-apple-darwin23.0.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin
実際の AtCoder での実行環境と異なっているのですが、一旦このままで始めています。
解答の管理
ローカル環境では、 下記のようなディレクトリ構成で解答を管理しています。
./
├── abc
│ ├── 001-100
│ │ ├── 001
│ │ │ └── cpp
│ │ │ ├── a.cpp
│ │ │ ├── b.cpp
│ │ │
│ │ ├── 002
│ │ │ └── cpp
│ │ ...
│ ├── 101-200
│ │ ...
│ ├── 201-300
│ │ ...
│ └── 301-400
│ ...
├── arc
│ ├── 001-100
│ └── 101-200
└── onlinejudge
└── test
└── ...
新たに解答を作成する場合は、 new.sh
として下記のようなシェルスクリプトを用意しています。
#!/bin/bash
set -eu
contest_type=$1
contest_number=$2
# contest_type must be abc, arc or agc
if [[ "${contest_type}" != "abc" && "${contest_type}" != "arc" && "${contest_type}" != "agc" ]]; then
echo "contest_type must be abc, arc or agc"
exit 1
fi
# contest_number must be positive integer
if [[ ! "${contest_number}" =~ ^[0-9]+$ ]]; then
echo "contest_number must be positive integer"
exit 1
fi
# contest_range is used for directory name
if [[ "${contest_number}" -ge 401 ]]; then
contest_range="401-500"
elif [[ "${contest_number}" -ge 301 ]]; then
contest_range="301-400"
elif [[ "${contest_number}" -ge 201 ]]; then
contest_range="201-300"
elif [[ "${contest_number}" -ge 101 ]]; then
contest_range="101-200"
else
contest_range="001-100"
fi
# contest_number is used for directory name. 001, 002, ...
contest_number=$(printf "%03d" "${contest_number}")
# contest_name is used for README.md
if [[ "${contest_type}" == "abc" ]]; then
contest_name="AtCoder Beginner Contest"
elif [[ "${contest_type}" == "arc" ]]; then
contest_name="AtCoder Regular Contest"
elif [[ "${contest_type}" == "agc" ]]; then
contest_name="AtCoder Grand Contest"
fi
# create directory
mkdir -p "./${contest_type}/${contest_range}/${contest_number}/cpp"
# create a, b and c files
touch "./${contest_type}/${contest_range}/${contest_number}/cpp/a.cpp"
touch "./${contest_type}/${contest_range}/${contest_number}/cpp/b.cpp"
touch "./${contest_type}/${contest_range}/${contest_number}/cpp/c.cpp"
touch "./${contest_type}/${contest_range}/${contest_number}/README.md"
# update README.md
readme_content="\
${contest_name} ${contest_number}
===
[問題 - ${contest_name} ${contest_number}](https://atcoder.jp/contests/${contest_type}${contest_number}/tasks)"
echo "${readme_content}" >"./${contest_type}/${contest_range}/${contest_number}/README.md"
例えば、 ABC 123 への解答を作成する場合は、下記のように実行します。
./new.sh abc 123
online-judge-tools の導入
問題を解いているときに例題に対するテストを簡単に実行するために、 online-judge-tools 1 を導入しています。
online-judge-tools は下記のような構成で利用しています。
- 例題を取得するシェルスクリプトを用意
- VSCode のタスクとして、実装中のファイルに対して例題を取得してテストを実行する
1. 例題を取得するシェルスクリプトを用意
まず、 online-judge-tools をインストールします。
pip install online-judge-tools
次に、例題を取得するスクリプトを用意します。
#!/bin/bash
## target directory to problem name
## ex) abc/001-100/001/cpp -> abc001
target_problem_dir=$1
IFS='/' read -ra ADDR <<< "${target_problem_dir}"
problem_name="${ADDR[0]}${ADDR[2]}"
problem_number=$2
test_dir="onlinejudge/test/${problem_name}_${problem_number}"
base_url=${problem_name}
code_path=$3
ac_url="https://atcoder.jp/contests/${base_url}/tasks/${problem_name}_${problem_number}"
# make test directory
if [ ! -e "${test_dir}" ]; then
oj dl -d "${test_dir}" "${ac_url}"
fi
# C++ 20 (Clang 16.0.6) compile
# https://atcoder.jp/contests/APG4b/rules?lang=ja
clang++ -std=c++20 \
-Wall \
-Wextra \
-O2 \
-DONLINE_JUDGE \
-DATCODER \
-mtune=native \
-march=native \
-fconstexpr-depth=2147483647 \
-fconstexpr-steps=2147483647 \
-I/opt/boost/clang/include \
-I/opt/ac-library \
-I/usr/include/eigen3 \
-o ./a.out "${code_path}" \
&& oj test -c "./a.out " -d "${test_dir}"
このスクリプトは引数として下記の値を受け取るようになっています。
target_problem_dir
: 実装中の解答が格納されているディレクトリproblem_number
: 実装中の解答の問題番号code_path
: 実装中の解答のファイルパス
それぞれ、次に示す VSCode の task.json
から渡されます。
受け取った値をもとに、 online-judge-tools の oj dl
コマンドで例題を取得し、 oj test
コマンドでテストを実行しています。
2. VSCode のタスクとして、実装中のファイルに対して例題を取得してテストを実行する
VSCode のビルドタスクとしてテストを実行できるように、下記のような JSON を .vscode/tasks.json
として作成します。
{
"tasks": [
{
"type": "shell",
"label": "test_atcoder_sample",
"command": "${workspaceFolder}/onlinejudge/cpp-test.sh",
"args": [
"${relativeFileDirname}",
"${fileBasenameNoExtension}",
"${file}"
],
"group": {
"kind": "build",
"isDefault": true
}
}
],
"version": "2.0.0"
}
環境にも依りますが、上記の task.json
を用意すれば Command + Shift + B
で実装中のコードでテスト実行できるようになります。
以上がローカル環境の構築方法でした。
これから
冒頭にも書きましたが、今後は自分が解いた問題について解説を書いていこうと思います。
実際に解いた問題については michimani/atcoder に追加しています。
comments powered by Disqus