簡単&便利! C# × .NET Blazor で AHC ビジュアライザ作り
この記事は、競技プログラミングのヒューリスティックコンテスト*1で用いられるビジュアライザを C# と ASP.NET Core Blazor*2 で自作しよう! というものです。
C# が分からない方でもできるよう、全手順を解説しています。また Visual Studio というエディタを使いますが、その使い方も全手順を解説していますので、安心して挑戦してみてください!
今回の記事で制作できるビジュアライザ
対象読者
- 自作ビジュアライザに興味のある方
- C# や .NET 開発をしていて Blazor にも興味のある方
- React や Angular などのSPA*3 技術は知っているが、他の選択肢も気になる方
- Web アプリ制作に興味があるが、サーバサイドとクライアントサイドで別の言語を使うのが億劫だと思っている方
ちなみに「まえがき」と「C# や Blazor とは?」は導入や基本説明なので、読み飛ばして「実際に作ってみる」から始めても構いません。
まえがき
競プロ er の皆さん、ヒューリスティックコンテスト楽しんでますか?
私はヒューリスティック初心者ですが、アルゴ*4とは違う魅力を感じながら、マイペースに参加しています。
得点を伸ばすためのアプローチが多様に存在することや、アルゴに比べれば設計能力などを問われる点がチャレンジングで面白いです。
ビジュアライザ
さて、そんなヒューリスティックコンテスト(特に AHC)では、ビジュアライザが公開され、誰もが利用することができます。
例えば、つい先日開催された AtCoder Heuristic Contest 011 - AtCoder でも公式ビジュアライザが配布され、それによって生成された画像は twitter ハッシュタグで共有され*5、誰もが閲覧することができるようになっていました。
twitter.com
ヒューリスティックコンテスト参加者は、ビジュアライザによって自分のコードの動作を可視化することで、以下のような効果を期待できます。
- 書いたコードが想定通りに動いていることを確認できる
- 試しに色々と動かしてみることで考察の参考になる
- やったー動いた! 楽しい! という気持ちが生まれる
コンテスト中は、得点を伸ばすためのアプローチが全く浮かばず、何をどうしていいのやら途方に暮れる、そんな苦悩の時間も長いです。そんな時、ビジュアライザから得られる情報が大きなインスピレーションをくれることがあります。逆に「あんな動作や、こんな動作も可視化できたら手掛かりがつかめるのに!」とフラストレーションを感じたりもします。
自作ビジュアライザ
さて、そんな大事なビジュアライザですが、中にはコンテスト中に自作するという人がいます。
公式ビジュアライザが提供されているのに、なぜ自分で作るんでしょうか?
ビジュアライザを自作するメリットは、大別すれば以下の 2 つだと思います。
- 自己満足(単純に作るのが楽しい、自作に憧れていたなど)
- 得点を伸ばすのに役立つ(公式ビジュアライザでは手に入らない情報を得たり、表示法を変えて分かりやすくするなど)
逆にデメリットとなり得るのは以下の点です。
- ビジュアライザ制作に時間を取られる
これらのメリットとデメリットを踏まえて、今回は
- Blazor は欲しい情報を柔軟に、時間をかけずに可視化できる
というところを見せて、C# や Blazor を布教していきたいと思います!
GitHub リポジトリ
今回の記事で作成するビジュアライザの完成品は GitHub リポジトリとしてアップロードしてあります。途中で詰まった際など、適宜参考にしてください。
github.com
C# や Blazor とは?
今回のメインテーマは Blazor ですが、関連性の深い C# 及び .NET についても簡単に説明します。
C#
C# とは、Microsoft が開発しているプログラミング言語です。
Python や JavaScript などのインタプリタ言語ではなくコンパイラ言語ですが、Microsoft の Visual Studio というソフトを利用することで Windows でも簡単に環境構築できます。
わりと新しめの言語であり、汎用性も高い*6ため、「競プロだけでなく色々開発してみたいけど、いくつも言語を学ぶのは時間がかかって大変そう」という方におすすめできます。
かく言う私も IT と全然関係の無い仕事をしている社会人(労働行政分野の公務員)で、日曜プログラマなので、1 つの言語で何でも作れる C# には非常に助けられています。
.NET
詳しくは説明しませんが、.NET とは Microsoft が主導しているクロスプラットフォームな開発者用プラットフォームです。つまりは .NET に基づいてアプリケーションやゲームを作れば Windows でも Linux でも Mac でも Android でも簡単に動かせるよ、的なことです。そして C# や Blazor は .NET の上に乗っかっています。
まあ、Microsoft は .NET という大きな世界を作っていて、その中に C# や 後述の Blazor も入ってるんだなと思ってもらえれば十分です。
Blazor
Blazor もしくは ASP.NET Core Blazor とは、Web アプリケーション*7を作る・動かすためのフレームワークの一つです。ついでに言うと SPA(シングルページアプリケーション) を開発できるフレームワークでもあります。
Web アプリケーションフレームワークには React, Django, Ruby on Rails など色々ありますが、Blazor には非常に珍しい特徴があります。
Web アプリ(特に SPA)を制作していない方は「同じ言語で書けるから何? 普通じゃないの?」と思ったかもしれませんが、これはなかなかすごいんです。
というのも、通常クライアントサイドのロジック、つまりブラウザ上に表示される要素の動作やユーザの対話(例えば「↑」ボタンをクリックしたらリアルタイムで画像が動くとか)は、通常サーバサイドのロジックとは別の言語(具体的には JavaScript)で書かれるものだからです。
Blazor ではどちらも C# を使って書くので、両方で同じクラスやメソッドを使い回すことができます。
また、JavaScript などをわざわざ新しく勉強する必要もありません。
私の場合、Unity で制作しているタイピングゲームの C# クラスをランキング Web アプリにそのまま利用したりしています。なんて便利!
ビジュアライザ向きな Blazor
C# 競プロ er にとって、Blazor でビジュアライザを作るのは非常に簡単でメリットが大きいです。
- 提出用に書いたクラスやメソッドをそのままビジュアライザに流用できる!
とにかくこれが最高! 提出用コード内の C# メソッドを HTML ファイル*9から呼び出せてしまいます。例えとして、以下の HTML コードを見てください。
<table> <tbody> @foreach (var cc in solver.GetCCS(N, state)) { <tr> @foreach (var idx in cc) { <td>@idx</td> } </tr> } </tbody> </table>
このコードは、自作 Solver クラスを HTML ファイル内でインスタンス化し、Solver.GetCCS() という自作メソッド(グラフ内の連結成分の配列を得る)を呼び出し、foreach 文を用いて一覧表示しています。コンテスト中、自作の点数評価メソッドのデバッグするために表示しました。
この例だけでも、かなり柔軟かつ手間無く書けることが分かってもらえるのではないでしょうか?
- SPA なのでリアルタイム表示・対話的表示が簡単
またビジュアライザではリアルタイム的な表示や対話的な表示の実装が必要になります。
例えば 50ms おきにターンを進めて描画するとか、スクロールバーを動かす毎に表示を切り替えるといったことですね。
これらの動作を JavaScript で書くとなると慣れない人間にはハードルが高いですが、Blazor なら C# で簡単に書くことができます。
- ホットリロードで効率的に開発できる!
多少の変更であれば、再度コンパイルせずとも即座に表示が更新されます。
表示の仕方を色々と試行錯誤することになるビジュアライザ制作にはとても便利です。
実際に作ってみる
前置きはこのぐらいにして、作り方の概略を紹介していきます。
C# のコードが随所に出てきますが、C# を書いたことがない方も「こんな風に作れるんだな~」と思いながらユルく読んでいただければ幸いです。
手順の概略
- 環境構築(Visual Studio のインストール)
- ソリューションと本体プロジェクトの作成
- Blazor プロジェクトの追加
- 試しに実行してみる
- Blazor の基本と「コンポーネント」
- 盤面表示&移動ボタンの実装
- できあがり!
環境構築(Visual Studio のインストール)
Microsoft 公式サイトから Visual Studio 2022 をインストールしてください。(Visual Studio 2019 でも恐らく大丈夫です。)
以下のような追加の機能(ワークロード)のインストール画面が出てきたら、下記の 2 機能にチェックを入れてインストールしてください。
- ASP.NET と Web 開発
- .NET デスクトップ開発
ちなみに Visual Studio Code でも開発できなくはないですが、Visual Studio を使っておくのが最も楽だと思います。私も元々 VS Code ユーザでしたが、C# や Blazor 開発は Visual Studio を使っています。
ソリューションと本体プロジェクトの作成
次は、ソリューション*10と、提出の本体となるプロジェクトを作成しましょう。
ソリューションの中には複数のプロジェクトを入れることができ、プロジェクト間は(適切に設定すれば)相互に参照し合うことができます。
起動後の画面で「新しいプロジェクトの作成」を選びます。
その後は、「コンソールアプリ」を選び、適当なプロジェクト名(例えば AHC011 など*11)を設定して、表示通りに進めてください。
(重要!)下の選択肢では .NET 6.0 ではなく .NET Core 3.1 を選んでください。AtCoder のジャッジ環境は .NET Core 3.1 なので .NET 6.0 の機能を用いてしまうとジャッジに通りません。
プロジェクトが無事作成されると、ソリューションエクスプローラーという部分にこのような画面が表示されます。これが先ほど作成したプロジェクトです。
通常はこの後 Program.cs に提出コードを書いていくわけですが、今回は出来上がった提出コードを用意しました。
こちらのファイルの内容(GitHub リポジトリへのリンク)を Program.cs にそのまま貼り付けて上書き保存(Ctrl+S)してください。
「せっかくだから実際に自分で書きながら進めてみたいな」という方は、以下の記事(最低限の書き方が分かります)などをどうぞ。
Blazor プロジェクトの追加
今度は本題の Blazor プロジェクトを作成します。
先ほど作成したソリューションを開いたままで、ファイル→新規作成→プロジェクト(Ctrl+Shift+N)を実行し、
プロジェクトテンプレート選択画面で Blazor と入力して検索し、Blazor Server アプリを選択して「次へ」を押します。
(ちなみに Blazor には Blazor WebAssembly と Blazor Server という 2 種類あります。両者の違いは C# で書かれたクライアントサイドのコードをブラウザ上で実行する(WebAssembly)かサーバ上で実行する(Server)かという点ですが、パフォーマンスを真剣に考える場合でない限りどちらでも大して変わりません。ただ、両者でファイル構成やコードに差異が出てくる部分があるため、今回は Blazor Server を使用して頂ければと思います。)
新規プロジェクトの基本設定をします。プロジェクト名は何でもいいですが、今回は分かりやすく "AHC011Visualiser" とでもしておきましょう。
ソリューション(S) の欄は「新しいソリューションを作成する」から「ソリューションに追加」に変えてください。こうすることで、先ほど作成したの中に Blazor プロジェクトが配置され、本体プロジェクトから Blazor プロジェクトへを参照しやすくなります。場所(L) の欄は変更する必要はありません。
追加情報を設定します。フレームワークはどれでもいいのですが、最新の .NET 6.0 を選択するのが良いでしょう。
最初の本体プロジェクト作成では .NET Core 3.1 を選びましたが、今回は .NET 6.0 を選びました。今回の選択は Blazor プロジェクトを動作させるための .NET(Blazor や C# を動かす土台)のバージョンを指定するものです。.NET Core 3.1 はホットリロード*12に対応していないため .NET 6.0 の方が便利です。「本体プロジェクトと .NET のバージョンが食い違うけどいいの?」と心配になるかもしれませんが、問題ありません。プロジェクト毎にバージョンが異なっても構わないですし、.NET 6.0 非対応の AtCoder にビジュアライザを提出するわけではないことが理由です。なお注意点として、例えば .NET Core 3.1 で作成したのに .NET 6.0 以降にしかない機能を使おうとしても動かないので、メソッドや機能を検索する際には「今回使っている .NET バージョンの情報」を探すよう意識しておく必要があります。
試しに実行してみる
Blazor プロジェクトが作成できました。この時点で Web アプリとして表示できるテンプレートが備わっているので、試しにデバッグ実行をしてみましょう。
Visual Studio の画面上部に、下の画像のような部分を探してください。黄色線の部分を参考に Debug, AHC011Visualiser(あなたが設定した Blazor プロジェクト名)に切り替え*13。そしてすぐ右側にある緑三角(赤線部分)をクリックします。
(設定により、一部のボタンが消えている場合があります。その場合は、これらのボタンが表示されている行の一番右側にある下向きの矢印ボタンから、ツールバーの表示/非表示を切り替えることで表示できます。)
実行すると自動的にブラウザが立ち上がり、このように Blazor プロジェクトのテンプレートページが表示されます。
簡単にページテンプレートを生成することができました。このページを改造してビジュアライザを作っていきます。
Blazor の基本と「コンポーネント」
次は Blazor を使うためのポイントとテンプレートの構成を説明しつつ、制作の下準備を進めていきます。
ソリューションエクスプローラー(右側に表示されています。無ければ「表示(V)」から切り替え可能)で Blazor プロジェクトを開き、Pages ディレクトリ内の Index.razor をダブルクリックして開いてください。
(このような *.razor 拡張子*14のファイルは Razor ファイルと呼ばれます。
Razor ファイルは「C# や Razor 構文と呼ばれる独自の記法を用いて色んなことができる、拡張版の HTML ファイル」と思ってもらえれば良いです。)
Index.razor の中身は以下のとおりですが、内容の簡素さに驚くのではないでしょうか。
Blazor では拡張性を高めるために 1 つのページをいくつかの部品に分けて管理しています。例えば head タグや body タグ、他にもページのデザインを記述する CSS などは Pages/_Layout.cshtml などに分割して記述されています。それらのファイル構成の詳細説明は割愛します。
さて、Index.razor の中で気になるのは SurveyPrompt という見慣れないタグです。こんな HTML タグは通常ありません。これは Blazor が用意している(もしくは自分で用意する)特殊なタグで、「コンポーネント」と呼ばれています。
コンポーネントの正体を知るために、SurveyPrompt の本体を探してみましょう。ソリューションエクスプローラーから Shared/SurveyPrompt.razor というファイルを見つけ、表示してください。
SurveyPrompt.razor
これが SurveyPrompt コンポーネントの本体です。内容を読むと、先ほどのデバッグ実行で表示された以下の部分であることが分かります。
このように *.razor ファイルは HTML を構成するための部品(コンポーネント)として使うことができます。コンポーネントの使い方はページ本体となる *.razor ファイル内で HTML タグのように記述するだけです。*15
この SurveyPrompt.razor コンポーネントを修正しながら、Blazor の仕組みに慣れていきましょう。
まずはコード内の @code{ ... } という部分に注目してください。@code ブロック内には C# のコードを記述できるようになっています。
@code ブロック内を以下のように書き換えてください。*16
SurveyPrompt.razor 変更前
[Parameter] public string? Title { get; set; }
SurveyPrompt.razor 変更後([Parameter] 属性を消し、変数 Title に初期値を設定する)
public string Title = "AHC011 Visualiser with Blazor";
ついでに、下の画像で選択している部分は要らないので消してしまいましょう。
次に Index.razor を以下のように書き換えます。
Index.razor 変更前
<SurveyPrompt Title="How is Blazor working for you?" />
Index.razor 変更後
<SurveyPrompt />
書き換えたら Ctrl+S で上書き保存(Index.razor と SurveyPrompt.razor 両方を上書き保存)し、再度デバッグ実行を行ってください。(実行中のまま閉じていなければ、上書き保存後にホットリロードで自動的に書き換わるはずですが、うまく書き換わらなければ一度テンプレートページを閉じて再度デバッグ実行を行ってください。)
実行すると、以下のように表示が変更されているはずです。
このように、以下の手順で簡単に C# と HTML を結びつけられます。
- @code ブロック内で変数やクラスなどを定義
- HTML 内に @変数名 や @クラス名.メンバ名 などと記述
ちなみにコンポーネント名はソリューションエクスプローラーから自由に変更できます(ファイル名が自動的にコンポーネント名として解釈されます)。今回は適当に Visualiser01.razor としてみました。コンポーネント名を変更した場合は、もちろん Index.razor 内のコンポーネント呼び出し部分も SurveyPrompt → Visualiser01 に書き換えておいて下さい。
次に、ビジュアライザ制作の下準備を兼ねて、以下のようなボタンを配置してみることにしましょう。
Visualiser01.razor の div タグ内
<table> <tbody> <tr> <td></td> <td><input type="button" value="U" @onclick="MoveUp" /></td> <td></td> </tr> <tr> <td><input type="button" value="L" @onclick="MoveLeft" /></td> <td></td> <td><input type="button" value="R" @onclick="MoveRight" /></td> </tr> <tr> <td></td> <td><input type="button" value="D" @onclick="MoveDown" /></td> <td></td> </tr> </tbody> </table> <input type="text" @bind="UserName" /> <p>@ControllerMessage</p>
これでボタンを配置できました。このボタンは後々パズル操作用のコントローラにしますが、とりあえずメソッドの呼び出しや変数の操作を試してみましょう。@code 内に以下の記述を追加してください。(null 参照に関する警告が表示されると思いますが、今回は無視してください。)
public string? UserName = "Your Name"; public string? ControllerMessage; public void MoveUp() { ControllerMessage = UserName.ToString() + " pushed U button."; } public void MoveLeft() { ControllerMessage = UserName.ToString() + " pushed L button."; } public void MoveRight() { ControllerMessage = UserName.ToString() + " pushed R button."; } public void MoveDown() { ControllerMessage = UserName.ToString() + " pushed D button."; }
再度デバッグ実行をしてみてください。以下のような画面が表示されるはずです。
次に、テキストボックスにあなたの名前を入れて、適当なボタンをクリックしてみて下さい。入力された内容が反映されたメッセージが表示されるはずです。
これは @bind="UserName" によって @code 内の UserName 変数の値がテキストボックスの値と結びつけ(bind)られ、結果として MoveHoge() メソッドの実行結果が変わったことを示しています。
// この @bind によってテキストボックスと C# の変数が結びついた <input type="text" @bind="UserName" />
……どうでしょう? とても簡単に HTML と C# が結びつきました。以下のような関係になっているわけですね。
@bind による HTML と C# の結びつき
このように Blazor では、入力可能な HTML コンポーネント(テキストボックスやテキストエリア、レンジスライダーなど)に @bind="変数名" を加えるだけで、対話的な動作が実現できてしまうのです*17。とても便利ですし、記法がこの上なく直感的なところも好印象です。
これで Blazor の基本のキは終わりです。次からが本番です!
盤面表示&移動ボタンの実装
それでは、ビジュアライザを本格的に作成していきましょう。
- 本体プロジェクトへの参照を追加
まずは、Blazor プロジェクトから本体プロジェクトへの参照を追加します。「参照を追加する」とは、大雑把に言えば、「あるプロジェクトから別のプロジェクトのクラスやメソッドを呼び出せるようにする」ことを指します。
ソリューションエクスプローラー上で Blazor プロジェクトを右クリックして「追加」→「プロジェクト参照」と進み……
本体プロジェクトの名称にチェックを入れて OK をクリックします。
これで Blazor プロジェクトから本体プロジェクトを参照できるようになりました。ここで注意してほしいのは、まだ参照できるようになっただけで、実際に参照してはいないということです。この状態でクラスにアクセスしようとしても、エラーが発生します。イメージとしては「国境が開放されただけで、道路が開通していない」といったところでしょうか?
ということで、Blazor プロジェクト内の Visualiser01.razor の先頭行に以下のコードを追加します。AHC011WithB の部分は本体プロジェクトの名前です。違う名前を設定した方はその名前に変更してください。
@using AHC011WithB
これで Visualiser01.razor から本体プロジェクト AHC011WithB 内の public なクラスや変数にアクセスする準備が整いました。
一応、本体プロジェクト Program.cs 内の構成を簡単に説明しておきます。
- Solver クラス
私の場合、AtCoder で問題を解く場合はいつも Solver という名前のクラスを作成して利用しています。このようなクラスを作ることは必須ではないですが、色々カスタマイズしやすくなるため利用しています。
- Solver.BoardState クラス
盤面の状態を管理するためのクラス。インスタンス名[i, j] で盤面の i 行 j 列にアクセスできます。*18
- Solver.CanMoveHoge(N, state) 及び .MoveHoge(N, state)
盤面サイズと盤面状態を引数として渡し、移動可能か否かを返すメソッドと、移動を実行する(state の状態を変化させる)メソッドです。(C++ ユーザーの方などは「この渡し方では state の deepcopy が変更されるだけで、state 自体は変更されないのでは?」と思うかもしれませんが、C# ではこの渡し方で state が保持する値を変更することができます。)
ページに HTML オブジェクトを配置し、動作を実装していきましょう。
- 盤面用アイコンをプロジェクトに追加
公式ビジュアライザのアイコンを掲載するのは著作権法上の問題があるので、ここでは自作アイコン画像を使います。私の推し動物であるチーターのアイコンを作成しましたのでご利用ください(個人利用なら公式ビジュアライザの画像を流用してもいいかもしれませんが、自己責任でお願いします。)
1. icons.zip をダウンロードして解凍
2. wwwroot ディレクトリを右クリックして新しいフォルダを追加(フォルダ名は icons としてください)
3. ソリューションエクスプローラーに icons フォルダが表示されるので、そこへ先ほど解凍した画像 16 枚をドラッグ&ドロップ
これで、先ほどの画像が Blazor プロジェクトに含まれました。このように wwwroot 内に格納したファイルは静的ファイルとして配信されます。例えば wwwroot/icons/0.png なら http(s)://デプロイしたアプリのURL/icons/0.png でアクセスできるようになります。
- (C# 側)盤面を管理するための変数等を定義
以下のコードを Visualiser01.razor の @code ブロック内に記述してください。
Visualiser01.razor の @code ブロック
Solver? solver; Solver.BoardState? state; public int N = 6; string inputString = "62ce43\na068f9\na89da9\n5d93cb\n276253\n424ba8";
- (HTML 側)盤面を表示するためのテーブル
以下のコードを Visualiser01.razor の div タグ内に記述してください。このように、for 文や foreach 文を用いて反復的に HTML オブジェクトを描画できます。便利なのでよく使います。ちなみに if (state != null) としているのは、null 参照エラー(ページ初回表示の際に state == null なのに state[i, j] を参照してしまうこと)を避けるためです。
Visualiser01.razor の div タグ内
@* Visualized table *@ @if (state != null) { <table> <tbody> @for (int i = 0; i < N; i++) { <tr> @for (int j = 0; j < N; j++) { @* @(state[i, j]) は 0 ~ 15 (盤面の状態)に変換される *@ <td><img src="icons/@(state[i, j]).png" width="64" height="64" /></td> } </tr> } </tbody> </table> }
- (HTML 側)入力受け取りなどの UI 作成
@bind や @onclick を使って @code ブロック内の変数やメソッドと結び付けます。先ほどと同様 div タグ内に記述してください。ちなみに N を入力するためのテキストボックスを追加したのは、入力文字列の処理を簡単にするためです。
N: <input type="text" @bind="@N" size="2" /><br /> <textarea @bind="@inputString" style="width: 200px; height: 250px;" /><br /> <input type="button" @onclick="Visualise" value="Visualise!" /><br />
- (C# 側)入力を受け取って盤面を生成するメソッド
以下 2 種類のコードを Visualiser01.razor に記述してください。2 個目のコードは少し長いですが、テキストエリアの入力を受け取って、盤面(state)を生成しているだけです。
Visualiser01.razor 先頭行(16 進数 -> 10 進数変換メソッドを呼び出すために必要)
@using System.Globalization
Visualiser01.razor @code ブロック内
/// <summary> /// inputString を入力として受け取り、Solver.BoardState state(盤面状態)を生成する /// </summary> void Visualise() { if (inputString != null) { solver = new Solver(); state = new Solver.BoardState(N); // 入力受け取り string[] lines = inputString.Replace("\r\n", "\n").Split('\n'); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { state[i, j] = int.Parse(lines[i][j].ToString(), NumberStyles.HexNumber); } } } }
- (C# 側)上下左右への移動を行うメソッド
Visualiser01.razor @code ブロック内の 4 つの移動メソッド(MoveUp|Right|Left|Down)を以下のように変更してください。Up の部分は Right|Left|Down にそれぞれ修正してください。
Visualiser01.razor @code ブロック内
public void MoveUp() { if (solver.CanMoveUp(N, state)) { solver.MoveUp(N, state); ControllerMessage = ""; } else { ControllerMessage = "can't move"; } } 以下 MoveLeft(), MoveRight(), MoveDown() は略
- 前の章で使ったテスト部分の削除
余計なもの(以下の 2 行)を削除しておきましょう。面倒なら放っておいても構いません。
<input type="text" @bind="UserName" />
public string? UserName = "Your Name";
おわりに
お疲れ様でした! Blazor 体験はいかがでしたか?
- 提出コードの C# メソッド・クラスをそのまま参照して、同じソリューション内で利用できる
という Blazor のメリットを活かして、本体プロジェクト以外には大したコードを書かずにビジュアライザを作成することができました。
Blazor に慣れた C# プログラマにとっては、これらの手順は(アイコン作成は別として)10 分ほどで終わります。つまり、僅かな時間と手間をかけるだけで、以下の 3 つをビジュアルで分かりやすくデバッグできるということです!
・盤面を管理する BoardState クラス
・ある盤面状態から移動処理を行うメソッド
・移動が可能かどうか判別するメソッド
テストプロジェクトの作成や printf デバッグの手間が減り、より設計やコーディング、問題考察に集中できますね! また、変数と表示が直接結びついており処理過程を段階的に表示することが容易なので、バグ箇所の特定にかなり効果的だと思います。
今回の記事をきっかけに、競プロ er の皆さんにゲーム、デスクトップ・スマホアプリ、Web アプリ、競プロと何でもできる C# と .NET の世界に興味を持っていただければ幸いです!
(付録)この後のカスタマイズ例
今回は入門記事ということで、ごく基本的な機能だけを持つビジュアライザを作成しましたが、実際のコンテスト中にはもう少し本格的なビジュアライザを制作していました。
コードが汚かったり色々とマズいところがあるので、それらを整理してから、カスタマイズ例として掲載する予定です。
こんな感じで、コンテスト中に欲しい情報を雑に追加していったため、とてもじゃないけどぱぱっとアップロードできるコードではないのです
(付録)実装 Tips
余裕があったら追加します
追加したいもの
- 非同期処理(メソッドの途中での HTML 再描画)
(付録)参考リンク集
余裕があったら追加していきます
*1:ヒューリスティックコンテストって何? と思った方へは、入門記事として 競プロ〜ヒューリスティック/マラソン事始め〜 - Qiita をおすすめします。
*2:後述しますが、サーバサイドだけでなくクライアントサイドのロジックまで C# で記述することができる Web アプリケーションフレームワークです。
*3:シングルページアプリケーション。単一のページで構成され、ページ遷移やページ更新を挟まず対話的に動作する。
*4:いわゆる一般的な競プロ問題のこと。ABC や AGC など、AC さえすれば回答時間以外に得点差がつかないのが一般的。
*5:コンテスト中はアップロードできる画像の種類に制限があるので注意
*6:Microsoft が強力に後押ししているため、コンソールアプリ、ゲーム、Windows アプリ、Web アプリケーションなど様々な場面で使われており、Unity という超有名ゲーム開発プラットフォームでも採用されています。C# で作れないものは少ないです。
*7:Web アプリケーションとは、Web 上で動き、ブラウザを通して利用できるアプリケーションのことと思ってもらえばいいです。AtCoder や AtCoder Problems、他にも銀行のアプリなどなど。
*8:過去の私と同様「コンパイラ言語の C# でクライアントサイドを書けるわけが無いでしょ!」と思うかもしれません。大丈夫です。Blazor には Blazor Server と Blazor WebAssembly という 2 つの種類があり、それぞれ違うやり方でクライアントサイドでの C# 実行(のような挙動)を実現しています。
*9:正確には Razor ファイルです。後述します。
*10:私も正確に理解できていない気がしますが、まあ 1 つの大きなアプリケーションを指していると捉えればいいはずです。
*11:今回の記事に掲載する手順のスクリーンショットでは、プロジェクト名が AHC011 になっているものと AHC011WithB になっているものが混在しています。これは元々 AHC011 で作成していたものをブログ執筆用に AHC011WithB で作り直したためです。表示名は違いますが、同じものだと思ってお読み下さい。
*12:デバッグ実行中にコードを上書き保存すると、軽微な変更ならリアルタイムに適応してくれる機能です。つまりデバッグ実行を再起動する手間が減ります。
*13:この設定の意味は、文字通り「Debug モードで AHC011Visualiser プロジェクトを起動する」という意味の設定です。右側の選択部分を AHC011 などの本体プロジェクト名に切り替えれば、本体プロジェクトの方をデバッグ実行することができます。
*14:他にも *.cshtml という拡張子もほぼ同じもので、これも Razor ファイルと呼ばれます。
*15:フォルダを整理するため、コンポーネントな各ページで共有する部品は Shared フォルダに、ページ本体は Pages フォルダに保存するのが良いと思います。
*16:テンプレートではIndex.razor からページタイトルをプロパティとして受け取るようになっていましたが、その仕様は必要無いので削除します。
*17:ちなみに今回はボタンクリックを契機として自動でページが描画された(メッセージが表示された)わけですが、これは Blazor 側が「あ、ボタンが押されたな。値が変更されたかも」と分かって気を利かせて更新してくれたからです。Blazor 側が値の変更に気付かない場合(例えばボタンなどユーザの操作は無く、単にメソッド内で値が変更されただけなど)は StateHasChanged() というメソッドを呼び出して Blazor に変更を伝える必要があります。またメソッドの途中で描画を変更させたい場合は非同期処理(async, await など)を入れる必要が出てくることもあります。その辺りは今回割愛させてください。
*18:C# のインデックスプロパティという機能を利用して、N*N サイズの 1 次元配列である Placement に "インスタンス名[i, j]" でアクセスできるようになっています。
*19:上の画像では見栄えを良くするために Table タグを追加しています。気になる方はこの Visualiser01.razor を参考に HTML を整えてみてください。