Help us understand the problem. What is going on with this article?

AtCoderの提出自動化をする(Python)

はじめに

MacBookを買いました。Air 2020 i7 16GBRAM 512GBSSDのモデルです。
これまで使っていためちゃくちゃ重い(物理)Windowsの2倍は余裕で超えるベンチマークが出るのでその性能ももちろん嬉しかったのですが、Macの一番惹かれていたのは画面内外のデザインとフォントレンダリングです。UIも綺麗で大満足です。せっかくなので環境構築ついでに最近ハマっているAtCoderの提出自動化をすることにしました。提出言語はPythonですが他の言語でも同様です。20/5/1追記:AtCoderの仕様変更について

準備

必要なもの

まだ入っていない場合は以下を事前に入れておきます。ちなみにエディタはコンテスト用にVSCodeと普段のエディタ用にAtomとで兼用しているので、以下適宜読み替えてください。

Hyper

MacのターミナルはWindowsに比べてだいぶマシですが、それでも多少のダサさがあります。
いろいろ探したところ、Hyperというターミナルがとても綺麗です。サードパーティーでポケモンの着せ替えテーマとかもあるらしくて可愛いです。以下、$記号が頭につく行はターミナルでそれに続くコマンド($ ​は含めない)を入力しReturn(Enter)で実行することを意味します。
image.png
コンテスト中はVSCodeのターミナルから作業をすることにしました。

Homebrew

有名なパッケージ管理システムです。公式ページに貼ってあるコマンドをコピーしてターミナルに貼り付けて実行します。途中でRETURNキーを押さないといけないところがあります。

==> The Xcode Command Line Tools will be installed.
Press RETURN to continue or any other key to abort

その直後パスワードの入力を求められます。ターミナル何もわからないぼくはここで詰まりました。パスワードを入力しているのに何も表示されないと思ったら、セキュリティーのためにhiddenされているだけでした... Macのパスワードを入力して進みます。

==> /usr/bin/sudo /bin/...
  Password:
 ==> Installation successful!

Python

Python公式からインストーラをダウンロードしてセットアップします。またはbrewを使います。

$ brew install python3

Node.js

こちらもNode.js公式からインストーラをダウンロードします。インストーラ使ったほうが早いです。

ツール

他に必要なツールを入れていきます。テストにはonline-judge-toolsを使います。提出やディレクトリ作成、テストケースの取得にはatcoder-cliをメインに使います。seleniumはジャッジ実行のために必要です。

$ pip3 install online-judge-tools
$ oj --version    # check

$ npm install -g atcoder-cli
$ acc -v          # check
$ acc check-oj    # check

$ pip3 install selenium

いらないかもしれませんが、ディレクトリ構造を把握するためにツリー表示を使いたいのでインストールします。

$ brew install tree

VSCodeのcodeコマンドを使いたいので、バージョンチェック(-v)してインストールされていなければインストールします。VSCodeのコマンドパレットを+Shift+Pで開きshellなどと打ち込み「Shell Command: Install 'code' command in PATH command」を実行します。

$ code -v   # check

設定

まず、Finderで開いているディレクトリにターミナルに移動したくて不便だったのでこちらの記事を参考(丸パクリ)にスクリプトを書きました。テキストエディタなどでホームディレクトリ(/Users/_username_~)直下に言語設定をShellScript/拡張子を.shにして以下の内容を保存します。

Finderset.sh
#!/usr/bin/osascript

tell application "Finder"
    if exists Finder window 1 then
        set currentDir to target of Finder window 1 as Unicode text
        set posixPath to get POSIX path of currentDir
    end if
end tell

次に、ホームディレクトリ直下にエイリアスなどの設定を管理する.zshrcを置きます。まだない場合は言語設定をShellScriptにしてファイル名.zshrcを作ります。(Finderでは隠しファイルなので非表示設定になっていると見えません;切り替えはCtrl+Shift+.)以下はぼくが追記したエイリアスの一例です。cdfは前述のFinderで開いているディレクトリにターミナルに移動するコマンドのショートカット、atcはディレクトリを置く場所への移動ですが、それ以外については下で書きます。(cdはターミナルのカレントディレクトリ移動コマンド)個人的にディレクトリはGitHubのローカルリポジトリを置いている場所に作ることにしました。

.zshrc
# vscode
alias vs='code -r main.py'

# cd
alias cdf='cd $(osascript ~/xxx.sh)'  # さっき書いたファイルの名前
alias atc='cd /Users/xxx...'  # ディレクトリを作りたい場所
alias a='cd ../a ; vs'
alias b='cd ../b ; vs'
alias c='cd ../c ; vs'
alias d='cd ../d ; vs'

# test
alias test='oj t -c "python3 main.py" -d ./tests/'
alias py='python3 main.py'

# start
sta () {
  atc
  acc new $1
  code -r $1
  source cd $1/a
  vs
}

# submit
sd () {
  problem=`pwd | xargs basename`
  contest=`echo $(basename $(pwd | xargs dirname)) | tr '[:upper:]' '[:lower:]'`
  echo "${contest:0:3}$problem" | acc s
  source cd ../`echo $(printf "\x$(printf "%x" $(($(printf "%d" \'$problem)+1)))")`
  vs
}
alias sf='if test ; then sd ; fi'

スクリーンショット 2020-04-28 19.03.35.png

VSCodeでターミナルを快適に使うためにショートカットを設定しました。+Shift+Pでコマンドパレットを出しKeyboardなどと打ち込み「基本設定:キーボードショートカットを開く(JSON)/Preferences: Open Keyboard Shortcuts file (JSON)」を選択します。keybinding.jsonに二つ付け加えます。Shift+lは未使用みたいなのでこれに割り当ててみました。こうすればShift+lだけでターミナルとエディタの移動を行えます。

keybinding.json
[
    { "key": "shift+l", 
    "command": "workbench.action.terminal.focus" },
    { "key": "shift+l", 
    "command": "workbench.action.focusActiveEditorGroup", "when":"terminalFocus" },
]

次に、AtCoderへのログインをします。

$ acc login
$ oj login https://atcoder.jp/

コンテストの全ての問題について同時にディレクトリが作られるように設定します。

$ acc config default-task-choice all

コンフィグディレクトリの場所を取得して移動します。cdaccの結果を渡すためには'シングルクォートではなく` バッククォートを使います。フォルダを作成し、その中にテンプレート main.pyとテンプレートの設定ファイルtemplate.jsonを作成します。

$ cd `acc config-dir`
$ mkdir py
$ cd py
$ touch main.py template.json

template.jsonを開き書き込みます。これでディレクトリ作成と同時にmain.pyを開き提出時もmain.pyを対象とすることができます。

$ open -a Atom template.json
template.json
{
  "task":{
    "program": ["main.py"],
    "submit": "main.py"
  }
}

main.pyを開き書き込みます。以下のように一行目にはShebungを書きます(必須)。テンプレートなので二行目以降にはよく使うコードを書いておいても良いかもしれません。

$ open -a Atom main.py
main.py
#!/usr/bin/env python3

テンプレートの設定をpyにします。

$ acc config default-template py
$ acc templates   # check
search template directories in /Users/../Library/Preferences/atcoder-cli-nodejs
[NAME]  SUBMIT-PROGRAM
[py]    main.py

実際に使ってみる

ディレクトリ作成

atcでディレクトリを作る場所に移動して、acc new [ContestName]で新しくディレクトリを生成します。具体的には、AtCoderのURLの該当部分の名前になります。以下のようなtreeが確認できたら成功です。

$ atc
$ acc new ABC164    # name of contest 
$ tree
.
├── a
│   ├── main.py
│   └── tests
│       ├── sample-1.in
│       ├── sample-1.out
...
│       └── sample-3.out
├── b
│   ├── main.py
│   └── tests
│       ├── sample-1.in
│       ├── sample-1.out
│       ├── sample-2.in
│       └── sample-2.out
├── c
│   ├── main.py
│   └── tests
│       ├── sample-1.in
...
│       └── sample-3.out
├── contest.acc.json
├── d
│   ├── main.py
│   └── tests
│       ├── sample-1.in
│       ├── sample-1.out
│       ├── sample-2.in
│       ├── sample-2.out
...

12 directories, 43 files

先ほどの.zshrcには以下のように書いて自動でA問題のフォルダにカレントディレクトリを移しています。これでA問題提出時にディレクトリを変える必要はないです。(同じターミナルで一貫してディレクトリ作成〜提出までの作業をする場合)

.zshrc(only_one_part)
sta () {
  atc
  acc new $1
  code -r $1
  source cd $1/a
  vs
}

テスト

実際にA問題を開いて解いてみます。staを実行したらそのまま開いたa/main.pyにプログラムを書きます。そのまま先ほど設定したショートカットでターミナルに移り、問題のディレクトリ(a)にいることを確認してoj t -c "python3 main.py" -d ./tests/を実行します。長ったらしいので先ほどの.zshrcのエイリアスではtestに割り当てています。ちゃんとACと判定してくれました。

$ pwd     # check current directory (not necessary)
/Users/.../abc164/a

$ test
[*] 3 cases found
time: illegal option -- f
usage: time [-lp] command.
[!] GNU time is not available: time

[*] sample-1
[x] time: 0.022793 sec
[+] AC

...

[x] slowest: 0.022793 sec  (for sample-1)
[+] test success: 3 cases

スクリーンショット 2020-04-29 11.18.21.png

競プロしかやってないとわかんなくなりそうですが、本来input()が実行されるとターミナルの標準入力が入力待機状態にになります。なので、サンプルケース以外の任意のケースで実行したい場合は普通にpython3 main.pyとやって一行ずつ標準入力を打ち込めば良いです。(open(0)とかはどうなんだろう...)

提出

該当ディレクトリにさえいればacc sだけで可能です。ただ、確認コードを入力するように言われます。どうやら、コンテスト名の頭三文字と問題番号を組み合わせた計4文字のようです([ContestName;3][problem];ex.abca)。流石にしている暇はないので、sd関数を用意しています。sdでは提出後に自動で次のディレクトリに移動するようにしてあります。.zshrcにはもう一つ、testの結果が[AC](=返り値が0)の時のみ提出するコマンド(sf)を用意しています。

.zshrc(only_one_part)
# submit
sd () {
  problem=`pwd | xargs basename`
  contest=`echo $(basename $(pwd | xargs dirname)) | tr '[:upper:]' '[:lower:]'`
  echo "${contest:0:3}$problem" | acc s
  source cd ../`echo $(printf "\x$(printf "%x" $(($(printf "%d" \'$problem)+1)))")`
  vs
}
alias sf='if test ; then sd ; fi'

提出作業が進むと自動でブラウザが開きます。
他の問題への移動もエイリアスにしました。一応自動で動きますが、これで前/先の問題に動くときはカレントディレクトリの移動とVSCodeのファイル操作はabc...だけでOKですね。

.zshrc(only_one_part)
alias a='cd ../a ; vs'
alias b='cd ../b ; vs'
alias c='cd ../c ; vs'
alias d='cd ../d ; vs' # ...

これで完璧ですね!!

おまけ(詰まったところ)

.zshrcを試行錯誤しながら編集していて、即反映させたいとき(普通に反映させるにはターミナルを再起動)

$ source ~/.zshrc

なぜか関数が使えないとき

.zshrc
function func () {
  # do_something
}
alias func=func

シェルスクリプトは関数定義時のfunctionが省略できますが、同名のエイリアスを定義したいときは明示的に宣言しないと以下のようなエラーが出ます。

$ source ~/.zshrc
/Users/xxx/.zshrc:19: defining function based on alias `func'
/Users/xxx/.zshrc:19: parse error near `()'

AtCoderの仕様変更について(追記)

5月1日から提出ができないな〜と思ったらどうやらojの問題っぽい。

$ oj -v s [URL] main.py
...
  File "/Users/kazuhiroserizawa/.pyenv/versions/3.8.0/lib/python3.8/site-packages/onlinejudge/service/atcoder.py", line 728, in _parse_available_languages
    languages += [Language(option.attrs['value'], option.string)]
KeyError: 'value'

[ERROR] 'value'

スクリーンショット 2020-05-01 13.16.19.png

728行目から狂ってますね。GitHubにもプルリクがありました。すぐ修正されると思いますが一応直しておきます。

# Install-Location
$ pip show online-judge-tools
...
Location: /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages
...
# Open
$ cd /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages
$ open -a Atom onlinejudge/service/AtCoder.py

コードの修正内容はこちら。あとはこの通りに書き換えます。

参考文献

GitHubにはスターを送るのを忘れずに!

免責

AtCoderの利用規約では明示的に禁止されていませんが、サーバーに負荷をかけご迷惑をおかけするわけにはいかないので、テストケースの取得は一回にしています。良識の範囲内での利用を。

おわりに

Unix系のコマンドが全くわからず検索しまくりました!

Orangeat
映像 / Orangeatという名前でデザイナーをしている高校生です
https://note.com/orange_aus
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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした