AHC初心者向け、ローカルテスタの使い方

## はじめに

AHCを始めたばかりの方の「そもそもローカルテスタの使い方がわからない」という声をよくみかけます。
AtCoderユーザは一般的なプログラマー層と比べ、コマンドプロンプトの使い方がわからないという方が多い印象なので、そのあたりを解説する記事が必要かなと思います。
具体的には[あぷりしあさんの記事](https://qiita.com/aplysia/items/942b1c0e47d36d0ca622)でAHC040のコマンドの使い方を解説していますが、もう少し汎化し、今後の新しいAHCでも自力でコマンドを打てるようになってもらうことを目指します。
**1回だけテスターを実行する**ならもっと楽な方法（ビルド済みテスタ）があると思いますが、今回は**継続的にAHCをやる**ためにちゃんと環境構築から紹介します。

## WSL(Windows Subsystem for Linux)をインストールする

#### 前書き

Windowsユーザ以外は無視してください。
WSLはWindows上でLinuxを動かすことができるものです。
一部のAHCのローカルテスタのReadmeにはPowerShellへの言及もありますが、どうせなんやかんやLinuxを使いたくなる時がいずれ来ますし、多くのエンジニア向けの記事はLinuxを前提としています。
この機会にLinux上で開発することを覚えちゃいましょう。
なお、ここではLinuxの中でも特にUbuntuというものを前提とします。
Ubuntu以外のLinuxを使っている人は十分にコマンドの理解がある人が多いと思いますので、本記事の対象外とします。

#### インストール

基本的には[公式ドキュメント](https://learn.microsoft.com/ja-jp/windows/wsl/install)に従えばいいのですが、
PowerShell または Windows コマンド プロンプトを**管理者モード**で開き、wsl --install コマンドを入力し、マシンを**再起動**します。
なお、コマンドプロンプトを管理者モードで開くには、[Windowsキー] + [R]を押した後に`cmd`と入力し、[Ctrl] + [Shift] + [Enter]を押せばいいです。


```
wsl --install
```

古いPCの場合はBIOSの設定を変更する必要があるかもしれませんが、最近のPCであれば概ね上記のコマンドで大丈夫だと思います。

## Rust環境のインストール

#### 前書き

Windows用にビルドされたローカルテスタのバイナリが提供されていますが、Readmeの書き方が「cargo runの部分を●●に置き換えてください」のような書き方をされがちで、そこが混乱のもとになっているようです。
最初はめんどうかもですが、Rustの環境を先に整えておいたほうが、今後のAHCでReadmeの書き方が変わっても対応しやすいと思います。

#### インストール

これも基本的には[公式ドキュメント](https://www.rust-lang.org/learn/get-started)に従えばいいのですが、ドキュメントに当たり前のように書いてある`curl`コマンドは、デフォルトのLinux環境では存在しません。


そこで、以下のようなコマンドを打ってcurlをインストールします。

```bash
sudo apt update
sudo apt install curl
```



まず、`sudo`というコマンドは、そのコマンドを実行するユーザの権限を上げるコマンドです。 [^1]
`hoge`というコマンドを実行するときに`sudo コマンド名`とすると、管理者権限で`コマンド名`を実行できます。
`apt`というコマンドは、パッケージ管理用のコマンドで、引数によって異なる挙動をします。
`apt update`でパッケージのインデックスファイルを更新します。
`apt install パッケージ名`でそのパッケージをインストールします。
`apt update`をしないとそのパッケージの最新版があっても古いバージョンがインストールされる可能性があるため、基本的には`apt update`と`apt install パッケージ名`はセットで実行します。
これらはシステムに変更を加えるコマンドのため、管理者権限がないと実行できません。


curlのインストールの流れをまとめると、以下のような流れです。
1. `sudo apt update`でパッケージのインデックスファイルを更新する（管理者権限でないと実行できないため、`sudo`をつけている）
2. `sudo apt install curl`で`curl`をインストールする（管理者権限でないと実行できないため、`sudo`をつけている）

[^1]: 多くの記事ではこのようなコマンドの説明までしないと思いますが、本記事はLinuxコマンドを知らない人を対象としているので、細かく説明します。わかる人にはかなり冗長になるかもしれませんが、ご了承ください。

|コマンド|説明|
|:---|:---|
|`sudo`|引数に与えたコマンドを管理者権限で実行する|
|`apt`|パッケージ管理用のコマンド<br>`update`でパッケージのインデックスファイルを更新する<br>`install パッケージ名`で`パッケージ名`をインストールする|

続いて、以下のコマンドでRustをインストールします。

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

`curl`はデータを取得するコマンドで、下表に記したように、各種オプションで指定した形で、指定したURLからデータを取得します。
`|`はパイプというもので、左辺のコマンドの出力を右辺のコマンドの入力として扱います。
`sh`はシェルスクリプトを実行するコマンドで、この場合の意味としては標準入力をシェルスクリプトとして実行します。

Rustのインストールの流れをまとめると、以下のような流れです。
1. `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs`でRustのインストーラーをシェルスクリプトとして取得する
2. `|`で後続のコマンドの入力として1のシェルスクリプトを渡す
3. `sh`でシェルスクリプトを実行する

なお、ここでインストールしたrustupはRustのツールチェーンで、コンパイラである`rustc`やビルドシステム兼パッケージマネージャである`cargo`といったRustの重要な一連のツールが入っています。

|コマンド|説明|
|:---|:---|
|`curl`|データを取得するコマンド<br>`--proto '=https'`でHTTPSプロトコルを使用する<br>`--tlsv1.2`で暗号化し、安全に通信する<br>`-s`で進捗を表示しない<br>`-S`を`-s`と併用すると、エラーメッセージだけ表示する<br>`-f`で異常検知<br>`https://sh.rustup.rs`でRustのインストーラーのURLを指定する|
|`sh`|シェルスクリプトを実行するコマンド|

## ローカルテスタのコマンドとその意味

#### ローカルテスタの配置

さて、ようやく本題です。
まず、問題文の中の以下のようなリ記述の中から**ローカル版**のリンクをクリックし、zipファイルをダウンロードします。回によっては、**ローカルテスタ**など微妙に異なる言い回しなので注意してください。

![question_html.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535132/1fa91e4a-bc44-42b1-bbb4-332b2bc11ff1.png)


ダウンロードしたzipファイルを展開し、`tools`に移動します。
toolsディレクトリの場所は人によりますが、以下は一例です。
なお、WSLを使う際に`/mnt`以下のディレクトリにアクセスすると処理が遅くなりますが、今回は無視することにします。

```bash
cd /mnt/c/Users/ユーザ名/Downloads/ahc002/tools
```

|コマンド|説明|
|:---|:---|
|`cd`|指定したディレクトリに移動するコマンド|

toolsディレクトリは、だいたい以下のような構成になっています。

```bash
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に記載されています。

1. `cargo run -r --bin gen seeds.txt` 
   seed.txtに記載された乱数seed値を用いて入力ファイルを生成する
2. `cargo run -r --bin tester cmd < in.txt > out.txt` 
   指定された入力ファイルを用いてコマンドを実行し、その結果をout.txtに出力する
3. `cargo run -r --bin vis in.txt out.txt` 
   指定された入力ファイルと出力ファイルを用いて、過程を可視化する

3つに共通するのは、`cargo run -r --bin コマンド名`です。
これは、先ほど`cargo`によって`src/bin/コマンド名.rs`をビルドし、バイナリを実行するコマンドです。

|コマンド|説明|
|:---|:---|
|`cargo`|Rustのビルドシステム<br>`run`で実行可能ファイルのビルドと実行をする<br>`-r`は`--release`の省略形で、デバッグ情報を含まないバイナリをビルドする<br>`--bin`は`src/bin/コマンド名.rs`を対象にする。(Cargo.tomlに記載があればそこも対象になる)|

ここで、`gen`は入力ファイルを生成する目的ですが、zipファイルに入力ファイルが100ケースほど付属しているため、**実行しなくてもAHCの参加は可能**です。
また、`vis`はビジュアライザですが、web版のビジュアライザが優秀なため、これもやはり**実行しなくてもAHCの参加は可能**です。
よって、参加に必須なのは`tester`のみです。

#### テスト方法（testerがない場合）

`tester`は提供されるコンテストと提供されないコンテストがあります。
多くの場合、AtCoderの問題は参加者のプログラムが標準入力で入力を1回だけ受け取り、標準出力で1回だけ答えを出す仕組みをとっています。
これは、参加者が作成したプログラムだけで処理が完結するため、特に外部のテスタを用いる必要がないためです。
この場合、`tools`ディレクトリ内で以下のようなコマンドでテストを行います。
`コマンド < 入力ファイル`は、入力リダイレクトというもので、`入力ファイル`の内容を`コマンド`に標準入力として渡します。
`コマンド > 出力ファイル`は、出力リダイレクトというもので、`コマンド`の標準出力を`出力ファイル`に書き込みます。

```bash
mkdir -p out
あなたのプログラム実行コマンド < in/0000.txt > out/0000.txt
```


|コマンド|説明|
|:---|:---|
|`mkdir`|指定したディレクトリを作成する<br>`-p`は複数階層の作成。既に存在する場合にエラーを出さない目的でも使える。|



`あなたのプログラム実行コマンド`は、どの言語を使っているかによって異なりますが、一般的にはコンパイル言語とインタプリタ言語に分かれると思います。（Javaみたいに中間コードを作成する言語は別の名称な気がしますが、その辺使ってる人は雰囲気でわかってくれるでしょう）

コンパイル言語であるc++を使い、`tools`直下に`main.cpp`を配置した場合は以下のように一度コンパイルをした後に実行します。

```bash
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`を配置した場合は以下のように実行します。

```bash
mkdir -p out
python main.py < in/0000.txt > out/0000.txt # 実行(tools直下にmain.pyを配置したと仮定)
``` 

#### テスト方法（testerがある場合）

一部のコンテストでは、参加者のプログラムがが標準入力で入力を受け取り、標準出力に出力したらまた入力を受け取り、出力し、…と繰り返すタイプのものがあります。
このようなコンテストは**インタラクティブ**と呼ばれ、参加者のプログラムだけでは完結しません。
そのため、`tester`というツールを用いてテストを行います。[^2]
何も知らない状態で見ると複雑に見えるかもしれませんが、testerがない場合を学んだので、ほとんど同じコマンドだということがわかりますね。

```bash
mkdir -p out
cargo run -r --bin tester あなたのプログラム実行コマンド < in/0000.txt > out/0000.txt
```

あとは`あなたのプログラム実行コマンド`の部分を置き換えるだけです。

コンパイル言語の例

```bash
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をビルドしたと仮定)
```

インタプリタ言語の例

```bash
mkdir -p out
cargo run -r --bin tester python main.py < in/0000.txt > out/0000.txt # 実行(tools直下にmain.pyを配置したと仮定)
```

[^2]: AHC015のように、入力が工夫されていてtester無しで実行できるインタラクティブ問題もあります。

## 今後の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                    |