はじめに
MacBookを買いました。Air 2020 i7 16GBRAM 512GBSSDのモデルです。
これまで使っていためちゃくちゃ重い(物理)Windowsの2倍は余裕で超えるベンチマークが出るのでその性能ももちろん嬉しかったのですが、Macの一番惹かれていたのは画面内外のデザインとフォントレンダリングです。UIも綺麗で大満足です。せっかくなので環境構築ついでに最近ハマっているAtCoderの提出自動化をすることにしました。提出言語はPythonですが他の言語でも同様です。20/5/1追記:AtCoderの仕様変更について
#準備
必要なもの
まだ入っていない場合は以下を事前に入れておきます。ちなみにエディタはコンテスト用にVSCodeと普段のエディタ用にAtomとで兼用しているので、以下適宜読み替えてください。
Hyper
MacのターミナルはWindowsに比べてだいぶマシですが、それでも多少のダサさがあります。
いろいろ探したところ、Hyperというターミナルがとても綺麗です。サードパーティーでポケモンの着せ替えテーマとかもあるらしくて可愛いです。以下、$
記号が頭につく行はターミナルでそれに続くコマンド($
は含めない)を入力しReturn(Enter)で実行することを意味します。
コンテスト中は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にして以下の内容を保存します。
#!/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
を置きます。
$ cd ~
$ touch .zshrc
$ code .zshrc
以下は追記エイリアスの一例です。cdf
は前述のFinderで開いているディレクトリにターミナルに移動するコマンドのショートカット、atc
はディレクトリを置く場所への移動ですが、それ以外については下で書きます。(cd
はターミナルのカレントディレクトリ移動コマンド)個人的にディレクトリはGitHubのローカルリポジトリを置いている場所に作ることにしました。
# 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'
alias d='cd ../e ; vs'
alias d='cd ../f ; 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'
VSCodeでターミナルを快適に使うためにショートカットを設定しました。⌘
+Shift
+P
でコマンドパレットを出しKeyboardなどと打ち込み「基本設定:キーボードショートカットを開く(JSON)/Preferences: Open Keyboard Shortcuts file (JSON)」を選択します。keybinding.json
に二つ付け加えます。Ctrl
+l
は未使用みたいなのでこれに割り当ててみました。こうすればCtrl
+l
だけでターミナルとエディタの移動を行えます。
[
{ "key": "Ctrl+l",
"command": "workbench.action.terminal.focus" },
{ "key": "Ctrl+l",
"command": "workbench.action.focusActiveEditorGroup", "when":"terminalFocus" },
]
次に、AtCoderへのログインをします。
$ acc login
$ oj login https://atcoder.jp/
コンテストの全ての問題について同時にディレクトリが作られるように設定します。
$ acc config default-task-choice all
コンフィグディレクトリの場所を取得して移動します。cd
にacc
の結果を渡すためには'
シングルクォートではなく`
バッククォートを使います。フォルダを作成し、その中にテンプレート 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
{
"task":{
"program": ["main.py"],
"submit": "main.py"
}
}
main.py
を開き書き込みます。以下のように一行目にはShebung
を書きます(必須)。テンプレートなので二行目以降にはよく使うコードを書いておいても良いかもしれません。
$ open -a Atom 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問題提出時にディレクトリを変える必要はないです。(同じターミナルで一貫してディレクトリ作成〜提出までの作業をする場合)
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
競プロしかやってないとわかんなくなりそうですが、本来input()
が実行されるとターミナルの標準入力が入力待機状態にになります。なので、サンプルケース以外の任意のケースで実行したい場合は普通にpython3 main.py
とやって一行ずつ標準入力を打ち込めば良いです。(open(0)
とかはどうなんだろう...)
提出
該当ディレクトリにさえいればacc s
だけで可能です。ただ、確認コードを入力するように言われます。どうやら、コンテスト名の頭三文字と問題番号を組み合わせた計4文字のようです([ContestName;3][problem]
;ex.abca)。流石にしている暇はないので、sd
関数を用意しています。sd
では提出後に自動で次のディレクトリに移動するようにしてあります。.zshrc
にはもう一つ、test
の結果が[AC]
(=返り値が0
)の時のみ提出するコマンド(sf
)を用意しています。
# 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のファイル操作はa
・b
・c
...だけでOKですね。
alias a='cd ../a ; vs'
alias b='cd ../b ; vs'
alias c='cd ../c ; vs'
alias d='cd ../d ; vs' # ...
これで完璧ですね!!
おまけ(詰まったところ)
.zshrc
を試行錯誤しながら編集していて、即反映させたいとき(普通に反映させるにはターミナルを再起動)
$ source ~/.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'
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にはスターを送るのを忘れずに!
- GitHub - atcoder-cli
- GitHub - online-judge-tools
- AtCoder初心者こそ環境構築をしよう! - Qiita
- コマンドラインからVS Codeでファイルやフォルダを開く
- ShellScript・ターミナルなど
免責
AtCoderの利用規約では明示的に禁止されていませんが、サーバーに負荷をかけご迷惑をおかけするわけにはいかないので、テストケースの取得は一回にしています。良識の範囲内での利用を。
おわりに
Unix系のコマンドが全くわからず検索しまくりました!