【Xamarin.Forms】数独解くやつにUIつけてみた
前回↓の続きです。
※WordPress用のブログカードあったんだね…
まぁ御託はさておき先日の数独解法アルゴリズムをC#に移植してついでにUIもつけてみましたよ、というお話です。
作ったもの
Demo
見た目的にはまぁ何のひねりもないですが、強いて言えばPCのテーマ設定に合わせてライト/ダークモードに切り替わる機能を備えているはずです。
速度がPython版より速くなったように見えるのは気のせい。多分Pythonが実行時コンパイルなのでそう感じると思います。
Xamarin.Forms
こう言ってしまうと「それだけかい」ってなってしまうと思うので今回の工夫したところ。
一見WPFと何ら変わらないように見えますが、今回はUIフレームワークとしてXamarin.Formsを採用しました。
Xamarin.Formsを使う最大の利点。それは、↑で動いているのと全く同じコードで、↓が動くということです。
見た目はやや不格好ですが(ボタン見切れてるしw)皆さんお馴染みのAndroidですね。
Xamarinはクロスプラットフォーム開発環境の一つで、iOSとAndroidのネイティブAPIを.NET経由で呼び出すことができます。
つまり、ビジネスロジックのコードに関してはプラットフォームごとの改修を必要とせず、共通化することができます。逆にPCL(Portable Class Library…現在非推奨らしい)や.NET Standardライブラリを使うことによってiOS、Androidアプリ上で.NETネイティブなAPIを呼び出すこともできるのです。
なので、開発者はプラットフォーム固有の機能、主にはUI層にフォーカスしてゴリゴリ書けばよいことになります。
Xamarin.Formsはこれに加えて、プラットフォームにある程度共通するUIコントロールをXAMLで扱えるようにしたUIフレームワークです。
つまり、シンプルなアプリケーションであれば、ビジネスロジックから見た目まで徹頭徹尾同じコードで動作してしまうのです。
現在のVisual Studio 2019でXamarin.Formsプロジェクトを作成すると、共有プロジェクト、Android用プロジェクト、iOS用プロジェクト、UWP用プロジェクトの4つが生成されますが、今回の数独アプリでは共有プロジェクト以外にはほとんど手を加えていません。それでもここまで動くものがひよっこエンジニアに作れてしまうのですからこれはもう驚天動地以外の何者でもありません。
苦労話
せっかくの機会なので普段WPFを使っている私が今回初めてXamarinを弄ってみて難しいなと思ったところ、ハマったところを幾つかご紹介したいと思います。
MVVMフレームワークがなかった件
「Xamarinには標準でMVVMフレームワークが搭載されてるよっ」みたいな話を小耳に挟んだことはあったのでそんなに心配はしていなかったのですが、今回テンプレートなしの空プロジェクトから作成したため、初めからViewとかViewModelとかいう決め事がありませんでした。
まぁそれ自体はフォルダ分けをしてApp.xaml書き換えれば済む話なのでいいのですが、ViewModelに変更通知を実装するクラス――いわゆるViewModelBaseから作り込む必要があったのはちょっと煩わしかったかなぁ、と。
DeepCopyクソ重すぎ問題
Windowsネイティブ、つまりC++が使えないということで一番大変だったのが数独盤面オブジェクトのコピーです。
前回のロジックを極力そのまま移植したかったため、探索の “戻り” 処理がある関係上オブジェクトコピーが必須だったのですが、これが普通にやるとまぁ重いの何のって。某数独サイトの初級クラスの問題を解くので精一杯というw
QiiナントカとかStackナントカを漁っていると割と出てくるのが「オブジェクトを一度JSONにシリアライズしてからデシリアライズせよ」というものでしたがこれでも解決には至りませんでした。
結局、.NET Standardのバージョンを2.0.3まで上げると使えるようになったBinaryFormatterというのを拡張メソッドに実装することにしました。あまり深追いできないのですが、多分メモリアドレスを指定してオブジェクトのサイズ確保して丸ごとコピーとかそういうことしてる。
2次元配列バインド
今回一番大変だったのがこれです。
前述の通りC++の移植元で二次元配列を使っており、これをXAMLにバインドせなあかんかったのですが、どうもMultiConverterやらでは上手くいかず、ConvertBackのやり方も意味不明でした。頭がこんがらがってきて本当に投げようかとも思いました。
これも色々調べた結果「配列の文字列でバインドすればいいじゃん」とのたまう天才がいらっしゃいまして、二次元配列そのものの型をバインド可能なものに置き換えることで解決できました。
なお、バインド先は普通のTextBox (Entry) ですゆえ、バインドソースに配列のインデックスがそのまま直書きされてる残念無念な作り込みですが、今回は目をつむることにします。つむらせて。( ˘ω˘ )スヤァ…
今後の展望
数独の問題をカメラで撮ってその画像をAzure Cognitive Serivices辺りで認識させて瞬時に解く、みたいなのをやりたいです。やる気と技術力が間に合えば。年内くらい目標で。