112
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

cargo-atcoder を使って Rust での AtCoder ライフを超快適にする

追記(2020/08/10)

このエントリで紹介している cargo-atcoder の進化版として、@qryxip さんにより、cargo-compete が開発されました。
↓↓↓詳しくはご本人による紹介エントリをご覧ください!↓↓↓
cargo-atcoderの代替品を作った - Qiita

(追記終わり)

Rust で競技プログラミングをやっている方であれば、
Rustで競技プログラミングの入力をスッキリ記述するマクロ - Qiita
で紹介されている input! マクロのことを見聞きしたことがある方が多いのではないかと思います。競プロにおいて第一級市民である C++ に比べて標準入力からの受け取りが冗長になりがちな Rust において、さまざまな入力形式に対応する汎用性と記述の短さをもつ input! マクロがもたらす恩恵はとても大きなものです。

さて、このマクロを作成した @tanakh さんによって、Rust での AtCoder ライフを超絶快適にするツールが開発されています。その名は cargo-atcoder

このエントリでは、cargo-atcoder の導入方法や使い方について紹介し、Rust で AtCoder に参加していらっしゃる方々の競プロライフを豊かにする手助けができればなと思います。

なお、このエントリで紹介する内容はほとんど GitHub の README に書かれていますので、一次ソースを確認したい方はそちらをぜひご覧ください。
↓リポジトリへのリンク↓
tanakh/cargo-atcoder: Cargo subcommand for AtCoder

【更新履歴】

  • 2020/08/10 次世代ツール cargo-compete の紹介エントリへのリンクを貼りました。
  • 2020/06/06 Cargo.toml に書く dependencies を欲張りすぎるとビルド時間が長くなって大変だという旨の記述を追加しました。
  • 2020/05/10 インストール方法を変更しました (crates.io に公開されたため)

cargo-atcoder とは

@tanakh さんによって作成された、Rust での AtCoder ライフを豊かにするためのツールです。
豊かにするというのは、具体的には以下のようなことができます。

  • Cargo.toml や 提出コードの雛形を決めておけば、コンテスト名を指定するだけでコンテスト用のプロジェクトディレクトリが作成できます。
    • 例: cargo atcoder new abc164 (ABC164 のためのディレクトリを作成)
  • 書いたコードがテストケースをパスするかどうかをコマンド一発で確認できます。
    • 例: $ cargo atcoder test a (A問題のテストケースを確認) Apr-27-2020 22-15-04.gif
  • 解答の提出がコマンド一発でできます。
    • 例: $ cargo atcoder submit a (A問題のコードを提出) スクリーンショット 2020-04-27 22.36.00.png
  • 提出結果をコマンドで習得できます。リアルタイム更新です。
    • 例: $ cargo atcoder status

導入方法

インストール

もし cargo が入っていない場合は、何よりもまずインストールしてください。
以下のページにあるコマンドをコピペして実行すればインストールできます。
rustup.rs - The Rust toolchain installer

cargo が入ったら以下のコマンドを実行すればOKです。ビルドをするので結構時間かかります。コーヒーでも飲みながら気長にお待ちください。(手元の環境では3分ほどかかりました)

$ cargo install cargo-atcoder

インストールが完了したら、以下のコマンドを打ってみてください。コマンドの説明がずらずら〜と出てきたらOKです。

$ cargo atcoder --help
cargo-atcoder 0.1.0

USAGE:
    cargo atcoder <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

(以下略)

セットアップ

ログインする

まず、AtCoder へのログイン情報 (Cookie) をローカルに保存する必要があります。
以下のコマンドを実行すると、ユーザー名とパスワードを聞かれるので、指示通り入力してください。

$ cargo atcoder login

成功すると Login succeeded. と表示されます。
(ちなみに Cookie の有効期限は180日間のようです。)

設定ファイルを用意する

各プロジェクトの雛形を設定ファイルに書く必要があります。
設定ファイルの場所は OS や設定している環境変数ごとに異なるので、下記を参考にしてください。

OS 設定ファイルの場所
Linux $XDG_CONFIG_HOME/cargo-atcoder.toml または $HOME/.config/cargo-atcoder.toml /home/maguro/.config/cargo-atcoder.toml
macOS $HOME/Library/Preferences/cargo-atcoder.toml /Users/maguro/Library/Preferences/cargo-atcoder.toml
Windows {FOLDERID_RoamingAppData}\cargo-atcoder.toml C:\Users\maguro\AppData\Roaming\cargo-atcoder.toml

ログイン済みであればすでに上記の場所に cargo-atcoder.toml が作成されているはずですので、中身を覗いてみてください。
いくつかの項目について簡単に説明します。

submit_via_binary

闇の力です。Rust の真のパワーを発揮するべく、ローカルでビルドし、バイナリでの提出を行います
つまり、このオプションが true にセットされている場合、ローカルでビルドして生成された実行バイナリを提出して、ジャッジサーバー上ではそのバイナリが実行されます。
ジャッジサーバー上の実行環境に関係なく、最新バージョン、nightly バージョンの Rust を使ったり、好きなクレートを使ったりすることができます

闇の力の代償として、

  • 提出ファイルサイズが極めて大きくなる(設定にもよりますが、ABC の A問題程度のコード量であっても、300KB を超えます)
  • 実行時のオーバーヘッドが発生する(最低でも 4ms の実行時間がかかる)
  • 手元でビルドするので、提出までに少し時間がかかる

といったデメリットがあります。しかし、メリットの大きさはデメリットを補って余りあるものでしょう。

use_cross

ジャッジサーバーは Linux 環境です。上記の闇の力を使用してバイナリ提出を行う場合、Linux 向けの実行バイナリを生成する必要があります。
use_cross オプションを true にしておくと、cross という設定要らずのクロスコンパイル環境を使ってコンパイルが行われるようになります。
お使いの環境が Linux ではない場合、とりあえず true にしておくほうが面倒事が少なくて良いかなと思います。
なお、cross を使うのに Docker が必要となるので、事前にご準備ください。

cross のリポジトリ↓
rust-embedded/cross: “Zero setup” cross compilation and “cross testing” of Rust crates

strip_path

バイナリで提出をする際、ファイルサイズを減らすために strip というコマンドで処理されます。その strip コマンドへのパスを指定するためのオプションです。
PATH が通った場所に strip がある場合は特に指定する必要はありません。

GNU 版の strip が必要なので、macOS ユーザーの方は以下のコマンドでインストールしてください。

$ brew install binutils

target

デフォルトで "x86_64-unknown-linux-musl" が入っていると思いますが、そのままでOKです。

[dependencies]

ここに書いたクレートが、プロジェクト作成時に Cargo.toml へとコピーされます。
とりあえず新ジャッジサーバーで使えるクレートを全部指定しておきたい場合は、以下をコピペしてください。
(以下のクレートは、バイナリ提出せずとも使えるクレートです)

※依存クレートが多くなるとビルド時間がとても長くなるので、実際には本当に使うクレートだけに絞って書いたほうが良いと思います。

[dependencies]
num = "0.2.1"
num-bigint = "0.2.6"
num-complex = "0.2.4"
num-integer = "0.1.42"
num-iter = "0.1.40"
num-rational = "0.2.4"
num-traits = "0.2.11"
num-derive = "0.3.0"
ndarray = "0.13.0"
nalgebra = "0.20.0"
alga = "0.9.3"
libm = "0.2.1"
rand = { version = "0.7.3", features = ["small_rng"] }
getrandom = "0.1.14"
rand_chacha = "0.2.2"
rand_core = "0.5.1"
rand_hc = "0.2.0"
rand_pcg = "0.2.1"
rand_distr = "0.2.2"
petgraph = "0.5.0"
indexmap = "1.3.2"
regex = "1.3.6"
lazy_static = "1.4.0"
ordered-float = "1.0.2"
ascii = "1.0.0"
permutohedron = "0.2.4"
superslice = "1.0.0"
itertools = "0.9.0"
itertools-num = "0.1.3"
maplit = "1.0.2"
either = "1.5.3"
im-rc = "14.3.0"
fixedbitset = "0.2.0"
bitset-fixed = "0.1.0"
proconio = { version = "0.3.6", features = ["derive"] }
text_io = "0.1.8"
whiteread = "0.5.0"
rustc-hash = "1.1.0"
smallvec = "1.2.0"

template

ここに書いた内容が、問題ごとのコードファイルの初期コードとして展開されます。
例えば、以下のように設定ファイルに書いておくと

template = """
use proconio::{input, fastout};

#[fastout]
fn main() {
    todo!();
}
"""

プロジェクト作成したときに、以下のように展開されます。

A~Fの6問構成であれば、a.rsからf.rsまで6つのファイルが作成されます
use proconio::{input, fastout};

#[fastout]
fn main() {
    todo!();
}

使い方

プロジェクトを作成する

AtCoder Beginner Contest 163 のためのプロジェクトを作成することを考えます。

以下のコマンドを実行します。

$ cargo atcoder new abc163

abc163 というディレクトリができます。ディレクトリ構造は以下のような感じです。

abc163
├── Cargo.lock
├── Cargo.toml
└── src
    └── bin
        ├── a.rs
        ├── b.rs
        ├── c.rs
        ├── d.rs
        ├── e.rs
        └── f.rs

Cargo.tomldependencies にさきほど設定ファイルで書いたものが、そして src/bin/ 以下にある a.rs などにも設定ファイルの templates に書いた内容があることが確認できると思います。

ちなみに、プロジェクト作成時に指定した abc163 というのは、コンテストページのURLの末尾に書いてあるものです。
例えば パナソニックプログラミングコンテスト2020 のプロジェクトを作成したいときは、このコンテストのURLが
https://atcoder.jp/contests/panasonic2020
なので、以下のようにすればOKです。

$ cargo atcoder new panasonic2020

テストケースを試す

ABC163 の A問題を解いてみました。

a.rs
use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        r: f64,
    }
    println!("{}", 2.0 * r * std::f64::consts::PI);
}

テストケースが通るか確認してみます。以下のコマンドを実行します。

$ cargo atcoder test a

以下のように実行され、テストが通ることが確認できました。テストケースをブラウザからコピペして1つ1つ試す必要はもうありません。

Apr-27-2020 22-15-04.gif

提出する

テストが通ったので、提出しましょう。A問題を提出する場合は以下のコマンドを実行します。

$ cargo atcoder submit a

このようになります。🎉 AC 🎉

スクリーンショット 2020-04-27 22.36.00.png

なお、 submit する時にもテストケースが通るかどうかの確認がされます。なので test せずにいきなり submit でも良いと思います。

ちなみに、submit コマンドは、デフォルトでは提出前のテストケースチェックで通過しなかった場合には提出を行わない仕様となっています。
「いくつか答えがありうるけど、そのうち1つが出力されてればOK」というタイプの問題については、用意されているテストの出力例と一致しなくても正解である、という場合がありえます。
この問題タイプの場合には submit--force オプションをつけてください。テストが通らなくても提出を行うようになります。
-f でもOKです)

テストで落ちてもsubmitしたいとき
$ cargo atcoder submit a --force

最後に

cargo-atcoder の導入方法と主要機能の使い方をご紹介しました。

超強力な機能であるバイナリ提出に関して「闇の力」という表現をしましたが、運営サイドとしてもこれはグレーであるという認識のようで、chokudaiさんが以下のようなツイートをしています。

どのような制限になるかは分かりませんが、さきほど書いた通りバイナリ提出はファイルサイズが大きくなるという欠点があります。例えば提出ファイルサイズの上限が現在の 512KiB から 64KB に下げられるだけでも、バイナリ提出は厳しくなるものと思われます。
(Codeforces のファイズサイズ上限が 64KB です)

バイナリ提出は cargo-atcoder の超強力な機能の1つであることは間違いないですが、それを抜きにしても、コマンドラインからのテストケースの実行や提出などはとても体験が良いものなので、ぜひお試しください。

別の cargo-xxx として、@hatoo@github さん作の cargo-snippet があります。スニペット管理をスマートに行えるこれまた素晴らしいツールです。
こちらもおすすめです!
Rustで競技プログラミングをするときの"スニペット管理"をまじめに考える(cargo-snippetの紹介) - Qiita

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
112
Help us understand the problem. What are the problem?