はじめに
AHCを始めたばかりの方の「そもそもローカルテスタの使い方がわからない」という声をよくみかけます。
AtCoderユーザは一般的なプログラマー層と比べ、コマンドプロンプトの使い方がわからないという方が多い印象なので、そのあたりを解説する記事が必要かなと思います。
具体的にはあぷりしあさんの記事でAHC040のコマンドの使い方を解説していますが、もう少し汎化し、今後の新しいAHCでも自力でコマンドを打てるようになってもらうことを目指します。
1回だけテスターを実行するならもっと楽な方法(ビルド済みテスタ)があると思いますが、今回は継続的にAHCをやるためにちゃんと環境構築から紹介します。
WSL(Windows Subsystem for Linux)をインストールする
前書き
Windowsユーザ以外は無視してください。
WSLはWindows上でLinuxを動かすことができるものです。
一部のAHCのローカルテスタのReadmeにはPowerShellへの言及もありますが、どうせなんやかんやLinuxを使いたくなる時がいずれ来ますし、多くのエンジニア向けの記事はLinuxを前提としています。
この機会にLinux上で開発することを覚えちゃいましょう。
なお、ここではLinuxの中でも特にUbuntuというものを前提とします。
Ubuntu以外のLinuxを使っている人は十分にコマンドの理解がある人が多いと思いますので、本記事の対象外とします。
インストール
基本的には公式ドキュメントに従えばいいのですが、
PowerShell または Windows コマンド プロンプトを管理者モードで開き、wsl --install コマンドを入力し、マシンを再起動します。
なお、コマンドプロンプトを管理者モードで開くには、[Windowsキー] + [R]を押した後にcmdと入力し、[Ctrl] + [Shift] + [Enter]を押せばいいです。
wsl --install
古いPCの場合はBIOSの設定を変更する必要があるかもしれませんが、最近のPCであれば概ね上記のコマンドで大丈夫だと思います。
Rust環境のインストール
前書き
Windows用にビルドされたローカルテスタのバイナリが提供されていますが、Readmeの書き方が「cargo runの部分を●●に置き換えてください」のような書き方をされがちで、そこが混乱のもとになっているようです。
最初はめんどうかもですが、Rustの環境を先に整えておいたほうが、今後のAHCでReadmeの書き方が変わっても対応しやすいと思います。
インストール
これも基本的には公式ドキュメントに従えばいいのですが、ドキュメントに当たり前のように書いてあるcurlコマンドは、デフォルトのLinux環境では存在しません。
そこで、以下のようなコマンドを打ってcurlをインストールします。
sudo apt update
sudo apt install curl
まず、sudoというコマンドは、そのコマンドを実行するユーザの権限を上げるコマンドです。 1
hogeというコマンドを実行するときにsudo コマンド名とすると、管理者権限でコマンド名を実行できます。
aptというコマンドは、パッケージ管理用のコマンドで、引数によって異なる挙動をします。
apt updateでパッケージのインデックスファイルを更新します。
apt install パッケージ名でそのパッケージをインストールします。
apt updateをしないとそのパッケージの最新版があっても古いバージョンがインストールされる可能性があるため、基本的にはapt updateとapt install パッケージ名はセットで実行します。
これらはシステムに変更を加えるコマンドのため、管理者権限がないと実行できません。
curlのインストールの流れをまとめると、以下のような流れです。
-
sudo apt updateでパッケージのインデックスファイルを更新する(管理者権限でないと実行できないため、sudoをつけている) -
sudo apt install curlでcurlをインストールする(管理者権限でないと実行できないため、sudoをつけている)
| コマンド | 説明 |
|---|---|
sudo |
引数に与えたコマンドを管理者権限で実行する |
apt |
パッケージ管理用のコマンドupdateでパッケージのインデックスファイルを更新するinstall パッケージ名でパッケージ名をインストールする |
続いて、以下のコマンドでRustをインストールします。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
curlはデータを取得するコマンドで、下表に記したように、各種オプションで指定した形で、指定したURLからデータを取得します。
|はパイプというもので、左辺のコマンドの出力を右辺のコマンドの入力として扱います。
shはシェルスクリプトを実行するコマンドで、この場合の意味としては標準入力をシェルスクリプトとして実行します。
Rustのインストールの流れをまとめると、以下のような流れです。
-
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rsでRustのインストーラーをシェルスクリプトとして取得する -
|で後続のコマンドの入力として1のシェルスクリプトを渡す -
shでシェルスクリプトを実行する
なお、ここでインストールしたrustupはRustのツールチェーンで、コンパイラであるrustcやビルドシステム兼パッケージマネージャであるcargoといったRustの重要な一連のツールが入っています。
| コマンド | 説明 |
|---|---|
curl |
データを取得するコマンド--proto '=https'でHTTPSプロトコルを使用する--tlsv1.2で暗号化し、安全に通信する-sで進捗を表示しない-Sを-sと併用すると、エラーメッセージだけ表示する-fで異常検知https://sh.rustup.rsでRustのインストーラーのURLを指定する |
sh |
シェルスクリプトを実行するコマンド |
ローカルテスタのコマンドとその意味
ローカルテスタの配置
さて、ようやく本題です。
まず、問題文の中の以下のようなリ記述の中からローカル版のリンクをクリックし、zipファイルをダウンロードします。回によっては、ローカルテスタなど微妙に異なる言い回しなので注意してください。
ダウンロードしたzipファイルを展開し、toolsに移動します。
toolsディレクトリの場所は人によりますが、以下は一例です。
なお、WSLを使う際に/mnt以下のディレクトリにアクセスすると処理が遅くなりますが、今回は無視することにします。
cd /mnt/c/Users/ユーザ名/Downloads/ahc002/tools
| コマンド | 説明 |
|---|---|
cd |
指定したディレクトリに移動するコマンド |
toolsディレクトリは、だいたい以下のような構成になっています。
tools
├── Cargo.lock
├── Cargo.toml
├── README.html
├── README.md
├── in
│ ├── 0001.txt
│ ├── ...
│ └─── 0099.txt
├── seeds.txt
└── src
├── bin
│ ├── gen.rs
│ ├── tester.rs ない場合もある
│ └── vis.rs
└── lib.rs
ここで、多くのAHCでは以下の3つ以内のコマンドの使い方がREADMEに記載されています。
-
cargo run -r --bin gen seeds.txt
seed.txtに記載された乱数seed値を用いて入力ファイルを生成する -
cargo run -r --bin tester cmd < in.txt > out.txt
指定された入力ファイルを用いてコマンドを実行し、その結果をout.txtに出力する -
cargo run -r --bin vis in.txt out.txt
指定された入力ファイルと出力ファイルを用いて、過程を可視化する
3つに共通するのは、cargo run -r --bin コマンド名です。
これは、先ほどcargoによってsrc/bin/コマンド名.rsをビルドし、バイナリを実行するコマンドです。
| コマンド | 説明 |
|---|---|
cargo |
Rustのビルドシステムrunで実行可能ファイルのビルドと実行をする-rは--releaseの省略形で、デバッグ情報を含まないバイナリをビルドする--binはsrc/bin/コマンド名.rsを対象にする。(Cargo.tomlに記載があればそこも対象になる) |
ここで、genは入力ファイルを生成する目的ですが、zipファイルに入力ファイルが100ケースほど付属しているため、実行しなくてもAHCの参加は可能です。
また、visはビジュアライザですが、web版のビジュアライザが優秀なため、これもやはり実行しなくてもAHCの参加は可能です。
よって、参加に必須なのはtesterのみです。
テスト方法(testerがない場合)
testerは提供されるコンテストと提供されないコンテストがあります。
多くの場合、AtCoderの問題は参加者のプログラムが標準入力で入力を1回だけ受け取り、標準出力で1回だけ答えを出す仕組みをとっています。
これは、参加者が作成したプログラムだけで処理が完結するため、特に外部のテスタを用いる必要がないためです。
この場合、toolsディレクトリ内で以下のようなコマンドでテストを行います。
コマンド < 入力ファイルは、入力リダイレクトというもので、入力ファイルの内容をコマンドに標準入力として渡します。
コマンド > 出力ファイルは、出力リダイレクトというもので、コマンドの標準出力を出力ファイルに書き込みます。
mkdir -p out
あなたのプログラム実行コマンド < in/0000.txt > out/0000.txt
| コマンド | 説明 |
|---|---|
mkdir |
指定したディレクトリを作成する-pは複数階層の作成。既に存在する場合にエラーを出さない目的でも使える。 |
あなたのプログラム実行コマンドは、どの言語を使っているかによって異なりますが、一般的にはコンパイル言語とインタプリタ言語に分かれると思います。(Javaみたいに中間コードを作成する言語は別の名称な気がしますが、その辺使ってる人は雰囲気でわかってくれるでしょう)
コンパイル言語であるc++を使い、tools直下にmain.cppを配置した場合は以下のように一度コンパイルをした後に実行します。
mkdir -p out
g++ -std=gnu++20 -O2 -o a.out main.cpp # コンパイル(言語によって異なる)
./a.out < in/0000.txt > out/0000.txt # 実行(tools直下にa.outをビルドしたと仮定)
インタプリタ言語であるpythonを使い、tools直下にmain.pyを配置した場合は以下のように実行します。
mkdir -p out
python main.py < in/0000.txt > out/0000.txt # 実行(tools直下にmain.pyを配置したと仮定)
テスト方法(testerがある場合)
一部のコンテストでは、参加者のプログラムがが標準入力で入力を受け取り、標準出力に出力したらまた入力を受け取り、出力し、…と繰り返すタイプのものがあります。
このようなコンテストはインタラクティブと呼ばれ、参加者のプログラムだけでは完結しません。
そのため、testerというツールを用いてテストを行います。2
何も知らない状態で見ると複雑に見えるかもしれませんが、testerがない場合を学んだので、ほとんど同じコマンドだということがわかりますね。
mkdir -p out
cargo run -r --bin tester あなたのプログラム実行コマンド < in/0000.txt > out/0000.txt
あとはあなたのプログラム実行コマンドの部分を置き換えるだけです。
コンパイル言語の例
mkdir -p out
g++ -std=gnu++20 -O2 -o a.out main.cpp # コンパイル(言語によって異なる)
cargo run -r --bin tester ./a.out < in/0000.txt > out/0000.txt # 実行(tools直下にa.outをビルドしたと仮定)
インタプリタ言語の例
mkdir -p out
cargo run -r --bin tester python main.py < in/0000.txt > out/0000.txt # 実行(tools直下にmain.pyを配置したと仮定)
今後のAHCにむけて
今までの傾向から、testerの有無だけ気を付けておけば最低限のテストができそうなことがわかりました。
しかし、今後のAHCで、gen,tester,visの3つ以外のコマンドができる可能性や、これらのコマンドの仕様が変わる可能性があります。後述のAppendixにも記載しますが、過去にgenの仕様が変わったことがあります。
そのため、リダイレクト<,>やパイプ|の意味をしっかり理解し、READMEを自力で読み解けるようにはなっておきましょう。
Appendix: 今までのローカルテスタのREADMEのコマンドのまとめ
今回の記事を書くにあたり、過去のREADMEを全て確認しました。
ahc001はまだ運営側もどう説明するか探っている印象があります。
<input_file>はファイル名を自由に変えていいという意図があるんでしょうが、リダイレクトと勘違いしそうなので、ahc002以降ではin.txtとして統一しているようです。
cargo runのオプションも、-rと--releaseで表記揺れがありますが、同じ意味だと知っていれば怖くないですね。
ahc001のgenのみ、seeds.txtの指定方法が引数ではななく入力リダイレクトになっています。これは明確に異なるものなので、過去問を復習する際に自作ツールで自動化している人は注意しましょう。
| contest | gen | tester | vis |
|---|---|---|---|
| ahc001 | cargo run --release --bin gen < seeds.txt | cargo run --release --bin vis <input_file> <output_file> | |
| ahc002 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc003 | cargo run --release --bin gen seeds.txt | cargo run --release --bin tester in.txt cmd > out.txt | cargo run --release --bin vis in.txt out.txt |
| ahc004 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc005 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc006 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc007 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc008 | cargo run --release --bin gen seeds.txt | cargo run --release --bin tester cmd < in.txt > out.txt | cargo run --release --bin vis in.txt out.txt |
| ahc009 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc010 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc011 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc012 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc013 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc014 | cargo run --release --bin gen seeds.txt' | cargo run --release --bin vis in.txt out.txt | |
| ahc015 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc016 | cargo run --release --bin gen seeds.txt | cargo run --release --bin tester cmd < in.txt > out.txt | |
| ahc017 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc018 | cargo run --release --bin gen seeds.txt | cargo run --release --bin tester cmd < in.txt > out.txt | cargo run --release --bin vis in.txt out.txt |
| ahc019 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc020 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc021 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc022 | cargo run --release --bin gen seeds.txt | cargo run --release --bin tester cmd < in.txt > out.txt | cargo run --release --bin vis in.txt out.txt |
| ahc023 | cargo run --release --bin gen seeds.txt | cargo run --release --bin vis in.txt out.txt | |
| ahc024 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc025 | cargo run -r --bin gen seeds.txt | cargo run -r --bin tester cmd < in.txt > out.txt | cargo run -r --bin vis in.txt out.txt |
| ahc026 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc027 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc028 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc029 | cargo run -r --bin gen seeds.txt | cargo run -r --bin tester cmd < in.txt > out.txt | cargo run -r --bin vis in.txt out.txt |
| ahc030 | cargo run -r --bin gen seeds.txt | cargo run -r --bin tester cmd < in.txt > out.txt | cargo run -r --bin vis in.txt out.txt |
| ahc031 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc032 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc033 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc034 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc035 | cargo run -r --bin gen seeds.txt | cargo run -r --bin tester cmd < in.txt > out.txt | cargo run -r --bin vis in.txt out.txt |
| ahc036 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc037 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc038 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc039 | cargo run -r --bin gen seeds.txt | cargo run -r --bin vis in.txt out.txt | |
| ahc040 | cargo run -r --bin gen seeds.txt | cargo run -r --bin tester cmd < in.txt > out.txt | cargo run -r --bin vis in.txt out.txt |
