LoginSignup
1

More than 1 year has passed since last update.

VS Code Remote ContainersでAtCoder用F#環境を楽にする

Last updated at Posted at 2021-11-11

はじめに

F#でAtCoderに参加するための環境を、VS Code Remote Containers等を利用して自動化しました。

  • Docker, dotnet templates, Powershell module等の開発
  • Docker Hub, nuget, Powershell Gallery等へのパッケージ公開
    • GitHub ACtionsを利用したコンテナイメージのビルド

等の勉強も兼ねてます。

コンテナイメージ

Language Test 202001こちらの記事を参考にしました。

対応言語:F#(.NET Core3.1)

組み込みインストール

作業簡略化のため、.netテンプレートとPowershellモジュールを作成しました。

# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/ubuntu/.devcontainer/base.Dockerfile

# [Choice] Ubuntu version (use hirsuite or bionic on local arm64/Apple Silicon): hirsute, focal, bionic
# ARG VARIANT="bionic"
FROM mcr.microsoft.com/vscode/devcontainers/base:0-bionic

# install software-properties-common(add-apt-repository)
RUN apt-get update \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install --no-install-recommends software-properties-common \
    && rm -rf /var/lib/apt/lists/* \
# for pypy3
    && add-apt-repository ppa:pypy/ppa -y \
# for nodejs
    && curl -sL https://deb.nodesource.com/setup_14.x | bash - \
# for .NET and powershell
    && wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \
    && dpkg -i packages-microsoft-prod.deb \
    && rm packages-microsoft-prod.deb \
    && apt-get update \
    && apt-get -y install --no-install-recommends \
# .NET
        dotnet-sdk-3.1 \
        dotnet-sdk-6.0 \
# Powershell
        powershell \
# Python3, PyPy3の3つの環境想定
        python3.8 \
        python3-pip \
        pypy3 \
# node
        nodejs \
# online-judge-tools用ライブラリ
        time \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
# update-alternatives
    && 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 \
# コンテスト補助アプリケーションをインストール
    && pip install online-judge-tools \
    && rm -rf ~/.cache/pip \
    && npm install -g atcoder-cli \
    && npm cache clean --force

SHELL ["pwsh","-command"]

# powershell セットアップ
RUN Set-PSRepository -Name PSGallery -InstallationPolicy Trusted \
    && Install-Module Pester,AngleParse,InvokeBuild,AtcoderFs.Pwsh  -Scope AllUsers -Confirm:$False -AcceptLicense  -Force -Verbose \
    && Install-Script -Name Invoke-Build.ArgumentCompleters  -Scope AllUsers -Confirm:$False -AcceptLicense  -Force -Verbose \
    && 'Import-Module AtcoderFs.Pwsh' | Add-Content $PROFILE.AllUsersAllHosts -Force

使い方

セットアップ

vscodeにremote-containers拡張機能をインストールします。

ワークスペースフォルダーに./.devcontainer/devcontainer.jsonを作ります。

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/cpp
{
	"name": "Atcoder helper for F#",
	"image": "syamorock/atcoder-fs:main",

	// Set *default* container specific settings.json values on container create.
	"settings": {
	},

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": [
		"ms-vscode.cpptools",
		"ms-dotnettools.csharp",
		"Ionide.Ionide-fsharp",
		"alfonsogarciacaro.vscode-template-fsharp-highlight",
		"ms-vscode.powershell-preview"
	],

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],

	// Use 'postCreateCommand' to run commands after the container is created.
	"postCreateCommand": "dotnet tool install paket -g",

	// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
	"remoteUser": "vscode"
}

image.png

開発コンテナを起動します

image.png

image.png

コンテナの初回起動時

accojにログインします。

※それぞれ、Enter-AtCoderCliEnter-AtCoderOnlineJudgeというラッパー関数を作成しています。

image.png

Update-FsTemplateを実行してテンプレート関連の初期化を行います。
Initialize-PaketDepandenciesを実行してpaketの初期化を行います。

これで環境構築、初期設定は完了です。

コンテスト参加時の流れ

参加するコンテストのコンテストIDを使用します。

https://atcoder.jp/contests/<contestId>

例:AtCoder Beginners Selection
URL…https://atcoder.jp/contests/abs
→コンテストIDはabs

コンテスト用のソリューションを作成する

開発コンテナを開いてNew-AtCoderContestを実行します。

New-AtCoderContest -contestId abs

コンテストID名のフォルダが生成されます。
構成は以下の通りです。

abs
│  contest.acc.json
│  abs.sln
│  
├─abc088b                       …… 各問題ごとに1フォルダ
│  │  abc088b.fsproj
│  │  Program.fs                …… 提出用F#ファイル
│  │  abc088b.draft.fsx         …… ラフスケッチ用のソリューションから独立したF#スクリプト
│  │  
│  ├─test
│  ├─obj
│  └─bin
│                      
(略)                      
│                      
└─abs.Tests                     …… テスト用のプロジェクト
   │  abs.Tests.fsproj
   │  paket.references
   │  Utils.fs
   │  CustomTests.fs
   │  Tests.fs
   │  Program.fs
   │  
   ├─obj
   └─bin

image.png

問題を解く

解きたい問題のフォルダのProgram.fsを開いてプログラムを作成します。
問題のフォルダ名の関数に処理を記述する想定です。

image.png

テスト

Test-AtCoderを使って2種類のテストが可能です。

自動生成されたテストプロジェクトによるテスト
Test-AtCoder -FolderPath <コンテストのフォルダ>

自動生成されたテストプロジェクトを利用してコンテストプロジェクト全体のテストを実行します。
初期状態ではすべてのテストがコメントアウトされています。

image.png

実行例
Test-AtCoder ./abs/

結果

初期状態では何も実行されない
[03:45:53 INF] EXPECTO? Running tests... <Expecto>
[03:45:53 INF] EXPECTO! 0 tests run in 00:00:00.0187893 for miscellaneous – 0 passed, 0 ignored, 0 failed, 0 errored. Success! <Expecto>

実施したいテストの行のコメントアウトを解除してください。

image.png

テストが実施されます。

何も処理を書いてないでテストは失敗します
[03:49:13 INF] EXPECTO? Running tests... <Expecto>
[03:49:13 ERR] Sample File Tests.Welcome to AtCoder sample-2 failed in 00:00:00.0450000. 
WA:{ Parent = { Title = "Welcome to AtCoder"
             FolderName = "practicea"
             Memory = 256
             Timeout = 00:00:02 }
  InFile = /workspaces/test/abs/practicea/test/sample-2.in
  OutFile = /workspaces/test/abs/practicea/test/sample-2.out }

. String actual was shorter than expected, at pos 0 for expected item '4'.
expected:
456 myonmyon

  actual:

   at Utils.generateSampleFileTestCase@68-2.Invoke(StringWriter _arg2) in /workspaces/test/abs/abs.Tests/Utils.fs:line 81
   at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
   at Utils.generateSampleFileTestCase@67-1.Invoke(StreamReader _arg1) in /workspaces/test/abs/abs.Tests/Utils.fs:line 67
   at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
   at Utils.generateSampleFileTestCase@64.Invoke(Unit unitVar) in /workspaces/test/abs/abs.Tests/Utils.fs:line 64 <Expecto>
[03:49:13 ERR] Sample File Tests.Welcome to AtCoder sample-1 failed in 00:00:00. 
WA:{ Parent = { Title = "Welcome to AtCoder"
             FolderName = "practicea"
             Memory = 256
             Timeout = 00:00:02 }
  InFile = /workspaces/test/abs/practicea/test/sample-1.in
  OutFile = /workspaces/test/abs/practicea/test/sample-1.out }

. String actual was shorter than expected, at pos 0 for expected item '6'.
expected:
6 test

  actual:

   at Utils.generateSampleFileTestCase@68-2.Invoke(StringWriter _arg2) in /workspaces/test/abs/abs.Tests/Utils.fs:line 81
   at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
   at Utils.generateSampleFileTestCase@67-1.Invoke(StreamReader _arg1) in /workspaces/test/abs/abs.Tests/Utils.fs:line 67
   at Microsoft.FSharp.Core.Operators.Using[T,TResult](T resource, FSharpFunc`2 action) in D:\a\_work\1\s\src\fsharp\FSharp.Core\prim-types.fs:line 4806
   at Utils.generateSampleFileTestCase@64.Invoke(Unit unitVar) in /workspaces/test/abs/abs.Tests/Utils.fs:line 64 <Expecto>
[03:49:13 INF] EXPECTO! 2 tests run in 00:00:00.1339686 for Sample File Tests – 0 passed, 0 ignored, 2 failed, 0 errored.  <Expecto>

oj tによるテスト

スイッチパラメータ-UseOJで、指定したフォルダにあるプロジェクトをコンパイルしてoj tを実行します。

Test-AtCoder -FolderPath <各問題のフォルダ> -UseOJ

`posh
Test-AtCoder ./abs/practicea/ -UseOJ

  Determining projects to restore...
  Restored /workspaces/test/abs/practicea/practicea.fsproj (in 253 ms).
  practicea -> /workspaces/test/abs/practicea/bin/Release/netcoreapp3.1/ubuntu.18.04-x64/practicea.dll
  practicea -> /workspaces/test/abs/practicea/ojTest/
[INFO] online-judge-tools 11.5.1 (+ online-judge-api-client 10.10.0)
[INFO] 2 cases found

[INFO] sample-1
[INFO] time: 0.047700 sec
[FAILURE] WA
input:
1
2_3
test

output:
(empty)
expected:
6_test


[INFO] sample-2
[INFO] time: 0.056302 sec
[FAILURE] WA
input:
72
128_256
myonmyon

output:
(empty)
expected:
456_myonmyon


[INFO] slowest: 0.056302 sec  (for sample-2)
[INFO] max memory: 23.488000 MB  (for sample-2)
[FAILURE] test failed: 0 AC / 2 cases

問題を解いてもう一度テストしてみます。

let practicea argv =
    let a = stdin.ReadLine() |> int

    let bc =
        stdin.ReadLine().Split ' ' |> Array.sumBy int

    let s = stdin.ReadLine()

    printfn "%i %s" (a + bc) s


[<EntryPoint>]
let main argv =
    practicea argv
    0

テスト合格です。

PS /workspaces/test> Test-AtCoder ./abs/                 
[04:02:22 INF] EXPECTO? Running tests... <Expecto>
[04:02:22 INF] EXPECTO! 2 tests run in 00:00:00.0614051 for Sample File Tests – 2 passed, 0 ignored, 0 failed, 0 errored. Success! <Expecto>
PS /workspaces/test> Test-AtCoder ./abs/practicea/ -UseOJ
  Determining projects to restore...
  Restored /workspaces/test/abs/practicea/practicea.fsproj (in 249 ms).
  practicea -> /workspaces/test/abs/practicea/bin/Release/netcoreapp3.1/ubuntu.18.04-x64/practicea.dll
  practicea -> /workspaces/test/abs/practicea/ojTest/
[INFO] online-judge-tools 11.5.1 (+ online-judge-api-client 10.10.0)
[INFO] 2 cases found

[INFO] sample-1
[INFO] time: 0.087652 sec
[SUCCESS] AC

[INFO] sample-2
[INFO] time: 0.080748 sec
[SUCCESS] AC

[INFO] slowest: 0.087652 sec  (for sample-1)
[INFO] max memory: 32.928000 MB  (for sample-1)
[SUCCESS] test success: 2 cases

解答提出

解答が出来上がったらSubmit-AtCoderTaskで提出します。
acc submitのラッパーです。

Submit-AtCoderTask -FolderPath <各問題のフォルダ>

image.png

課題

  • Powershellモジュールのテストコードを完成させる。
  • GitHub Actionsの改修

終わりに

つい先日、.NET 6、およびF# 6 がリリースされました。

「.NET 6」の最大の魅力は、パフォーマンスの向上だ。プロファイルに基づく動的な最適化(Dynamic Profile-guided Optimization、Dynamic PGO)と呼ばれる技術が採用されており、たとえばTechEmpowerのMVCベンチマークではPGOにより1秒あたりに処理できるリクエスト数が26%(51万→64万)にまで改善されたという。

Atcoderでパフォーマンスの向上したF#6、およびC#10が使える日が早く来ることを願っています。

ということでリクエストしたいのですが......

このコンテストは、言語のアップデートテスト用コンテストです
こちらのスプレッドシートにて募集を行っていた各言語のバージョンアップならびに新規追加した言語のテストを行うためのコンテストです。

リクエストするにはシート1に行を追加すれば良いのでしょうか……?

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1