【C++/Python】WSLとVimで始める競プロ環境構築
んぐです。タイトル詐欺です。
前回からかなり時間が立って、環境もかなり煮詰まってきたので改めて紹介しようと思います。 環境構築大好き人間なのでまた実力が変化すればそれに応じて変化しそうですが、いまのところこれで満足しています。
いかにも丁寧に説明してありそうなブログタイトルですが、自分用に雑にまとめてるだけです。はい。
コンテスト用のディレクトリ作成スクリプト
Pythonで書きました。雑な作りなので、ABC000形式でないとエラーが出ます。エラーになるときは手動で作ります、なんの問題ないです。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import shutil from colorama import Fore, Back, Style def toml_data(contest_name): data = "[dependencies]\n\n"\ "[package]\n"\ "authors = ['ngng628 <メールアドレス>']\n"\ "edition = '2018'\n"\ "name = '" + contest_name.lower() + "'\n"\ "version = '0.1.0'\n"\ "[profile.release]\n"\ "lto = true\n"\ "panic = 'abort'\n" return data def main(): argvs = sys.argv argc = len(argvs) check = '[' + Fore.MAGENTA + 'x' + Fore.RESET + ']' plus = '[' + Fore.LIGHTGREEN_EX + '+' + Fore.RESET + ']' if argc < 1: print('Usege\nstart-atcoder.py contest-name') exit(0) contest_name = argvs[1] if os.path.exists(contest_name): print(check, contest_name, 'is exists') exit(0) else: os.mkdir(contest_name) print(check, contest_name, 'is created\n') # downlow.pyの生成 src = '/home/akri/atelier/atcoder/download-testcase-tool/download.py' shutil.copyfile(src, contest_name + '/download.py') print(plus, 'download.py created\n') # Cargo.tomlの生成 f = open(contest_name + '/Cargo.toml', 'w') f.write(toml_data(contest_name)) f.close() print(plus, 'Cargo.toml created\n') contest_class = contest_name[:3] contest_number = int(contest_name[3:]) if contest_class == 'ABC' and contest_number >= 126: problem_number = 6 elif contest_class == 'AGC': problem_number = 6 else: problem_number = 4 for i in range(problem_number): path = contest_name+'/'+chr(ord('A')+i) # +'/main.cpp' if os.path.isfile(path): print(check, chr(ord('A')+i), 'is exists') else: os.mkdir(path) src = '/home/akri/.config/nvim/template/cpp/base-atcoder.cpp' shutil.copyfile(src, path + '/main.cpp') print(plus, chr(ord('A')+i), 'created') if __name__ == '__main__': main()
このスクリプト走らせるとdownload.py
なるファイルが生成されます。といっても中身は作られず毎回スニペット使って次のコードを挿入しています。
なぜこれも自動化しないかというと「コンテスト毎に適切なURLをlinks
に突っ込むのがだるかった」からです。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os from argparse import ArgumentParser import subprocess def main(): links = ['https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_a', 'https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_b','https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_c','https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_d','https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_e','https://atcoder.jp/contests/${1:contest_name}/tasks/${1:contest_name}_f'] for i in range(len(links)): directory = './'+chr(ord('A')+i) res = subprocess.run(['oj', 'download', links[i]], cwd=directory) if __name__ == '__main__': main()
このスクリプトを走らせると自動でコンテストのすべての問題のテストケースがダウンロードされます。便利です。 (後述のonline-judge-toolsが必要)
online-judge-toolsの導入
有名なツールです。
online-judge-tools.readthedocs.io
テストケースを自動でダウンロードしてくれて、oj t
と打つだけでテストケースでジャッジしてくれます。test/
ディレクトリに自作のテストケースを突っ込むこともできます。提出もoj s main.cpp
と打つだけでできて簡単です。拡張子から言語を自動で選んでくれるので言語選択でミスる心配もありません。
あとコンテスト中にする余裕は僕にはないですが、ストレステストなんていう機能もあるみたいです。便利そうなので練習してみたい気持ちもあります。
実行ファイルが生成されないPythonではテストケースをジャッジするときoj t -c "python3 main.py"
としなければなりません。これは面倒臭いので.zshrc
にスクリプトを書いています。これでojt main.py
とするだけでジャッジできます。
function ojt() { case $1 in *.py ) oj t -c "python3.5 $1" ;; * ) oj test ;; esac }
また、臨時的にクリップボードにコピーして提出したいこともあるのでclip()
スクリプトも書いています。
function clip() { cat $1 | clip.exe }
run
スクリプト
今はこれを改造して次のスクリプトを定義してます。そこそこ便利ですが、コンパイル済みの場合は再コンパイルしない機能を加えたいです。
function run() { case "$1" in *.c ) run_c $1 ;; *.cpp ) run_cpp $1 ;; *.java ) run_java $1 ;; *.py ) run_py $1 ;; esac } ## C function run_c() { echo -e "[\e[34mx\e[0m] gcc -std=gnu11 -Wall -Wextra -O3 $1 -o a.out" gcc -std=gnu11 -Wall -Wextra -O3 $1 -o a.out result=$? if [ $result -ne 0 ] then echo -e "[\e[31m-\e[0m] compile failed" else echo -e "[\e[32m+\e[0m] successful complie" echo -e "[\e[34mx\e[0m] run ./a.out" ./a.out fi } ## C++ function run_cpp() { echo -e "[\e[34mx\e[0m] g++ -std=gnu++17 -Wall -Wextra -O2 $1 -o a.out" g++ -std=gnu++17 -Wall -Wextra -O2 $1 -o a.out result=$? if [ $result -ne 0 ] then echo -e "[\e[31m-\e[0m] compile failed" else echo -e "[\e[32m+\e[0m] successful complie" echo -e "[\e[34mx\e[0m] run ./a.out" ./a.out fi } ## Java function run_java() { echo -e "[\e[34mx\e[0m] javac $1" javac $1 result=$? if [ $result -ne 0 ] then echo -e "[\e[31m-\e[0m] compile failed" else echo -e "[\e[32m+\e[0m] successful complie" echo -e "[\e[34mx\e[0m] run ${1%.java}" java ${1%.java} fi } ## Python function run_py() { echo -e "[\e[34mx\e[0m] python3 $1" python3 $1 }
C++のテンプレートとスニペット
C++はテンプレート化しておきたい部分がたくさんあるので、毎回次のテンプレートを展開するようにしています。 「なるべくテンプレートは小さめに…」と思っているのですがなんだかんだ長くなっちゃいます。
# include <bits/stdc++.h> # define rep(i, n) for(int i=0, i##_len=(n); i<i##_len; ++i) # define reps(i, n) for(int i=1, i##_len=(n); i<=i##_len; ++i) # define rrep(i, n) for(int i=((int)(n)-1); i>=0; --i) # define rreps(i, n) for(int i=((int)(n)); i>0; --i) # define range_for(i, b, e) for(int i=(b), i##_len=(e); i<=i##_len; ++i) # define step(n) rep(_, n) # define ALL(x) (x).begin(), (x).end() # define RALL(x) (x).rbegin(), (x).rend() # define Unique(a) a.erase(unique(Range(a)), a.end()) # define pb push_back # define len(x) ((int)(x).size()) # define optimize_cin() cin.tie(0); ios::sync_with_stdio(false) # define debug(x) std::cerr<<#x<<": "<<(x)<<endl; # define LINT_MAX (LLONG_MAX) # define LINT_MIN (LLONG_MIN) # define cauto const auto using namespace std; using lint = long long; template <class Type> inline constexpr Type Square(Type x) { return x * x; } template <class Type> inline constexpr bool InRange(const Type& x, const Type& i, const Type& a) { return (i <= x) && (x <= a); } template<class Integer>bool chmax(Integer &a, const Integer &b) { if (a<b) { a=b; return 1; } return 0; } template<class Integer>bool chmin(Integer &a, const Integer &b) { if (b<a) { a=b; return 1; } return 0; } template<class Integer>bool IsOdd(Integer &n) { return n & 1; } template<class Integer>bool IsEven(Integer &n) { return !(n & 1); } int ctoi(const char c) { return ('0' <= c && c <= '9') ? (c - '0') : -1; } string YesNo(bool b) { return b ? "Yes" : "No"; } string YESNO(bool b) { return b ? "YES" : "NO"; } string yesno(bool b) { return b ? "yes" : "no"; } void _cin(){} template <class Head, class... Tail> void _cin(Head&& head, Tail&&... tail){ cin >> head; _cin(forward<Tail>(tail)...); } #define Cin(Type, ...) Type __VA_ARGS__; _cin(__VA_ARGS__) #define Cinv(Type, xs, n) vector<Type> xs(n); rep(i, n) cin >> xs[i] #define Cinv2(Type, xs, ys, n) vector<Type> xs(n), ys(n); rep(i, n) cin >> xs[i] >> ys[i] #define Cinv3(Type, xs, ys, zs, n) vector<Type> xs(n), ys(n), zs(n); rep(i, n) cin >> xs[i] >> ys[i] >> zs[i] #define Cinvv(Type, xs, h, w) Vec2(Type, n, m, xs); rep(i, n) rep(j, m) cin >> xs[i][j] #define Cinm(Type, map, n) unordered_map<Type, int> map; Rep(n){ Cin(Type, x); map[x] ++; } void Print() { cout << endl; } template <class Head, class... Tail> void Print(Head&& head, Tail&&... tail) { cout << head; if (sizeof...(tail) != 0) cout << " "; Print(forward<Tail>(tail)...); } template <class Type> void Print(vector<Type> &vec) { for (auto& a : vec) { cout << a; if (&a != &vec.back()) cout << " "; } cout << endl; } template <class Type> void Print(vector<vector<Type>> &df) { for (auto& vec : df) { Print(vec); } } void Debug() { cerr << endl; } template <class Head, class... Tail> void Debug(Head&& head, Tail&&... tail) { cerr << head; if (sizeof...(tail) != 0) cerr << " "; Debug(forward<Tail>(tail)...); } template <class Type> void Debug(vector<Type> &vec) { for (auto& a : vec) { cerr << a; if (&a != &vec.back()) cerr << " "; } cerr << endl; } template <class Type> void Debug(vector<vector<Type>> &df) { for (auto& vec : df) { Debug(vec); } } int main() { return 0; }
テンプレートの解説は別記事にします。
その他問題によっては使いたくなる関数・マクロはスニペット登録することで解決しています。ここに貼るのは冗長なのでGistリンク貼っときます。
Vim関係
coc.nvim
コードの静的チェックとかサジェストとかやってくれます。スニペットもこのプラグインの機能を使ってやってます。
ただ設定がめんどくさい上にわかりにくいです。基本的には:CocInstall lang
でインストール終わるらしいんですが、僕はうまくできませんでした。
syntax
neovimでは~/.config/nvim/after/syntax/cpp.vim
を編集すると既存のcpp syntax+独自のsyntaxが定義できるみたいです。
色がつかないと怖くなっちゃう質なので、標準ライブラリに入っている関数や自作テンプレにある関数やマクロは全部syntaxを設定してます。
特にマクロ関係は色がつかないと不安です。(なかなか色なしでpush_back
をpb
にできない)
これもGistに上げておきます。(yat*
は昔YatCoder.hppを使っていた名残りです。)
tmuxinator
mux atcoder
で次のセッションが起動するようにしています。かっこいいです。
その他
いちいち書くのが面倒臭かったやつ
- エディタは
e
で起動するようにした - tmuxinatorでnvimとdownload.pyを実行しその後コンパイル等に使う用のペインと
cargo atcoder status
を走らせるセッションを定義してる