んぐのルーズリーフ

んぐの日記。最近はScrapBoxが主

【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スクリプト

qiita.com

今はこれを改造して次のスクリプトを定義してます。そこそこ便利ですが、コンパイル済みの場合は再コンパイルしない機能を加えたいです。

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リンク貼っときます。

競プロC++用のスニペット · GitHub


Vim関係


coc.nvim

github.com

コードの静的チェックとかサジェストとかやってくれます。スニペットもこのプラグインの機能を使ってやってます。 ただ設定がめんどくさい上にわかりにくいです。基本的には:CocInstall langでインストール終わるらしいんですが、僕はうまくできませんでした。

syntax

neovimでは~/.config/nvim/after/syntax/cpp.vimを編集すると既存のcpp syntax+独自のsyntaxが定義できるみたいです。 色がつかないと怖くなっちゃう質なので、標準ライブラリに入っている関数や自作テンプレにある関数やマクロは全部syntaxを設定してます。 特にマクロ関係は色がつかないと不安です。(なかなか色なしでpush_backpbにできない)

これもGistに上げておきます。(yat*は昔YatCoder.hppを使っていた名残りです。)

cpp.vim · GitHub


tmuxinator

mux atcoderで次のセッションが起動するようにしています。かっこいいです。 f:id:luling:20200507134048p:plain


その他

いちいち書くのが面倒臭かったやつ

  • エディタはeで起動するようにした
  • tmuxinatorでnvimとdownload.pyを実行しその後コンパイル等に使う用のペインとcargo atcoder statusを走らせるセッションを定義してる