DockerでAtCoderができる環境を作る【Python・C++】

# はじめに
Dockerは仮想化技術のひとつであり、コンテナ型仮想化のためのソフトウェアです。
一般的にいわれる利点はこんな感じ（[この記事](https://qiita.com/gold-kou/items/44860fbda1a34a001fc1)を参考にしています） 

- 処理が速い
- 起動が速い
- 可搬性が高い
- イメージが軽い
- Dockerイメージを簡単に共有できる仕組みが整っている

AtCoderなど競技プログラミングをする際は指定の動作環境があり、ユーザー環境もAtCoder用の環境を独立させたくなります。なので仮想環境としてひとつ持っておくのがよくあるプラクティスなわけです。
pythonとかだとpipenvやcondaを使って仮想環境をつくるって対処法もあるんですが、DockerHubというなかなかいけてる共有サービスがあるのでDockerの勉強がてら環境を作成してみることにしました。

# AtCoder環境をDockerで整えると何が嬉しいの？
1. Dockerさえあれば、手軽かつ簡単にAtCoderをする環境を整えられる
2. 複数の端末でAtCoderの環境を統一できる
3. VSCodeの拡張が偉い！

1~2についてはそのままです。DockerHubにイメージをpush、GitHubに関連ファイルなどをレポジトリにpushしておけば、pullするだけで環境が整います！
3については「Remote-Containers」という偉すぎるVSCodeの拡張があり、ローカルのファイルを編集するノリでコンテナ内のファイルを編集することができます。

# Docker環境の中身について
今回はPyPy環境とPython環境、C++環境が使えるAtCoder環境としてDocker Imageを作ります。
C++ではac-libraryとbits/stdc++.hが使えるようにします。
PythonではAtCoderでも使える非標準ライブラリをインストールしておきます。
また、online-judge-toolsもインストールします。
OSやシェルは任意ですが、今回はUbuntu、zshにしました。

```Docker:Dockerfile
# Ubuntuの公式コンテナを軸に環境構築
# 22.04ではaptからpython3.8が入っていなかったので20.04で固定する
FROM ubuntu:20.04

# インタラクティブモードにならないようにする
ARG DEBIAN_FRONTEND=noninteractive
# タイムゾーンを日本に設定
ENV TZ=Asia/Tokyo

# インフラを整備
RUN apt-get update && \
    apt-get install -y zsh time tzdata tree git curl

# デフォルトシェルをZ shellにする
RUN chsh -s /bin/zsh

# C++, Python3, PyPy3の3つの環境想定
RUN apt-get update && \
    apt-get install -y gcc-9 g++-9 python3.8 python3-pip pypy3 nodejs npm

# 一般的なコマンドで使えるように設定
# e.g. python3.8 main.py => python main.py
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 30 && \
    update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 30 && \
    update-alternatives --install /usr/bin/python python /usr/bin/python3.8 30 && \
    update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 30 && \
    update-alternatives --install /usr/bin/pypy pypy /usr/bin/pypy3 30 && \
    update-alternatives --install /usr/bin/node node /usr/bin/nodejs 30

# AtCoderでも使えるPythonライブラリをインストール
RUN pip install -U pip && \
    pip install numpy==1.18.2 scipy==1.4.1 scikit-learn==0.22.2.post1 \
    numba==0.48.0 networkx==2.4

# C++でAtCoder Library(ACL)を使えるようにする
RUN git clone https://github.com/atcoder/ac-library.git /lib/ac-library
ENV CPLUS_INCLUDE_PATH /lib/ac-library

# Pythonでの競技プログラミング用データ構造をインストール
RUN pip install git+https://github.com/hinamimi/ac-library-python && \
    pip install git+https://github.com/hinamimi/python-sortedcontainers

# コンテスト補助アプリケーションをインストール
RUN pip install online-judge-tools==11.5.1
RUN npm install -g atcoder-cli@2.2.0

# # AHC用のRustのinstall
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH $PATH:/home/root/.cargo/bin

WORKDIR /root/problems
WORKDIR /root/library
```

次にイメージの立ち上げ方の設定、docker-compose.ymlを編集します。このあたりは各々の環境に合わせて、お好みの設定をチューニングするといいでしょう。また、online-judge-toolsとatcoder-cliのコンフィグ、セッション情報の作成は煩雑になるのでここでは説明しません。[atcoder-cli チュートリアル](http://tatamo.81.la/blog/2018/12/07/atcoder-cli-tutorial/)を参考にしてください。


```yaml:docker-compose.yml
version: '3'
services:
  dev:
    build:
      context: .
      dockerfile: Dockerfile

    # イメージ名を指定
    image: hinamimi/atcoder:dev

    # localとcontainer間のファイルを同期させる
    # ${local}:${container}
    volumes:
      - ../problems:/root/problems:cached
      - ../library:/root/library:cached
      - ../.vscode:/root/.vscode:cached
    # online-judge-toolsとatocder-cliのコンフィグ・セッション情報
    #   - ../.login:/root/.login
    #   - ../atcoder-cli-nodejs:/root/.config/atcoder-cli-nodejs

    # オプション これらのconfigファイルが必要なければコメントアウト
      - ../dotfiles/.zshrc:/root/.zshrc
      - ../dotfiles/.zinit:/root/.zinit
      - ../dotfiles/.zsh_history:/root/.zsh_history
      - ../dotfiles/.aliases:/root/.aliases

    # Overrides default command so things don't shut down after the process ends.
    command: /bin/sh -c "while sleep 1000; do :; done"
```

ついでにonline-judge-toolでDLしてきたサンプルをVSCodeで簡単にデバッグする設定をつくっておきます。(C++の設定も気が向いたらします)

```json:.vscode/launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: AtCoder Test1",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "cwd":"${fileDirname}",
            "console": "integratedTerminal",
            "args": [
                "<",
                "test/sample-1.in"
            ]
        },
        {
            "name": "Python: AtCoder Test2",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "cwd":"${fileDirname}",
            "console": "integratedTerminal",
            "args": [
                "<",
                "test/sample-2.in"
            ]
        },
        {
            "name": "Python: AtCoder Test3",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "cwd":"${fileDirname}",
            "console": "integratedTerminal",
            "args": [
                "<",
                "test/sample-3.in"
            ]
        }
    ]
    
}
```

そしてこれらをまとめておいて、DockerHubとGitHubにpushします。
これらのリポジトリは
GitHub (https://github.com/hinamimi/docker-atcoder)
DockerHub (https://hub.docker.com/repository/docker/hinamimi/atcoder)
においてあります。

# とりあえず使ってみる
1. 次のコマンドを実行

```sh:shell
$ git clone https://github.com/hinamimi/docker-atcoder
$ docker pull hinamimi/atcoder-test:dev
$ code ./docker-atcoder
```
2. `docker-compose.yml`をいじる
3. VSCodeで`Remote-Containers: Reopen in Container`を選択

で多分動くと思います。

# おわりに
本記事やレポジトリなど、改善があれば追記していきます。Docker初心者なので多々間違っている点などもあるかと思いますが、見つけ次第ご指摘くださると助かります。

# 更新履歴
- 2021年01月24日　初版
- 2021年07月18日　Dockerfile・docker-compose.ymlを更新
    - atcoder-cliを利用
    - AHCに対応
    - tzdataのインストール時にインタラクティブに質問してくる問題に対応
    - configファイルをhomeディレクトリからマウントせず、直下のdotfilesからマウントするように改善
- 2022年05月02日 Ubuntu 22.04で`docker build`に失敗する問題に対応
  - Dockerfileを更新
    - ベースとなるイメージのバージョンを固定
    - pip installの書き方をより簡潔に変更
    - online-judge-toolsとatcoder-cliのバージョンをアップデート
    - atcoder-cliの設定を削除
  - docker-compose.ymlのディスクリプションと内容を更新
    - atcoder-cliのコンフィグについての誘導を追記
    - セッション情報のマウントを記述